/* ==========================================================================
   PUDL, the Pleasantly Usable Design Language (pronounced "puddle").
   Version 0.107.1.

   PUDL gives every affordance one visual representation, so that somebody
   who has learned it once can use any application built with it on sight.

   Elevation signals interactivity:
     - Raised, with a hairline border and an inset highlight, is pressable (.btn).
     - Sunken, with an inset shadow, is an input (.form-input).
     - Flat is display only (.kv-table cells, .form-input[readonly]).
   Colour is never the only signal: every meaningful state carries a glyph.
   One .btn-primary per visible context.

   State is marked with the ARIA attribute that states it, so the eye and
   assistive technology are told the same thing: aria-current for where the
   reader is, aria-pressed and aria-checked for toggles, aria-selected for
   the tab of a tab panel. The older .active class still works as an alias.

   A project themes PUDL with a stylesheet loaded after this one. It may
   change any colour token, including the background and the topbar, and
   the font tokens, within the restrictions in the README. It never changes
   what a raised, sunken or flat surface means.
   ========================================================================== */

/* BEGIN INTER: generated by build/fonts.py. */
/* Inter by Rasmus Andersson, SIL OFL 1.1 (fonts/Inter-LICENSE.txt).
   Script subsets keep weights 400 to 900, the optical size of text, and
   the OpenType features CSS asks for by name; pudl-fonts-full.css has the rest. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-rest.woff2") format("woff2");
  unicode-range: U+02CD, U+02D8-02D9, U+02DB, U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+0E3F, U+1DC0-1DF5, U+1DFC-1DFF, U+2070-2071, U+2074-208E, U+2090-209C, U+20DB-20DE, U+20E8, U+20F0, U+2100-2101, U+2103, U+2105-2106, U+2109, U+2117, U+211E-2121, U+2126, U+212A-212B, U+212E, U+2132, U+213B, U+214D, U+2150-217F, U+2183-2186, U+2189, U+2202, U+2205-2206, U+220F, U+2211, U+221A, U+221E, U+222B, U+2236, U+2248, U+2260, U+2264-2265, U+2295-2298, U+2303-2305, U+2318, U+2325-2327, U+232B, U+2380, U+2387, U+238B, U+23CE-23CF, U+2423, U+2460-2468, U+24B6-24CF, U+24EA, U+25A0-25A2, U+25AA, U+25B2-25B3, U+25B6-25B7, U+25BA-25BD, U+25C0-25C1, U+25C4-25C7, U+25CA-25CB, U+25CF, U+25E6, U+25EF, U+2600, U+2605-2606, U+263C, U+2661, U+2665, U+266A-266B, U+26A0, U+2713, U+2717, U+2756, U+2764, U+2780-2788, U+27EF, U+27F5-27FA, U+2913, U+2A38, U+2B06, U+2B12-2B13, U+2B1C, U+2B24, U+2DFF, U+2E18, U+A69F, U+A92E, U+E000, U+E002-E05E, U+E06A-E0BD, U+E0C8-E0CC, U+E0DC-E0E6, U+E0F3-E0F5, U+E106, U+E109-E10A, U+E10C-E10F, U+E111-E113, U+E117-E118, U+E121-E122, U+E124, U+E12A-E15E, U+E163, U+E1C3, U+E1D2-E1DF, U+E1E1-E2DC, U+EE01-EE07, U+EE09-EE0A, U+EE0C-EE12, U+EE14, U+EE17, U+EE1D-EE45, U+EE47-EE84, U+EE87-EED6, U+EED8-EEE1, U+F6C3, U+1F12F-1F149, U+1F16A-1F16B, U+1F850, U+1F852;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0460-0479, U+0480-049D, U+04A0-04FF, U+052F, U+1DC0-1DF5, U+1DFC-1DFF, U+20B4, U+20DB-20DE, U+20E8, U+20F0, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-cyrillic.woff2") format("woff2");
  unicode-range: U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0400-045F, U+0483-0489, U+0490-0491, U+04B0-04B1, U+1DC0-1DF5, U+1DFC-1DFF, U+20DB-20DE, U+20E8, U+20F0, U+2116, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-greek-ext.woff2") format("woff2");
  unicode-range: U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+1DC0-1DF5, U+1DFC-1DFF, U+1F00-1F15, U+1F18-1F1D, U+1F20-1F45, U+1F48-1F4D, U+1F50-1F57, U+1F59, U+1F5B, U+1F5D, U+1F5F-1F7D, U+1F80-1FB4, U+1FB6-1FC4, U+1FC6-1FD3, U+1FD6-1FDB, U+1FDD-1FEF, U+1FF2-1FF4, U+1FF6-1FFE, U+20DB-20DE, U+20E8, U+20F0, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-greek.woff2") format("woff2");
  unicode-range: U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0374-0376, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03D7, U+03DC-03DD, U+03F0-03F6, U+03F9-03FA, U+03FC-03FF, U+0483-0489, U+1DC0-1DF5, U+1DFC-1DFF, U+20DB-20DE, U+20E8, U+20F0, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-vietnamese.woff2") format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+1DC0-1DF5, U+1DFC-1DFF, U+1EA0-1EF9, U+20AB, U+20DB-20DE, U+20E8, U+20F0, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-0148, U+014A-01C3, U+01C5-0254, U+0256-027B, U+027E-0284, U+0286-0290, U+0292-02A4, U+02A6-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+1D00, U+1D0D, U+1D1B, U+1D43, U+1D47-1D49, U+1D4D, U+1D4F-1D50, U+1D52, U+1D56-1D58, U+1D5B, U+1D62-1D65, U+1D9C, U+1DA0, U+1DBB, U+1DBF-1DF5, U+1DFC-1E9B, U+1E9D-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20AF, U+20B1-20B5, U+20B8-20BA, U+20BC-20C0, U+20DB-20DE, U+20E8, U+20F0, U+2113, U+2C7C, U+2C7F, U+2DFF, U+A69F, U+A7FF;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-latin.woff2") format("woff2");
  unicode-range: U+0000, U+0020-007E, U+00A0-00AC, U+00AE-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+1DC0-1DF5, U+1DFC-1DFF, U+2000-200B, U+2010-2027, U+202F-2055, U+2057, U+205F, U+20AC, U+20DB-20DE, U+20E8, U+20F0, U+2122, U+2190-2199, U+21A9-21AA, U+21B0-21B1, U+21B3-21B5, U+21BA-21BB, U+21D0, U+21D2, U+21D4, U+21DE-21DF, U+21E4-21E5, U+21E7, U+21EA, U+2212, U+2DFF, U+A69F, U+FEFF;
}
@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-Italic-rest.woff2") format("woff2");
  unicode-range: U+02CD, U+02D8-02D9, U+02DB, U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+0E3F, U+1DC0-1DF5, U+1DFC-1DFF, U+2070-2071, U+2074-208E, U+2090-209C, U+20DB-20DE, U+20E8, U+20F0, U+2100-2101, U+2103, U+2105-2106, U+2109, U+2117, U+211E-2121, U+2126, U+212A-212B, U+212E, U+2132, U+213B, U+214D, U+2150-217F, U+2183-2186, U+2189, U+2202, U+2205-2206, U+220F, U+2211, U+221A, U+221E, U+222B, U+2236, U+2248, U+2260, U+2264-2265, U+2295-2298, U+2303-2305, U+2318, U+2325-2327, U+232B, U+2380, U+2387, U+238B, U+23CE-23CF, U+2423, U+2460-2468, U+24B6-24CF, U+24EA, U+25A0-25A2, U+25AA, U+25B2-25B3, U+25B6-25B7, U+25BA-25BD, U+25C0-25C1, U+25C4-25C7, U+25CA-25CB, U+25CF, U+25E6, U+25EF, U+2600, U+2605-2606, U+263C, U+2661, U+2665, U+266A-266B, U+26A0, U+2713, U+2717, U+2756, U+2764, U+2780-2788, U+27EF, U+27F5-27FA, U+2913, U+2A38, U+2B06, U+2B12-2B13, U+2B1C, U+2B24, U+2DFF, U+2E18, U+A69F, U+A92E, U+E000, U+E002-E02B, U+E04B-E05E, U+E06A-E0BD, U+E0C8-E0CC, U+E0DC-E0E6, U+E0F3-E0F5, U+E106, U+E109-E10A, U+E10C-E10F, U+E111-E113, U+E117-E118, U+E121-E122, U+E124, U+E12A-E15E, U+E163, U+E1C3, U+E1D2-E1DF, U+E1E1-E274, U+E27B-E2DC, U+EE01-EE07, U+EE09-EE0A, U+EE0C-EE12, U+EE14, U+EE17, U+EE1D-EE45, U+EE47-EE84, U+EE87-EEE1, U+F6C3, U+1F12F-1F149, U+1F16A-1F16B, U+1F850, U+1F852;
}
@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-Italic-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0460-0479, U+0480-049D, U+04A0-04FF, U+052F, U+1DC0-1DF5, U+1DFC-1DFF, U+20B4, U+20DB-20DE, U+20E8, U+20F0, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-Italic-cyrillic.woff2") format("woff2");
  unicode-range: U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0400-045F, U+0483-0489, U+0490-0491, U+04B0-04B1, U+1DC0-1DF5, U+1DFC-1DFF, U+20DB-20DE, U+20E8, U+20F0, U+2116, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-Italic-greek-ext.woff2") format("woff2");
  unicode-range: U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+1DC0-1DF5, U+1DFC-1DFF, U+1F00-1F15, U+1F18-1F1D, U+1F20-1F45, U+1F48-1F4D, U+1F50-1F57, U+1F59, U+1F5B, U+1F5D, U+1F5F-1F7D, U+1F80-1FB4, U+1FB6-1FC4, U+1FC6-1FD3, U+1FD6-1FDB, U+1FDD-1FEF, U+1FF2-1FF4, U+1FF6-1FFE, U+20DB-20DE, U+20E8, U+20F0, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-Italic-greek.woff2") format("woff2");
  unicode-range: U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0374-0376, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03D7, U+03DC-03DD, U+03F0-03F6, U+03F9-03FA, U+03FC-03FF, U+0483-0489, U+1DC0-1DF5, U+1DFC-1DFF, U+20DB-20DE, U+20E8, U+20F0, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-Italic-vietnamese.woff2") format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+1DC0-1DF5, U+1DFC-1DFF, U+1EA0-1EF9, U+20AB, U+20DB-20DE, U+20E8, U+20F0, U+2DFF, U+A69F;
}
@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-Italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-0148, U+014A-01C3, U+01C5-0254, U+0256-027B, U+027E-0284, U+0286-0290, U+0292-02A4, U+02A6-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+1D00, U+1D0D, U+1D1B, U+1D43, U+1D47-1D49, U+1D4D, U+1D4F-1D50, U+1D52, U+1D56-1D58, U+1D5B, U+1D62-1D65, U+1D9C, U+1DA0, U+1DBB, U+1DBF-1DF5, U+1DFC-1E9B, U+1E9D-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20AF, U+20B1-20B5, U+20B8-20BA, U+20BC-20C0, U+20DB-20DE, U+20E8, U+20F0, U+2113, U+2C7C, U+2C7F, U+2DFF, U+A69F, U+A7FF;
}
@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/Inter-Italic-latin.woff2") format("woff2");
  unicode-range: U+0000, U+0020-007E, U+00A0-00AC, U+00AE-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0304, U+0306-030A, U+030C, U+030F, U+0313, U+0315, U+031B, U+0323, U+0326-0328, U+032C, U+0337-0338, U+0342-0343, U+0346-036F, U+0483-0489, U+1DC0-1DF5, U+1DFC-1DFF, U+2000-200B, U+2010-2027, U+202F-2055, U+2057, U+205F, U+20AC, U+20DB-20DE, U+20E8, U+20F0, U+2122, U+2190-2199, U+21A9-21AA, U+21B0-21B1, U+21B3-21B5, U+21BA-21BB, U+21D0, U+21D2, U+21D4, U+21DE-21DF, U+21E4-21E5, U+21E7, U+21EA, U+2212, U+2DFF, U+A69F, U+FEFF;
}
/* END INTER */

/* BEGIN TOKENS: generated by build/tokens.js from vendor/pudl-spec. Edit the specification's tokens, not this block. */
/* The tokens of PUDL specification 0.52.3. */

:root {
  /* === The palette =========================================================
     These are the values a project theme sets. Everything under "Derived"
     further down is computed from them, so a new palette is this block and
     its dark counterpart, and nothing else. Set them on :root and on
     [data-theme="dark"], because the derived tokens are computed there.
     --pr is the old name for --positive; a theme that sets --pr still
     reaches --positive, and new themes set --positive. */

  /* The page, its surfaces and its text. */
  --bg: #f3f4f6;
  --surface: #fbfbfc;
  --surface-alt: #e4e7eb;
  --text: #1b1f24;
  --text-muted: #5f6873;
  --border: #cfd4da;

  /* The accent, which marks the primary action and where the reader is, and
     the status colours. None of them is ever the only signal of what it
     means. */
  --accent: #2c5282;
  --accent-hover: #1f3d63;
  --on-accent: #ffffff;
  --warn: #b86e1c;
  --danger: #b42318;
  --danger-hover: #8f1c13;
  --pr: #2e7d4f;
  --positive: var(--pr);

  /* The light that falls on a raised control and the shadow it casts. light
     is the colour of the light and shade the colour of the shadow; lit is
     how strongly the light shows and depth how strongly the shadow does,
     each from 0 to 1. A light theme wants a strong highlight and a soft
     shadow, and a dark theme the reverse. */
  --light: #ffffff;
  --shade: #0e1621;
  --lit: 90%;
  --depth: 18%;

  /* The application bar keeps colours of its own, so it can stay dark on a
     light page. */
  --tb-bg: #262b31;
  --tb-fg: #f3f4f6;

  /* The colours of highlighted code, in a code block or an editor. Each
     reaches 4.5:1 on surface, surface-alt and input-bg, and a theme that
     sets them keeps that. */
  --syntax-keyword: #9b2c63;
  --syntax-string: #1d6a39;
  --syntax-number: #8a4a00;
  --syntax-comment: #57606b;
  --syntax-name: #1d4f91;
  --syntax-tag: #6b36a3;
  --syntax-attr: #6d5500;
  --syntax-heading: #1b1f24;
  --syntax-link: #2c5282;
  --syntax-error: #b42318;

  /* Eight colours that tell things apart without saying anything about
     their state, such as a chart's series, tags or the lanes of a history
     graph, assigned in this order. Each reaches 3:1 on bg, surface and
     input-bg, stays apart from the status colours and the accent, and is
     never the only way a thing is identified. A theme that changes bg,
     surface, input-bg or the lighting sets all eight and keeps those rules. */
  --category-1: #4e5efe;
  --category-2: #149d99;
  --category-3: #83296b;
  --category-4: #769805;
  --category-5: #106e9a;
  --category-6: #7400bb;
  --category-7: #8483ca;
  --category-8: #ce1a6f;

  /* === Fixed by the language ===============================================
     A theme does not change these, except the faces, within the roles the
     README sets out. */

  /* Inter ships with PUDL, so an implementation does not depend on what the
     reader has installed. The rest of each list stands in until it loads,
     or where it cannot. */
  --font: "Inter", system-ui, -apple-system, "Segoe UI", "Roboto", sans-serif;
  --font-display: var(--font);
  --mono: ui-monospace, "Cascadia Code", "SF Mono", "Consolas", monospace;

  /* The type scale. Every size PUDL uses is one of these. */
  --text-2xs: 11px;
  --text-xs: 12px;
  --text-sm: 13px;
  --text-md: 14px;
  --text-base: 15px;
  --text-lg: 17px;
  --text-xl: 20px;
  --text-2xl: 24px;
  --text-3xl: 30px;

  /* The spacing grid, on a 4px base. Spacing between and around components
     comes from these. A control's own padding belongs to the control and is
     fixed with it. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  /* The corner radii. */
  --radius: 12px;
  --radius-sm: 7px;
  --radius-xs: 4px;
  --radius-pill: 999px;

  /* Glyphs. Every glyph PUDL draws in its chrome is one of these SVG masks,
     filled with the element's own colour, so it looks the same on every
     platform, can never turn into a colour emoji, and follows the theme. */
  --glyph-account: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='8' fill-opacity='0.18'/%3E%3Ccircle cx='8' cy='6.25' r='2.75' fill-opacity='0.75'/%3E%3Cpath d='M3.25 13.75c.8-2.5 2.6-3.75 4.75-3.75s3.95 1.25 4.75 3.75a8 8 0 0 1-9.5 0z' fill-opacity='0.75'/%3E%3C/svg%3E");
  --glyph-app: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4'%3E%3Crect x='2' y='2' width='5' height='5' rx='1'/%3E%3Crect x='9' y='2' width='5' height='5' rx='1'/%3E%3Crect x='2' y='9' width='5' height='5' rx='1'/%3E%3Crect x='9' y='9' width='5' height='5' rx='1'/%3E%3C/svg%3E");
  --glyph-arrow-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2.5v11M3.5 9L8 13.5 12.5 9'/%3E%3C/svg%3E");
  --glyph-arrow-up: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 13.5v-11M3.5 7L8 2.5 12.5 7'/%3E%3C/svg%3E");
  --glyph-back: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10 3L5 8l5 5' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --glyph-board: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='2' y='2.5' width='12' height='11' rx='1.5'/%3E%3Cpath d='M6 2.5v11M10 2.5v11M3.8 5h.6M7.7 5h.6M7.7 7.2h.6M11.6 5h.6'/%3E%3C/svg%3E");
  --glyph-bold: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M4.5 2.5h4.25a2.75 2.75 0 0 1 1.6 5 3 3 0 0 1-1.6 6H4.5z'/%3E%3Cpath d='M4.5 7.5h4.25'/%3E%3C/svg%3E");
  --glyph-branch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5 1.5V9h8' fill='none' stroke='black' stroke-width='1.4'/%3E%3C/svg%3E");
  --glyph-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 5.5h9L8 11z'/%3E%3C/svg%3E");
  --glyph-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.2' fill='none' stroke='black' stroke-width='1.6'/%3E%3Cpath d='M5.2 8.2l2 2 3.8-4' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --glyph-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 6l4.5 4.5L12.5 6' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --glyph-circle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='5'/%3E%3C/svg%3E");
  --glyph-clip: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M13.2 7.6l-5.3 5.3a3.2 3.2 0 0 1-4.5-4.5l5.6-5.6a2.1 2.1 0 0 1 3 3l-5.5 5.5a1.05 1.05 0 0 1-1.5-1.5l5-5'/%3E%3C/svg%3E");
  --glyph-close-all: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1.5' y='4.5' width='9' height='9' rx='1.5'/%3E%3Cpath d='M5.5 4V3a1.5 1.5 0 0 1 1.5-1.5h6A1.5 1.5 0 0 1 14.5 3v6a1.5 1.5 0 0 1-1.5 1.5h-1'/%3E%3Cpath d='M4 7l4 4M8 7l-4 4'/%3E%3C/svg%3E");
  --glyph-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.2 4.2l7.6 7.6M11.8 4.2l-7.6 7.6' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-code: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M5.5 4.5L2 8l3.5 3.5M10.5 4.5L14 8l-3.5 3.5'/%3E%3C/svg%3E");
  --glyph-colour: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M8 2.5c2.5 3 4 5 4 7a4 4 0 0 1-8 0c0-2 1.5-4 4-7z' stroke-width='1.5'/%3E%3C/svg%3E");
  --glyph-comments: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M2 3.5a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6.5a1 1 0 0 1-1 1H7l-3 2.5V11H3a1 1 0 0 1-1-1z'/%3E%3Cpath d='M5 5.5h6M5 8h4'/%3E%3C/svg%3E");
  --glyph-commit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Ccircle cx='8' cy='8' r='2.8'/%3E%3Cpath d='M1.5 8h3.7M10.8 8h3.7'/%3E%3C/svg%3E");
  --glyph-compare: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='1.5' y='1.5' width='5' height='8.5' rx='1'/%3E%3Crect x='9.5' y='1.5' width='5' height='8.5' rx='1'/%3E%3Cpath d='M3 13.5h10M4.5 12L3 13.5 4.5 15M11.5 12l1.5 1.5-1.5 1.5'/%3E%3C/svg%3E");
  --glyph-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linejoin='round'%3E%3Crect x='5.5' y='5.5' width='8' height='8.5' rx='1.5'/%3E%3Cpath d='M10.5 5.5V3.5a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 3.5V9A1.5 1.5 0 0 0 4 10.5h1.5'/%3E%3C/svg%3E");
  --glyph-diamond: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2l6 6-6 6-6-6z'/%3E%3C/svg%3E");
  --glyph-dock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='2.25' y='2.75' width='11.5' height='10.5' rx='1.5' fill='none' stroke='black' stroke-width='1.5'/%3E%3Crect x='2.25' y='9' width='11.5' height='4.25' rx='1'/%3E%3C/svg%3E");
  --glyph-document: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M4 1.5h5.5l3 3v9.5a.5.5 0 0 1-.5.5H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z'/%3E%3Cpath d='M9.5 1.5v3h3M5.8 8h4.4M5.8 10.5h4.4M5.8 5.5h2'/%3E%3C/svg%3E");
  --glyph-download: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2.5v8M4.5 7.5L8 11l3.5-3.5'/%3E%3Cpath d='M2.5 11.5v1.5a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1v-1.5'/%3E%3C/svg%3E");
  --glyph-empty: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M1.8 9.2l2-5.4h8.4l2 5.4v3.6a.8.8 0 0 1-.8.8H2.6a.8.8 0 0 1-.8-.8z' fill='none' stroke='black' stroke-width='1.3' stroke-linejoin='round'/%3E%3Cpath d='M1.8 9.2h3.6l.9 1.6h3.4l.9-1.6h3.6' fill='none' stroke='black' stroke-width='1.3' stroke-linejoin='round'/%3E%3C/svg%3E");
  --glyph-eye-off: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M1.5 8C3.2 4.9 5.4 3.5 8 3.5s4.8 1.4 6.5 4.5c-1.7 3.1-3.9 4.5-6.5 4.5S3.2 11.1 1.5 8z' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'/%3E%3Ccircle cx='8' cy='8' r='2.2' fill='black'/%3E%3Cpath d='M2.5 13.5l11-11' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-eye: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M1.5 8C3.2 4.9 5.4 3.5 8 3.5s4.8 1.4 6.5 4.5c-1.7 3.1-3.9 4.5-6.5 4.5S3.2 11.1 1.5 8z' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'/%3E%3Ccircle cx='8' cy='8' r='2.2' fill='black'/%3E%3C/svg%3E");
  --glyph-file: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'%3E%3Cpath d='M4 1.5h5.5l3 3v9.5a.5.5 0 0 1-.5.5H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z'/%3E%3Cpath d='M9.5 1.5v3h3'/%3E%3C/svg%3E");
  --glyph-flag: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M3.5 14.5V1.8'/%3E%3Cpath d='M3.5 2.5h9l-2.2 3.25 2.2 3.25h-9'/%3E%3C/svg%3E");
  --glyph-folder: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'%3E%3Cpath d='M1.5 4.5a1 1 0 0 1 1-1h3.6l1.4 1.5h5.9a1 1 0 0 1 1 1v6.5a1 1 0 0 1-1 1h-10.9a1 1 0 0 1-1-1z'/%3E%3C/svg%3E");
  --glyph-fork: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Ccircle cx='4.5' cy='3.5' r='1.7'/%3E%3Ccircle cx='4.5' cy='12.5' r='1.7'/%3E%3Ccircle cx='11.5' cy='3.5' r='1.7'/%3E%3Cpath d='M4.5 5.2v5.6'/%3E%3Cpath d='M11.5 5.2c0 3.6-7 2.6-7 5.6'/%3E%3C/svg%3E");
  --glyph-forward: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 3l5 5-5 5' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --glyph-gear: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round'%3E%3Cpath d='M6.9 1.5h2.2l.4 1.9 1.1.6 1.8-.7 1.1 1.9-1.4 1.3v1.2l1.4 1.3-1.1 1.9-1.8-.7-1.1.6-.4 1.9H6.9l-.4-1.9-1.1-.6-1.8.7-1.1-1.9 1.4-1.3V7.4L2.5 6.1l1.1-1.9 1.8.7 1.1-.6z'/%3E%3Ccircle cx='8' cy='8' r='2'/%3E%3C/svg%3E");
  --glyph-grip: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='3.5' r='1.3'/%3E%3Ccircle cx='8' cy='8' r='1.3'/%3E%3Ccircle cx='8' cy='12.5' r='1.3'/%3E%3C/svg%3E");
  --glyph-history: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M2.5 8a5.5 5.5 0 1 0 1.6-3.9L2.5 5.7'/%3E%3Cpath d='M2.5 2.6v3.1h3.1'/%3E%3Cpath d='M8 5v3l2 1.5'/%3E%3C/svg%3E");
  --glyph-home: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 7.5l6-5 6 5'/%3E%3Cpath d='M3.5 6.5v7h3.5v-4h2v4h3.5v-7'/%3E%3C/svg%3E");
  --glyph-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='2' y='3' width='12' height='10' rx='1.2'/%3E%3Ccircle cx='5.6' cy='6.4' r='1.1'/%3E%3Cpath d='M2.5 11.5l3.6-3.4 2.6 2.4 1.9-1.7 3 2.7'/%3E%3C/svg%3E");
  --glyph-inbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M1.5 9l2-6.5h9l2 6.5v4a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1z'/%3E%3Cpath d='M1.5 9h3.5l1 2h4l1-2h3.5'/%3E%3C/svg%3E");
  --glyph-info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.2' fill='none' stroke='black' stroke-width='1.6'/%3E%3Ccircle cx='8' cy='5' r='1'/%3E%3Cpath d='M8 7.4v4.2' stroke='black' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-italic: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M6.5 2.5h5M4.5 13.5h5M9.5 2.5l-3 11'/%3E%3C/svg%3E");
  --glyph-link: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 3H3.5a1 1 0 0 0-1 1v8.5a1 1 0 0 0 1 1H12a1 1 0 0 0 1-1V9'/%3E%3Cpath d='M9.5 2.5h4v4M13.5 2.5l-6 6'/%3E%3C/svg%3E");
  --glyph-list-numbered: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M7 4h6.5M7 8h6.5M7 12h6.5'/%3E%3Cpath d='M2 3h1.5v3M1.6 9.3a1.3 1.3 0 0 1 2.3.6c0 1-2.3 1.8-2.3 3h2.6' stroke-width='1.1'/%3E%3C/svg%3E");
  --glyph-list: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 4h7M6.5 8h7M6.5 12h7' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3Ccircle cx='3' cy='4' r='1.1'/%3E%3Ccircle cx='3' cy='8' r='1.1'/%3E%3Ccircle cx='3' cy='12' r='1.1'/%3E%3C/svg%3E");
  --glyph-maximize: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3.2' y='3.2' width='9.6' height='9.6' rx='1.5' fill='none' stroke='black' stroke-width='1.8'/%3E%3C/svg%3E");
  --glyph-menu: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.5 4h11M2.5 8h11M2.5 12h11' fill='none' stroke='black' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-merge: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M3.5 2.5L8 7.5v6M12.5 2.5L8 7.5'/%3E%3Cpath d='M5.5 11.5L8 14l2.5-2.5'/%3E%3C/svg%3E");
  --glyph-minimize-all: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1.5' y='1.5' width='13' height='9' rx='1.5'/%3E%3Cpath d='M4.5 14h7'/%3E%3Cpath d='M6 5.5l2 2 2-2'/%3E%3C/svg%3E");
  --glyph-minimize: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5h9' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8h9' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-more: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='3' cy='8' r='1.5' fill='black'/%3E%3Ccircle cx='8' cy='8' r='1.5' fill='black'/%3E%3Ccircle cx='13' cy='8' r='1.5' fill='black'/%3E%3C/svg%3E");
  --glyph-move: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='1.5' y='4.5' width='5' height='7' rx='1'/%3E%3Cpath d='M8.5 8h6M12 5.5L14.5 8 12 10.5'/%3E%3C/svg%3E");
  --glyph-new-window: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M7 2.5H3.5a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1V9'/%3E%3Cpath d='M9.5 2.5h4v4'/%3E%3Cpath d='M13.5 2.5L8 8'/%3E%3C/svg%3E");
  --glyph-open: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 3.5h6.5V10M12.5 3.5L4 12' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --glyph-panel: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='1.5' y='2.5' width='13' height='11' rx='1.5'/%3E%3Cpath d='M4.5 6h7M4.5 8.5h7M4.5 11h4' stroke-width='1.3'/%3E%3C/svg%3E");
  --glyph-paragraph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M9.5 2.5H6.75a2.75 2.75 0 0 0 0 5.5H9.5M9.5 2.5v11M12 2.5v11'/%3E%3C/svg%3E");
  --glyph-pause: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 3h3v10H4zM9 3h3v10H9z'/%3E%3C/svg%3E");
  --glyph-pencil-off: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M11 2l3 3-8.5 8.5L2 14l.5-3.5z'/%3E%3Cpath d='M9.5 3.5l3 3'/%3E%3Cpath d='M2 2l12 12'/%3E%3C/svg%3E");
  --glyph-pencil: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M11 2l3 3-8.5 8.5L2 14l.5-3.5z'/%3E%3Cpath d='M9.5 3.5l3 3'/%3E%3C/svg%3E");
  --glyph-pin: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M10 1.5l4.5 4.5-2 .5L10 9l.5 3L9 13.5 2.5 7 4 5.5l3 .5L9.5 3.5z'/%3E%3Cpath d='M5.5 10.5l-4 4'/%3E%3C/svg%3E");
  --glyph-play: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 2l9 6-9 6z'/%3E%3C/svg%3E");
  --glyph-plus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 3.5v9M3.5 8h9' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-quote: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 9.5a3 3 0 0 1 3-3V4a5.5 5.5 0 0 0-5.5 5.5V12H6V9.5zM9.5 9.5a3 3 0 0 1 3-3V4A5.5 5.5 0 0 0 7 9.5V12h5.5V9.5z'/%3E%3C/svg%3E");
  --glyph-reload: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M14 8a6 6 0 1 1-6-6c1.68 0 3.29.67 4.49 1.83L14 5.3'/%3E%3Cpath d='M14 1.8v3.5h-3.5'/%3E%3C/svg%3E");
  --glyph-restore-all: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1.5' y='1.5' width='13' height='9' rx='1.5'/%3E%3Cpath d='M4.5 14h7'/%3E%3Cpath d='M6 7.5l2-2 2 2'/%3E%3C/svg%3E");
  --glyph-restore: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='2.5' y='5.5' width='8' height='8' rx='1.2' fill='none' stroke='black' stroke-width='1.6'/%3E%3Cpath d='M5.5 5.5V3.6a1.1 1.1 0 0 1 1.1-1.1h5.8a1.1 1.1 0 0 1 1.1 1.1v5.8a1.1 1.1 0 0 1-1.1 1.1h-1.9' fill='none' stroke='black' stroke-width='1.6'/%3E%3C/svg%3E");
  --glyph-ribbon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='1.5' y='2.5' width='13' height='11' rx='1.5'/%3E%3Cpath d='M1.5 6h13'/%3E%3Cpath d='M4 9h1.5M7.25 9h1.5M10.5 9h1.5' stroke-width='1.6'/%3E%3C/svg%3E");
  --glyph-ring: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='4.2' fill='none' stroke='black' stroke-width='1.6'/%3E%3C/svg%3E");
  --glyph-script: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='1.5' y='2.5' width='13' height='11' rx='1.5'/%3E%3Cpath d='M4.5 6.5l2 1.75-2 1.75'/%3E%3Cpath d='M8 11h3.5'/%3E%3C/svg%3E");
  --glyph-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='6.5' cy='6.5' r='4.75' fill='none' stroke='black' stroke-width='2'/%3E%3Cpath d='M10.2 10.2L14.5 14.5' stroke='black' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-shield: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M8 1.5l5.5 2v4c0 3.5-2.5 5.8-5.5 7-3-1.2-5.5-3.5-5.5-7v-4z'/%3E%3C/svg%3E");
  --glyph-slash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M10.5 2.5l-5 11' stroke='black' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E");
  --glyph-sort-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 6L8 11.5 12.5 6z'/%3E%3C/svg%3E");
  --glyph-sort-up: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 10L8 4.5l4.5 5.5z'/%3E%3C/svg%3E");
  --glyph-sort: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 6.5L8 3l3.5 3.5zM4.5 9.5L8 13l3.5-3.5z'/%3E%3C/svg%3E");
  --glyph-square: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3' y='3' width='10' height='10' rx='1'/%3E%3C/svg%3E");
  --glyph-stash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M5.5 7.5h-3v5.5a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1V7.5h-3'/%3E%3Cpath d='M5 11h6'/%3E%3Cpath d='M8 1.5v7M5.5 6L8 8.5 10.5 6'/%3E%3C/svg%3E");
  --glyph-stop: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M5.4 1.8h5.2l3.6 3.6v5.2l-3.6 3.6H5.4l-3.6-3.6V5.4z' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round'/%3E%3Cpath d='M8 4.8v4' stroke='black' stroke-width='1.8' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='11' r='1'/%3E%3C/svg%3E");
  --glyph-strike: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M11.5 4.5A3 3 0 0 0 8 2.5C6 2.5 4.75 3.5 4.75 5c0 1 .5 1.6 1.5 2M4.5 11.5A3.2 3.2 0 0 0 8 13.5c2.2 0 3.5-1 3.5-2.5 0-.8-.3-1.4-.9-1.9'/%3E%3Cpath d='M2.5 8h11'/%3E%3C/svg%3E");
  --glyph-table: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='1.5' y='2.5' width='13' height='11' rx='1'/%3E%3Cpath d='M1.5 6.5h13M1.5 10h13M6 2.5v11M10 2.5v11' stroke-width='1.2'/%3E%3C/svg%3E");
  --glyph-tag: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M2.5 2.5h5l6.1 6.1a1.4 1.4 0 0 1 0 2l-3 3a1.4 1.4 0 0 1-2 0L2.5 7.5z'/%3E%3Ccircle cx='5.5' cy='5.5' r='1'/%3E%3C/svg%3E");
  --glyph-terminal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Crect x='1.5' y='2.5' width='13' height='11' rx='1.5'/%3E%3Cpath d='M1.5 5.5h13'/%3E%3Cpath d='M4.5 8l2 1.6-2 1.6'/%3E%3Cpath d='M8 11.5h3.5'/%3E%3C/svg%3E");
  --glyph-theme: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='5.7' fill='none' stroke='black' stroke-width='1.6'/%3E%3Cpath d='M8 2.3a5.7 5.7 0 0 0 0 11.4z'/%3E%3C/svg%3E");
  --glyph-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.5l3.2 3.2L13 4.8' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --glyph-ticket: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.4' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M2 4.5h12v2.2a1.3 1.3 0 0 0 0 2.6v2.2H2V9.3a1.3 1.3 0 0 0 0-2.6z'/%3E%3Cpath d='M10.5 4.5v7' stroke-dasharray='1.2 1.4'/%3E%3C/svg%3E");
  --glyph-triangle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 2l7 12H1z'/%3E%3C/svg%3E");
  --glyph-underline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round' stroke-linecap='round'%3E%3Cpath d='M4.5 2.5v5.5a3.5 3.5 0 0 0 7 0V2.5'/%3E%3Cpath d='M3.5 13.5h9'/%3E%3C/svg%3E");
  --glyph-undock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.25' y='2.75' width='11.5' height='10.5' rx='1.5'/%3E%3Cpath d='M5.5 9.5L8 7l2.5 2.5'/%3E%3C/svg%3E");
  --glyph-upload: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 11V3M4.5 6.5L8 3l3.5 3.5'/%3E%3Cpath d='M2.5 11.5v1.5a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1v-1.5'/%3E%3C/svg%3E");
  --glyph-warning: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.8l6.6 12H1.4z' fill='none' stroke='black' stroke-width='1.6' stroke-linejoin='round'/%3E%3Cpath d='M8 6.2v3.6' stroke='black' stroke-width='1.8' stroke-linecap='round'/%3E%3Ccircle cx='8' cy='11.9' r='1'/%3E%3C/svg%3E");
  --glyph-window: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.5' stroke-linejoin='round'%3E%3Crect x='2.75' y='2.75' width='10.5' height='10.5' rx='1.5'/%3E%3Cpath d='M2.75 6h10.5'/%3E%3C/svg%3E");
  --glyph-working: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M12.2 8a4.2 4.2 0 1 1-4.2-4.2' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");

  /* === Derived =============================================================
     These are computed from the palette, so a theme should not need to set
     any of them. The specification states each as a formula, which is set
     out here in CSS. */

  /* An input is sunken, so it takes the surface with only a touch of light.
     Its border reaches 3:1 against the surfaces a field sits on, as WCAG
     2.2 asks of a control's bounds. */
  --input-bg: color-mix(in srgb, var(--light) var(--lit), var(--surface));
  --input-border: color-mix(in srgb, var(--text) 55%, var(--surface));
  --entry-shadow: inset 0 1px 2px color-mix(in srgb, var(--shade) calc(var(--depth) * 0.7), transparent);

  /* A raised control is lit from the top and shades to surface-alt at its
     foot. */
  --raise-top: color-mix(in srgb, var(--light) var(--lit), var(--surface-alt));
  --raise-grad: linear-gradient(var(--raise-top), var(--surface-alt));
  --raise-grad-hover: linear-gradient(
      color-mix(in srgb, var(--text) 3%, var(--raise-top)),
      color-mix(in srgb, var(--text) 6%, var(--surface-alt)));
  --raise-active-bg: color-mix(in srgb, var(--shade) calc(var(--depth) * 0.45), var(--surface-alt));
  --raise-border: color-mix(in srgb, var(--shade) calc(var(--depth) * 0.9), var(--surface-alt));
  --raise-border-hover: color-mix(in srgb, var(--shade) calc(var(--depth) * 1.6), var(--surface-alt));
  --raise-shadow:
      inset 0 1px color-mix(in srgb, var(--light) var(--lit), transparent),
      0 1px 2px color-mix(in srgb, var(--shade) var(--depth), transparent);
  --raise-active-shadow: inset 0 1px 3px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.2), transparent);
  --shadow-btn-primary:
      inset 0 1px rgb(255 255 255 / 0.25),
      0 1px 2px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.2), transparent);

  /* A recessed band, such as a tab bar or a switch's track, is darker than
     the surface around it and shaded from above. */
  --recess-bg: color-mix(in srgb, var(--shade) calc(var(--depth) * 0.35), var(--surface-alt));
  --recess-shadow: inset 0 2px 3px color-mix(in srgb, var(--shade) calc(var(--depth) * 0.6), transparent);

  /* A card lifts off the page with a soft shadow and a faint ring. A dialog
     lifts further, with a deep shadow as well, over a backdrop that dims
     the rest. It is one step brighter than the page it sits on: surface on
     a light page, and on a dark page, where surface is barely lighter than
     bg, surface-alt. */
  --shadow-card:
      0 1px 3px color-mix(in srgb, var(--shade) calc(var(--depth) * 0.6), transparent),
      0 0 0 1px color-mix(in srgb, var(--shade) calc(var(--depth) * 0.25), transparent);
  --shadow-dialog:
      0 1px 3px color-mix(in srgb, var(--shade) calc(var(--depth) * 0.6), transparent),
      0 0 0 1px color-mix(in srgb, var(--shade) calc(var(--depth) * 0.25), transparent),
      0 24px 64px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.8), transparent);
  --backdrop: color-mix(in srgb, var(--shade) 40%, transparent);
  --dialog-bg: var(--surface);
  --section-current-bg: var(--surface);

  /* The focus rings take their colour tokens, so a theme's accent reaches
     focus with no further work. */
  --focus-ring: color-mix(in srgb, var(--accent) 26%, transparent);
  --focus-ring-danger: color-mix(in srgb, var(--danger) 26%, transparent);

  /* A diff's colours. Added and removed take the status colours by default,
     as readers expect, and a theme may point them elsewhere. The tint is
     the strongest at which every syntax colour still reaches 4.5:1 over
     input-bg; 10% holds for the default palette. */
  --diff-add: var(--positive);
  --diff-del: var(--danger);
  --diff-add-bg: color-mix(in srgb, var(--diff-add) 10%, transparent);
  --diff-del-bg: color-mix(in srgb, var(--diff-del) 10%, transparent);

  /* The application bar's chrome, from its two colours and the accent. */
  --tb-grad: linear-gradient(
      color-mix(in srgb, var(--tb-fg) 8%, var(--tb-bg)),
      var(--tb-bg));
  --tb-border: color-mix(in srgb, #000 40%, var(--tb-bg));
  --tb-hi: color-mix(in srgb, var(--tb-fg) 12%, transparent);
  --tb-brand: var(--tb-fg);
  --tb-chrome-fg: color-mix(in srgb, var(--tb-fg) 80%, var(--tb-bg));
  --tb-chrome-hover-fg: var(--tb-fg);
  --tb-link-hover: color-mix(in srgb, var(--accent) 45%, var(--tb-fg));
  --tb-chip: linear-gradient(
      color-mix(in srgb, var(--tb-fg) 19%, transparent),
      color-mix(in srgb, var(--tb-fg) 10%, transparent));
  --tb-chip-shadow:
      inset 0 1px color-mix(in srgb, var(--tb-fg) 22%, transparent),
      0 1px 2px color-mix(in srgb, #000 35%, transparent);
}

[data-theme="dark"] {
  /* === The dark palette ====================================================
     The same tokens as the light palette, and the derived tokens whose
     formula differs in the dark. */

  --bg: #121417;
  --surface: #1a1d21;
  --surface-alt: #23272d;
  --text: #e6e8eb;
  --text-muted: #9aa3ad;
  --border: #353b43;

  --accent: #6b9bd1;
  --accent-hover: #8db3e0;
  --on-accent: #121417;
  --warn: #d9954a;
  --danger: #e06156;
  --danger-hover: #f0857b;
  --pr: #5fb37f;
  --positive: var(--pr);

  --light: #e8eef6;
  --shade: #000000;
  --lit: 7%;
  --depth: 48%;

  --tb-bg: #262b31;
  --tb-fg: #e6e8eb;

  --syntax-keyword: #f08dc0;
  --syntax-string: #85cf9b;
  --syntax-number: #efad6b;
  --syntax-comment: #9aa3ad;
  --syntax-name: #8fbcef;
  --syntax-tag: #c6a3f2;
  --syntax-attr: #e2c275;
  --syntax-heading: #e6e8eb;
  --syntax-link: #8db3e0;
  --syntax-error: #f0857b;

  --category-1: #4368fd;
  --category-2: #07d5d1;
  --category-3: #f558d8;
  --category-4: #68863e;
  --category-5: #1280a5;
  --category-6: #ab40df;
  --category-7: #a19efd;
  --category-8: #dd1e78;

  --dialog-bg: var(--surface-alt);
}
/* END TOKENS */

/* Standard control paint is separate from the vendored palette formulas.
   Neutral boundaries remain distinct even at a shallow theme Depth. */
:root {
  --raise-border: var(--text-muted);
  --raise-border-hover: var(--text);
}

/* Annotation content stays flat; only its explicit actions are raised. */
.selection-actions { position: fixed; z-index: 90; display: flex; flex-wrap: wrap; gap: var(--space-1); }
.selection-actions[hidden], .note-marker[hidden], .note-back[hidden] { display: none; }
.noted { position: relative; min-width: 0; }
.noted-body { max-width: 42rem; margin-inline: auto; }
.margin-note { box-sizing: border-box; padding: var(--space-2) var(--space-3); margin-block: var(--space-2); background: var(--surface); border: 1px solid var(--border); border-inline-start: 3px solid var(--accent); border-radius: var(--radius-sm); font-size: var(--text-sm); overflow-wrap: anywhere; }
.noted[data-notes-beside] > .margin-note { margin-block: 0; }
.note-marker, .note-back { margin: var(--space-1); }
.note-anchor { background: color-mix(in srgb, var(--accent) 14%, transparent); text-decoration: underline dotted; text-decoration-color: var(--accent); text-underline-offset: .2em; }
@media print {
  .selection-actions, .note-marker, .note-back { display: none !important; }
  .noted { min-height: 0 !important; }
  .noted > .margin-note { position: static !important; width: auto !important; }
}

:root[data-theme="dark"] { color-scheme: dark; }
/* A destructive button's words at rest, in the dark theme, lean nearly
   half way to the text colour (0.97.5), so they keep 4.5:1 against the
   lightest of every skin's raised fill under them in every palette; the
   lighter red alone falls below it. The rule outranks a skin's own, which
   still sets the light theme's. */
:root[data-theme="dark"], :root [data-theme="dark"] { --danger-rest-fg: color-mix(in srgb, var(--danger) 55%, var(--text)); }
:root[data-theme="light"] { color-scheme: light; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

/* Words for assistive technology alone, such as a live region's message
   or a control's fuller name: present to a screen reader, taking no room
   on the screen. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* The hidden attribute always hides, even on a component that sets its own
   display, which would otherwise beat the browser's rule. hidden="until-found"
   is left to the browser, which hides it by other means so that in-page
   search can still reveal it. */
[hidden]:not([hidden="until-found"]) { display: none !important; }
body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: var(--text-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Headings, from the type scale. :where() gives these no weight at all, so
   any class a component or project puts on a heading wins over them. */
:where(h1, h2, h3, h4) {
  font-family: var(--font-display); font-weight: 700;
  line-height: 1.25; letter-spacing: -0.01em;
  margin: 0 0 var(--space-3);
}
:where(h1) { font-size: var(--text-3xl); }
:where(h2) { font-size: var(--text-2xl); }
:where(h3) { font-size: var(--text-xl); }
:where(h4) { font-size: var(--text-lg); }

/* Tabular numerals wherever numbers are data, so identifiers, counts, money
   and timestamps line up. Running prose keeps proportional figures, because
   Inter's tabular feature also widens the hyphen, which spaces out every
   hyphenated word. <time>, <data> and .num mark data inside prose. */
table, input, select, textarea, time, data, output, code, kbd, samp, .num,
.kv-table, .form-input, .badge, .chip, .filter-chip, .seg, .topbar-pill,
.md-item, .md-row {
  font-variant-numeric: tabular-nums;
}

/* === Glyphs ==============================================================
   Every chrome glyph is a pseudo-element drawn by this one rule: a square
   the size of the text, filled with the element's colour through an SVG
   mask named by --glyph. Each component below sets which glyph, and its
   size where the text's own size is not right. */
.badge::before, .form-error::before, .menu-btn::after, .md-back::before,
.notice::before, .toast::before, .data-table th[aria-sort] > :is(a, button)::after,
.notice-close::before, .toast-close::before, .empty-state::before,
.page-link[rel="prev"]::before, .page-link[rel="next"]::after {
  content: ""; flex: none;
  width: var(--glyph-size, 1em); height: var(--glyph-size, 1em);
  background: currentColor;
  -webkit-mask: var(--glyph) center / contain no-repeat;
  mask: var(--glyph) center / contain no-repeat;
}

/* === Topbar / brand chrome ============================================== */
/* The topbar wraps rather than widening the page: on a screen too narrow
   for the brand and its chrome on one line, the chrome moves to a second
   line, at the end, and wraps within itself if it must. */
.topbar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-2);
  --tb-pad-y: 10px;
  padding: var(--tb-pad-y) 20px;
  background: var(--tb-grad);
  border-bottom: 1px solid var(--tb-border);
  box-shadow: inset 0 1px var(--tb-hi);
}
.topbar.native-chrome {
  --native-chrome-size: calc(var(--win-button-size, var(--text-md) * 2) + 4px);
  position: relative; flex-wrap: nowrap; min-height: 40px; box-sizing: border-box;
  padding-left: calc(var(--native-chrome-size) + 24px);
  padding-right: calc(var(--native-chrome-size) * 3 + 8px);
}
.topbar.native-chrome > [data-native-chrome-slot],
.topbar.native-chrome.menubar-ribbon-host > [data-native-chrome-slot] { position: absolute; top: 0; bottom: 0; margin: 0; align-self: stretch; }
.native-window-menu { left: 0; width: calc(var(--native-chrome-size) + 16px); display: flex; align-items: center; justify-content: center; }
.native-window-controls { right: 0; display: flex; direction: ltr; width: calc(var(--native-chrome-size) * 3); }
.native-window-target { position: relative; display: flex; align-items: center; justify-content: center; width: var(--native-chrome-size); }
.native-window-menu > .menu-btn, .native-window-target > .icon-btn {
  box-sizing: border-box; width: var(--win-button-size, calc(var(--text-md) * 2)); height: var(--win-button-size, calc(var(--text-md) * 2)); min-height: 0;
  border-radius: var(--radius-sm); padding: 0; flex: none; color: var(--text-muted);
  border: 1px solid var(--raise-border); background: var(--raise-grad); box-shadow: var(--raise-shadow);
  font: 600 var(--text-sm)/1 var(--font);
}
.native-window-menu > .menu-btn { display: inline-flex; justify-content: center; width: auto; gap: 4px; padding-inline: 8px 6px; }
.native-window-menu > .menu-btn::after { margin: 0; width: calc(var(--text-md) * .75); height: calc(var(--text-md) * .75); }
/* The transparent hit area reaches the screen edge around the standard
   rounded button. It does not change the button's visible dimensions. */
.native-window-menu > .menu-btn::before, .native-window-target > .icon-btn::before { content: ''; position: absolute; inset: 0; }
.native-window-menu .glyph, .native-window-controls .glyph { --glyph-size: var(--win-glyph-size, var(--text-md)); }
.native-window-icon { width: var(--win-glyph-size, var(--text-md)); height: var(--win-glyph-size, var(--text-md)); object-fit: contain; }
.native-window-menu > .menu-btn:not(:disabled):hover, .native-window-target > .icon-btn:not(:disabled):hover { color: var(--text); background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.native-window-menu > .menu-btn:not(:disabled):active, .native-window-target > .icon-btn:not(:disabled):active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.native-window-target > [data-native-action="close"]:not(:disabled):hover { color: var(--danger); }
.native-inactive .native-window-target > .icon-btn { color: var(--text-muted); }
[data-native-chrome-slot][hidden] { display: none; }
@media (pointer: coarse) { .topbar.native-chrome { --native-chrome-size: 48px; } }
@media print { .topbar.native-chrome > [data-native-chrome-slot] { display: none; } }
.brand {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-display);
  font-size: var(--text-lg); font-weight: 700; letter-spacing: -0.01em;
  color: var(--tb-brand); text-decoration: none;
}
/* The page's link colours are made for the page, not for the topbar, so a
   plain link on the topbar takes the topbar's colours instead, and the
   brand, like it, hovers in --tb-link-hover. */
/* A plain link on the topbar takes the bar's colours. :where() gives the
   bar no weight here, so a component on the bar, such as a segmented
   control of links, keeps its own colours. */
:where(.topbar) a:not([class]) { color: var(--tb-chrome-fg); }
.brand:hover, :where(.topbar) a:not([class]):hover { color: var(--tb-link-hover); }
.topbar-chrome {
  display: inline-flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: var(--space-2);
  min-width: 0; margin-inline-start: auto;
}
/* A topbar pill is raised, so it is always a link or a button, never a label. */
.topbar-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border-radius: var(--radius-sm);
  font-size: var(--text-sm); font-weight: 600;
  color: var(--tb-chrome-fg);
  background: var(--tb-chip); box-shadow: var(--tb-chip-shadow);
  text-decoration: none;
}
.topbar-pill:hover { color: var(--tb-chrome-hover-fg); }
/* A pill may carry a glyph with its words in a .topbar-pill-label. On a
   phone the words are then heard but not shown, and the pill is its
   glyph, so a topbar with several pills still fits on one line. */
/* The pill for the page the reader is on, marked aria-current, is pressed
   in, as the current tab of a dock or a segmented control is. */
.topbar-pill:active,
.topbar-pill[aria-current]:not([aria-current="false"]) {
  color: var(--tb-fg);
  background: color-mix(in srgb, #000 24%, var(--tb-bg));
  box-shadow: var(--tb-active-shadow, inset 0 1px 3px color-mix(in srgb, #000 50%, transparent));
}

/* Tabs in the topbar, as a browser puts its tabs in its title bar. The
   row stands on the topbar's bottom edge. The tabs the reader can go to
   are raised in the topbar's own chip colours, and the tab for where the
   reader is stands flat and a little taller, covers the topbar's bottom
   border and takes the colour of what lies below the topbar, so it opens
   into the page, as a section tab does. That colour is the page's
   background, --bg, unless the page sets --section-current-bg on the row,
   as one whose content below the topbar sits on --surface does. */
.topbar-tabs {
  --section-current-bg: var(--bg);
  display: flex; align-items: flex-end; gap: 2px;
  align-self: flex-end; min-width: 0;
  margin-bottom: calc(-1 * var(--tb-pad-y, 10px) - 1px);
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  position: relative; z-index: 1;
}
.topbar-tabs > a {
  flex: none; margin-bottom: 1px;
  padding: 5px 14px;
  font: 600 var(--text-sm) var(--font); white-space: nowrap;
  color: var(--tb-chrome-fg); text-decoration: none;
  background: var(--tb-chip); box-shadow: var(--tb-chip-shadow);
  border: 1px solid transparent; border-bottom: 0;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.topbar-tabs > a:hover { color: var(--tb-chrome-hover-fg); }
.topbar-tabs > a:active { color: var(--tb-fg); background: color-mix(in srgb, #000 24%, var(--tb-bg)); box-shadow: var(--tb-active-shadow, inset 0 1px 3px color-mix(in srgb, #000 50%, transparent)); }
.topbar-tabs > a:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--focus-ring), 0 0 0 2px var(--focus-ring); }
.topbar-tabs > a[aria-current]:not([aria-current="false"]) {
  margin-bottom: 0;
  padding: 7px 14px 7px;
  color: var(--text);
  background: var(--section-current-bg);
  border-color: var(--tb-border);
  box-shadow: none;
}

/* === The menu bar ========================================================
   pudl-menubar.js builds it in a [data-menubar] on the topbar. Each menu
   is one raised surface with a plain button's surface in every theme, the
   menu glyph at its start, and its titles are flat words that take their
   press from the
   surface, as a segmented control's positions do: a title lights under
   the pointer, and the open one is pressed into the surface. Until the
   script has built it, the bar's own fallback content shows. */
[data-menubar] { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; }
.menubar-ready > [data-menubar-fallback] { display: none; }
.menubar-row { display: flex; align-items: center; gap: var(--space-2); min-width: 0; overflow: hidden; }
/* A menu bar unfolded into a ribbon (0.79.0): each title is a tab where
   it stands, the chosen one pressed in as an open title is, and one row of
   tools under the topbar holds its menu's commands. Show as ribbon stands
   at the far right of the topbar. */
.menubar-ribbon-toggle { flex: none; margin-inline-start: var(--space-2); }
.topbar > .menubar-ribbon-toggle:last-child { margin-inline-end: 0; }
/* A topbar holding a menu bar that folds itself keeps to one line, so the
   bar, not the topbar's wrapping, decides what gives way. */
.topbar:has(> [data-menubar][data-menubar-ribbon]) { flex-wrap: nowrap; }
/* The first thing in the bar to give way: Show as ribbon as its glyph
   alone, its words its name and its tooltip. */
.menubar-ribbon-toggle.menubar-ribbon-toggle-glyph > .toolbar-words { display: none; }
.menubar-ribbon-toggle.menubar-ribbon-toggle-glyph { padding-inline: 7px; }
/* Unfolded, the menus are tabs. The topbar gives up its padding below so
   the tabs stand on its foot, and everything else in it keeps its place,
   lifted by that padding; the chosen tab takes the row's colour and
   reaches through the topbar's rule into the row, as a tab opens into its
   panel. The menus' raised groups and their glyphs go, and a gap keeps
   the site's tabs apart from the applet's. */
/* The topbar's rule is then the tab bar's, drawn in the chosen tab's own
   `border`, so the line round that tab runs on along the foot as it does
   on any tab bar (0.89.4); a dark topbar's own rule is darker. */
.topbar.menubar-ribbon-host { padding-bottom: 0; align-items: stretch; border-bottom-color: var(--border); }
.topbar.menubar-ribbon-host > :not([data-menubar]) { align-self: center; margin-bottom: var(--tb-pad-y); }
.topbar.menubar-ribbon-host > [data-menubar] { align-self: stretch; display: flex; align-items: flex-end; min-height: calc(30px + var(--tb-pad-y)); }
.menubar-row.menubar-ribbon-on { align-self: flex-end; align-items: flex-end; overflow: visible; gap: var(--space-4); }
.menubar-row.menubar-ribbon-on > .menubar-menu:not(.menubar-one, .menubar-pins) {
  align-items: flex-end; gap: var(--space-1);
  padding: 0; border: 0; background: none; box-shadow: none;
}
.menubar-row.menubar-ribbon-on > .menubar-menu:not(.menubar-pins) > .menubar-glyph { display: none; }
/* The pins are not tabs: their group looks as it does folded, and stands
   where the topbar's other controls do, lifted by its padding. */
.menubar-row.menubar-ribbon-on > .menubar-pins { align-self: flex-end; margin-bottom: var(--tb-pad-y); }
.menubar-row.menubar-ribbon-on > .menubar-menu:not(.menubar-pins) > .menubar-title {
  padding: 5px 14px 4px;
  font: 600 var(--text-sm) var(--font); color: var(--text);
  background: var(--raise-grad);
  border: 1px solid var(--raise-border); border-bottom: 0;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  box-shadow: var(--surface-edge-shadow, inset 0 1px color-mix(in srgb, var(--light) var(--lit), transparent));
}
.menubar-row.menubar-ribbon-on > .menubar-menu:not(.menubar-pins) > .menubar-title:hover { background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.menubar-row.menubar-ribbon-on > .menubar-menu:not(.menubar-pins) > .menubar-title[aria-expanded="true"] { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.menubar-row.menubar-ribbon-on > .menubar-menu:not(.menubar-pins) > .menubar-title[aria-selected="true"] {
  position: relative; z-index: 1;
  padding: 7px 14px 7px; margin-top: -4px; margin-bottom: -1px;
  background: var(--surface); border-color: var(--border);
  box-shadow: none;
}
.menubar-row.menubar-ribbon-on > .menubar-menu:not(.menubar-pins) > .menubar-title:focus-visible { box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
/* The ribbon's row starts where the topbar's contents do, so its first tool
   lines up under the first thing in the topbar. */
.menubar-ribbon { display: flex; align-items: center; gap: var(--space-2); flex: none; min-width: 0; padding: 6px var(--menubar-ribbon-pad, var(--space-3)); background: var(--surface); border-bottom: 1px solid var(--border); }
.menubar-ribbon > .menubar-ribbon-tabs { flex: none; }
.menubar-ribbon > .toolbar { flex: 1; min-width: 0; }
.menubar-ribbon[hidden] { display: none; }
.toolbar .toolbar-icon { display: inline-flex; flex: none; width: 16px; height: 16px; }
.toolbar .toolbar-icon > * { width: 100%; height: 100%; }
/* Menu groups are raised controls in both themes. */
:root:not([data-theme="dark"]) .topbar:has([data-menubar]) {
  background: var(--surface);
  --tb-bg: var(--surface-alt);
  --tb-fg: var(--text);
  --tb-brand: var(--text);
  --tb-chrome-fg: color-mix(in srgb, var(--text) 80%, var(--surface-alt));
  --tb-chrome-hover-fg: var(--text);
  --tb-link-hover: var(--accent);
  --tb-chip: var(--raise-grad);
  --tb-chip-shadow: var(--raise-shadow);
  --tb-border: var(--border);
}
/* In the dark theme the bar keeps its own colour, and its pills, status
   area and menus all take a plain button's surface, so the controls on a
   bar with a menu bar look uniform in both themes (0.63.0). */
[data-theme="dark"] .topbar:has([data-menubar]) {
  --tb-chip: var(--raise-grad);
  --tb-chip-shadow: var(--raise-shadow);
}.menubar-menu {
  display: inline-flex; align-items: center; flex: none; gap: 1px;
  padding: calc(2px - var(--menubar-group-border-width, 1px)); border-radius: var(--radius-sm);
  border: var(--menubar-group-border-width, 1px) solid var(--menubar-group-border, var(--raise-border));
  background: var(--menubar-group-bg, var(--raise-grad)); box-shadow: var(--menubar-group-shadow, var(--raise-shadow));
}
.menubar-glyph {
  padding: 0; border: 0; background: none;
  flex: none; width: 26px; height: 26px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  color: color-mix(in srgb, var(--text) 80%, var(--surface-alt));
}
.menubar-glyph::before {
  content: ""; width: 16px; height: 16px; background: currentColor;
  -webkit-mask: var(--glyph-menu) center / contain no-repeat;
  mask: var(--glyph-menu) center / contain no-repeat;
}
.menubar-title {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border: 0; border-radius: var(--radius-xs);
  background: none; cursor: pointer; white-space: nowrap;
  font: 600 var(--text-sm) var(--font); color: color-mix(in srgb, var(--text) 80%, var(--surface-alt));
}
.menubar-title.menubar-brand { color: var(--text); }
/* The front menu's titles come from whatever is in front, and a wordy one
   is cut short with an ellipsis at 12em rather than widening the bar; its
   whole words stay its name, and the script makes them its tooltip
   (0.80.0). */
.menubar-front > .menubar-title > .menubar-words { display: block; max-width: var(--menubar-title-max, 12em); overflow: hidden; text-overflow: ellipsis; }
/* While the menu bar shows as a ribbon, a toolbar marked to give way to it
   is hidden (0.80.0). */
:root[data-menubar-ribbon-on] .toolbar[data-toolbar-ribbon="hide"] { display: none; }
.menubar-title > .glyph { flex: none; width: 16px; height: 16px; }
/* Short of room, a title with a glyph shows its glyph alone (0.79.0). */
.menubar-row.menubar-title-glyphs .menubar-title.menubar-glyphed > .menubar-words { display: none; }
.menubar-title.menubar-brand img { height: 18px; width: auto; }
.menubar-title:hover { color: var(--text); background: color-mix(in srgb, var(--text) 12%, transparent); }
.menubar-title[aria-expanded="true"] {
  color: var(--text);
  background: var(--raise-active-bg);
  box-shadow: var(--raise-active-shadow);
}
.menubar-title:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.menubar-one .menubar-title { padding: 0; }
.menubar-row:not(.menubar-collapsed) > .menubar-one,
.menubar-row.menubar-collapsed > .menubar-menu:not(.menubar-one) { display: none; }
/* The pins group, between the site's and the applet's: the menu glyph,
   as every group has, so it is known for a menu; the Pins menu, its title
   marked by the pin glyph; then a button for each pinned item, which is a
   link to the item and shows its icon and title. When the buttons do not
   fit they all give way to one Items menu, before the bar itself
   collapses. */
.menubar-pin {
  display: inline-flex; align-items: center; gap: 6px; max-width: 12rem;
  padding: 4px 10px; border-radius: var(--radius-xs); white-space: nowrap;
  font: 600 var(--text-sm) var(--font); text-decoration: none;
  color: color-mix(in srgb, var(--text) 80%, var(--surface-alt));
}
.menubar-pin > span:not(.glyph) { overflow: hidden; text-overflow: ellipsis; }
.menubar-pin img, .menubar-pin .glyph, :is(.menubar-panel, .menu-panel) .menubar-icon { flex: none; width: 16px; height: 16px; }
:is(.menubar-panel, .menu-panel) .menubar-icon { display: inline-flex; }
:is(.menubar-panel, .menu-panel) .menubar-icon > * { width: 100%; height: 100%; }
/* A row's mark, such as a window's state mark, stands after its words. */
.menu-action > .menu-mark { display: inline-flex; align-items: center; flex: none; }
.menubar-pin:hover { color: var(--text); background: color-mix(in srgb, var(--text) 12%, transparent); }
.menubar-pin:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--focus-ring); }
.menubar-row.menubar-pins-collapsed .menubar-pin,
.menubar-row:not(.menubar-pins-collapsed) .menubar-pins-more { display: none; }

/* Its panels are menu panels. A command's shortcut stands at its end, a
   submenu's row carries a caret, and a command that switches carries a
   tick while it is on. */
:is(.menubar-panel, .context-menu) .menu-action { min-width: 13rem; }
.menubar-label { flex: 1 1 auto; min-width: 0; }
.menubar-words { display: block; }
/* A row's second line: its detail, or why it is disabled. */
.menubar-detail { display: block; font-size: var(--text-xs); color: var(--text-muted); white-space: normal; }
/* Each title's and row's letter is underlined while it can be typed: in
   the titles while the access-key modifier is held or focus is in the
   bar, and in a panel opened from the keyboard. */
.mnemonic { text-decoration: none; text-underline-offset: 2px; }
.menubar-row.menubar-letters .mnemonic, .menubar-row:focus-within .mnemonic,
.menubar-panel[data-keyboard] .mnemonic { text-decoration: underline; }
.menubar-shortcut { margin-inline-start: var(--space-4); color: var(--text-muted); font-size: var(--text-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }
.menubar-has-sub::after {
  content: ""; flex: none; width: 10px; height: 10px; margin-inline-start: var(--space-3); background: currentColor;
  -webkit-mask: var(--glyph-caret) center / contain no-repeat;
  mask: var(--glyph-caret) center / contain no-repeat;
  transform: rotate(-90deg);
}
.menubar-has-sub:dir(rtl)::after { transform: rotate(90deg); }
.menubar-back::before {
  content: ""; flex: none; width: 12px; height: 12px; background: currentColor;
  -webkit-mask: var(--glyph-back) center / contain no-repeat;
  mask: var(--glyph-back) center / contain no-repeat;
}
:is(.menubar-panel, .context-menu) .menu-action[aria-checked]::before {
  content: ""; flex: none; width: 12px; height: 12px; background: currentColor;
  -webkit-mask: var(--glyph-tick) center / contain no-repeat;
  mask: var(--glyph-tick) center / contain no-repeat;
  visibility: hidden;
}
:is(.menubar-panel, .context-menu) .menu-action[aria-checked="true"]::before { visibility: visible; }
:is(.menubar-panel, .context-menu):has(> .menu-action[aria-checked]) > .menu-action:not([aria-checked]):not(.menubar-back) {
  padding-inline-start: calc(14px + 12px + var(--space-2));
}
.menubar-panel a.menu-action { text-decoration: none; }
/* A disabled row is faded as well as muted, as a disabled button is, so
   it is told from a live row at a glance; its reason line fades with it
   and stays readable. */
:is(.menubar-panel, .context-menu) .menu-action[aria-disabled="true"] { color: var(--text-muted); opacity: .5; cursor: default; }
:is(.menubar-panel, .context-menu) .menu-action[aria-disabled="true"]:hover { background: none; }
/* A context menu (0.74.0) is a menu panel opened at a point, whose rows
   are the menu bar's: words, a second line, a tick, a key shown and a
   submenu's caret. A row's glyph, where it has one, is muted and at the
   tick's size, and a menu bar's panel draws its rows' glyphs the same way
   (0.77.0). A menu bar row's icon may also be a picture, such as an
   applet's own, which keeps 16px, so a menu bar panel's column is 16px
   and a context menu's 14px. Every other menu panel, such as a menu
   button's or a folded toolbar group's, lines its rows up the same way
   (0.78.0), a switching row ticked by aria-checked or aria-pressed. */
.menu-panel { --menu-icon-col: 14px; }
.menubar-panel { --menu-icon-col: 16px; }
.menu-panel .menu-action > .glyph { --glyph-size: 14px; flex: none; color: var(--text-muted); }
.menubar-panel .menu-action > .glyph.menubar-icon { width: 14px; height: 14px; margin-inline: 1px; }
/* Where any row has a glyph, the glyph, a switching row's tick and a
   destructive row's warning share one column, and a row with none of
   them keeps the column empty, so every row's words start in one line.
   A row with no words, such as a zone of the windows' layout picker, and
   a segmented control's choice keep their own shape. */
.menu-panel:has(> .menu-action > :is(.glyph, .menubar-icon)) > .menu-action:has(> :is(.glyph, .menubar-icon)):not([aria-checked], [aria-pressed]):not(.menubar-back):not(:empty):not(.seg-choice),
.menu-panel:has(> .menu-action > :is(.glyph, .menubar-icon)) > .menu-action:not(:empty):not(.seg-choice) { padding-inline-start: 14px; }
.menu-panel:has(> .menu-action > :is(.glyph, .menubar-icon)) > .menu-action:not(:has(> :is(.glyph, .menubar-icon))):not([aria-checked], [aria-pressed]):not(.danger):not(.menubar-back):not(:empty):not(.seg-choice) {
  padding-inline-start: calc(14px + var(--menu-icon-col) + var(--space-2));
}
.menu-panel:has(> .menu-action > :is(.glyph, .menubar-icon)) > .menu-action:is([aria-checked], [aria-pressed])::before,
.menu-panel:has(> .menu-action > :is(.glyph, .menubar-icon)) > .menu-action.danger:not(:has(> :is(.glyph, .menubar-icon)))::before {
  width: var(--menu-icon-col); height: 14px;
  -webkit-mask-size: 14px; mask-size: 14px;
}

/* The element a context menu is for, marked by the page with
   detail.mark(element) or openAt's for, keeps the look of a row under the
   pointer while the menu is open, so the reader can see what its commands
   act on once the pointer has moved into the menu (0.76.0). The rule has
   no weight, so a component with a hover look of its own restyles
   [data-menu-for] as it does :hover. */
:where([data-menu-for]) { background: var(--surface-alt); }
.data-table tbody tr[data-menu-for] { background: color-mix(in srgb, var(--text) 4%, transparent); }
@media (forced-colors: active) {
  :where([data-menu-for]) { outline: 2px solid Highlight; outline-offset: -2px; }
}
/* A row's actions button (0.81.0): a small raised glyph button at the
   row's end that opens the row's context menu. It shows only on the row
   under the pointer, the selected or current row, a row with focus in it
   and a row whose menu is open, and elsewhere keeps its place empty, so
   nothing moves as it comes and goes. Left empty, it shows the more
   glyph. pudl-menu.js marks the rows as the pointer and focus move. */
[data-row-actions] {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 24px; height: 24px; padding: 0; margin-inline-start: auto;
  border-radius: var(--radius-sm); border: 1px solid var(--raise-border);
  background: var(--raise-grad); box-shadow: var(--raise-shadow);
  color: var(--text-muted); cursor: pointer;
  visibility: hidden;
}
[data-row-actions]:empty::before {
  content: ""; width: 16px; height: 16px; background: currentColor;
  -webkit-mask: var(--glyph-more) center / contain no-repeat;
  mask: var(--glyph-more) center / contain no-repeat;
}
[data-row-actions]:hover { color: var(--text); background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
[data-row-actions]:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
:is([data-row-hot], [data-row-focus], [data-menu-for], [aria-selected="true"], [aria-current]:not([aria-current="false"]),
    :has(> [aria-selected="true"], > [aria-current]:not([aria-current="false"]))) > [data-row-actions],
tr:is([data-row-hot], [data-row-focus], [data-menu-for], [aria-selected="true"], [aria-current]:not([aria-current="false"])) > :is(td, th) > [data-row-actions] { visibility: visible; }
[data-menu-for] > [data-row-actions],
tr[data-menu-for] > :is(td, th) > [data-row-actions] { color: var(--text); background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
@media (forced-colors: active) {
  [data-row-actions] { border-color: ButtonText; }
  [data-row-actions]:empty::before { forced-color-adjust: none; background: ButtonText; }
}
@media (forced-colors: active) {
  .menubar-menu { border: 1px solid CanvasText; }
  .menubar-title[aria-expanded="true"] { forced-color-adjust: none; background: Highlight; color: HighlightText; }
  .menubar-glyph::before, .menubar-has-sub::after, .menubar-back::before,
  :is(.menubar-panel, .context-menu) .menu-action[aria-checked]::before { forced-color-adjust: none; background: CanvasText; }
}

/* A menu button on the topbar may hold its words in a .menu-btn-label,
   which a phone then cuts short with an ellipsis rather than letting the
   button widen the chrome. */
.menu-btn-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 640px) {
  .topbar { --tb-pad-y: 8px; padding: var(--tb-pad-y) 12px; }
  /* On a phone the tabs take a row of their own, the last, so they still
     stand on the topbar's edge above the page, and scroll sideways. */
  .topbar-tabs { order: 1; flex: 1 0 100%; }
  .topbar .menu-btn:has(> .menu-btn-label) { max-width: 10rem; }
  .topbar-pill:has(> .glyph) { position: relative; padding-inline: 8px; }
  .topbar-pill:has(> .glyph) > .topbar-pill-label {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}
/* The theme toggle shares the chip styling on the topbar chrome. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  padding: 0; border-radius: var(--radius-sm);
  font-size: var(--text-md); line-height: 1; font-family: var(--font);
  color: var(--tb-chrome-fg);
  background: var(--tb-chip); box-shadow: var(--tb-chip-shadow);
  border: 0; cursor: pointer;
}
/* The glyph is drawn, so the button is left empty with an aria-label; any
   character a page left in it from before 0.34 is hidden. */
.theme-toggle { font-size: 0; }
.theme-toggle::before {
  content: ""; width: 16px; height: 16px; background: currentColor;
  -webkit-mask: var(--glyph-theme) center / contain no-repeat;
  mask: var(--glyph-theme) center / contain no-repeat;
}
.theme-toggle:active { box-shadow: var(--tb-toggle-active-shadow, inset 0 1px 3px color-mix(in srgb, #000 35%, transparent)); }
.theme-toggle:hover { color: var(--tb-chrome-hover-fg); }

/* === The status area =====================================================
   The icons of what runs in the background, last in the topbar's chrome:
   one raised group holding flat items that take their press from it. On a
   topbar with a menu bar it is drawn exactly as the menu bar's groups and
   titles beside it are; without one
   the topbar may be dark in the light theme, so the group takes the
   topbar's chip colours instead. Either way it is 30px tall, as a group
   is: a 26px item in 1px of padding and a 1px border. An item is a glyph
   or a 24px picture, which may be round, since the group is what is
   raised, or the account glyph standing in for a picture, and an optional
   badge; a badge with nothing to say is empty or hidden, and is not shown.
   The host keeps each item's accessible name in step with its badge,
   which is hidden from assistive technology. */
.status-area {
  order: 1;
  display: inline-flex; align-items: center; flex: none; gap: 1px;
  padding: 1px; border-radius: var(--radius-sm);
  border: 1px solid var(--tb-border, var(--raise-border));
  background: var(--tb-chip); box-shadow: var(--tb-chip-shadow);
}
.status-item {
  box-sizing: border-box; display: inline-flex; align-items: center; gap: 4px; height: 26px;
  padding: 0 6px; border-radius: var(--radius-xs);
  font: 600 var(--text-sm) var(--font); white-space: nowrap; text-decoration: none;
  color: var(--tb-chrome-fg);
  background: none; border: 0; cursor: pointer;
}
.status-item > .glyph { --glyph-size: 16px; }
.status-item > :is(img, .status-picture) { width: 24px; height: 24px; flex: none; object-fit: cover; }
.status-item > .glyph.status-picture { --glyph-size: 24px; }
.status-item:has(> :is(img, .status-picture)) { padding-inline: 1px; }
.status-item:has(> :is(img, .status-picture)):has(> :not(img, .status-picture, .badge)) { padding-inline-end: 6px; }
.status-item > .badge:is(:empty, [hidden]) { display: none; }
/* A badge mixes its status colour into the text colour of what it sits
   on, which here is the topbar's, dark in the light theme unless a menu
   bar has lightened it. */
.status-item > .badge:is(.warn, .danger, .positive, .accent) { color: color-mix(in srgb, var(--badge-color) 62%, var(--tb-fg)); }
.status-item:hover { color: var(--tb-chrome-hover-fg); background: color-mix(in srgb, var(--tb-fg) 12%, transparent); }
.status-item:active { color: var(--tb-chrome-hover-fg); background: color-mix(in srgb, #000 24%, var(--tb-bg)); box-shadow: var(--tb-active-shadow, inset 0 1px 3px color-mix(in srgb, #000 50%, transparent)); }
.status-item:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--tb-focus-ring, var(--focus-ring)); }
.topbar:has([data-menubar]) .status-item:focus-visible { box-shadow: 0 0 0 2px var(--focus-ring); }
/* Beside a menu bar: the group's surface and the titles' colours. */
.topbar:has([data-menubar]) .status-area {
  border: var(--menubar-group-border-width, 1px) solid var(--menubar-group-border, var(--raise-border));
  padding: calc(2px - var(--menubar-group-border-width, 1px));
  background: var(--menubar-group-bg, var(--raise-grad)); box-shadow: var(--menubar-group-shadow, var(--raise-shadow));
}
.topbar:has([data-menubar]) .status-item { color: color-mix(in srgb, var(--text) 80%, var(--surface-alt)); }
.topbar:has([data-menubar]) .status-item:hover { color: var(--text); background: color-mix(in srgb, var(--text) 12%, transparent); }
.topbar:has([data-menubar]) .status-item:active { color: var(--text); background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.topbar:has([data-menubar]) .status-item > .badge:is(.warn, .danger, .positive, .accent) { color: color-mix(in srgb, var(--badge-color) 62%, var(--text)); }

/* === Section tabs: a notebook ============================================
   After the notebook widget of desktop toolkits. The bar is a recessed
   band. Each tab the reader can go to is a raised tab standing on the
   band's baseline, because it can be pressed. The tab for where the reader
   is, marked aria-current, is flat and lit, a little taller, and open at
   the bottom into the content below, so the page visibly hangs from it.

   The bar's baseline is drawn as part of the bar's own background, and the
   current tab paints over it from inside the bar. Nothing reaches past the
   bar's edge, which matters because the bar scrolls sideways on a narrow
   screen, and a box that scrolls clips whatever reaches past its edges.

   The other tabs stand 1px above the bar's bottom edge, on the baseline
   rather than over it, so the baseline runs unbroken in front of them and
   only the current tab, which reaches the edge, opens it (0.89.2).

   The bar is always as tall as it is with a current tab. Its top padding
   holds the 4px the current tab stands above the others, and the current
   tab rises into it with a negative margin, so a page with no current tab
   has a bar of the same height and the content below does not jump. */
.app-section-bar, .tablist {
  display: flex; flex: none; align-items: flex-end; gap: var(--space-1);
  padding: 9px var(--space-4) 0;
  overflow-x: auto;
  background: var(--recess-bg);
  box-shadow: inset 0 -1px var(--border), var(--recess-shadow);
}
.section-tab, .tablist > [role="tab"] {
  padding: 5px 14px 4px;
  font: 600 var(--text-sm) var(--font);
  color: var(--text); text-decoration: none;
  white-space: nowrap; cursor: pointer;
  background: var(--raise-grad);
  border: 1px solid var(--raise-border); border-bottom: 0;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  box-shadow: var(--surface-edge-shadow, inset 0 1px color-mix(in srgb, var(--light) var(--lit), transparent));
  margin-bottom: 1px;
}
.section-tab:hover, .tablist > [role="tab"]:hover { background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.section-tab:active, .tablist > [role="tab"]:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.section-tab:focus-visible, .tablist > [role="tab"]:focus-visible { outline: none; box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.section-tab[aria-current]:not([aria-current="false"]),
.section-tab.active,
.tablist > [role="tab"][aria-selected="true"] {
  padding: 7px 14px 6px;
  margin-top: -5px; margin-bottom: 0;
  color: var(--text);
  background: var(--section-current-bg);
  border-color: var(--border);
  box-shadow: none;
}
.section-tab[aria-current]:not([aria-current="false"]):focus-visible,
.section-tab.active:focus-visible,
.tablist > [role="tab"][aria-selected="true"]:focus-visible { box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }

/* === Tabs within a page ==================================================
   The ARIA tab pattern: a .tablist of role="tab" buttons over role="tabpanel"
   panels, inside a .tabs wrapper. They look like section tabs, since to the
   reader both are tabs; the difference is that section tabs go to other
   addresses and these switch panels in place. pudl-tabs.js shows the chosen
   panel, moves between tabs with the arrow keys, and keeps the chosen
   panel in the address's fragment, so it can be linked to and survives a
   reload. Without the script the tab list is hidden and every panel shows,
   one after another. */
.tabs { --section-current-bg: var(--surface); }
.tabs:not(.tabs-ready) > .tablist { display: none; }
.tabs > [role="tabpanel"] {
  padding: var(--space-4);
  background: var(--section-current-bg);
  border: 1px solid var(--border); border-top: 0;
}
.tabs:not(.tabs-ready) > [role="tabpanel"] { border-top: 1px solid var(--border); margin-bottom: var(--space-3); }
.tabs > [role="tabpanel"]:focus-visible { outline: none; box-shadow: inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }

/* Tabs that keep one size (0.71.0). In a dialog, which takes its size from
   its content, every panel is laid in the same place and the panels not
   chosen are kept out of sight rather than out of the layout, so the tab
   set is as large as its largest panel and choosing a tab never changes
   the dialog's size or moves it. A panel out of sight cannot be seen,
   focused, found or reached by assistive technology, as one not laid out
   cannot. Tabs anywhere else do the same when marked
   data-tabs-size="largest", and a dialog's tabs that should follow each
   panel's size are marked data-tabs-size="each". pudl-windows.css does the
   same for a window's tabs. */
dialog .tabs.tabs-ready:not([data-tabs-size="each"]),
.tabs.tabs-ready[data-tabs-size="largest"] {
  display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr);
}
dialog .tabs.tabs-ready:not([data-tabs-size="each"]) > [role="tabpanel"],
.tabs.tabs-ready[data-tabs-size="largest"] > [role="tabpanel"] { grid-row: 2; grid-column: 1; min-width: 0; }
dialog .tabs.tabs-ready:not([data-tabs-size="each"]) > [role="tabpanel"][hidden],
.tabs.tabs-ready[data-tabs-size="largest"] > [role="tabpanel"][hidden] { display: block !important; visibility: hidden; }

/* A dialog's tabs reach its sides (0.71.0): the bar runs from edge to edge
   under the title, as the head and the actions do, and the panels take
   the dialog's own colour and padding, with the dialog's border as theirs. */
dialog.dialog > .tabs, dialog.dialog > form > .tabs {
  --section-current-bg: var(--dialog-bg, var(--surface));
  margin-inline: calc(-1 * var(--space-5));
}
dialog.dialog > .tabs > [role="tabpanel"], dialog.dialog > form > .tabs > [role="tabpanel"] {
  border: 0; padding: var(--space-4) var(--space-5);
}

/* === Document tabs =======================================================
   Tabs that come and go, one per open document, record or view, as in an
   editor: a .tablist.doc-tabs, so they look like every other tab. A tab
   may carry a .doc-tab-dirty mark, a dot whose words, "unsaved" say, are
   heard by assistive technology and hidden from the eye, and a
   .doc-tab-close, a small raised button drawn inside the tab and hidden
   from assistive technology, since a tab list may hold only tabs; the
   Delete key closes the focused tab for the keyboard. pudl-tabs.js turns
   both into pudl:tab-close, and the host decides. */
.doc-tabs > [role="tab"] { display: inline-flex; align-items: center; gap: 6px; }
.doc-tabs > [role="tab"]:has(.doc-tab-close) { padding-inline-end: 6px; }
.doc-tab-dirty {
  flex: none; width: 10px; height: 10px; overflow: hidden;
  font-size: 0; color: transparent;
  background: var(--accent);
  -webkit-mask: var(--glyph-circle) center / contain no-repeat;
  mask: var(--glyph-circle) center / contain no-repeat;
}
.doc-tab-close {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: var(--dismiss-radius, 50%);
  border: 1px solid var(--raise-border); background: var(--raise-grad); box-shadow: var(--raise-shadow);
  color: var(--text-muted); cursor: pointer;
}
.doc-tab-close::before {
  content: ""; width: 9px; height: 9px; background: currentColor;
  -webkit-mask: var(--glyph-close) center / contain no-repeat;
  mask: var(--glyph-close) center / contain no-repeat;
}
.doc-tab-close:hover { color: var(--danger); background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.doc-tab-close:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }

/* A region that pudl-regions.js is replacing dims a little while the new
   content is on its way, after a moment, so a fast answer never flickers. */
[data-region][aria-busy="true"] { opacity: .6; transition: opacity .15s .12s; cursor: progress; }
/* A window whose applet is waiting to start, or is not yet ready, is busy
   in the same way (0.66.0): the applet is dimmed and shows the progress
   cursor, and an applet still waiting is hidden behind its loading line. */
.win[aria-busy="true"] > .win-body { cursor: progress; }
.win[aria-busy="true"] [data-applet][data-applet-busy] { opacity: .6; transition: opacity .15s .12s; }
[data-applet][data-applet-state="waiting"] { display: none; }

/* === Buttons (raised; one look) ========================================= */
/* Every button is raised. There is no flat. Disabled is dimmed.
   At most one .btn-primary per visible context. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px 14px; border-radius: var(--button-radius, var(--radius-sm));
  font-size: var(--text-md); font-weight: 600; font-family: var(--font);
  color: var(--text); cursor: pointer; text-decoration: none;
  white-space: nowrap;
  border: 1px solid var(--raise-border);
  background: var(--raise-grad);
  box-shadow: var(--raise-shadow);
  transition: background .1s, border-color .1s, box-shadow .1s;
}
.btn:hover {
  background: var(--raise-grad-hover);
  border-color: var(--raise-border-hover);
  text-decoration: none;
}
.btn:active {
  background: var(--raise-active-bg);
  box-shadow: var(--raise-active-shadow);
}
/* A disclosure button shows or hides content in place, beneath it or
   beside it, and says which with aria-expanded and the chevron glyph after
   its words, pointing down while the content is hidden and up while it is
   shown. It stays a raised button;
   it does not latch, since the chevron says its state. */
.btn.disclosure::after {
  content: ""; flex: none; width: 16px; height: 16px; background: currentColor;
  -webkit-mask: var(--glyph-chevron) center / contain no-repeat;
  mask: var(--glyph-chevron) center / contain no-repeat;
  transition: transform .15s ease;
}
.btn.disclosure[aria-expanded="true"]::after { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .btn.disclosure::after { transition: none; } }
/* The chevron is a mask over its own background, which high-contrast mode
   would replace, so it takes the button's text colour there (0.80.0). */
@media (forced-colors: active) { .btn.disclosure::after { forced-color-adjust: none; background: ButtonText; } }
.btn:focus-visible {
  outline: none;
  box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
/* A toggle button latched on stays pressed in. aria-pressed carries the
   state to assistive technology as well as to the eye, and the state shows
   through elevation, not colour. A project flips the attribute. */
.btn[aria-pressed="true"],
.btn[aria-pressed="true"]:hover {
  background: var(--raise-active-bg);
  border-color: var(--raise-border-hover);
  box-shadow: var(--raise-active-shadow);
}
.btn[aria-pressed="true"]:focus-visible {
  box-shadow: var(--raise-active-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
/* Every small raised key is 28px tall, a glyph button's height, whatever
   it holds, words or a glyph, so small buttons, small segmented controls
   and glyph buttons line up in a toolbar (0.74.1). */
.btn-sm { box-sizing: border-box; min-height: 28px; padding: 0 10px; font-size: var(--text-xs); }
.btn-primary {
  color: var(--on-accent);
  border-color: color-mix(in srgb, var(--accent) 62%, #000);
  background: var(--primary-fill, linear-gradient(color-mix(in srgb, var(--accent) 78%, #fff), var(--accent)));
  box-shadow: var(--shadow-btn-primary);
}
.btn-primary:hover {
  color: var(--on-accent);
  background: var(--primary-hover-fill, linear-gradient(color-mix(in srgb, var(--accent) 88%, #fff), color-mix(in srgb, var(--accent) 94%, #000)));
}
.btn-primary:active {
  background: color-mix(in srgb, var(--accent) 86%, #000);
  box-shadow: var(--primary-active-shadow, inset 0 1px 3px rgba(0, 0, 0, 0.32));
}
/* A primary button latched on keeps its accent and its text, and is
   pressed in: the flat accent without the lit gradient, shaded from the
   top. It is not darkened, because the dark theme's accent carries dark
   text, which a darker fill would swallow; the change of elevation is
   what says it is on, as for any latched button. */
.btn-primary[aria-pressed="true"],
.btn-primary[aria-pressed="true"]:hover {
  color: var(--on-accent);
  background: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 62%, #000);
  box-shadow: var(--primary-pressed-shadow, inset 0 2px 4px rgba(0, 0, 0, 0.38));
}
.btn-primary[aria-pressed="true"]:focus-visible {
  box-shadow: var(--primary-pressed-shadow, inset 0 2px 4px rgba(0, 0, 0, 0.38)), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
.btn-danger {
  color: var(--danger-rest-fg, var(--danger)); border-color: color-mix(in srgb, var(--danger) 35%, var(--raise-border));
}
.btn-danger:hover {
  color: var(--on-accent);
  background: var(--danger-hover-fill, linear-gradient(color-mix(in srgb, var(--danger) 78%, #fff), var(--danger)));
  border-color: color-mix(in srgb, var(--danger) 62%, #000);
}

/* A disabled button of any kind looks like a disabled plain button: the
   neutral raised face, the ordinary text colour and border, dimmed. It
   matches :disabled, so a button disabled by its fieldset is included,
   and it comes after the kinds, with a second selector for the hovered,
   pressed and latched states, so that nothing a kind sets shows through.
   A link styled as a button cannot be disabled, so it is marked
   aria-disabled, and it then also takes no clicks. */
.btn:is(:disabled, .is-disabled, [aria-disabled="true"]),
.btn:is(:disabled, .is-disabled, [aria-disabled="true"]):is(:hover, :active, [aria-pressed="true"]) {
  opacity: .45; cursor: not-allowed;
  color: var(--text); border-color: var(--raise-border);
  background: var(--raise-grad); box-shadow: var(--raise-shadow);
}
.btn[aria-disabled="true"] { pointer-events: none; }

/* Icon button: raised square for row actions (drag, remove, archive). */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: var(--radius-sm);
  background: var(--raise-grad); border: 1px solid var(--raise-border);
  box-shadow: var(--raise-shadow);
  color: var(--text-muted); cursor: pointer;
}
.icon-btn:hover { color: var(--text); background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.icon-btn:focus-visible { outline: none; box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
/* A glyph button latched on, as a toolbar's is while the cursor is in
   what it gives, stays pressed in, as a toggle button does. */
.icon-btn[aria-pressed="true"], .icon-btn[aria-pressed="true"]:hover { color: var(--text); background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.icon-btn[aria-pressed="true"]:focus-visible { box-shadow: var(--raise-active-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.icon-btn.danger:hover { color: var(--danger); }
/* Pressed in while it is pressed, like every raised control. */
.icon-btn:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.icon-btn:disabled, .icon-btn[aria-disabled="true"] {
  opacity: .45; cursor: not-allowed; pointer-events: none;
  background: var(--raise-grad); box-shadow: var(--raise-shadow);
}

/* === Links ============================================================== */
a, .link {
  color: var(--accent); text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: 2px;
}
a:hover, .link:hover {
  color: var(--accent-hover);
  text-decoration-thickness: 2px;
}
.link-muted {
  color: var(--text-muted); text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: 2px;
}
.link-muted:hover { color: var(--text); }
button.link {
  border: 0; background: none; padding: 0; font: inherit; cursor: pointer;
  color: var(--accent); text-decoration: underline;
}

/* === Forms ============================================================== */
.form-group { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--space-4); }
.form-label { font-size: var(--text-sm); font-weight: 600; color: var(--text); }
/* A label's line may end in a command on the field, a small raised control
   such as a menu that inserts into the text: the label at the start, the
   command at the end on the label's baseline, and under the label when the
   line is too narrow for both. */
.form-label-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px var(--space-3); min-width: 0; }
.form-label-row > .form-label { flex: 1 0 auto; }
.form-label-end { display: inline-flex; flex-wrap: wrap; gap: var(--space-1); align-items: center; }
.form-input, .form-select, .form-textarea {
  padding: 8px 11px; font-size: var(--text-md); font-family: var(--font);
  background: var(--input-bg); color: var(--text);
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  width: 100%;
  box-shadow: var(--entry-shadow);
}
/* A one-line field is one height whatever its type, because browsers draw
   a select or a date field a little taller than a text field, and fields
   side by side in a form should line up. A select with multiple or size
   shows a list, so it keeps its own height. The minimum grows with text
   enlargement so the line and vertical padding remain visible. */
.form-input, .form-select:not([multiple], [size]) { height: max(36px, calc(1.25em + 18px)); line-height: 1.25; }
.form-textarea { min-height: 5em; resize: vertical; line-height: 1.5; }
/* A select in a .form-select-wrap is drawn by PUDL: the browser's own
   drawing is turned off, since WebKit's is raised and lit, and the wrap
   draws the opener, the caret glyph in the select's text colour, which a
   select cannot carry itself. A select showing several rows has no
   opener. A select outside a wrap keeps the browser's drawing. */
.form-select-wrap { position: relative; display: block; min-width: 0; color: var(--text); }
.form-select-wrap > .form-select { appearance: none; }
.form-select-wrap > .form-select:not([multiple], [size]) { padding-inline-end: 34px; }
.form-select-wrap:has(> .form-select:not([multiple], [size]))::after {
  content: ""; position: absolute; inset-inline-end: 11px; top: 50%;
  width: 12px; height: 12px; margin-top: -6px; pointer-events: none;
  background: currentColor;
  -webkit-mask: var(--glyph-caret) center / contain no-repeat;
  mask: var(--glyph-caret) center / contain no-repeat;
}
.form-select-wrap:has(> .form-select:disabled)::after { opacity: .55; }

/* A combo box (0.72.0) is a field the reader types in with a list of
   suggestions, a .combo holding the input and its datalist; a searchable
   select, a select marked data-combo, becomes one too. pudl-combo.js adds
   the opener at the field's end, a raised strip inside the field's
   border, as every keyed field has (see "Fields with keys" below),
   because it is pressed while the field is typed in. A select is drawn
   with its caret inside, since the whole select opens it. The list is
   drawn as a menu panel, of options, the one the field's value matches
   ticked, the one the keyboard is on lit as a focused menu row is. */
.combo { position: relative; display: flex; align-items: stretch; min-width: 0; }
.combo > .form-input { flex: 1; min-width: 0; }
.combo-open { flex: none; width: 34px; height: auto; }
.combo-open::before {
  content: ""; width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: var(--glyph-caret) center / contain no-repeat;
  mask: var(--glyph-caret) center / contain no-repeat;
}
.combo-open[aria-expanded="true"] { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.combo:has(> .form-input:disabled) > .combo-open { pointer-events: none; }
.combo:has(> .form-input[readonly]) > .combo-open { display: none; }
.combo-list {
  position: fixed; inset: auto; margin: 0;
  min-width: 0; max-width: min(28rem, calc(100vw - 16px));
  max-height: min(22rem, calc(100svh - 16px));
}
.combo-row { cursor: pointer; }
.combo-row::before {
  content: ""; flex: none; width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: var(--glyph-tick) center / contain no-repeat;
  mask: var(--glyph-tick) center / contain no-repeat;
  visibility: hidden;
}
.combo-row[aria-selected="true"]::before { visibility: visible; }
.combo-row.is-active { background: var(--menu-row-hover, var(--surface-alt)); box-shadow: inset 3px 0 var(--accent); }
.combo-row.is-active:dir(rtl) { box-shadow: inset -3px 0 var(--accent); }
.combo-row mark { background: none; color: inherit; font-weight: 700; }
.combo-note { padding: 6px 14px; font-size: var(--text-sm); color: var(--text-muted); }
@media (forced-colors: active) {
  .combo-open::before, .combo-row::before { forced-color-adjust: none; background: CanvasText; }
  .combo-row.is-active { forced-color-adjust: none; background: Highlight; color: HighlightText; }
}

/* List boxes (0.74.0). A list box is a select showing several rows,
   marked data-listbox, which pudl-listbox.js draws as a sunken list,
   since the reader puts a choice into it as into any field. Each row is
   drawn as a menu row is, with a check box where several may be chosen
   and a radio button where one may, because those are the controls that
   invite a press to set state. The row the keyboard is on is lit as a
   focused menu row is, with the accent bar on its start edge, but only
   while the list has the focus. The list is as many rows tall as the
   select's size, from --listbox-rows, and scrolls beyond that. */
.listbox {
  --listbox-row: 30px;
  --menu-row-hover: color-mix(in srgb, var(--text) 8%, var(--input-bg));
  display: block; width: 100%;
  height: calc(var(--listbox-rows, 6) * var(--listbox-row) + 10px);
  overflow-y: auto; overscroll-behavior: contain;
  padding: 4px 0;
  background: var(--input-bg); color: var(--text);
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  box-shadow: var(--entry-shadow);
}
.listbox:focus {
  border-color: var(--accent); outline: none;
  box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
.listbox-row {
  display: flex; align-items: center; gap: var(--space-2);
  min-height: var(--listbox-row); padding: 0 12px;
  font-size: var(--text-sm); line-height: 1.3;
  cursor: pointer; user-select: none;
}
.listbox-row > input {
  flex: none; width: 16px; height: 16px; margin: 0;
  accent-color: var(--accent); pointer-events: none;
}
.listbox-text { min-width: 0; overflow-wrap: anywhere; }
.listbox-row:hover { background: var(--menu-row-hover); }
.listbox:focus .listbox-row.is-active { background: var(--menu-row-hover); box-shadow: inset 3px 0 var(--accent); }
.listbox:focus .listbox-row.is-active:dir(rtl) { box-shadow: inset -3px 0 var(--accent); }
.listbox-row[aria-disabled="true"] { cursor: not-allowed; }
.listbox-row[aria-disabled="true"] > .listbox-text { opacity: .55; }
/* An optgroup's label heads its rows, as a menu's section label does, and
   its rows stand in from it. */
.listbox-group {
  display: flex; align-items: center; min-height: var(--listbox-row); padding: 0 12px;
  font-size: var(--text-2xs); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted);
}
.listbox [role="group"] > .listbox-row { padding-inline-start: 24px; }
/* A rich list box's row (0.97.0) holds a title, a second line in muted
   small text, and a badge at its end, each held to one line; the check
   box or radio button stands beside both lines. */
.listbox:has(.listbox-title) { --listbox-row: 44px; }
.listbox-row:has(> .listbox-title) {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  column-gap: var(--space-2); row-gap: 0; align-content: center; padding-block: 4px;
}
.listbox-row:has(> .listbox-title) > input { grid-column: 1; grid-row: 1 / span 2; }
.listbox-title, .listbox-detail { grid-column: 2; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.listbox-detail { font-size: var(--text-xs); color: var(--text-muted); }
.listbox-row > .badge { grid-column: 3; grid-row: 1 / span 2; justify-self: end; }
.listbox-row[aria-disabled="true"] > :is(.listbox-title, .listbox-detail, .badge) { opacity: .55; }
.listbox[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }
.listbox[aria-disabled="true"] .listbox-row { cursor: not-allowed; }
.listbox[aria-disabled="true"] .listbox-row:hover { background: none; }
.listbox[aria-invalid="true"] { border-color: var(--danger); }
.listbox[aria-invalid="true"]:focus { border-color: var(--danger); box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring-danger); }
@media (forced-colors: active) {
  .listbox { border: 1px solid CanvasText; }
  .listbox:focus .listbox-row.is-active { forced-color-adjust: none; background: Highlight; color: HighlightText; }
}

/* Token fields (0.74.0). A token field is a combo box marked
   data-combo-tokens, whose values pudl-combo.js gathers below the field
   as chips, drawn as the filter chips are, each with its raised remove
   button. The field stays a plain sunken field, so its width never jumps
   as values come and go. A chip's words can be pressed to take the value
   back into the field, so they show the text cursor. A chip whose value
   is typed again flashes, and the note under the chips says, with the
   info glyph, when the field takes no more. */
.tokens { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.tokens:empty { display: none; }
.token { max-width: 100%; }
.token-text { min-width: 0; overflow-wrap: anywhere; cursor: text; }
.token.is-again { animation: pudl-token-again 1s ease-out; }
@keyframes pudl-token-again {
  0%, 50% { background: color-mix(in srgb, var(--accent) 28%, transparent); box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
  100% { background: color-mix(in srgb, var(--accent) 8%, transparent); box-shadow: none; }
}
@media (prefers-reduced-motion: reduce) {
  .token.is-again { animation: none; background: color-mix(in srgb, var(--accent) 28%, transparent); box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
}
.tokens-note {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--text-sm); color: var(--text-muted);
}
.tokens-note::before {
  content: ""; flex: none; width: 14px; height: 14px; background: currentColor;
  -webkit-mask: var(--glyph-info) center / contain no-repeat;
  mask: var(--glyph-info) center / contain no-repeat;
}
.tokens-note:empty { display: none; }
@media (forced-colors: active) {
  .tokens-note::before { forced-color-adjust: none; background: CanvasText; }
  .token.is-again { outline: 3px solid Highlight; }
}

/* Progress bars (0.74.0) are native progress elements of class .progress,
   drawn by PUDL alike in every engine and needing no script. A progress
   bar is display, so its track is flat: a muted fill inside a hairline,
   with no shadow, and the value fills it in the accent. A bar with no
   value attribute is indeterminate, and a block slides along it; where the
   reader has asked for less motion the block stands still in the middle.

   Words always go with a bar, since the bar alone says nothing to someone
   who cannot see it. In a .progress-row the bar sits beside its value in
   words, a .progress-value such as 42%, or Working… for a bar with no
   value, under its name, a .progress-label, which spans the row. A bar
   that is not in a row takes the whole width. Meters share the track and
   the row, below. */
.progress, .meter {
  --bar-track: color-mix(in srgb, var(--text) 9%, var(--surface));
  -webkit-appearance: none; appearance: none;
  display: block; box-sizing: border-box; width: 100%; height: 16px; margin: 0;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--bar-track); color: var(--accent);
  overflow: hidden; vertical-align: middle;
}
.progress::-webkit-progress-bar { background: transparent; border-radius: var(--radius-pill); }
.progress::-webkit-progress-value { background: var(--accent); border-radius: var(--radius-pill); }
.progress::-moz-progress-bar { background: var(--accent); border-radius: var(--radius-pill); }
.progress:indeterminate {
  background: linear-gradient(var(--accent), var(--accent)) -54% 0 / 35% 100% no-repeat, var(--bar-track);
  animation: pudl-progress-slide 1.4s ease-in-out infinite;
}
.progress:indeterminate:dir(rtl) { animation-direction: reverse; }
.progress:indeterminate::-webkit-progress-value { background: transparent; }
.progress:indeterminate::-moz-progress-bar { background: transparent; }
@keyframes pudl-progress-slide {
  from { background-position: -54% 0, 0 0; }
  to { background-position: 154% 0, 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  .progress:indeterminate { animation: none; background-position: 50% 0, 0 0; }
}
.progress-row, .meter-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "bar value";
  align-items: center; gap: 4px 10px; width: 100%;
  font-size: var(--text-sm); line-height: 1.4;
}
.progress-row:has(> .progress-label), .meter-row:has(> .meter-label) {
  grid-template-areas: "label label" "bar value";
}
.progress-label, .meter-label { grid-area: label; font-weight: 600; color: var(--text); }
.progress-row > .progress, .meter-row > .meter { grid-area: bar; }
.progress-value, .meter-value {
  grid-area: value; display: inline-flex; align-items: center; gap: 5px;
  color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* Meters (0.74.0) are native meter elements of class .meter, a level
   within a known range such as storage used, drawn on the progress bar's
   flat track. A meter never moves by itself and never animates, so it is
   not mistaken for progress. The meter's own low, high and optimum say
   which values are good, and pudl-fields.js sets data-band to good, fair
   or poor on the meter and its .meter-row, as the HTML specification's
   algorithm finds it. The fill takes the band's colour, positive, warn or
   danger, and in a .meter-row the .meter-value's words are led by the
   band's glyph: the warning triangle for fair, and the stop octagon for
   poor, so that fair and poor differ in shape as well as colour and good
   needs no glyph. Without the script a meter is drawn in the accent, since
   a colour with no glyph beside it would stand alone.

   A .meter.marks is calmer. Its fill stays in the accent, and in a
   .meter-row a mark crosses the track at each threshold the meter has,
   standing out above and below it so that it shows on the fill and on the
   empty track alike. The reader judges the level against the marks, and
   the words say the band, as in "78%, high". */
/* Chromium places the bar absolutely, centred on a line, so it is pinned
   to the meter's edges here. */
.meter::-webkit-meter-inner-element { display: block; height: 100%; }
.meter::-webkit-meter-bar {
  box-sizing: border-box; inset: 0; height: 100%; border: 0; border-radius: var(--radius-pill);
  background: transparent;
}
.meter::-webkit-meter-optimum-value,
.meter::-webkit-meter-suboptimum-value,
.meter::-webkit-meter-even-less-good-value {
  background: var(--meter-fill, var(--accent)); border-radius: var(--radius-pill);
}
.meter::-moz-meter-bar { background: var(--meter-fill, var(--accent)); border-radius: var(--radius-pill); }
.meter[data-band="good"] { --meter-fill: var(--positive); }
.meter[data-band="fair"] { --meter-fill: var(--warn); }
.meter[data-band="poor"] { --meter-fill: var(--danger); }
.meter.marks[data-band] { --meter-fill: var(--accent); }
.meter-row:is([data-band="fair"], [data-band="poor"]):not(:has(> .meter.marks)) > .meter-value::before {
  content: ""; flex: none; width: 14px; height: 14px;
  background: var(--warn);
  -webkit-mask: var(--glyph-warning) center / contain no-repeat;
  mask: var(--glyph-warning) center / contain no-repeat;
}
.meter-row[data-band="poor"]:not(:has(> .meter.marks)) > .meter-value::before {
  background: var(--danger);
  -webkit-mask-image: var(--glyph-stop); mask-image: var(--glyph-stop);
}
.meter-row[data-band]:has(> .meter.marks)::before,
.meter-row[data-band]:has(> .meter.marks)::after {
  content: ""; grid-area: bar; position: relative; z-index: 1;
  display: var(--meter-low-mark, none);
  justify-self: start; align-self: center;
  width: 2px; height: 22px; border-radius: 1px;
  margin-inline-start: calc(var(--meter-low) - 1px);
  background: var(--text-muted); pointer-events: none;
}
.meter-row[data-band]:has(> .meter.marks)::after {
  display: var(--meter-high-mark, none);
  margin-inline-start: calc(var(--meter-high) - 1px);
}
@media (forced-colors: active) {
  .progress, .meter { forced-color-adjust: none; background: Canvas; border-color: CanvasText; }
  .progress::-webkit-progress-value, .meter::-webkit-meter-optimum-value,
  .meter::-webkit-meter-suboptimum-value, .meter::-webkit-meter-even-less-good-value { background: Highlight; }
  .progress::-moz-progress-bar, .meter::-moz-meter-bar { background: Highlight; }
  .progress:indeterminate { background: linear-gradient(Highlight, Highlight) 50% 0 / 35% 100% no-repeat, Canvas; animation: none; }
  .meter-row > .meter-value::before, .meter-row::before, .meter-row::after { forced-color-adjust: none; background: CanvasText !important; }
}

/* Sliders (0.74.0) are range inputs of class .form-range. The track takes
   input, so it is sunken, as a field is, and as tall as a progress bar;
   the accent fills it up to the value. The thumb is what the reader drags,
   so it is raised, a 24px disc, and it takes the focus ring. Firefox fills
   the track itself; Chromium and WebKit fill it from --range-fill, which
   pudl-fields.js keeps at the value's place along the track. In a
   .range-row the slider has its value beside it in an <output for>, which
   the script keeps written in the page's language. The arrow keys step
   the value and PageUp and PageDown take larger steps, as the browser
   gives every range input. A slider says a value roughly, so where the
   exact value matters a number field goes beside it, and the application
   keeps the two in step. */
.form-range {
  -webkit-appearance: none; appearance: none;
  display: block; width: 100%; height: 24px; margin: 0; padding: 0;
  background: none; cursor: pointer;
}
.form-range:focus { outline: none; }
.form-range::-webkit-slider-runnable-track {
  box-sizing: border-box; height: 16px; border-radius: var(--radius-pill);
  border: 1px solid var(--input-border); box-shadow: var(--entry-shadow);
  background: linear-gradient(to right, var(--accent) var(--range-fill, 0%), var(--input-bg) 0);
}
.form-range:dir(rtl)::-webkit-slider-runnable-track {
  background: linear-gradient(to left, var(--accent) var(--range-fill, 0%), var(--input-bg) 0);
}
.form-range::-moz-range-track {
  box-sizing: border-box; height: 16px; border-radius: var(--radius-pill);
  border: 1px solid var(--input-border); box-shadow: var(--entry-shadow);
  background: var(--input-bg);
}
.form-range::-moz-range-progress {
  box-sizing: border-box; height: 16px; border-radius: var(--radius-pill);
  border: 1px solid transparent; background: var(--accent) padding-box;
}
.form-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; box-sizing: border-box;
  width: 24px; height: 24px; margin-top: -5px; border-radius: 50%;
  background: var(--raise-grad); border: 1px solid var(--raise-border);
  box-shadow: var(--raise-shadow);
}
.form-range::-moz-range-thumb {
  box-sizing: border-box; width: 24px; height: 24px; border-radius: 50%;
  background: var(--raise-grad); border: 1px solid var(--raise-border);
  box-shadow: var(--raise-shadow);
}
.form-range:hover::-webkit-slider-thumb { background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.form-range:hover::-moz-range-thumb { background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.form-range:active::-webkit-slider-thumb { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.form-range:active::-moz-range-thumb { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.form-range:focus-visible::-webkit-slider-thumb { box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.form-range:focus-visible::-moz-range-thumb { box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.form-range:disabled { opacity: .55; cursor: not-allowed; }
.range-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: 10px; width: 100%;
}
.range-row > output {
  min-width: 3em; text-align: end;
  font: 600 var(--text-sm) var(--font); font-variant-numeric: tabular-nums; color: var(--text);
}
@media (forced-colors: active) {
  .form-range::-webkit-slider-runnable-track { forced-color-adjust: none; border-color: CanvasText; background: linear-gradient(to right, Highlight var(--range-fill, 0%), Canvas 0); }
  .form-range::-moz-range-track { border-color: CanvasText; }
  .form-range::-webkit-slider-thumb { forced-color-adjust: none; background: ButtonFace; border-color: ButtonText; }
}

/* Number fields (0.74.0) are number inputs in a .number-field, which
   pudl-fields.js hides behind a text field of its own, written and read in
   the page's language. The field is sunken, since it takes input, and its
   digits sit at its end, as numbers in a column do. The minus and plus
   keys are raised strips inside the field's border, as a combo box's
   opener is, full height so they are easy to hit; each is dimmed at the
   end of the range it moves toward. They are not stops in the Tab order,
   since Up and Down in the field do what they do. Without the script the
   field is the browser's own number input. */
.number-field { position: relative; display: flex; align-items: stretch; min-width: 0; }
.number-field > .form-input { flex: 1; min-width: 0; text-align: end; }
.number-step { flex: none; width: 34px; height: auto; color: var(--text); }
.number-step::before {
  content: ""; width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: var(--glyph-minus) center / contain no-repeat;
  mask: var(--glyph-minus) center / contain no-repeat;
}
.number-increase::before { -webkit-mask-image: var(--glyph-plus); mask-image: var(--glyph-plus); }
.number-step:disabled { opacity: .45; }
.number-field:has(> .form-input[readonly]) > .number-step { display: none; }
@media (forced-colors: active) {
  .number-step { border: 1px solid ButtonText; }
  .number-step::before { forced-color-adjust: none; background: ButtonText; }
  .number-step:disabled::before { background: GrayText; }
}

/* Password fields (0.74.0) are password inputs in a .password-field. The
   input stays as it is, and pudl-fields.js puts a raised key inside its
   border at its end that shows the password: Show while it is hidden, and Hide, pressed in,
   while it shows. With data-password-toggle="glyph" the key is the eye,
   struck out while the password shows. Edge's own reveal button gives way
   to it. A .password-caps line under the field says when Caps Lock is on,
   led by the warning glyph in the warn colour, its words in the text
   colour so they stay readable; while Caps Lock is off it is empty and
   takes no room. */
.password-field { position: relative; display: flex; align-items: stretch; min-width: 0; }
.password-field > .form-input { flex: 1; min-width: 0; }
.password-field:has(> .password-toggle) > .form-input::-ms-reveal { display: none; }
.password-toggle { flex: none; }
.password-toggle.btn { min-width: 4.5rem; padding-inline: 12px; font-size: var(--text-sm); }
.password-toggle.icon-btn { width: 38px; height: auto; color: var(--text); }
.password-toggle.icon-btn::before {
  content: ""; width: 16px; height: 16px;
  background: currentColor;
  -webkit-mask: var(--glyph-eye) center / contain no-repeat;
  mask: var(--glyph-eye) center / contain no-repeat;
}
.password-toggle.icon-btn[aria-pressed="true"]::before { -webkit-mask-image: var(--glyph-eye-off); mask-image: var(--glyph-eye-off); }
.password-caps {
  display: flex; align-items: center; gap: 6px; margin-top: 5px;
  font-size: var(--text-sm); line-height: 1.4; color: var(--text);
}
.password-caps::before {
  content: ""; flex: none; width: 14px; height: 14px;
  background: var(--warn);
  -webkit-mask: var(--glyph-warning) center / contain no-repeat;
  mask: var(--glyph-warning) center / contain no-repeat;
}
.password-caps:empty { margin: 0; }
.password-caps:empty::before { display: none; }
@media (forced-colors: active) {
  .password-toggle.icon-btn::before, .password-caps::before { forced-color-adjust: none; background: CanvasText; }
}

/* Images and figures (0.74.0). A .figure holds an img and, where it has
   one, a figcaption. The img is drawn in a hairline frame with the quiet
   fill behind it, which shows while the picture loads. The img's width and
   height set the frame's proportion before the picture arrives, so the
   page never jumps. The picture covers the frame, and in a .figure.contain
   it fits inside it, on the fill. The caption sits below in muted words. A
   .figure.caption-over lays the caption over the picture's foot on a shade
   dark enough for white words on any picture, for a gallery where room is
   short, and a .figure.plain has no frame and no fill, for a picture with
   edges of its own, such as a diagram.

   A picture that fails to load is never the browser's broken-picture icon.
   pudl-fields.js marks its .figure data-broken and puts a .figure-fallback
   in the img's place, in a frame of the same proportion, which shows the
   picture's alternative text centred on the quiet fill under the picture
   glyph. */
.figure { display: grid; grid-template-columns: minmax(0, 1fr); margin: 0; min-width: 0; }
.figure > img {
  grid-area: 1 / 1; display: block; width: 100%; height: auto; max-width: 100%;
  aspect-ratio: var(--figure-ratio, auto);
  object-fit: cover;
  background: var(--surface-alt); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.figure.contain > img { object-fit: contain; }
.figure > figcaption { margin-top: 6px; font-size: var(--text-sm); line-height: 1.45; color: var(--text-muted); }
.figure.caption-over > figcaption {
  grid-area: 1 / 1; align-self: end; position: relative; z-index: 1;
  margin: 1px; padding: 28px 12px 9px;
  color: #ffffff; text-shadow: 0 1px 2px rgb(0 0 0 / 0.6);
  background: linear-gradient(rgb(0 0 0 / 0), rgb(0 0 0 / 0.55) 40%, rgb(0 0 0 / 0.78));
  border-radius: 0 0 max(0px, calc(var(--radius-sm) - 1px)) max(0px, calc(var(--radius-sm) - 1px));
}
.figure.plain > img { border: 0; border-radius: 0; background: none; }
.figure[data-broken] > img { display: none; }
.figure-fallback {
  grid-area: 1 / 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  aspect-ratio: var(--figure-ratio, auto);
  gap: 6px; min-height: 96px; padding: var(--space-3); overflow: hidden;
  text-align: center; font-size: var(--text-sm); line-height: 1.4; color: var(--text-muted);
  background: var(--surface-alt); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.figure-fallback::before {
  content: ""; flex: none; width: 24px; height: 24px;
  background: currentColor;
  -webkit-mask: var(--glyph-image) center / contain no-repeat;
  mask: var(--glyph-image) center / contain no-repeat;
}
@media (forced-colors: active) {
  .figure > img, .figure-fallback { border: 1px solid CanvasText; }
  .figure-fallback::before { forced-color-adjust: none; background: CanvasText; }
}

/* Avatars (0.74.0). A .avatar is a person's or an agent's picture, or
   their initials, in a circle: role="img" and an aria-label with their
   name, and the initials, or an img with empty alt, inside. It is neutral,
   the panel's colour with the initials in the text colour, so it is never
   mistaken for a status. Its sizes are 24px (.sm), 32px, 40px (.lg) and
   64px (.xl). A picture that fails to load gives way to the initials.

   A .avatar.tint takes a soft tint from the name, which pudl-fields.js
   works out as --avatar-hue, so the same person is the same colour
   everywhere; the tint means nothing else. Light and dark keep the
   initials above 4.5:1 against the tint at every hue.

   A .avatar-wrap holds an avatar and its .avatar-status, a mark at the
   foot of the circle, ringed in the surface colour so it stands clear of
   any picture. Each status has its shape as well as its colour: online a
   filled circle in positive, away a half-filled circle in warn, busy a
   minus in danger, and offline a ring in the muted text colour. The mark has
   an aria-label of its own, such as Online. */
.avatar {
  --avatar-size: 32px;
  position: relative; display: inline-grid; place-items: center; flex: none;
  box-sizing: border-box; width: var(--avatar-size); height: var(--avatar-size);
  border-radius: 50%; overflow: hidden; vertical-align: middle;
  background: var(--surface-alt); border: 1px solid var(--border); color: var(--text);
  font: 700 var(--text-sm)/1 var(--font); letter-spacing: .02em; white-space: nowrap;
  user-select: none;
}
.avatar.sm { --avatar-size: 24px; font-size: var(--text-2xs); letter-spacing: 0; }
.avatar.lg { --avatar-size: 40px; font-size: var(--text-base); }
.avatar.xl { --avatar-size: 64px; font-size: var(--text-2xl); }
.avatar > img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; border-radius: inherit; background: var(--surface-alt);
}
.avatar[data-broken] > img { display: none; }
.avatar.tint {
  background: hsl(var(--avatar-hue, 210) 45% 88%);
  border-color: hsl(var(--avatar-hue, 210) 30% 72%);
  color: hsl(var(--avatar-hue, 210) 55% 22%);
}
[data-theme="dark"] .avatar.tint {
  background: hsl(var(--avatar-hue, 210) 30% 26%);
  border-color: hsl(var(--avatar-hue, 210) 25% 40%);
  color: hsl(var(--avatar-hue, 210) 60% 88%);
}
.avatar-wrap { --avatar-mark: 12px; position: relative; display: inline-flex; flex: none; vertical-align: middle; }
.avatar-wrap:has(> .avatar.sm) { --avatar-mark: 10px; }
.avatar-wrap:has(> .avatar.lg) { --avatar-mark: 14px; }
.avatar-wrap:has(> .avatar.xl) { --avatar-mark: 18px; }
.avatar-status {
  position: absolute; inset-inline-end: -2px; bottom: -2px;
  width: var(--avatar-mark); height: var(--avatar-mark);
  border-radius: 50%; background: var(--surface); color: var(--text-muted);
}
.avatar-status::before {
  content: ""; position: absolute; inset: 0;
  background: currentColor;
  -webkit-mask: var(--avatar-status-glyph, var(--glyph-ring)) center / 125% no-repeat;
  mask: var(--avatar-status-glyph, var(--glyph-ring)) center / 125% no-repeat;
}
.avatar-status[data-status="online"] { color: var(--positive); --avatar-status-glyph: var(--glyph-circle); }
.avatar-status[data-status="away"] { color: var(--warn); --avatar-status-glyph: var(--glyph-theme); }
.avatar-status[data-status="busy"] { color: var(--danger); --avatar-status-glyph: var(--glyph-minus); }
.avatar-status[data-status="offline"] { color: var(--text-muted); --avatar-status-glyph: var(--glyph-ring); }
@media (forced-colors: active) {
  .avatar { border: 1px solid CanvasText; }
  .avatar-status { forced-color-adjust: none; background: Canvas; }
  .avatar-status::before { background: CanvasText; }
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
  border-color: var(--accent); outline: none;
  box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
/* A read-only field is display, so it is flat. A disabled field is still
   an input, which is why it keeps its sunken look and is dimmed below. */
.form-input[readonly] {
  background: transparent; border-color: transparent; box-shadow: none;
  padding-inline: 0; cursor: default;
}
/* Placeholder text reaches 4.5:1 on the field. */
::placeholder { color: var(--text-muted); opacity: 1; }

/* Checkbox + label as one inline control. */
.check {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-md); cursor: pointer;
}
.check input { width: 16px; height: 16px; margin: 0; accent-color: var(--accent); }

/* Toggle switch (a boolean preference). The track is sunken, since it is
   the slot the thumb runs in, and the thumb is raised, since it is what the
   reader presses. When the switch is on the track fills with the accent
   and the thumb slides to the far end, so position says it as well as
   colour. The switch is a button with role="switch", which is what lets
   it carry aria-checked. */
.switch {
  display: inline-flex; align-items: center; gap: 10px;
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--text); cursor: pointer;
}
.switch-track {
  position: relative; flex: none;
  width: 40px; height: 22px; border-radius: var(--radius-pill);
  background: var(--recess-bg); border: 1px solid var(--input-border);
  box-shadow: var(--entry-shadow);
  transition: background .15s, border-color .15s;
}
.switch-thumb {
  position: absolute; top: 2px; inset-inline-start: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--raise-grad);
  border: 1px solid var(--raise-border);
  box-shadow: var(--raise-shadow);
  transition: transform .15s;
}
.switch[aria-checked="true"] .switch-track {
  background: var(--accent); border-color: var(--accent-hover);
}
.switch[aria-checked="true"] .switch-thumb { transform: translateX(18px); }
.switch:dir(rtl)[aria-checked="true"] .switch-thumb { transform: translateX(-18px); }
.switch:hover .switch-thumb { border-color: var(--raise-border-hover); }
.switch:focus-visible { outline: none; }
.switch:focus-visible .switch-track {
  box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
.switch:active .switch-thumb { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.switch:disabled, .switch[aria-disabled="true"] { opacity: .45; cursor: not-allowed; pointer-events: none; }

.form-error {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--text-sm); color: var(--danger); margin: var(--space-1) 0 var(--space-3);
}
.form-error::before { --glyph: var(--glyph-warning); }

/* A field whose value the server refused is marked aria-invalid="true",
   with its .form-error tied to it by aria-describedby. It takes a danger
   border and a danger focus ring, and the message beside it carries the
   glyph, so the state never rests on the border's colour alone. */
.form-input[aria-invalid="true"], .form-select[aria-invalid="true"], .form-textarea[aria-invalid="true"] {
  border-color: var(--danger);
}
.form-input[aria-invalid="true"]:focus, .form-select[aria-invalid="true"]:focus, .form-textarea[aria-invalid="true"]:focus {
  border-color: var(--danger);
  box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring-danger);
}
.form-input:disabled, .form-select:disabled, .form-textarea:disabled { opacity: .55; cursor: not-allowed; }

/* Fields with keys. A combo box, a number field, a password field and the
   calendar's year are one sunken field whose border runs round everything,
   with each key a raised strip inside that border at its end, behind a
   hairline. The wrapper draws the field, so its focus ring, its danger
   border and its dimming go round the whole control, keys included. A key
   shows its own keyboard focus as a ring inside its strip, since the
   field's border would clip a ring drawn outside it. A read-only field
   hides its keys and is drawn flat by its own rule above. */
:is(.combo, .number-field, .password-field):has(> :is(.combo-open, .number-step, .password-toggle, .date-step)):not(:has(> .form-input[readonly])) {
  background: var(--input-bg);
  border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  box-shadow: var(--entry-shadow);
  overflow: hidden;
}
:is(.combo, .number-field, .password-field):has(> :is(.combo-open, .number-step, .password-toggle, .date-step)):not(:has(> .form-input[readonly])):focus-within {
  border-color: var(--accent);
  box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
:is(.combo, .number-field, .password-field):has(> :is(.combo-open, .number-step, .password-toggle, .date-step)):has(> .form-input[aria-invalid="true"]) { border-color: var(--danger); }
:is(.combo, .number-field, .password-field):has(> :is(.combo-open, .number-step, .password-toggle, .date-step)):has(> .form-input[aria-invalid="true"]):focus-within {
  box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring-danger);
}
:is(.combo, .number-field, .password-field):has(> :is(.combo-open, .number-step, .password-toggle, .date-step)):has(> .form-input:disabled) { opacity: .55; cursor: not-allowed; }
:is(.combo, .number-field, .password-field):has(> :is(.combo-open, .number-step, .password-toggle, .date-step)):not(:has(> .form-input[readonly])) > .form-input {
  height: 34px; border: 0; border-radius: 0;
  background: transparent; box-shadow: none;
}
:is(.combo, .number-field, .password-field):has(> :is(.combo-open, .number-step, .password-toggle, .date-step)) > .form-input:disabled { opacity: 1; }
:is(.combo, .number-field, .password-field):has(> :is(.combo-open, .number-step, .password-toggle, .date-step)) > .form-input:focus { box-shadow: none; }
:is(.combo, .number-field, .password-field) > :is(.combo-open, .number-step, .password-toggle, .date-step) {
  height: auto; margin: 0;
  border: 0; border-inline-start: 1px solid var(--raise-border); border-radius: 0;
  box-shadow: none;
}
:is(.combo, .number-field, .password-field) > :is(.combo-open, .number-step, .password-toggle, .date-step):focus-visible {
  outline: none; box-shadow: inset 0 0 0 2px var(--focus-ring);
}
:is(.combo, .number-field, .password-field) > :is(.combo-open, .number-step, .password-toggle, .date-step):is(:active, [aria-expanded="true"]) {
  background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow);
}
.password-field > .password-toggle.btn { min-width: 0; }

/* A required field's label carries an asterisk, drawn by the stylesheet
   from the field's own required attribute and hidden from assistive
   technology, which hears "required" from the field itself. */
.form-group:has(:required) > .form-label::after,
.form-group:has(:required) > .form-label-row > .form-label::after {
  content: "*" / "";
  margin-inline-start: 3px; color: var(--danger);
}
.form-help { margin: 0; font-size: var(--text-sm); color: var(--text-muted); }
.form-group > .form-error { margin: 0; }

.form-fieldset {
  margin: 0 0 var(--space-4); padding: var(--space-3) var(--space-4) var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.form-fieldset > legend { padding: 0 var(--space-1); font-size: var(--text-sm); font-weight: 600; }
.form-options { display: flex; flex-direction: column; gap: var(--space-2); }
.form-options.inline { flex-direction: row; flex-wrap: wrap; gap: var(--space-2) var(--space-5); }

/* A file field: its button is a raised button, the file name flat text. */
.form-file { font: inherit; font-size: var(--text-sm); color: var(--text-muted); max-width: 100%; }
.form-file::file-selector-button {
  margin-inline-end: var(--space-3);
  padding: 6px 12px; border-radius: var(--radius-sm);
  font: 600 var(--text-sm) var(--font); color: var(--text);
  border: 1px solid var(--raise-border);
  background: var(--raise-grad); box-shadow: var(--raise-shadow);
  cursor: pointer;
}
.form-file::file-selector-button:hover { background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.form-file::file-selector-button:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }

/* Date and time fields (0.74.0) are date and time inputs marked data-date,
   which pudl-date.js hides behind a .combo. The field is the sunken input
   the reader types in, and the opener is the raised key joined at its end,
   both drawn by the combo box's rules above. Under the field the echo,
   .date-echo, says in words the date or time the field holds, in small
   muted text, and keeps a line's height when it is empty so the form never
   jumps.

   The calendar is a .date-panel, drawn as a menu panel is. Every key in it
   is raised: the arrows, the month and year at the top, which carry a
   menu's caret because they open the year's months, and Today and Clear
   at the foot. The days, and the months
   in the chooser, are flat cells, as a list's rows are, and light under
   the pointer. The chosen one is pressed in and set bold in the accent, so
   it never rests on depth alone. Today has a 1px ring in the accent. A day
   outside the field's min and max is dimmed and struck through, so more
   than its shade tells it apart. The arrows show the back and forward
   glyphs, swapped in a right-to-left language, and stand as tall as the
   month key between them. In the chooser the
   year is a sunken field with its minus and plus keys joined to it. Days
   are 32px square, and 36px where the pointer is a finger. The time list
   is a .combo-list of .combo-row rows and needs nothing more. */
.date-echo {
  display: block; min-height: calc(var(--text-xs) * 1.5); margin-top: 3px;
  font-size: var(--text-xs); line-height: 1.5; color: var(--text-muted);
}
.date-panel {
  --date-cell: 32px;
  position: fixed; inset: auto; margin: 0;
  min-width: 0; max-width: calc(100vw - 16px);
}
/* Its own padding outranks the menu panel's rows-only padding, which
   comes later in this file. */
.date-panel[popover] { padding: var(--space-2) var(--space-3) var(--space-3); }
@media (pointer: coarse) { .date-panel { --date-cell: 36px; } }
.date-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-1); }
.date-head > .icon-btn, .date-head > .date-title { flex: none; box-sizing: border-box; height: 28px; }
.date-head > .date-title { flex: 1; min-width: 0; padding-block: 0; }
.date-title::after {
  content: ""; flex: none; width: 10px; height: 10px; opacity: .75;
  background: currentColor;
  -webkit-mask: var(--glyph-caret) center / contain no-repeat;
  mask: var(--glyph-caret) center / contain no-repeat;
}
.date-title[aria-expanded="true"] { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.date-prev::before, .date-next::before, .date-earlier::before, .date-later::before {
  content: ""; flex: none; width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: var(--glyph-back) center / contain no-repeat;
  mask: var(--glyph-back) center / contain no-repeat;
}
.date-next::before, .date-prev:dir(rtl)::before { -webkit-mask-image: var(--glyph-forward); mask-image: var(--glyph-forward); }
.date-next:dir(rtl)::before { -webkit-mask-image: var(--glyph-back); mask-image: var(--glyph-back); }
.date-earlier::before { -webkit-mask-image: var(--glyph-minus); mask-image: var(--glyph-minus); }
.date-later::before { -webkit-mask-image: var(--glyph-plus); mask-image: var(--glyph-plus); }
.date-grid { border-collapse: separate; border-spacing: 2px; margin: 0 auto; font: var(--text-sm)/1 var(--font); }
.date-grid th {
  width: var(--date-cell); height: 24px; padding: 0; text-align: center;
  font-size: var(--text-xs); font-weight: 600; color: var(--text-muted);
}
.date-grid td, .date-month {
  width: var(--date-cell); height: var(--date-cell); padding: 0; text-align: center;
  border-radius: var(--radius-sm); color: var(--text);
  font-variant-numeric: tabular-nums;
}
.date-month {
  width: auto; padding: 0 var(--space-1);
  background: none; border: 0; font: var(--text-sm)/1 var(--font); cursor: pointer;
}
.date-grid td[data-date] { cursor: pointer; }
.date-grid td[data-date]:hover, .date-month:hover { background: var(--menu-row-hover); }
.date-grid td:focus, .date-month:focus { outline: none; }
.date-grid td:focus-visible, .date-month:focus-visible { position: relative; box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.date-grid td[aria-selected="true"], .date-grid td[aria-selected="true"]:hover,
.date-month[aria-current="true"], .date-month[aria-current="true"]:hover {
  background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow);
  color: var(--accent); font-weight: 700;
}
.date-grid td[aria-selected="true"]:focus-visible, .date-month[aria-current="true"]:focus-visible { box-shadow: var(--raise-active-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.date-grid td[aria-current="date"] { outline: 1px solid var(--accent); outline-offset: -1px; }
.date-grid td[aria-disabled="true"], .date-month:disabled { opacity: .4; text-decoration: line-through; cursor: default; }
.date-grid td[aria-disabled="true"]:hover, .date-month:disabled:hover { background: none; }
.date-chooser { width: calc(7 * var(--date-cell) + 16px); }
.date-year-row { display: flex; align-items: center; gap: var(--space-2); margin: var(--space-1) 0 var(--space-2); }
.date-year { flex: none; width: 9rem; }
.date-year > .form-input { text-align: center; font-variant-numeric: tabular-nums; }
.date-step { flex: none; width: 30px; height: auto; }
.date-year-name { min-width: 0; font-size: var(--text-sm); color: var(--text-muted); }
.date-months { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; }
.date-foot {
  display: flex; justify-content: space-between; gap: var(--space-2);
  margin-top: var(--space-2); padding-top: var(--space-2); border-top: 1px solid var(--border);
}
@media (forced-colors: active) {
  .date-prev::before, .date-next::before, .date-earlier::before, .date-later::before, .date-title::after { forced-color-adjust: none; background: ButtonText; }
  .date-grid td[aria-selected="true"], .date-grid td[aria-selected="true"]:hover,
  .date-month[aria-current="true"], .date-month[aria-current="true"]:hover { forced-color-adjust: none; background: Highlight; color: HighlightText; }
  .date-grid td:focus-visible, .date-month:focus-visible { outline: 2px solid CanvasText; outline-offset: 1px; }
  .date-grid td[aria-disabled="true"], .date-month:disabled { color: GrayText; opacity: 1; }
}

/* === Segmented control / view switcher ================================== */
/* A row of keys joined side by side, of which one is in, as on a radio's
   bank of buttons (0.72.0). Use it for two to four options: a small scale,
   kg|lbs, Read|Edit. Every choice that can be pressed is raised, as every
   other control that can be pressed is, and the chosen one is pressed in,
   as a pressed toolbar button is; until 0.72.0 the choices were flat on a
   sunken track and the chosen one raised, the opposite of the rest of the
   grammar. Its items are buttons, marked aria-pressed or aria-checked, or
   links to views, marked aria-current. */
.seg { display: inline-flex; align-items: stretch; }
/* A key is 30px tall, a menu bar's groups' height, so a segmented control
   and a menu bar line up in an application bar (0.74.0). */
.seg button, .seg a {
  display: inline-flex; align-items: center; gap: 6px;
  box-sizing: border-box; min-height: 30px;
  margin: 0; padding: 0 12px;
  font: 600 var(--text-sm) var(--font); color: var(--text);
  text-decoration: none; cursor: pointer;
  background: var(--raise-grad);
  border: 1px solid var(--raise-border);
  box-shadow: var(--raise-shadow);
  border-radius: var(--radius-sm);
}
/* Joined: neighbours share a border, and only the ends are rounded. */
.seg > :is(button, a) + :is(button, a) { margin-inline-start: -1px; }
.seg > :is(button, a):not(:first-child) { border-start-start-radius: 0; border-end-start-radius: 0; }
.seg > :is(button, a):not(:last-child) { border-start-end-radius: 0; border-end-end-radius: 0; }
.seg button:hover, .seg a:hover { background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.seg button:active, .seg a:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.seg button:focus-visible, .seg a:focus-visible { outline: none; box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); position: relative; z-index: 2; }
.seg button[aria-pressed="true"], .seg button[aria-checked="true"],
.seg [aria-current]:not([aria-current="false"]),
.seg button.active {
  color: var(--text);
  background: var(--raise-active-bg);
  border-color: var(--border);
  box-shadow: var(--raise-active-shadow);
  cursor: default; position: relative; z-index: 1;
}
.seg button[aria-pressed="true"]:focus-visible, .seg button[aria-checked="true"]:focus-visible,
.seg [aria-current]:not([aria-current="false"]):focus-visible {
  box-shadow: var(--raise-active-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
.seg button:disabled, .seg button[aria-disabled="true"], .seg a[aria-disabled="true"] {
  opacity: .45; cursor: not-allowed; pointer-events: none;
}
.seg.pill > :is(button, a):first-child { border-start-start-radius: var(--radius-pill); border-end-start-radius: var(--radius-pill); }
.seg.pill > :is(button, a):last-child { border-start-end-radius: var(--radius-pill); border-end-end-radius: var(--radius-pill); }
.seg.sm button, .seg.sm a { min-height: 28px; padding: 0 9px; font-size: var(--text-xs); }

/* === Badges, chips and filter chips =====================================
   Three small labels, each looking like what it is:
     - a badge states an entity's state, and always leads with its glyph;
     - a chip names an attribute the thing carries, a tag or a category,
       and is a plain neutral label;
     - a filter chip is a filter in force, outlined, with a raised × that
       removes it. */
.badge {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 1px 8px; border-radius: var(--radius-pill);
  font-size: var(--text-2xs); font-weight: 700; line-height: 1.55;
  background: color-mix(in srgb, var(--text-muted) 18%, transparent);
  color: var(--text);
  --glyph-size: 9px;
}
/* A status badge's text is its status colour drawn toward the text colour,
   because a mid-tone status colour cannot reach 4.5:1 on a tint of itself,
   least of all in a selected table row, whose tint darkens it further.
   Mixing in the text colour darkens the text on a light theme and lightens
   it on a dark one, keeps the hue, and holds for a theme's own status
   colours, so the tokens themselves stay as the theme set them. */
.badge.warn { --badge-color: var(--warn); background: color-mix(in srgb, var(--warn) 22%, transparent); }
.badge.danger { --badge-color: var(--danger); background: color-mix(in srgb, var(--danger) 18%, transparent); }
.badge.positive, .badge.pr { --badge-color: var(--positive); background: color-mix(in srgb, var(--positive) 22%, transparent); }
.badge.accent { --badge-color: var(--accent); background: color-mix(in srgb, var(--accent) 18%, transparent); }
/* A badge's words are its status colour drawn toward the text colour, far
   enough that they keep 4.5:1 on every surface a badge sits on, a current
   list row's surface-alt included, in both themes. */
.badge:is(.warn, .danger, .positive, .pr, .accent) { color: color-mix(in srgb, var(--badge-color) 50%, var(--text)); }
.badge::before { content: none; }
.badge.warn::before { content: ""; --glyph: var(--glyph-triangle); }
.badge.danger::before { content: ""; --glyph: var(--glyph-square); }
.badge.positive::before, .badge.pr::before { content: ""; --glyph: var(--glyph-circle); }
.badge.accent::before { content: ""; --glyph: var(--glyph-diamond); }
/* An execution-state badge (0.96.4): data-state of working, waiting,
   stuck or idle gives a badge the state's static glyph and colour, the
   same ones the window system draws on title bars, taskbars and stack
   rows, so a host's own rows state a thing's execution state with the
   chip the taskbar uses. The words inside are the host's. */
.badge[data-state="working"] { --badge-color: var(--accent); background: color-mix(in srgb, var(--accent) 18%, transparent); }
.badge[data-state="waiting"] { --badge-color: var(--warn); background: color-mix(in srgb, var(--warn) 22%, transparent); }
.badge[data-state="stuck"] { --badge-color: var(--danger); background: color-mix(in srgb, var(--danger) 18%, transparent); }
.badge[data-state="idle"] { --badge-color: var(--text-muted); background: color-mix(in srgb, var(--text-muted) 18%, transparent); }
.badge:is([data-state="working"], [data-state="waiting"], [data-state="stuck"], [data-state="idle"]) { color: color-mix(in srgb, var(--badge-color) 50%, var(--text)); }
.badge[data-state="working"]::before { content: ""; --glyph: var(--glyph-play); }
.badge[data-state="waiting"]::before { content: ""; --glyph: var(--glyph-pause); }
.badge[data-state="stuck"]::before { content: ""; --glyph: var(--glyph-warning); }
.badge[data-state="idle"]::before { content: ""; --glyph: var(--glyph-square); }

.chip {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 1px 8px; border-radius: var(--radius-sm);
  font-size: var(--text-xs); font-weight: 600;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text);
}

/* === Cards ============================================================== */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--space-4);
  box-shadow: var(--shadow-card);
}
.card-title { margin: 0 0 6px; font-size: var(--text-lg); font-weight: 700; }
.card-subtitle { margin: 0 0 6px; font-size: var(--text-sm); font-weight: 600; color: var(--text-muted); }
.card-desc { margin: 0 0 var(--space-3); font-size: var(--text-sm); color: var(--text-muted); }

/* A disclosure card opens and closes by one raised toggle at the start of
   its head, the tree's toggle, since a card is read and not pressed; the
   rest of the head is flat. The summary shows while the card is closed,
   the body and the foot while it is open. pudl-disclose.js answers the
   toggle and keeps aria-expanded true; until it runs, everything shows. */
.card.disclosure { padding: 0; }
.card.disclosure > .card-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px var(--space-2);
  padding: var(--space-3) var(--space-4); min-width: 0;
}
.card.disclosure > .card-head > .card-title { margin: 0; font-size: var(--text-md); min-width: 0; }
.card.disclosure > .card-head > .card-summary {
  flex: 1 1 12em; min-width: 0; font-size: var(--text-sm); color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.card.disclosure > .card-body { padding: 4px var(--space-4) var(--space-4); }   /* with the head's 12px, the card's 16px above the body */
.card.disclosure > .card-actions {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  padding: var(--space-3) var(--space-4); border-top: 1px solid var(--border);
}
.card.disclosure.disclose-ready > .card-head > .card-toggle[aria-expanded="true"] ~ .card-summary,
.card.disclosure.disclose-ready > .card-head:has(> .card-toggle[aria-expanded="false"]) ~ .card-body,
.card.disclosure.disclose-ready > .card-head:has(> .card-toggle[aria-expanded="false"]) ~ .card-actions { display: none; }
.card-toggle { padding: 0; }
.card-toggle::before { transition: transform .12s; }
.card-toggle[aria-expanded="true"]::before { transform: none; }
@media (prefers-reduced-motion: reduce) { .card-toggle::before { transition: none; } }

/* === Layout ==============================================================
   Arranging components in a page, a pane or a window: .vstack, one above
   another; .hstack, side by side, wrapping onto further lines rather than
   widening its space; .auto-grid, equal columns, as many as fit at
   --grid-min (224px), one column below it. Each keeps its components in
   document order with a gap from the spacing grid, space-3 unless a
   .gap-0 to .gap-6 says otherwise, and adds nothing around them. A row
   aligns its components with .align-center, .align-end or .align-baseline
   (the last lines fields of different heights up by their labels), and
   .push-end on its last component pushes it to the end. .span-all makes a
   grid's component span its line. Layout has no surface. */
.vstack, .hstack, .auto-grid { --gap: var(--space-3); gap: var(--gap); min-width: 0; }
.vstack { display: flex; flex-direction: column; }
.hstack { display: flex; flex-wrap: wrap; align-items: flex-start; }
.hstack.align-center { align-items: center; }
.hstack.align-end { align-items: flex-end; }
.hstack.align-baseline { align-items: baseline; }
.hstack > .push-end { margin-inline-start: auto; }
.auto-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(var(--grid-min, 224px), 100%), 1fr)); }
.auto-grid > .span-all { grid-column: 1 / -1; }
.vstack > *, .hstack > *, .auto-grid > * { min-width: 0; }
.vstack > .form-group, .hstack > .form-group, .auto-grid > .form-group { margin-bottom: 0; }
.gap-0 { --gap: 0px; }
.gap-1 { --gap: var(--space-1); }
.gap-2 { --gap: var(--space-2); }
.gap-3 { --gap: var(--space-3); }
.gap-4 { --gap: var(--space-4); }
.gap-5 { --gap: var(--space-5); }
.gap-6 { --gap: var(--space-6); }

/* A settings panel: a column of cards, each a group of settings with its
   title, a muted description, its controls and a row of actions, in a
   page or filling a window. A .settings-status line under the actions says what the last
   one did, and takes no room while it is empty. */
.settings-panel {
  display: flex; flex-direction: column; gap: var(--space-3);
  max-width: 720px; margin-inline: auto; padding: var(--space-3);
  font-size: var(--text-sm);
}
.settings-panel > .card { margin: 0; }
.settings-panel .card-title { margin-bottom: var(--space-2); }
.settings-panel > .card > :last-child { margin-bottom: 0; }
.settings-panel .form-group { margin-block: var(--space-3) 0; }
.settings-panel .check { display: flex; margin: 4px 0; }
.settings-panel .seg { max-width: 100%; flex-wrap: wrap; }
.settings-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.settings-actions form { margin: 0; }
.settings-status { margin: var(--space-2) 0 0; min-height: 1.5em; color: var(--text-muted); }
.settings-status:empty { display: none; }

/* === Key/value table for read-only record fields ======================== */
/* Detail panes use this for displayed-only fields, not fake input boxes
   (elevation = interactivity). */
.kv-table { width: 100%; border-collapse: collapse; font-size: var(--text-md); }
.kv-table th {
  text-align: start; font-weight: 600; color: var(--text-muted); font-size: var(--text-sm);
  padding-block: var(--space-2); padding-inline: 0 var(--space-4); white-space: nowrap; vertical-align: top; width: 1%;
}
.kv-table td { padding: var(--space-2) 0; color: var(--text); vertical-align: top; overflow-wrap: anywhere; }
.kv-table tr + tr th, .kv-table tr + tr td { border-top: 1px solid var(--border); }

/* === Toolbars =============================================================
   A row of groups of raised controls, as a page editor's formatting
   toolbar: glyph buttons, menu buttons and small buttons, each group set
   apart by a hairline, wrapping with groups whole when the row is
   narrow. The toolbar is one tab stop, which pudl-toolbar.js keeps on the
   control last used; a control in the state the cursor is in carries
   aria-pressed and is drawn pressed in. */
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) 0; min-width: 0; }
.toolbar-group { display: inline-flex; align-items: center; gap: var(--space-1); padding: 0 var(--space-2); }
.toolbar-group + .toolbar-group { border-inline-start: 1px solid var(--border); }
.toolbar-group:first-child { padding-inline-start: 0; }
.toolbar-group:last-child { padding-inline-end: 0; }
.toolbar .icon-btn > .glyph { --glyph-size: 14px; }
.toolbar .icon-btn.menu-btn { width: auto; padding: 0 4px 0 6px; gap: 2px; }
.toolbar .icon-btn.menu-btn::after { --glyph-size: 10px; }

/* A folding toolbar (preview, docs/proposals/toolbar-commands.md) keeps to
   one row. pudl-toolbar.js sets each group's data-toolbar-stage: at 1 the
   group's name and each control's glyph and words, at 2 the glyphs alone,
   at 3 one menu button with the group's glyph and name, at 4 that button
   with its glyph alone, and at 5 the group is in the More menu at the
   row's end. A group's name is flat and muted, since it only says what
   the group holds. A control's words and its badge are spans that
   pudl-commands.js fills, or a page writes. */
.toolbar[data-toolbar-fold] { flex-wrap: nowrap; overflow: hidden; }
.toolbar[data-toolbar-fold] > .toolbar-group { flex: none; border-inline-start: 0; }
/* The rule between groups is drawn only between groups still on the row. */
.toolbar[data-toolbar-fold] > .toolbar-group:not([data-toolbar-stage="5"]) ~ .toolbar-group:not([data-toolbar-stage="5"]) { border-inline-start: 1px solid var(--border); }
.toolbar[data-toolbar-fold] > .toolbar-group[data-toolbar-end] { margin-inline-start: auto; }
.toolbar[data-toolbar-fold] > .toolbar-group[data-toolbar-end] ~ .toolbar-group[data-toolbar-end] { margin-inline-start: 0; }
.toolbar-label { font-size: var(--text-xs); font-weight: 600; color: var(--text-muted); white-space: nowrap; margin-inline-end: 2px; }
.toolbar .btn > .glyph { --glyph-size: 14px; }
.toolbar .btn.btn-sm { gap: 6px; }
.toolbar-words { white-space: nowrap; }
.toolbar-badge {
  min-width: 18px; padding: 0 5px; border-radius: var(--radius-pill); font-size: var(--text-2xs); font-weight: 700;
  font-variant-numeric: tabular-nums; text-align: center; line-height: 16px;
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--text);
}
.toolbar-group > .toolbar-fold.menu, .toolbar > .toolbar-more.menu { display: none; flex: none; }
.toolbar-group[data-toolbar-stage="2"] :is(button, a):has(> .glyph) > .toolbar-words { display: none; }
.toolbar-group[data-toolbar-stage="2"] .btn.btn-sm:has(> .glyph):not(.menu-btn) { padding-inline: 7px; }
.toolbar-group:is([data-toolbar-stage="3"], [data-toolbar-stage="4"]) > :not(.toolbar-fold) { display: none; }
.toolbar-group:is([data-toolbar-stage="3"], [data-toolbar-stage="4"]) > .toolbar-fold { display: inline-flex; }
.toolbar-group[data-toolbar-stage="4"] > .toolbar-fold .menu-btn:has(> .glyph) > .toolbar-words { display: none; }
.toolbar-group[data-toolbar-stage="5"] { display: none; }
.toolbar[data-toolbar-more] > .toolbar-more { display: inline-flex; margin-inline-start: var(--space-2); }
/* A command bar (0.78.0) is a folding toolbar whose first group, marked
   data-toolbar-menus, holds a menu bar: one raised .menubar-menu with the
   menu glyph and a flat title for each menu, the open one pressed in. A
   title is a menu button, so the menu keys reach it, drawn as a title. */
.toolbar-group[data-toolbar-menus] > .menubar-menu > .menu { display: contents; }
.menubar-menu .menubar-title.menu-btn::after { display: none; }
.menubar-menu .menu:has(> .menu-panel:popover-open) > .menubar-title { color: var(--text); }
/* A command whose glyph nearly every reader knows, marked
   data-toolbar-glyph-only, shows its glyph alone at every stage, its
   words kept as its accessible name and its tooltip. A toolbar marked
   data-toolbar-captions="none" shows no group names before its groups;
   each group's name is still its fold button's words. A group marked
   data-toolbar-captioned shows its name all the same (0.79.0), as a
   choice must, since its positions are answers that mean nothing without
   their question. */
.toolbar [data-toolbar-glyph-only] > .toolbar-words { display: none; }
.toolbar .btn.btn-sm[data-toolbar-glyph-only]:has(> .glyph) { padding-inline: 7px; }
.toolbar[data-toolbar-captions="none"] .toolbar-group:not([data-toolbar-captioned]) > .toolbar-label { display: none; }
/* A folded group's field opens in a popover against the group's fold
   button, since a field typed into cannot be a menu row. */
.toolbar-field-popover { min-width: min(18rem, calc(100vw - 16px)); }
.toolbar-field-popover > :is(.form-input, .form-select, .md-filter, .combo, .md-filter-group, .number-field, .password-field) { width: 100%; }

/* === Ribbons (0.78.0) =====================================================
   A single-line ribbon, for a long toolbar such as an editor's: PUDL's tab
   list along the top, the chosen tab joined to one folding toolbar of its
   tools beneath. Show as ribbon, a toggle at the end of the tabs, folds
   the whole ribbon into a menu bar of the same names, each a menu of its
   tab's groups, and back. pudl-toolbar.js builds the toggle and the menu
   bar; without it every row shows, one after another, and the tabs are
   hidden. */
.ribbon { --section-current-bg: var(--surface); background: var(--surface); border-bottom: 1px solid var(--border); min-width: 0; }
/* The band holds the tab list and, beside it, Show as ribbon, since a tab
   list holds only tabs; it is drawn as a tab bar is. Its tabs stand on its
   foot with no padding above, so Show as ribbon is centred in it, where it
   stands on the folded menu bar, and does not move as the ribbon folds. */
.ribbon-bar {
  display: flex; align-items: center; min-width: 0;
  padding-inline-end: var(--space-3);
  background: var(--recess-bg);
  box-shadow: inset 0 -1px var(--border), var(--recess-shadow);
}
.ribbon-bar > .tablist { flex: 1 1 auto; align-self: stretch; padding-top: 0; padding-inline: var(--space-3); background: none; box-shadow: none; }
.ribbon-bar > .ribbon-toggle { flex: none; }
.ribbon-bar > .ribbon-tabs { flex: none; }
.ribbon > .toolbar { padding: 6px var(--space-3); background: var(--section-current-bg); }
.ribbon.ribbon-ready > .toolbar[hidden] { display: none; }
.ribbon:not(.ribbon-ready) > .tablist { display: none; }
.ribbon:not(.ribbon-ready) > .toolbar + .toolbar { border-top: 1px solid var(--border); }
.ribbon-menus { display: flex; align-items: center; gap: var(--space-2); padding: 6px var(--space-3); min-width: 0; }
.ribbon-menus > .ribbon-toggle { margin-inline-start: auto; flex: none; }
.ribbon-menus > .menubar-menu > .menu { display: contents; }
/* The tab band and the folded menu bar are one height, so folding the
   ribbon takes away only the row of tools and the page below moves by
   that row alone. */
.ribbon-bar, .ribbon-bar > .tablist, .ribbon-menus { box-sizing: border-box; min-height: 44px; }
.ribbon:not([data-ribbon-folded]) > .ribbon-menus,
.ribbon[data-ribbon-folded] > :is(.ribbon-bar, .toolbar) { display: none; }
/* A command's tone colours its control and its menu row with a status
   colour and nothing else (0.69.0); pudl-commands.js gives a toned
   command without a glyph the status's glyph, so colour is never alone.
   A toned control's badge takes the tone too. */
.toolbar [data-tone="danger"], .menu-action[data-tone="danger"] { color: var(--danger); }
.toolbar [data-tone="warn"], .menu-action[data-tone="warn"] { color: var(--warn); }
.toolbar [data-tone="positive"], .menu-action[data-tone="positive"] { color: var(--positive); }
.toolbar [data-tone] > .toolbar-badge { background: color-mix(in srgb, currentColor 18%, transparent); color: var(--text); }
/* A command's own content in a menu, under its name. */
.menu-command-panel { padding: 4px 14px 8px; }
/* A folded group's menu rows: words, then a second line, then the key. */
.menu-action > .menu-row-text { display: flex; flex-direction: column; min-width: 0; }
.menu-row-detail { font-size: var(--text-xs); color: var(--text-muted); }
.menu-row-key { margin-inline-start: auto; padding-inline-start: var(--space-4); font-size: var(--text-xs); color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* === Code ================================================================
   A code block is for reading, so it is flat: a tinted panel with no
   shadow. An editor's surface takes input, so it is sunken like a field.
   Both set text in the monospace face, and highlighted code in either
   takes the --syntax colours, which dist/pudl-hljs.css maps onto
   highlight.js and the README onto CodeMirror. */
pre.code {
  margin: 0 0 var(--space-4); padding: var(--space-3) var(--space-4);
  overflow: auto;
  background: var(--surface-alt); color: var(--text);
  border-radius: var(--radius-sm);
  font: var(--text-sm)/1.55 var(--mono);
  tab-size: 4;
}
/* A code block with actions: pudl-code.js wraps a pre marked
   data-code-actions in a .code-block, with a strip above the code naming
   its language and holding Copy and Download. The strip is flat, as the
   code is, and its buttons are raised, because they are pressed. */
.code-block { margin: 0 0 var(--space-4); border-radius: var(--radius-sm); background: var(--surface-alt); }
.code-block > pre.code { margin: 0; border-radius: 0 0 var(--radius-sm) var(--radius-sm); }
.code-head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 4px 6px 4px var(--space-4);
  border-bottom: 1px solid var(--border);
  font: 600 var(--text-xs)/1.4 var(--font); color: var(--text-muted);
}
.code-lang { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.code-actions { display: inline-flex; gap: var(--space-1); margin-inline-start: auto; }
.code-actions .icon-btn { width: 26px; height: 26px; }
.code-actions .glyph { --glyph-size: 14px; }
.code-actions .icon-btn[data-done] { color: var(--positive); }
/* A block with lines too long for it scrolls sideways, so it carries
   tabindex="0" for a reader who scrolls by keyboard, and shows focus. */
pre.code:focus-visible { outline: none; box-shadow: inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.code-surface {
  font-family: var(--mono); font-size: var(--text-sm);
  background: var(--input-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--entry-shadow);
}
.code-surface:focus-within { border-color: var(--accent); box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
/* Monospace text is read character for character, so ligatures are off
   wherever PUDL sets it. Cascadia Code, first in --mono on Windows, joins
   "|---|" into one line and "!=" into a single sign by default. */
pre.code, .code-surface, .path.mono { font-variant-ligatures: none; }

/* === Dialogs ==============================================================
   A dialog is a native <dialog class="dialog">, rendered by the server and
   shown with showModal(), or with a button carrying command="show-modal"
   and commandfor naming it. The browser then keeps focus inside it, closes
   it on Escape and draws its backdrop. The backdrop dims gently, to 40%,
   because the panel's shadow does the elevation work, and the panel is
   always one step brighter than the page. pudl-dialog.js adds the command
   buttons in browsers that lack them.

   The older markup, a .dialog panel inside a .dialog-backdrop shown by a
   project's own script, still works. */
.dialog {
  width: 100%; max-width: 480px;
  background: var(--dialog-bg, var(--surface));
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-dialog);
  padding: var(--space-5);
}
dialog.dialog { margin: auto; max-height: calc(100svh - 2 * var(--space-5)); overflow: auto; }
dialog.dialog:not([open]) { display: none; }
dialog.dialog::backdrop { background: var(--backdrop); }
.dialog-backdrop {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center; padding: var(--space-5);
  background: var(--backdrop);
}
.dialog-title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-lg); font-weight: 700;
  color: var(--text);
}
.dialog-body {
  margin: 0 0 var(--space-5);
  color: var(--text-muted);
  font-size: var(--text-md); line-height: 1.55;
}
/* The actions keep their distance from whatever comes before them, a list
   or a table as well as the body, whose margin collapses with this one. */
.dialog-actions {
  display: flex; justify-content: flex-end; gap: var(--space-2);
  margin-top: var(--space-5);
}

/* A dialog's head and its actions stay in view (0.65.0). They are bands
   that stick to the top and bottom of the panel, filled with the panel's
   colour and reaching its sides, and the content between them scrolls
   beneath them, so no dialog's markup changes: the actions may be the
   dialog's own or in a form that wraps the dialog or its fields. The head
   is the title, or a .dialog-head holding the title and controls that go
   with it, such as a filter. The panel gives up its padding at the top
   and bottom to the bands, and each band's border is transparent until
   pudl-dialog.js says content is hidden beneath it. The scroll padding is
   an estimate of the bands' heights, which the script replaces with the
   measured ones. */
dialog.dialog:has(.dialog-head, .dialog-title) { padding-top: 0; scroll-padding-top: 4.5rem; }
dialog.dialog:has(.dialog-actions) { padding-bottom: 0; scroll-padding-bottom: 5rem; }
dialog.dialog .dialog-head,
dialog.dialog .dialog-title:not(.dialog-head .dialog-title) {
  position: sticky; top: 0; z-index: 1;
  margin-inline: calc(-1 * var(--space-5)); margin-top: 0;
  padding: var(--space-5) var(--space-5) calc(var(--space-2) - 1px);
  background: var(--dialog-bg, var(--surface));
  border-bottom: 1px solid transparent;
}
dialog.dialog .dialog-title:not(.dialog-head .dialog-title) { margin-bottom: 0; }
dialog.dialog .dialog-head { margin-bottom: 0; display: flex; flex-direction: column; gap: var(--space-2); }
dialog.dialog .dialog-head > .dialog-title { margin: 0; }
dialog.dialog .dialog-actions {
  position: sticky; bottom: 0; z-index: 1;
  margin: var(--space-3) calc(-1 * var(--space-5)) 0;
  padding: calc(var(--space-3) - 1px) var(--space-5) var(--space-5);
  background: var(--dialog-bg, var(--surface));
  border-top: 1px solid transparent;
}
/* The band's own space above the buttons makes up the rest of the 24px, so
   a body directly before the actions gives up half its margin. */
dialog.dialog .dialog-body:has(+ .dialog-actions) { margin-bottom: var(--space-3); }
dialog.dialog[data-dialog-more-above] .dialog-head,
dialog.dialog[data-dialog-more-above] .dialog-title:not(.dialog-head .dialog-title) { border-bottom-color: var(--border); }
dialog.dialog[data-dialog-more-below] .dialog-actions { border-top-color: var(--border); }
/* Pinning is the default. A dialog that should scroll as one, its title
   and actions with its content, such as a long document the actions
   follow, is marked data-dialog-pin="none" (0.74.0). */
dialog.dialog[data-dialog-pin="none"] .dialog-head,
dialog.dialog[data-dialog-pin="none"] .dialog-title,
dialog.dialog[data-dialog-pin="none"] .dialog-actions { position: static; border-color: transparent; }

/* === Master-detail layout ================================================ */
/* body:has(> .md-layout) is full-viewport flex; the toolbar + chip row +
   body fill the page. Scoped to a DIRECT child so a reference demo or any
   other embedded .md-layout doesn't accidentally kill page scrolling. */
body:has(> .md-layout) {
  height: 100svh; display: flex; flex-direction: column; overflow: hidden;
}
.md-layout {
  display: flex; flex-direction: column; flex: 1; min-height: 0;
}
.md-toolbar {
  display: flex; align-items: center; gap: 10px;
  padding: var(--space-2) var(--space-4); background: var(--surface);
  border-bottom: 1px solid var(--border); flex: none;
  --ctl-h: 30px;
}
.md-toolbar .btn-sm,
.md-toolbar .seg,
.md-toolbar .md-filter {
  box-sizing: border-box; height: var(--ctl-h);
  display: inline-flex; align-items: center;
}
.md-toolbar .seg { align-items: stretch; }
.md-toolbar .md-filter { padding: 4px 12px; }
.md-filter {
  box-sizing: border-box; flex: none; width: 220px;
  padding: 6px 11px; font-size: var(--text-sm);
  background: var(--input-bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--entry-shadow);
}
.md-filter:focus {
  border-color: var(--accent); outline: none;
  box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}

/* A filter that applies on submission pairs the input with a raised apply
   button, so the action that can be pressed looks pressable. It is drawn
   as every field with a key is: the group is the sunken field, its border
   round both, and the button a raised strip inside it behind a hairline. */
.md-filter-group {
  box-sizing: border-box; display: inline-flex; align-items: stretch; margin: 0;
  background: var(--input-bg);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: var(--entry-shadow);
  overflow: hidden;
}
.md-filter-group:focus-within {
  border-color: var(--accent);
  box-shadow: var(--entry-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring);
}
.md-filter-group .md-filter {
  flex: 1; min-width: 0; height: auto;
  border: 0; border-radius: 0;
  background: transparent; box-shadow: none;
}
.md-filter-group .md-filter:focus { box-shadow: none; }
.md-filter-go {
  flex: none; width: 32px; height: auto; margin: 0;
  border: 0; border-inline-start: 1px solid var(--raise-border); border-radius: 0;
  box-shadow: none;
  color: var(--text-muted);
}
.md-filter-go:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.md-filter-go::before {
  content: ""; width: 14px; height: 14px;
  background: currentColor;
  -webkit-mask: var(--glyph-search) center / contain no-repeat;
  mask: var(--glyph-search) center / contain no-repeat;
}
.md-filter-go:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--focus-ring); }
.md-toolbar .md-filter-group { height: var(--ctl-h); }
.md-toolbar-spacer { flex: 1; }
.md-toolbar-cmds {
  flex: none; display: flex; align-items: center; gap: 6px;
}
.md-toolbar-sep {
  width: 1px; height: var(--ctl-h);
  background: var(--border); align-self: center; margin: 0 var(--space-1);
}

/* The sidebar's head (0.82.0): what acts on the list only, at the top of
   the list's own column rather than in a row across the page. The filter
   field runs across it, a .md-sidebar-tools row of filter menus and Clear
   all wraps under that, and the chips wrap under those, shown only while
   there are some. It stays put while the list scrolls under it, and goes
   wherever the sidebar goes: collapsed, resized, at either side, peeking
   and as a phone's list pane. Its parts are separate elements, so each
   may be a region of its own. */
.md-sidebar-head {
  position: sticky; top: 0; z-index: 2;
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--surface); border-bottom: 1px solid var(--border);
  --ctl-h: 30px;
}
.md-sidebar-head :is(.btn-sm, .seg, .md-filter) { box-sizing: border-box; height: var(--ctl-h); display: inline-flex; align-items: center; }
.md-sidebar-head .seg { align-items: stretch; }
.md-sidebar-head .md-filter { padding: 4px 12px; width: 100%; }
.md-sidebar-head > form { display: flex; margin: 0; min-width: 0; }
.md-sidebar-head .md-filter-group { display: flex; width: 100%; height: var(--ctl-h); }
.md-sidebar-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.md-sidebar-tools .md-chips-clear { margin-inline-start: auto; }
.md-sidebar-head .md-chips { padding: 0; background: none; border: 0; }
.md-sidebar-head .md-chips .filter-chip { max-width: 100%; }

/* Active-filter chips, below the toolbar. */
.md-chips {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  padding: var(--space-2) var(--space-4); background: var(--surface);
  border-bottom: 1px solid var(--border); flex: none;
}
/* A chips row with no chips is hidden. It is tested by what it holds, not
   by :empty, because a server's template leaves whitespace in an empty row
   and :empty counts that as content. The row stays in the page, so it can
   still be a region that a swap replaces. */
.md-chips:not(:has(.filter-chip)) { display: none; }
.filter-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 3px 2px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs); font-weight: 600;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  color: var(--accent);
}
/* .fc-kind is the old name for .filter-chip-kind. The kind reads as
   secondary by its size, weight and capitals; fading it with opacity
   would take it below readable contrast, whatever the theme's colours. */
.filter-chip-kind, .fc-kind {
  font-weight: 500; font-size: var(--text-2xs);
  text-transform: uppercase; letter-spacing: .04em;
}
/* The remove button takes the filter away, so it is a small raised button
   with the close glyph drawn in it. It is left empty with an aria-label;
   a × a page left in it from before 0.34 is hidden. */
.filter-chip-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: var(--dismiss-radius, 50%);
  background: var(--raise-grad); border: 1px solid var(--raise-border);
  box-shadow: var(--raise-shadow);
  color: var(--text-muted); text-decoration: none;
  font-size: 0; line-height: 1;
}
/* A remove key that is a button rather than a link has the browser's
   padding, which would squeeze the close glyph to a sliver (0.74.0). */
button.filter-chip-x { padding: 0; flex: none; cursor: pointer; }
.filter-chip-x::before {
  content: ""; width: 10px; height: 10px; background: currentColor;
  -webkit-mask: var(--glyph-close) center / contain no-repeat;
  mask: var(--glyph-close) center / contain no-repeat;
}
.filter-chip-x:hover { color: var(--text); background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.filter-chip-x:focus-visible { outline: none; box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.filter-chip-x:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.md-chips-clear {
  font-size: var(--text-xs); color: var(--accent); text-decoration: underline; cursor: pointer;
}

/* Body row: sidebar + resize + detail. */
.md-body { display: flex; flex: 1; min-height: 0; }
/* The sidebar's width is --md-sidebar-w, which pudl-md.js sets as the
   divider is dragged and a project may set from a stored preference. The
   stylesheet holds it between --md-sidebar-min and half the layout, so the
   limits apply however the width was set, and still apply when the layout
   narrows after a drag.

   --md-sidebar-min is registered as a length, so however a theme writes it,
   12rem say, its computed value is in pixels, which is what pudl-md.js
   reads to keep the divider's limits the same as the stylesheet's. */
@property --md-sidebar-min { syntax: "<length>"; inherits: true; initial-value: 180px; }
.md-sidebar {
  width: clamp(var(--md-sidebar-min, 180px), var(--md-sidebar-w, 260px), 50%);
  flex: none; min-height: 0; overflow-y: auto;
  background: var(--surface); border-inline-end: 1px solid var(--border);
}
.md-resize {
  flex: none; width: 6px; margin: 0 -3px; z-index: 3; cursor: col-resize;
  background: transparent; position: relative;
  touch-action: none;
}
.md-resize::before {
  content: ""; position: absolute; inset: 0 2px; border-radius: 2px;
  background: transparent; transition: background .12s;
}
.md-resize:hover::before,
.md-resize.dragging::before { background: var(--accent); }
.md-resize:focus-visible { outline: none; }
.md-resize:focus-visible::before { background: var(--accent); box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
/* A handle carries the grip glyph at rest, so it can be found without a
   pointer resting on it. The glyph keeps its 16px drawing and the handle
   shows its middle column of dots. */
.md-resize::after, .split-handle::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 16px; height: 16px; margin: -8px 0 0 -8px;
  background: var(--text-muted); pointer-events: none;
  -webkit-mask: var(--glyph-grip) center / 16px 16px no-repeat;
  mask: var(--glyph-grip) center / 16px 16px no-repeat;
}
.split.stacked > .split-handle::after { transform: rotate(90deg); }
.md-resize:hover::after, .md-resize.dragging::after, .md-resize:focus-visible::after,
.split-handle:hover::after, .split-handle.dragging::after, .split-handle:focus-visible::after { background: var(--accent); }

/* Persistent master-detail handles retain a real, operable track: 12px
   where the pointer is fine, and 20px where it is a finger, which is still
   large enough to touch and drag. */
@property --md-requested-w { syntax: "<length>"; inherits: true; initial-value: 260px; }
.md-layout[data-md-persistent] { --md-requested-w: var(--md-sidebar-w, 260px); }
.md-layout[data-md-persistent] > .md-body > .md-resize {
  display: block; width: var(--md-target-size, 12px); margin: 0;
  background: var(--raise-grad); box-shadow: var(--raise-shadow);
  /* The grip is drawn 16px wide and the handle shows its middle column,
     so the rest is clipped; unclipped, it reached 2px past a handle at
     the layout's end edge and widened the page (0.62.0). */
  overflow: hidden;
}
.md-layout[data-md-persistent] > .md-body > .md-resize::before {
  inset: 0 calc((100% - var(--md-rule-size, 2px)) / 2);
}
@media (pointer: coarse) {
  .md-layout[data-md-persistent] > .md-body > .md-resize { width: var(--md-target-size, 20px); }
}
.md-layout[data-md-persistent]:not([data-md-narrow]) > .md-body > .md-sidebar {
  width: var(--md-effective-w, var(--md-sidebar-w, 260px));
}
.md-layout[data-md-persistent][data-md-collapsed]:not([data-md-narrow]) > .md-body > .md-sidebar {
  width: 0; min-width: 0; border: 0; visibility: hidden; overflow: hidden;
}
.md-layout[data-md-persistent] > .md-body > .md-resize:focus-visible {
  outline: 3px solid var(--focus-ring); outline-offset: -3px;
}

/* The sidebar at the end edge, by data-md-side="end": the right in a
   left-to-right page, the
   left in a right-to-left one. The row is reversed, so the sidebar, its
   handle and the detail keep their order in the document, which is the
   order the keyboard and assistive technology meet them, and only their
   places change; the border moves to the sidebar's other side. A narrow
   layout shows one pane, so the side does not matter there. */
.md-layout[data-md-side="end"] > .md-body { flex-direction: row-reverse; }
.md-layout[data-md-side="end"] > .md-body > .md-sidebar { border-inline-end: 0; border-inline-start: 1px solid var(--border); }

/* A peek at a collapsed sidebar (pudl-md.js, data-md-peek): the sidebar
   is drawn over the detail pane from its edge, at its own width, raised
   with a dialog's shadow above the windows there, and the handle is drawn
   at its side, where an expanded sidebar would put it. Neither changes
   the layout, so nothing behind the peek moves. */
.md-layout[data-md-peek-open] > .md-body { position: relative; }
.md-layout[data-md-persistent][data-md-collapsed][data-md-peek-open]:not([data-md-narrow]) > .md-body > .md-sidebar {
  position: absolute; inset-block: 0; inset-inline-start: 0; z-index: 60;
  width: var(--md-effective-w, var(--md-sidebar-w, 260px)); visibility: visible; overflow-x: hidden; overflow-y: auto;
  border-inline-end: 1px solid var(--border); box-shadow: var(--shadow-dialog);
}
.md-layout[data-md-side="end"][data-md-persistent][data-md-collapsed][data-md-peek-open]:not([data-md-narrow]) > .md-body > .md-sidebar {
  inset-inline-start: auto; inset-inline-end: 0; border-inline-end: 0; border-inline-start: 1px solid var(--border);
}
.md-layout[data-md-peek-open] > .md-body > .md-resize { translate: var(--md-peek-shift, 0) 0; z-index: 61; }

/* === Splitters ===========================================================
   Two panes inside a component, a folder tree beside a file list or an
   editor above a terminal, with a handle between them that resizes the
   first. The first pane's size is a custom property on the .split, by
   default --split-a, which a server may render from a stored preference
   and pudl-split.js sets as the handle moves; the stylesheet does the
   layout. .stacked puts the panes one above the other. The handle is
   drawn as the master-detail divider is, a rule that takes the accent
   while it is hovered, dragged or focused. */
.split { display: flex; min-width: 0; min-height: 0; }
.split.stacked { flex-direction: column; }
.split > .split-pane { min-width: 0; min-height: 0; overflow: auto; }
.split > .split-pane:first-child { flex: none; width: var(--split-a, 50%); }
.split.stacked > .split-pane:first-child { width: auto; height: var(--split-a, 50%); }
.split > .split-pane:last-child { flex: 1; }
.split-handle {
  flex: none; width: var(--split-target-size, 6px); margin: 0; z-index: 1; cursor: col-resize;
  position: relative; touch-action: none;
}
.split.stacked > .split-handle { width: auto; height: var(--split-target-size, 6px); cursor: row-resize; }
.split-handle::before {
  content: ""; position: absolute; inset: 0 calc((100% - var(--split-rule-size, 1px)) / 2); border-radius: 2px;
  background: var(--border); transition: background .12s;
}
.split.stacked > .split-handle::before { inset: calc((100% - var(--split-rule-size, 1px)) / 2) 0; }
.split:is([data-split-pane="first"], [data-split-pane="second"]) > .split-handle,
.split[data-split-pane="first"] > .split-pane:last-child,
.split[data-split-pane="second"] > .split-pane:first-child { display: none; }
.split[data-split-pane="first"] > .split-pane:first-child,
.split[data-split-pane="second"] > .split-pane:last-child { flex: 1; width: auto; height: auto; }
.split-handle:hover::before, .split-handle.dragging::before { background: var(--accent); }
.split-handle:focus-visible { outline: none; }
.split-handle:focus-visible::before { background: var(--accent); box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.md-detail {
  flex: 1; overflow-y: auto; padding: var(--space-5); min-width: 0;
  background: var(--bg);
}
/* A detail pane scrolls, so the server gives it tabindex="0" for a reader
   who scrolls by keyboard; its focus ring is drawn inside it. */
.md-detail:focus-visible { outline: none; box-shadow: inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }

/* The back link returns from a record to its list. It shows only when the
   layout is narrow enough to show one pane at a time. */
.md-back { display: none; }

/* === One pane at a time =================================================
   When the layout itself is 640px wide or less (a phone, or a narrow window
   or panel on a larger screen), it shows either the list or the record,
   never both. The server says which with data-md-pane on .md-layout:
   "detail" when the URL names a record, and "list", or nothing, otherwise.
   Each pane is its own URL, so Back, reload and sharing all work, and the
   detail's .md-back link returns to the list. */
.md-layout { container: md / inline-size; }

@container md (max-width: 640px) {
  .md-toolbar { flex-wrap: wrap; padding: var(--space-2) var(--space-3); }
  .md-toolbar .md-filter { flex: 1 1 100%; width: auto; }
  .md-toolbar .md-filter-group { flex: 1 1 100%; }
  .md-toolbar-spacer { display: none; }
  .md-toolbar-cmds { flex-wrap: wrap; }
  .md-resize { display: none; }

  /* The list pane fills the layout, and its rows are touch targets. */
  .md-sidebar { flex: 1; width: auto; border-inline-end: 0; }
  .md-sidebar .md-item, .md-sidebar .tree a { padding-top: 10px; padding-bottom: 10px; }
  .md-detail { padding: var(--space-4); }
  .md-layout:not([data-md-pane="detail"]) .md-detail { display: none; }

  /* The detail pane: the list, its toolbar and its filters step aside. A
     toolbar of site-wide tools, such as a launcher or a window dock, is
     marked .md-site-tools and stays in both panes. PUDL only hides the
     pane it is not showing, and leaves the display of the one it shows
     alone, so a detail pane a project lays out as a flex column stays one. */
  .md-layout[data-md-pane="detail"] .md-toolbar:not(.md-site-tools),
  .md-layout[data-md-pane="detail"] .md-chips,
  .md-layout[data-md-pane="detail"] .md-sidebar { display: none; }

  .md-back {
    display: inline-flex; align-items: center; gap: var(--space-1);
    margin-bottom: var(--space-3);
    font-size: var(--text-md); font-weight: 600;
  }
}
.md-back::before { --glyph: var(--glyph-back); --glyph-size: 14px; }
.md-back:dir(rtl)::before { transform: scaleX(-1); }

/* Sidebar rows: a compact list, not big touch targets except on a narrow
   layout. The current row, marked aria-current on its link, carries a 3px
   accent edge on the side facing the detail pane and a bold title. */
.md-section-label {
  padding: 14px 14px var(--space-1);
  font-size: var(--text-2xs); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted);
}
/* A row list stands on its own, apart from a master-detail layout,
   wherever a page lists things to open: the same rows, links to their
   addresses, the same current mark, and pudl-md.js gives every list of
   rows a tree's keys. */
.row-list { list-style: none; margin: 0; padding: 0; }
.row-list > .md-row { border-inline-end-width: 3px; }
.md-row {
  display: flex; align-items: center;
  border-inline-end: 3px solid transparent;
  border-bottom: 1px solid var(--surface-alt);
}
.md-row:hover { background: var(--surface-alt); }
.md-row:has(> [aria-current]:not([aria-current="false"])),
.md-row.active {
  background: var(--surface-alt);
  border-inline-end-color: var(--accent);
}
.md-row:has(> [aria-current]:not([aria-current="false"])) .md-item,
.md-row.active .md-item { font-weight: 700; color: var(--text); }
.md-item {
  flex: 1; min-width: 0;
  padding: 5px 14px;
  font-size: var(--text-sm); line-height: 1.5;
  color: var(--text); text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: pointer;
}
/* A row's title keeps to one line and ends in an ellipsis, but its
   metadata wraps: a description cut at the sidebar's edge would lose its
   end with nothing to say so, since the ellipsis marks only the title's
   line. An item may hold several .md-meta lines, a date and then a
   description say. */
.md-item .md-meta {
  display: block; font-size: var(--text-2xs); font-weight: 500; color: var(--text-muted);
  white-space: normal; overflow-wrap: anywhere;
}
.md-grip {
  flex: none; width: 22px; text-align: center; cursor: grab;
  color: var(--text-muted); font-size: var(--text-md); line-height: 1;
}

/* === Trees ===============================================================
   A hierarchy of places, such as folders or nested categories: nested
   lists of links, each a place with an address, so without script a tree
   is an indented list the reader can follow. The server marks a node that
   has children with aria-expanded on its link, and the current node with
   aria-current. pudl-tree.js turns it into the ARIA tree pattern, draws
   the toggles, and collapses what aria-expanded="false" says is closed.

   The nodes are flat, like list rows, because they are places to go; the
   current node carries the accent edge and bold of the current list row.
   Each toggle is a small raised button, since pressing it opens or closes
   its branch. */
.tree, .tree ul { list-style: none; margin: 0; padding: 0; }
/* A host lines the tree up with what stands around it through two
   properties set on the tree (0.96.8): --tree-indent, the step each
   nested level moves in by, and --tree-row-pad-start, the space before a
   row's toggle or glyph. The toggle, glyph and gap are not theirs. */
.tree { --tree-indent: var(--space-4); --tree-row-pad-start: 6px; }
.tree ul { padding-inline-start: var(--tree-indent); }
.tree a {
  display: flex; align-items: center; gap: 6px;
  padding: 3px 10px 3px var(--tree-row-pad-start);
  border-inline-end: 3px solid transparent;
  font-size: var(--text-sm); line-height: 1.5;
  color: var(--text); text-decoration: none; white-space: nowrap;
}
.tree a:hover { background: var(--surface-alt); }
.tree a[aria-current]:not([aria-current="false"]) {
  background: var(--surface-alt); border-inline-end-color: var(--accent); font-weight: 700;
}
.tree a:focus-visible { outline: none; box-shadow: inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.tree-toggle, .tree-spacer, .card-toggle { flex: none; width: 16px; height: 16px; }
.tree-toggle, .card-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-xs); border: 1px solid var(--raise-border);
  background: var(--raise-grad); box-shadow: var(--raise-shadow);
  color: var(--text-muted); cursor: pointer;
}
.tree-toggle:active, .card-toggle:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.tree-toggle:hover, .card-toggle:hover { color: var(--text); background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.card-toggle:focus-visible { outline: none; box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.tree-toggle::before, .card-toggle::before {
  content: ""; width: 10px; height: 10px;
  background: currentColor;
  -webkit-mask: var(--glyph-caret) center / contain no-repeat;
  mask: var(--glyph-caret) center / contain no-repeat;
  transform: rotate(-90deg);
}
.tree-toggle:dir(rtl)::before, .card-toggle:dir(rtl)::before { transform: rotate(90deg); }
[aria-expanded="true"] > .tree-toggle::before { transform: none; }
.tree.tree-ready a[aria-expanded="false"] + ul { display: none; }
/* A node's actions button (0.81.0) is the node's last child, after its
   branch, and stands at the end of the node's own line. */
.tree li:has(> [data-row-actions]) { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
.tree li:has(> [data-row-actions]) > a { grid-column: 1; grid-row: 1; min-width: 0; }
.tree li:has(> [data-row-actions]) > ul { grid-column: 1 / -1; grid-row: 2; }
.tree li > [data-row-actions] { grid-column: 2; grid-row: 1; margin-inline: 4px; }
.md-row > [data-row-actions] { margin-inline: 4px 8px; }

/* === Path bar ============================================================
   Where the reader is inside a hierarchy they are browsing, such as the
   folder a file manager shows: an ordered list of the places above it,
   each a link, ending with the place itself, marked aria-current. It is
   for a browsed hierarchy only. A site's own sections are its tabs, and a
   record's way back is its back link, so a path bar is never a site's
   breadcrumb trail. The separators are drawn, and say nothing to
   assistive technology, which hears the list.

   The bar keeps to one line (0.97.7). When its places do not fit, the
   places above the current one shrink, each to an ellipsis, and the
   current place shrinks last; when they still do not fit, the bar scrolls
   sideways, and it starts scrolled to its end, so the current place is in
   sight at load without script. The bar is a scroll container of the
   opposite direction to the page, holding the list in the page's own
   direction, packed toward its end: a scroll container starts at its
   start edge, which for the bar is the page's end, and what the list
   overflows toward the page's start is the bar's scrollable overflow. The
   list's order, and what assistive technology hears, do not change. A
   scrolled-off place stays in the Tab order and is scrolled into view as
   it takes focus. The host gives each place its title, so a shortened
   name can be read by the pointer. A bar marked .wrap wraps as it did
   before. */
.path { display: flex; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.path::-webkit-scrollbar { display: none; }
.path:dir(ltr) { direction: rtl; }
.path:dir(ltr) > ol { direction: ltr; margin-right: auto; }
.path:dir(rtl) { direction: ltr; }
.path:dir(rtl) > ol { direction: rtl; margin-left: auto; }
.path ol {
  display: flex; flex-wrap: nowrap; align-items: center; justify-content: flex-end; gap: 0 2px;
  margin: 0; padding: 0; list-style: none; min-width: 0;
  font-size: var(--text-sm);
}
/* A place starts at its own width and may shrink to 3em, or to its own
   width where that is less (0.97.8), so a one-character place such as "/"
   is never padded out. Its flex basis is its content and its minimum is
   the flex item's automatic one, which the flexbox specification makes
   the smaller of its width and its content's minimum width. Its width is
   3em, written as min(3em, 100%) so that it holds a percentage of the
   list: CSS Sizing treats such a cyclic percentage as auto while the list
   measures its places, so the list is as wide as their content and not
   3em for each. */
.path li { display: inline-flex; align-items: center; flex: 0 1 max-content; width: min(3em, 100%); min-width: auto; }
.path li:last-child { flex-shrink: 0; width: auto; min-width: 0; max-width: 100%; }
.path.wrap:where(:dir(ltr), :dir(rtl)) { direction: inherit; display: block; overflow: visible; }
.path.wrap:where(:dir(ltr), :dir(rtl)) > ol { direction: inherit; margin: 0; flex-wrap: wrap; justify-content: flex-start; }
.path.wrap li { flex: none; width: auto; min-width: 0; }
/* In a toolbar the bar takes the room between the buttons, so it shrinks
   and scrolls there instead of taking a line of its own. */
.toolbar > .path { flex: 1 1 0; }
.path li + li::before {
  content: ""; flex: none; width: 12px; height: 12px; margin-inline: 1px;
  background: var(--text-muted);
  -webkit-mask: var(--glyph-slash) center / contain no-repeat;
  mask: var(--glyph-slash) center / contain no-repeat;
}
.path li > * { max-width: 16em; padding: 2px 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.path [aria-current]:not([aria-current="false"]) { font-weight: 700; color: var(--text); }
.path.mono { font-family: var(--mono); }

/* === Menus ===============================================================
   A menu button opens a panel of choices: places to go, and actions. The
   button is raised and carries a caret, so it reads as opening something
   rather than doing something, and it shows pressed while its panel is
   open. The panel is lifted like a dialog, without a backdrop, because it
   is not modal. It is an HTML popover, so it opens, closes on Escape or an
   outside click, and sits above everything with no script; pudl-menu.js
   places it against its button and adds the arrow keys and filtering.

   Places in a panel are list rows, the .md-row and .md-item of the
   master-detail sidebar, grouped under .md-section-label headings. Actions
   are .menu-action buttons, set apart by an .menu-sep rule. */
.menu { display: inline-flex; position: relative; }

/* A split button (0.74.0) does its usual thing when its main key is
   pressed, and offers the alternatives from a menu button joined to it:
   two raised keys sharing a border, as the segmented control's keys and a
   combo box's opener are, so the reader sees two things to press. The
   menu button holds only the caret, and its accessible name says what it
   offers. Its menu lines up under the whole button. */
.split-btn { display: inline-flex; align-items: stretch; vertical-align: middle; }
.split-btn > .btn:first-child { border-start-end-radius: 0; border-end-end-radius: 0; }
.split-btn > .menu > .menu-btn {
  height: 100%; margin-inline-start: -1px; padding-inline: 8px;
  border-start-start-radius: 0; border-end-start-radius: 0;
}
.split-btn > .btn:focus-visible, .split-btn > .menu > .menu-btn:focus-visible { position: relative; z-index: 1; }

/* A popover (0.74.0) is a panel of any content anchored to the control
   that opened it, a small form, a help text or a filter: not a menu and
   not modal. It is an HTML popover, so Escape and a press outside close it
   without a script, and pudl-menu.js places it against its control as a
   menu panel is placed, takes focus into its first field, and the browser
   returns focus when it closes. It is drawn as a menu panel with room for
   content. With .pointer it points at its control, for controls that sit
   close together; with a .popover-head it has a title and a close key,
   for content that needs a name. */
.popover {
  margin: auto; padding: var(--space-4);
  min-width: 16rem; max-width: min(24rem, calc(100vw - 16px));
  max-height: min(36rem, calc(100svh - 16px)); overflow: auto;
  background: var(--dialog-bg, var(--surface)); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--overlay-shadow,
      var(--shadow-card),
      0 16px 40px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.6), transparent));
  font: var(--text-md)/1.5 var(--font);
}
.popover.placing { visibility: hidden; }
.popover > :last-child { margin-bottom: 0; }
/* As a dialog's, a popover's head and its actions stay in view while
   what lies between them scrolls. */
.popover-head {
  position: sticky; top: calc(-1 * var(--space-4)); z-index: 1;
  display: flex; align-items: center; gap: var(--space-2);
  margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-4)) var(--space-3);
  padding: 8px 8px 8px var(--space-4);
  background: var(--dialog-bg, var(--surface));
  border-bottom: 1px solid var(--border);
}
.popover-title { flex: 1; min-width: 0; margin: 0; font-size: var(--text-sm); font-weight: 700; }
.popover-close::before {
  content: ""; width: 12px; height: 12px; background: currentColor;
  -webkit-mask: var(--glyph-close) center / contain no-repeat;
  mask: var(--glyph-close) center / contain no-repeat;
}
.popover-actions {
  position: sticky; bottom: calc(-1 * var(--space-4)); z-index: 1;
  display: flex; justify-content: flex-end; gap: var(--space-2);
  margin: var(--space-3) calc(-1 * var(--space-4)) calc(-1 * var(--space-4));
  padding: var(--space-2) var(--space-4) var(--space-4);
  background: var(--dialog-bg, var(--surface));
}
.popover.pointer { overflow: visible; }
.popover.pointer::before {
  content: ""; position: absolute; left: calc(var(--popover-pointer-x, 22px) - 6px); width: 12px; height: 12px;
  background: var(--dialog-bg, var(--surface));
  border: 1px solid var(--border); border-width: 1px 0 0 1px;
  transform: rotate(45deg);
}
.popover.pointer[data-menu-side="below"]::before { top: -7px; }
.popover.pointer[data-menu-side="above"]::before { bottom: -7px; transform: rotate(225deg); }
.popover.sheet { border-radius: var(--radius) var(--radius) 0 0; }
.popover.sheet.pointer::before { display: none; }
@media (forced-colors: active) {
  .popover { border: 1px solid CanvasText; }
  .popover-close::before { forced-color-adjust: none; background: ButtonText; }
}
.menu-btn::after { --glyph: var(--glyph-caret); --glyph-size: 12px; opacity: .75; }
.menu:has(> .menu-panel:popover-open) > .menu-btn {
  background: var(--raise-active-bg);
  box-shadow: var(--raise-active-shadow);
}

.menu-panel, .combo-list, .date-panel {
  margin: auto; padding: 6px 0;
  min-width: 14rem; max-width: min(22rem, calc(100vw - 16px));
  max-height: min(32rem, calc(100svh - 16px));
  overflow: auto;
  background: var(--dialog-bg, var(--surface)); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--overlay-shadow,
      var(--shadow-card),
      0 16px 40px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.6), transparent));
  font-family: var(--font);
}
/* The script hides a panel for the moment between opening and placing it,
   so it never flashes in the middle of the screen. */
.menu-panel.placing { visibility: hidden; }
/* On a narrow screen the script opens the panel as a sheet across the full
   width, so its rows stay large enough to tap. */
.menu-panel.sheet {
  width: 100%; max-width: none;
  border-width: 0 0 1px; border-radius: 0 0 var(--radius) var(--radius);
}
.menu-panel.sheet .md-item { padding-top: 10px; padding-bottom: 10px; }

/* Cue gutters are borders outside the scrolling content, so no command is covered. */
.menu-viewport-insets {
  position: fixed; visibility: hidden; pointer-events: none; width: 0; height: 0;
  padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
}
.menu-panel.menu-overflow { border-top-width: 18px; border-bottom-width: 18px; }
.menu-panel.menu-overflow::before, .menu-panel.menu-overflow::after {
  position: fixed; left: var(--menu-cue-left); width: var(--menu-cue-width); height: 18px;
  pointer-events: none; text-align: center; font: 12px/18px sans-serif;
  color: var(--text); background: var(--dialog-bg, var(--surface));
}
.menu-panel.menu-overflow::before { content: ''; top: var(--menu-cue-top); }
.menu-panel.menu-overflow::after { content: ''; top: var(--menu-cue-bottom); }
.menu-panel.menu-overflow.menu-more-above::before { content: '\25B2'; }
.menu-panel.menu-overflow.menu-more-below::after { content: '\25BC'; }

.menu-panel .md-section-label { padding: 10px 14px var(--space-1); }
.menu-panel .md-section-label:first-child { padding-top: 6px; }
.menu-panel .md-row { border-bottom: 0; }
/* The window switcher's list, a panel with no button, placed as a palette. */
.win-switcher { min-width: 18rem; }
.win-switcher .row-list { list-style: none; margin: 0; padding: 0; }
.win-switcher .md-item { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.win-switcher .md-item > .glyph { flex: none; --glyph-size: 16px; }
.win-switcher .md-item > .win-item-icon { flex: none; width: 16px; height: 16px; object-fit: contain; }
.win-switcher .md-item > .md-meta { flex: 1 0 100%; }
/* A row under the pointer or with focus is a shade of the panel's own
   colour. The panel is --dialog-bg, which is --surface-alt in the dark
   theme, so --surface-alt would not show there. */
.menu-panel, .combo-list, .date-panel { --menu-row-hover: color-mix(in srgb, var(--text) 8%, var(--dialog-bg, var(--surface))); }
.menu-panel .md-row:hover { background: var(--menu-row-hover); }
.menu-panel .md-item:focus-visible,
.menu-action:focus-visible {
  outline: none; background: var(--menu-row-hover, var(--surface-alt));
  box-shadow: inset 3px 0 var(--accent);
}
.menu-panel .md-item:dir(rtl):focus-visible,
.menu-action:dir(rtl):focus-visible { box-shadow: inset -3px 0 var(--accent); }
.menu-panel .md-filter { display: block; width: calc(100% - 20px); margin: var(--space-1) 10px 6px; }
.menu-panel form { margin: 0; }
.menu-empty { margin: 0; padding: 6px 14px; font-size: var(--text-sm); color: var(--text-muted); }

.menu-sep { border: 0; border-top: 1px solid var(--border); margin: 6px 0; }
.menu-action {
  display: flex; align-items: center; gap: var(--space-2); width: 100%;
  padding: 6px 14px;
  background: none; border: 0; border-radius: 0;
  font: var(--text-sm)/1.5 var(--font); color: var(--text); text-align: start;
  cursor: pointer;
}
.menu-action:hover { background: var(--menu-row-hover, var(--surface-alt)); }
/* A menu's checkbox row: a label.check holding a checkbox, set as a menu
   row, as a filter menu has. The
   box is the platform's, tinted with the accent, as a form's is. */
.menu-panel .menu-check {
  display: flex; align-items: center; gap: var(--space-2); margin: 0;
  padding: 6px 14px; font-size: var(--text-sm); line-height: 1.5; white-space: nowrap; cursor: pointer;
}
.menu-panel .menu-check:hover { background: var(--menu-row-hover, var(--surface-alt)); }
.menu-panel .menu-check:has(:focus-visible) { background: var(--menu-row-hover, var(--surface-alt)); box-shadow: inset 3px 0 var(--accent); }
.menu-panel .menu-check:dir(rtl):has(:focus-visible) { box-shadow: inset -3px 0 var(--accent); }
.menu-panel .menu-check input { margin: 0; accent-color: var(--accent); }
.menu-action.danger { color: var(--danger); }
/* A control that destroys something leads with the warning glyph, so its
   danger never rests on colour (0.66.0). A control that holds a picture of
   its own, a .glyph, an image or any decoration hidden from readers, keeps
   that instead, so a project that already wrote the glyph into its markup
   does not get two. A danger row that switches on and off has its tick
   there and is left alone. In a menu panel the glyph is the tick's size
   and takes the tick's column, so labels stay in line. */
.btn-danger:not(.icon-btn):not(:has(> .glyph, > img, > svg, > [aria-hidden="true"]:not(.menubar-shortcut)))::before,
.menu-action.danger:not([aria-pressed], [aria-checked]):not(:has(> .glyph, > img, > svg, > [aria-hidden="true"]:not(.menubar-shortcut)))::before {
  content: ""; flex: none; width: 1em; height: 1em;
  background: currentColor;
  -webkit-mask: var(--glyph-warning) center / contain no-repeat;
  mask: var(--glyph-warning) center / contain no-repeat;
}
.menu-action.danger:not([aria-pressed], [aria-checked]):not(:has(> .glyph, > img, > svg, > [aria-hidden="true"]:not(.menubar-shortcut)))::before { width: 12px; height: 12px; }
.menu-panel:has(.menu-action:is([aria-pressed], [aria-checked])) .menu-action.danger:not([aria-pressed], [aria-checked]):not(:has(> .glyph, > img, > svg, > [aria-hidden="true"]:not(.menubar-shortcut))),
:is(.menubar-panel, .context-menu):has(> .menu-action[aria-checked]) > .menu-action.danger:not([aria-checked]):not(:has(> .glyph, > img, > svg, > [aria-hidden="true"]:not(.menubar-shortcut))) {
  padding-inline-start: 14px;
}
@media (forced-colors: active) {
  .btn-danger::before, .menu-action.danger::before { forced-color-adjust: none; background: ButtonText; }
}
.menu-action:disabled, .menu-action[aria-disabled="true"] { color: var(--text-muted); opacity: .5; cursor: default; }
.menu-action:disabled:hover { background: none; }
/* A command that switches something on and off is a menuitemcheckbox, or
   one of several a menuitemradio, with aria-checked, and carries a tick
   when it is on (0.78.0); aria-pressed on a row still works as it did.
   The tick's column is kept when it is off, so the labels stay in line. */
.menu-action:is([aria-pressed], [role="menuitemcheckbox"][aria-checked], [role="menuitemradio"][aria-checked])::before {
  content: ""; flex: none; width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: var(--glyph-tick) center / contain no-repeat;
  mask: var(--glyph-tick) center / contain no-repeat;
  visibility: hidden;
}
.menu-action:is([aria-pressed="true"], [role][aria-checked="true"])::before { visibility: visible; }
/* A switching row that also has a glyph, such as Pin beside the pin glyph,
   keeps to the one column: its glyph stands there while it is off and the
   tick while it is on (0.78.0), so its words line up with every other
   row's and its state still shows. */
.menu-action:is([aria-pressed], [aria-checked]):not([aria-pressed="true"], [aria-checked="true"]):has(> :is(.glyph, .menubar-icon))::before { display: none; }
.menu-action:is([aria-pressed="true"], [aria-checked="true"]) > :is(.glyph, .menubar-icon) { display: none; }
/* In a menu with any such command, the other commands leave the tick's
   column empty, so every label starts in one line. */
/* Only a command with words is indented; an empty one, such as a zone of
   the windows' layout picker, draws only its shape and keeps its size. */
.menu-panel:has(.menu-action:is([aria-pressed], [aria-checked])):not(:has(> .menu-action > :is(.glyph, .menubar-icon))) .menu-action:not([aria-pressed], [aria-checked]):not(:empty):not(.seg-choice):not(.menubar-back):not(.danger) {
  padding-inline-start: calc(14px + 12px + var(--space-2));
}
@media (forced-colors: active) {
  .menu-action:is([aria-pressed], [aria-checked])::before, .seg-choice::before { forced-color-adjust: none; background: CanvasText; }
}

/* A segmented control marked data-seg-menu is, on a narrow screen, a
   pop-up button that shows the current choice, which pudl-menu.js builds
   beside it. Its menu lists every choice, the current one ticked; a
   choice that is a link is ticked by aria-current. */
.seg-menu { display: none; }
@media (max-width: 640px) {
  .seg[data-seg-menu]:has(+ .seg-menu) { display: none; }
  .seg[data-seg-menu] + .seg-menu { display: inline-flex; }
}
a.seg-choice { text-decoration: none; }
.seg-choice::before {
  content: ""; flex: none; width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: var(--glyph-tick) center / contain no-repeat;
  mask: var(--glyph-tick) center / contain no-repeat;
  visibility: hidden;
}
.seg-choice[aria-pressed="true"]::before,
.seg-choice[aria-current]:not([aria-current="false"])::before { visibility: visible; }

/* The commands an applet offers on its own page, where no window menu
   holds them: a row above the applet with a menu button. */
.applet-commands { display: flex; justify-content: flex-end; margin-bottom: var(--space-2); }

/* === Data tables =========================================================
   A table of many records, one per row. It sits in a .data-table-wrap,
   which scrolls sideways when the columns outrun the space and keeps the
   header in view as the rows scroll under it.

   A column the reader can sort by has a header whose link, or button, is
   raised, because pressing it re-sorts, and it carries a glyph: two small
   arrows while the column is not the sort, one arrow for the direction it
   is. The header's aria-sort says which, and the link is an ordinary
   address such as ?sort=amount&dir=desc, so sorting works without script
   and a sorted list can be shared. Columns of numbers carry .num and align
   to the end. A selected row is marked aria-selected="true", or holds a
   checked checkbox, and carries an accent edge at its start as well as a
   tint. A .data-table-empty row says there is nothing to show.

   With .stack on the table, a wrap 560px wide or less shows each row as a
   small card of label and value pairs, the labels coming from each cell's
   data-label. */
.data-table-wrap {
  overflow: auto; max-width: 100%;
  background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
  container: dt / inline-size;
}
.data-table {
  width: 100%; border-collapse: separate; border-spacing: 0;
  font-size: var(--text-md); font-variant-numeric: tabular-nums;
}
.data-table caption {
  caption-side: top; text-align: start;
  padding: var(--space-3) var(--space-4) var(--space-2);
  font-size: var(--text-sm); font-weight: 600; color: var(--text-muted);
}
.data-table th, .data-table td {
  padding: var(--space-2) var(--space-3);
  text-align: start; vertical-align: top;
  border-bottom: 1px solid var(--border);
}
.data-table thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface-alt);
  font-size: var(--text-sm); font-weight: 600; color: var(--text-muted);
  white-space: nowrap;
}
.data-table tbody tr:last-child > * { border-bottom: 0; }
.data-table tbody tr:hover { background: color-mix(in srgb, var(--text) 4%, transparent); }
.data-table .num { text-align: end; }
.data-table .data-table-check { width: 1%; }
.data-table .data-table-check input { width: 16px; height: 16px; margin: 0; accent-color: var(--accent); }
.data-table tr[aria-selected="true"],
.data-table tr:has(> .data-table-check input:checked) {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  box-shadow: inset 3px 0 var(--accent);
}
.data-table tr[aria-selected="true"]:dir(rtl),
.data-table tr:has(> .data-table-check input:checked):dir(rtl) { box-shadow: inset -3px 0 var(--accent); }

/* A sortable column's header is a raised control filling its cell. */
.data-table th[aria-sort] { padding: 0; }
.data-table th[aria-sort] > :is(a, button) {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: var(--space-2) var(--space-3);
  background: var(--raise-grad);
  border: 0; border-inline-end: 1px solid var(--raise-border);
  box-shadow: var(--surface-edge-shadow, inset 0 1px color-mix(in srgb, var(--light) var(--lit), transparent));
  font: inherit; color: var(--text); text-decoration: none; cursor: pointer;
  --glyph: var(--glyph-sort); --glyph-size: 12px;
}
.data-table th[aria-sort].num > :is(a, button) { justify-content: flex-end; }
.data-table th[aria-sort] > :is(a, button):hover { background: var(--raise-grad-hover); }
.data-table th[aria-sort] > :is(a, button):active { background: var(--raise-active-bg); }
.data-table th[aria-sort] > :is(a, button):focus-visible { outline: none; box-shadow: inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.data-table th[aria-sort] > :is(a, button)::after { opacity: .55; }
.data-table th[aria-sort="ascending"] > :is(a, button)::after { --glyph: var(--glyph-sort-up); opacity: 1; }
.data-table th[aria-sort="descending"] > :is(a, button)::after { --glyph: var(--glyph-sort-down); opacity: 1; }
.data-table th[aria-sort="ascending"] > :is(a, button),
.data-table th[aria-sort="descending"] > :is(a, button) { font-weight: 700; }

.data-table .data-table-empty td {
  padding: var(--space-6) var(--space-4);
  text-align: center; color: var(--text-muted);
}

/* A table whose rows are choices, such as a file list or an inbox, is a
   grid: role="grid" on the table. pudl-grid.js makes it one stop in the
   Tab order, moves the selection with the arrow keys, and opens a row, by
   its first link, with Enter or a double-click. The selected row already
   looks selected; the row with focus also carries the ring. */
.data-table[role="grid"] tbody tr { cursor: default; }
.data-table[role="grid"] tbody tr:focus-visible { outline: none; box-shadow: inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.data-table[role="grid"] tbody tr[aria-selected="true"]:focus-visible { box-shadow: inset 3px 0 var(--accent), inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.data-table[role="grid"] tbody tr[aria-selected="true"]:dir(rtl):focus-visible { box-shadow: inset -3px 0 var(--accent), inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }

@container dt (max-width: 560px) {
  .data-table.stack thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
  }
  .data-table.stack, .data-table.stack tbody, .data-table.stack tr, .data-table.stack td,
  .data-table.stack caption { display: block; }
  .data-table.stack tr { padding: var(--space-2) 0; border-bottom: 1px solid var(--border); }
  .data-table.stack tbody tr:last-child { border-bottom: 0; }
  /* The label keeps to the start and everything in the value, two badges
     say, stays together at the end. */
  .data-table.stack td {
    display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: baseline; gap: var(--space-1) var(--space-2);
    padding: var(--space-1) var(--space-4); border-bottom: 0; text-align: end;
  }
  .data-table.stack .data-table-check { width: auto; }
  .data-table.stack td[data-label]::before {
    content: attr(data-label); margin-inline-end: auto; padding-inline-end: var(--space-2);
    font-size: var(--text-sm); font-weight: 600; color: var(--text-muted); text-align: start;
  }
}

/* === Row editors =========================================================
   A list of like things edited as rows of fields: a table whose headings
   are flat (nothing sorts), with a danger glyph button at each row's end
   to remove it, in a column no wider than the button, and an Add button
   beneath. A row's second line, .row-editor-more, spans the columns. At
   480px of the editor's own width or less, each row stacks its fields
   under their headings as labels, from each cell's data-label, with the
   remove button at its top end. pudl-rows.js keeps focus where a reader
   expects it after Add and Remove. */
.row-editor-wrap { container: re / inline-size; min-width: 0; }
.row-editor { width: 100%; border-collapse: collapse; }
.row-editor th {
  padding: 0 var(--space-1) 4px; text-align: start;
  font-size: var(--text-xs); font-weight: 600; color: var(--text-muted);
}
.row-editor td { padding: var(--space-1); vertical-align: middle; }
.row-editor tbody tr { border-top: 1px solid var(--surface-alt); }
.row-editor tbody tr.row-editor-more { border-top: 0; }
.row-editor tbody tr.row-editor-more > td { padding-top: 0; }
.row-editor td > .form-input, .row-editor td > .form-textarea, .row-editor td > .form-select-wrap { width: 100%; }
.row-editor td > .form-select-wrap > .form-select { width: 100%; }
.row-editor td > label.check { white-space: nowrap; }
.row-editor .row-editor-remove, .row-editor th.row-editor-remove, .row-editor td.row-editor-remove { width: 36px; padding-inline-end: 0; }
.row-editor td.row-editor-remove { text-align: end; }
.row-editor-add { margin-top: var(--space-2); }
.row-editor-add > .glyph { --glyph-size: 12px; }
@container re (max-width: 480px) {
  .row-editor thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .row-editor, .row-editor tbody, .row-editor tr, .row-editor td { display: block; }
  .row-editor tbody tr { position: relative; padding: var(--space-2) 0; }
  .row-editor tbody tr.row-editor-more { padding-top: 0; }
  .row-editor td { padding: var(--space-1) 0; }
  .row-editor td[data-label]::before {
    content: attr(data-label); display: block; margin-bottom: 4px;
    font-size: var(--text-sm); font-weight: 600; color: var(--text-muted);
  }
  .row-editor td.row-editor-remove { position: absolute; top: var(--space-2); inset-inline-end: 0; width: auto; padding: 0; }
  .row-editor tbody tr:has(> td.row-editor-remove) > td:first-child { padding-inline-end: 36px; }
}

/* === Zoom ================================================================
   Zoom Out, the scale, Zoom In and Fit, above a viewport that scrolls a
   picture larger than itself. The buttons are raised, the scale is flat,
   and the viewport is sunken, as a text area is, and a tab stop, since it
   scrolls. pudl-zoom.js does the scaling. */
.zoom { display: inline-flex; align-items: center; gap: var(--space-1); }
.zoom-scale { min-width: 4ch; text-align: center; font-size: var(--text-sm); font-variant-numeric: tabular-nums; color: var(--text); }
.zoom .icon-btn > .glyph { --glyph-size: 14px; }
.zoom-viewport {
  overflow: auto; min-height: 0;
  background: var(--input-bg); border: 1px solid var(--input-border); border-radius: var(--radius-sm);
  box-shadow: var(--entry-shadow);
}
.zoom-viewport:focus-visible { outline: none; box-shadow: var(--entry-shadow), inset 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.zoom-viewport > svg, .zoom-viewport > img { display: block; max-width: none; }

/* === Notices and toasts ==================================================
   A notice is a message that stays on the page until it is dealt with: an
   error on submission, a warning about the record in view, a note of what
   has changed. A toast is a passing confirmation, "Saved", that leaves by
   itself. Both lead with the glyph of their kind (information, positive,
   warning, danger) and a coloured edge, so neither rests on colour alone.
   An error the reader must act on is a notice, never a toast, because a
   toast goes away. */
.notice {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid color-mix(in srgb, var(--notice-color) 45%, var(--border));
  border-inline-start: 4px solid var(--notice-color);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--notice-color) 9%, var(--surface));
  color: var(--text);
  font-size: var(--text-md);
  --notice-color: var(--accent);
  --glyph: var(--glyph-info); --glyph-size: 18px;
}
.notice::before { margin-top: 1px; color: var(--notice-color); background: var(--notice-color); }
.notice.positive { --notice-color: var(--positive); --glyph: var(--glyph-check); }
.notice.warn { --notice-color: var(--warn); --glyph: var(--glyph-warning); }
.notice.danger { --notice-color: var(--danger); --glyph: var(--glyph-stop); }
.notice-content { flex: 1; min-width: 0; }
.notice-title { margin: 0 0 2px; font-size: var(--text-md); font-weight: 700; }
.notice-body { margin: 0; }
.notice-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
/* A notice, and a toast, keep their content inside the card whatever the
   text: a path, a run's id or an address with no space in it breaks where
   it would otherwise run past the edge, prose breaks as before, a code
   block scrolls, and a long button label ends in an ellipsis, with the
   whole label in its title. */
.notice, .toast { min-width: 0; max-width: 100%; box-sizing: border-box; }
.notice > *, .toast > * { min-width: 0; }
.notice :is(.notice-content, .notice-body, .notice-title), .toast :is(.toast-content, .toast-body, .toast-title, .toast-text) { overflow-wrap: anywhere; }
.notice pre, .toast pre { max-width: 100%; overflow-x: auto; }
.notice-actions .btn, .toast-actions .btn { max-width: 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The small raised button that dismisses a notice or a toast. */
.notice-close, .toast-close {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; margin-inline-start: auto;
  border-radius: var(--dismiss-radius, 50%);
  background: var(--raise-grad); border: 1px solid var(--raise-border);
  box-shadow: var(--raise-shadow);
  color: var(--text-muted); cursor: pointer;
  --glyph: var(--glyph-close); --glyph-size: 12px;
}
.notice-close:hover, .toast-close:hover { color: var(--text); background: var(--raise-grad-hover); }
.notice-close:focus-visible, .toast-close:focus-visible { outline: none; box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.notice-close:active, .toast-close:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }

/* Toasts gather in a live region at the foot of the window, which
   pudl-toast.js creates if the page has not rendered one. They stand
   above a taskbar along the foot, whose room pudl-windows.js keeps as
   --win-bar-clear (0.80.0), so they never cover its tray or buttons. */
.toast-region {
  position: fixed; z-index: 1000;
  inset-block-end: calc(var(--space-4) + var(--win-bar-clear, 0px)); inset-inline-end: var(--space-4);
  display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-2);
  width: min(24rem, calc(100vw - 2 * var(--space-4)));
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  background: var(--dialog-bg, var(--surface)); color: var(--text);
  border: 1px solid var(--border);
  border-inline-start: 4px solid var(--notice-color);
  border-radius: var(--radius-sm);
  box-shadow: var(--toast-shadow,
      var(--shadow-card),
      0 10px 28px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.4), transparent));
  font-size: var(--text-md);
  transition: opacity .2s, transform .2s;
  --notice-color: var(--accent);
  --glyph: var(--glyph-info); --glyph-size: 18px;
}
.toast::before { margin-top: 1px; background: var(--notice-color); }
.toast.positive { --notice-color: var(--positive); --glyph: var(--glyph-check); }
.toast.warn { --notice-color: var(--warn); --glyph: var(--glyph-warning); }
.toast.danger { --notice-color: var(--danger); --glyph: var(--glyph-stop); }
.toast-text { flex: 1; min-width: 0; margin: 2px 0 0; }
/* A toast's one action (0.90.0) stands between its text and its dismiss
   button, a small raised button that gives up width to the text and ends
   in an ellipsis, its whole label its tooltip. */
.toast-action { flex: none; max-width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.toast-action + .toast-close { margin-inline-start: 0; }
.toast.leaving { opacity: 0; transform: translateY(8px); }

/* === Empty and loading states ============================================
   An empty state says there is nothing here yet, and what to do about it:
   a drawn glyph, a title, a line of text and perhaps an action. A loading
   state is a spinner with words beside it, in a role="status" element, so
   it is announced and never rests on the animation alone. A region being
   refreshed by pudl-regions.js dims as well. */
.empty-state {
  display: grid; justify-items: center; gap: var(--space-2);
  padding: var(--space-6) var(--space-4);
  text-align: center; color: var(--text-muted);
  --glyph: var(--glyph-empty); --glyph-size: 36px;
}
.empty-state::before { opacity: .7; margin-bottom: var(--space-1); }
.empty-state-title { margin: 0; font-size: var(--text-lg); font-weight: 700; color: var(--text); }
.empty-state-body { margin: 0; max-width: 42ch; font-size: var(--text-md); }
.empty-state-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); margin-top: var(--space-2); }

.spinner {
  display: inline-block; flex: none; vertical-align: middle;
  width: 1.2em; height: 1.2em; border-radius: 50%;
  border: 2px solid color-mix(in srgb, currentColor 22%, transparent);
  border-top-color: currentColor;
  animation: pudl-spin .8s linear infinite;
}
@keyframes pudl-spin { to { transform: rotate(360deg); } }
.loading {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  padding: var(--space-5) var(--space-4);
  font-size: var(--text-sm); color: var(--text-muted);
}

/* === Tooltips ============================================================
   A tooltip names a control that shows only a glyph, such as an icon
   button, a window button or a filter's apply button. pudl-tooltip.js
   shows one for any element with data-tooltip, or with an aria-label on a
   glyph-only control, after a moment of hover or at once on keyboard
   focus. It stays while the pointer moves onto it, and Escape dismisses
   it, as WCAG 1.4.13 asks. */
.tooltip {
  margin: 0; padding: 4px 8px;
  max-width: 18rem;
  border: 0; border-radius: var(--radius-xs);
  background: var(--text); color: var(--bg);
  font: 600 var(--text-xs)/1.4 var(--font);
  box-shadow: var(--tooltip-shadow, 0 4px 12px color-mix(in srgb, var(--shade) calc(var(--depth) * 1.4), transparent));
  pointer-events: auto;
}
.tooltip.placing { visibility: hidden; }

/* === Steppers (0.74.0) ===================================================
   The steps of a task that takes several pages, as an ordered list: a step
   done carries the class done and is a link back to its page, the current
   step carries aria-current="step", and the rest are to come. Each is told
   by a glyph as well as a colour: a done step's mark is the tick on the
   accent, the current one's is its number in an accent ring and its words
   are bold, and one to come is its number in a hairline ring with muted
   words. The steps run across, joined by lines that take the accent once
   done, and on a narrow screen only the current step keeps its words. A
   .stepper.vertical runs down the side, each step a row, the current one
   lit as a list row is, with room for long names and many steps. */
.stepper {
  display: flex; align-items: center; margin: 0; padding: 0; list-style: none;
  counter-reset: pudl-step; font-size: var(--text-sm);
}
.stepper > li {
  display: flex; align-items: center; gap: var(--space-2); flex: 1 1 0; min-width: 0;
  counter-increment: pudl-step; color: var(--text-muted);
}
.stepper > li:last-child { flex: 0 0 auto; }
.stepper > li::before {
  content: counter(pudl-step); flex: none; display: inline-grid; place-items: center;
  width: 24px; height: 24px; border-radius: 50%; box-sizing: border-box;
  border: 1px solid var(--border); background: var(--surface);
  font: 700 var(--text-xs) var(--font); color: var(--text-muted);
}
.stepper > li::after { content: ""; flex: 1 1 auto; min-width: 16px; height: 2px; margin-inline: var(--space-2); background: var(--border); }
.stepper > li:last-child::after { display: none; }
/* A done step's mark is the accent circle, and the tick on it is drawn by
   the step's link, in the colour words take on the accent, so the mark is
   a glyph as well as a colour. */
.stepper > li { position: relative; }
.stepper > li.done { color: var(--text); }
.stepper > li.done::before { content: ""; background: var(--accent); border-color: var(--accent); }
.stepper > li.done > :first-child::before {
  content: ""; position: absolute; inset-inline-start: calc(var(--stepper-pad, 0px) + 6px); top: 50%;
  width: 12px; height: 12px; margin-top: -6px;
  background: var(--on-accent);
  -webkit-mask: var(--glyph-tick) center / contain no-repeat;
  mask: var(--glyph-tick) center / contain no-repeat;
}
.stepper > li.done::after { background: var(--accent); }
.stepper > li[aria-current="step"] { color: var(--text); font-weight: 700; }
.stepper > li[aria-current="step"]::before { border: 2px solid var(--accent); color: var(--accent); }
.stepper > li > a { color: inherit; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stepper > li > a:hover { text-decoration: underline; }
.stepper > li > span, .stepper > li { white-space: nowrap; }
@media (max-width: 640px) {
  /* The words go from sight but stay for assistive technology, and the
     tick, which hangs from the step rather than the words, stays. */
  .stepper:not(.vertical) > li:not([aria-current="step"]) > :is(a, span) { max-width: 0; overflow: hidden; }
  .stepper:not(.vertical) > li:not([aria-current="step"]) { gap: 0; }
  .stepper:not(.vertical) > li::after { min-width: 8px; margin-inline: var(--space-1); }
}
.stepper.vertical { flex-direction: column; align-items: stretch; gap: 2px; }
.stepper.vertical > li { --stepper-pad: 10px; flex: none; padding: 6px 10px; border-radius: var(--radius-xs); }
.stepper.vertical > li::after { display: none; }
.stepper.vertical > li[aria-current="step"] { background: color-mix(in srgb, var(--text) 7%, var(--surface)); box-shadow: inset 3px 0 var(--accent); }
.stepper.vertical > li[aria-current="step"]:dir(rtl) { box-shadow: inset -3px 0 var(--accent); }
@media (forced-colors: active) {
  .stepper > li::before { forced-color-adjust: none; border: 1px solid CanvasText; color: CanvasText; background: Canvas; }
  .stepper > li.done::before { background: CanvasText; }
  .stepper > li.done > :first-child::before { forced-color-adjust: none; background: Canvas; }
  .stepper > li[aria-current="step"]::before { border: 2px solid Highlight; }
}

/* === Pagination ==========================================================
   Pages of a long list, each an address such as ?page=3, so paging works
   without script and a page can be shared. The page links are raised
   small buttons; the current page, marked aria-current="page", is pressed
   in, because the reader is already there. Previous and next carry rel
   and a glyph, and at either end are marked aria-disabled with no href. */
.pagination { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); }
.pagination-summary { margin-inline-end: var(--space-3); font-size: var(--text-sm); color: var(--text-muted); }
.page-link {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-1);
  min-width: 32px; height: 32px; padding: 0 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--raise-border);
  background: var(--raise-grad); box-shadow: var(--raise-shadow);
  font: 600 var(--text-sm) var(--font); color: var(--text);
  text-decoration: none; font-variant-numeric: tabular-nums;
  --glyph: var(--glyph-back); --glyph-size: 12px;
}
.page-link:hover { background: var(--raise-grad-hover); border-color: var(--raise-border-hover); }
.page-link:active { background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow); }
.page-link:focus-visible { outline: none; box-shadow: var(--raise-shadow), 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
.page-link[aria-current]:not([aria-current="false"]) {
  background: var(--raise-active-bg); box-shadow: var(--raise-active-shadow);
  border-color: var(--raise-border-hover);
}
.page-link[aria-disabled="true"] { opacity: .45; pointer-events: none; }
.page-link[rel="next"]::after { transform: scaleX(-1); }
.page-link[rel="prev"]:dir(rtl)::before { transform: scaleX(-1); }
.page-link[rel="next"]:dir(rtl)::after { transform: none; }
.page-gap { padding: 0 var(--space-1); color: var(--text-muted); }
/* On a phone the summary takes a line of its own, so the links stay on one. */
@media (max-width: 560px) { .pagination-summary { flex-basis: 100%; margin-bottom: var(--space-1); } }

/* === Glyphs as elements ==================================================
   Any glyph token drawn as an element, in the colour of the text around
   it, at the size of that text unless --glyph-size says otherwise:

     <span class="glyph" style="--glyph: var(--glyph-folder)" aria-hidden="true"></span>

   A glyph beside words that already say what it shows is aria-hidden; one
   that stands alone carries role="img" and an aria-label. */
.glyph {
  display: inline-block; flex: none; vertical-align: -0.125em;
  width: var(--glyph-size, 1em); height: var(--glyph-size, 1em);
  background: currentColor;
  -webkit-mask: var(--glyph) center / contain no-repeat;
  mask: var(--glyph) center / contain no-repeat;
}

/* === Drop targets ========================================================
   What may be dropped where is the application's rule, so the behaviour
   is the host's; PUDL supplies only the look, so every application shows
   the same one. While something is dragged over a place it may land, the
   host sets data-drop-over on a .drop-zone, or data-drop-target on an item
   such as a folder's row, and PUDL rings it in the accent. A .drop-hint
   inside a zone says what dropping there will do, and shows only then. */
.drop-zone { position: relative; }
.drop-zone[data-drop-over], [data-drop-target] {
  box-shadow: inset 0 0 0 2px var(--accent);
  background-color: color-mix(in srgb, var(--accent) 7%, transparent);
}
.drop-hint { display: none; }
.drop-zone[data-drop-over] > .drop-hint {
  display: block; position: absolute; z-index: 1;
  inset-inline-start: 50%; bottom: var(--space-3); translate: -50% 0;
  margin: 0; padding: 4px 12px; border-radius: var(--radius-pill);
  background: var(--accent); color: var(--on-accent);
  font-size: var(--text-sm); font-weight: 600; white-space: nowrap;
  pointer-events: none;
}
.drop-zone[data-drop-over]:dir(rtl) > .drop-hint { translate: 50% 0; }

/* === Reorderable lists (0.97.0) =========================================
   pudl-reorder.js gives each movable item of a list in reorder mode a
   grip, the handle it is dragged by and moved with the keys. The grip is
   a handle, like a splitter's, so it is flat and carries the grip glyph
   at rest, taking the accent when hovered, focused or dragged. In such a
   list the drop mark is a line where the item will land rather than a
   ring, since it lands between items, not on one: at the start of the
   item it will land before, or at the end of the list. The list's
   data-reorder-active says its axis, x or y. */
.reorder-grip {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; margin: 0; margin-inline-end: var(--space-1); padding: 0;
  border: 0; border-radius: var(--radius-sm); background: none; color: var(--text-muted);
  vertical-align: middle; cursor: grab; touch-action: none;
}
.reorder-grip::before {
  content: ""; width: 16px; height: 16px; background: currentColor;
  -webkit-mask: var(--glyph-grip) center / 16px 16px no-repeat;
  mask: var(--glyph-grip) center / 16px 16px no-repeat;
}
.reorder-grip:hover, .reorder-grip:focus-visible, .dragging .reorder-grip { color: var(--accent); }
.reorder-grip:focus-visible { outline: none; box-shadow: 0 0 0 var(--focus-ring-width, 3px) var(--focus-ring); }
[data-reorder-active] > .dragging {
  position: relative; z-index: 2; cursor: grabbing;
  background: var(--surface); box-shadow: var(--shadow-card);
}
[data-reorder-active] > [data-drop-target], [data-reorder-active][data-drop-target] { background-color: transparent; }
[data-reorder-active="y"] > [data-drop-target] { box-shadow: inset 0 2px 0 var(--accent); }
[data-reorder-active="x"] > [data-drop-target] { box-shadow: inset 2px 0 0 var(--accent); }
[data-reorder-active="x"]:dir(rtl) > [data-drop-target] { box-shadow: inset -2px 0 0 var(--accent); }
[data-reorder-active="y"][data-drop-target] { box-shadow: inset 0 -2px 0 var(--accent); }
[data-reorder-active="x"][data-drop-target] { box-shadow: inset -2px 0 0 var(--accent); }
[data-reorder-active="x"][data-drop-target]:dir(rtl) { box-shadow: inset 2px 0 0 var(--accent); }

/* === Reduced motion ======================================================
   A reader who has asked their system for less motion gets none: every
   transition and animation completes at once. */
@media (prefers-reduced-motion: reduce) {
  /* Transitions take no time at all. A duration of a hundredth of a
     millisecond, the usual trick, turns on a transition of every property
     on every element, since the default property is all, and a panel made
     visible then stays hidden for that moment, too long for focus to move
     into it. */
  *, *::before, *::after {
    transition-duration: 0s !important;
    transition-delay: 0s !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* === Diffs (0.93.0) ======================================================
   A read-only diff is markup the stylesheet draws with no script: a
   section.diff, inline or with two .diff-side columns, holding .diff-hunk
   groups of .diff-line rows marked data-diff="same", "add", "del" or "pad".
   A line's kind is its sign as well as its tint, since the tint is far
   below 3:1, so the sign column is always drawn. The tint, --diff-add-bg
   and --diff-del-bg, is the strongest at which every syntax colour still
   reaches 4.5:1 over --input-bg. Gutters, signs, headings and folded bands
   are not selected with the text. */
.diff {
  --diff-no-width: 4ch;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--input-bg); color: var(--text);
  overflow-x: auto; min-width: 0;
}
.diff-summary { position: sticky; inset-inline-start: 0; margin: 0; padding: var(--space-2) var(--space-3); font-size: var(--text-sm); color: var(--text-muted); border-bottom: 1px solid var(--border); }
.diff-sides { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.diff-side { overflow-x: auto; min-width: 0; }
.diff-side + .diff-side { border-inline-start: 1px solid var(--border); }
.diff > .diff-hunk, .diff-side > .diff-hunk { min-width: max-content; }
.diff-hunk-head {
  display: flex; align-items: center; gap: var(--space-2); min-height: 28px;
  padding: 2px var(--space-3); box-sizing: border-box;
  background: var(--surface-alt); border-block: 1px solid var(--border);
  font-size: var(--text-xs); color: var(--text-muted);
  user-select: none; -webkit-user-select: none;
}
.diff-hunk-head > code { font-family: var(--mono); color: var(--accent); }
.diff-hunk-actions { margin-inline-start: auto; display: flex; gap: var(--space-1); }
.diff-line { display: flex; align-items: stretch; min-height: 20px; font: var(--text-sm)/20px var(--mono); }
.diff[data-diff-choose] .diff-line { min-height: 24px; line-height: 24px; }
.diff-no, .diff-sign, .diff-choose { flex: none; user-select: none; -webkit-user-select: none; }
.diff-no { width: var(--diff-no-width); padding-inline: 6px; text-align: end; font-size: var(--text-xs); color: var(--text-muted); }
.diff-sign { width: 20px; display: grid; place-items: center; }
.diff-line:is([data-diff="add"], [data-diff="del"]) > .diff-sign::before {
  content: ""; width: 12px; height: 12px; background: var(--diff-add);
  -webkit-mask: var(--glyph-plus) center / contain no-repeat; mask: var(--glyph-plus) center / contain no-repeat;
}
.diff-line[data-diff="del"] > .diff-sign::before { background: var(--diff-del); -webkit-mask-image: var(--glyph-minus); mask-image: var(--glyph-minus); }
.diff-choose { width: 24px; display: grid; place-items: center; cursor: pointer; }
.diff-text { flex: 1; margin: 0; padding-inline-end: var(--space-3); white-space: pre; font: inherit; background: none; color: inherit; }
.diff-line[data-diff="add"] { background: var(--diff-add-bg); }
.diff-line[data-diff="del"] { background: var(--diff-del-bg); }
.diff-line[data-diff="pad"] {
  background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--border) 50%, transparent) 0 1px, transparent 1px 6px);
}
.diff-word { text-decoration: underline 2px; text-underline-offset: 3px; background: none; color: inherit; }
ins.diff-word { text-decoration-color: var(--diff-add); }
del.diff-word { text-decoration-color: var(--diff-del); }
.diff-line:has(> .diff-choose input:checked) { box-shadow: inset 3px 0 var(--accent); }
.diff-line:has(> .diff-choose input:checked):dir(rtl) { box-shadow: inset -3px 0 var(--accent); }
.diff-fold {
  display: flex; justify-content: center; padding: 2px var(--space-3);
  background: var(--surface-alt); border-block: 1px solid var(--border); color: var(--text-muted);
  user-select: none; -webkit-user-select: none;
}
@media (forced-colors: active) {
  .diff-line { border-inline-start: 2px solid transparent; }
  .diff-line[data-diff="add"] { border-inline-start-color: CanvasText; }
  .diff-line[data-diff="del"] { border-inline-start: 2px dashed CanvasText; }
  .diff-line:is([data-diff="add"], [data-diff="del"]) > .diff-sign::before { forced-color-adjust: none; background: CanvasText; }
  ins.diff-word { text-decoration: underline 2px CanvasText; }
  del.diff-word { text-decoration: line-through 2px CanvasText; }
  .diff-line[data-diff="pad"] { forced-color-adjust: none; background: repeating-linear-gradient(45deg, GrayText 0 1px, Canvas 1px 6px); }
  .diff-line:has(> .diff-choose input:checked) { forced-color-adjust: none; box-shadow: inset 3px 0 Highlight; }
  .diff-hunk-head, .diff-fold, .diff { border-color: CanvasText; }
}

/* === Forced colours ======================================================
   In Windows' high-contrast mode the browser replaces the page's colours
   with the reader's and removes every box-shadow. PUDL draws its focus
   rings, and much of its raised and sunken treatment, with shadows, so
   here focus becomes a real outline, controls keep visible borders, and
   selected and pressed states take the system's highlight colours, since
   an elevation change alone would not show. */
@media (forced-colors: active) {
  /* Category marks draw in the reader's text colour, and position, labels
     and legends tell them apart (0.91.0). */
  :root, [data-theme] { --category-1: CanvasText; --category-2: CanvasText; --category-3: CanvasText; --category-4: CanvasText;
          --category-5: CanvasText; --category-6: CanvasText; --category-7: CanvasText; --category-8: CanvasText; }
  :focus-visible { outline: 2px solid Highlight !important; outline-offset: 2px !important; }
  .btn, .icon-btn, .seg button, .seg a, .form-input, .form-select, .form-textarea, .md-filter,
  .switch-track, .switch-thumb, .menu-panel, .dialog, .card, .section-tab,
  .filter-chip, .filter-chip-x, .status-area { border: 1px solid CanvasText; }
  .status-item:is(:hover, :active) { forced-color-adjust: none; background: Highlight; color: HighlightText; }
  .status-item > .glyph { forced-color-adjust: none; background: CanvasText; }
  .status-item:is(:hover, :active) > .glyph { background: HighlightText; }
  .seg button[aria-pressed="true"], .seg button[aria-checked="true"],
  .seg [aria-current]:not([aria-current="false"]), .seg button.active,
  .section-tab[aria-current]:not([aria-current="false"]), .section-tab.active,
  .md-row:has(> [aria-current]:not([aria-current="false"])), .md-row.active,
  .btn[aria-pressed="true"], .menu:has(> .menu-panel:popover-open) > .menu-btn,
  .topbar-pill[aria-current]:not([aria-current="false"]) {
    forced-color-adjust: none;
    background: Highlight; color: HighlightText; border-color: Highlight;
  }
  .md-row:has(> [aria-current]:not([aria-current="false"])) .md-item,
  .md-row:has(> [aria-current]:not([aria-current="false"])) .md-meta,
  .md-row.active .md-item, .md-row.active .md-meta { color: HighlightText; }
  .md-row:not(.active):not(:has(> [aria-current]:not([aria-current="false"]))) { border-inline-end-color: Canvas; }
  .switch[aria-checked="true"] .switch-track { forced-color-adjust: none; background: Highlight; border-color: Highlight; }
  .switch-thumb { forced-color-adjust: none; background: Canvas; }
  /* Glyphs are masks, exempt from the system's recolouring so that they
     still paint, so they are painted in the system's own colours here. */
  .badge::before, .form-error::before, .menu-btn::after, .md-back::before,
  .md-filter-go::before, .md-resize::after, .split-handle::after, .form-select-wrap::after { forced-color-adjust: none; background: CanvasText; }
  .filter-chip-x::before, .theme-toggle::before, .icon-btn .glyph { forced-color-adjust: none; background: ButtonText; }
  .md-filter-go::before, .menu-btn::after { background: ButtonText; }
  .notice::before, .toast::before { forced-color-adjust: none; background: CanvasText; }
  .notice-close::before, .toast-close::before, .data-table th[aria-sort] > :is(a, button)::after {
    forced-color-adjust: none; background: ButtonText;
  }
  .notice, .toast, .notice-close, .toast-close, .data-table-wrap { border: 1px solid CanvasText; }
  .tablist > [role="tab"], .page-link, .tabs > [role="tabpanel"], .tooltip { border: 1px solid CanvasText; }
  .tablist > [role="tab"][aria-selected="true"], .page-link[aria-current]:not([aria-current="false"]) {
    forced-color-adjust: none; background: Highlight; color: HighlightText; border-color: Highlight;
  }
  .empty-state::before { forced-color-adjust: none; background: CanvasText; }
  .page-link[rel="prev"]::before, .page-link[rel="next"]::after { forced-color-adjust: none; background: ButtonText; }
  .spinner { border-color: GrayText; border-top-color: CanvasText; }
  .notice, .toast { border-inline-start-width: 4px; }
  .data-table th[aria-sort] > :is(a, button) { border: 1px solid ButtonText; }
  .data-table tr[aria-selected="true"], .data-table tr:has(> .data-table-check input:checked) {
    forced-color-adjust: none; background: Highlight; color: HighlightText;
  }
  .menu:has(> .menu-panel:popover-open) > .menu-btn::after { background: HighlightText; }
  .md-resize::before { background: CanvasText; inset: 0 2.5px; }
  .md-resize:hover::before, .md-resize.dragging::before, .md-resize:focus-visible::before { background: Highlight; }
  .split-handle::before { background: CanvasText; inset: 0 calc((100% - var(--split-rule-size, 1px)) / 2); }
  .split.stacked > .split-handle::before { inset: calc((100% - var(--split-rule-size, 1px)) / 2) 0; }
  .split-handle:hover::before, .split-handle.dragging::before, .split-handle:focus-visible::before { background: Highlight; }
  .badge { border: 1px solid CanvasText; }
  .tree a[aria-current]:not([aria-current="false"]) { forced-color-adjust: none; background: Highlight; color: HighlightText; }
  .tree-toggle, .card-toggle { border: 1px solid ButtonText; }
  .tree-toggle::before, .card-toggle::before { forced-color-adjust: none; background: ButtonText; }
  .glyph, .path li + li::before { forced-color-adjust: none; background: CanvasText; }
  .drop-zone[data-drop-over], [data-drop-target] { outline: 2px dashed Highlight; outline-offset: -2px; }
  .reorder-grip::before { forced-color-adjust: none; background: ButtonText; }
  .reorder-grip:focus-visible { outline: 2px solid Highlight; }
  .drop-zone[data-drop-over] > .drop-hint { forced-color-adjust: none; background: Highlight; color: HighlightText; }
  .doc-tab-dirty { forced-color-adjust: none; background: CanvasText; }
  .doc-tab-close { border: 1px solid ButtonText; }
  .doc-tab-close::before { forced-color-adjust: none; background: ButtonText; }
  .code-surface, pre.code { border: 1px solid CanvasText; }
  .code-block > pre.code { border: 0; }
  .code-block { border: 1px solid CanvasText; }
  .code-head { border-bottom-color: CanvasText; }
  .code-actions .glyph { forced-color-adjust: none; background: ButtonText; }
}

/* === Print ===============================================================
   A printed page carries the content, on white, without the chrome: the
   topbar, the tab bar, the toolbars, the filter chips and the divider.
   A master-detail layout showing a record prints the record alone. */
@media print {
  :root, [data-theme="dark"] {
    color-scheme: light;
    --bg: #ffffff; --surface: #ffffff; --surface-alt: #f2f2f2;
    --text: #000000; --text-muted: #444444; --border: #999999;
    --accent: #1a3d6b; --accent-hover: #1a3d6b;
    --syntax-keyword: #9b2c63; --syntax-string: #1d6a39; --syntax-number: #8a4a00; --syntax-comment: #57606b;
    --syntax-name: #1d4f91; --syntax-tag: #6b36a3; --syntax-attr: #6d5500; --syntax-heading: #000000;
    --syntax-link: #1a3d6b; --syntax-error: #b42318;
  }
  .topbar, .app-section-bar, .md-toolbar, .md-chips, .md-sidebar-head, .md-resize, .md-back,
  .menu, .menu-panel, .dialog-backdrop, .theme-toggle,
  .toast-region, .notice-close, .tablist, .pagination, .tooltip, .code-actions { display: none !important; }
  .tabs > [role="tabpanel"] { display: block !important; border: 0; padding: 0 0 var(--space-4); }
  .data-table-wrap { overflow: visible; border: 0; }
  .data-table thead th { position: static; }
  .data-table th[aria-sort] > :is(a, button) { background: none; box-shadow: none; border: 0; }
  body:has(> .md-layout) { height: auto; overflow: visible; display: block; }
  .md-layout, .md-body { display: block; height: auto; }
  .md-sidebar, .md-detail { width: auto; overflow: visible; border: 0; }
  .md-layout[data-md-pane="detail"] .md-sidebar { display: none; }
  .btn, .icon-btn, .card, .dialog { box-shadow: none; }
  a { color: #000000; }
}

/* Start catalogs compose the existing menu, filter, row-list and splitter surfaces. */
.start-menu { inset: auto auto calc(var(--space-6) + var(--space-6)) var(--space-3); margin: 0; padding: 0; width: 42rem; max-width: calc(100vw - var(--space-5)); height: min(36rem, calc(100dvh - 9rem)); max-height: none; overflow: hidden; }
.start-menu:not([popover]), .start-menu:popover-open { display: flex; flex-direction: column; }
.start-menu .start-header { margin: 0; top: 0; padding: var(--space-2) var(--space-3); flex: none; }
.start-menu .start-search { display: flex; align-items: stretch; gap: var(--space-2); padding: var(--space-3); flex: none; }
.start-menu .start-search-form { flex: 1; min-width: 0; }
.start-menu .start-content { --split-a: calc(100% - 12rem); --split-target-size: 12px; flex: 1; min-height: 0; border-top: 1px solid var(--border); }
.start-menu .start-items-pane { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.start-menu .start-heading { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); padding: var(--space-3) var(--space-3) var(--space-2); flex: none; }
.start-menu .start-count-summary, .start-menu [data-start-count] { font-size: var(--text-2xs); color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.start-menu .start-items { list-style: none; padding: 0; margin: 0; overflow-y: auto; flex: 1; min-height: 0; scrollbar-gutter: stable; }
.start-menu .start-item { display: flex; align-items: center; gap: var(--space-2); }
.start-menu .start-item > .glyph { flex: none; }
.start-menu .start-copy { min-width: 0; flex: 1; }
.start-menu .start-name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.start-menu .start-categories { overflow-y: auto; }
.start-menu .start-category-list { list-style: none; margin: 0; padding: 0; }
.start-menu .start-category { display: flex; align-items: center; gap: var(--space-2); }
.start-menu .start-category > .glyph { flex: none; }
.start-menu .start-category > span:not(.glyph):not(.start-category-count) { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.start-menu[data-start-side="left"] .start-content { --split-a: 12rem; }
.start-menu .start-category-count { margin-inline-start: auto; font-variant-numeric: tabular-nums; font-size: var(--text-2xs); }
.start-menu .start-footer { position: static; margin: 0; padding: var(--space-3); border-top: 1px solid var(--border); flex: none; justify-content: flex-start; align-items: center; }
.start-menu .start-footer a { display: inline-flex; gap: var(--space-1); align-items: center; }
.start-menu .start-key-hint { margin-inline-start: auto; color: var(--text-muted); font-size: var(--text-2xs); }
.start-menu [data-start-filtered], .start-menu [hidden] { display: none !important; }
.start-menu .start-item { width: 100%; text-align: start; }
.start-menu .start-copy { text-align: start; }
@media (max-width: 650px) {
 .start-menu { inset: var(--space-2) var(--space-2) 4rem; width: auto; height: auto; max-width: none; }
 .start-menu .start-content { --split-a: calc(100% - 8rem); --split-target-size: 20px; }
 .start-menu[data-start-side="left"] .start-content { --split-a: 8rem; }
 .start-menu .start-category-count, .start-menu .start-key-hint { display: none; }
 .start-menu .start-footer { justify-content: space-between; }
}

/* Host launcher presentations, composed with Start and split panes (0.99.0). */
.start-menu[data-launcher][popover] { inset: auto; margin: 0; width: 720px; height: 600px; min-width: min(280px, calc(100vw - 16px)); min-height: min(260px, calc(100dvh - var(--launcher-top, 48px) - 12px)); max-width: calc(100vw - 16px); max-height: calc(100dvh - var(--launcher-top, 48px) - 12px); resize: both; }
.start-menu[data-launcher]:not([popover]) { position: static; inset: auto; width: 100%; height: 100%; max-width: none; max-height: none; min-width: 0; border: 0; border-radius: 0; box-shadow: none; }
.start-menu[data-launcher-compact][popover] { min-width:0; min-height:0; max-width:none; max-height:none; border:0; border-radius:0; box-shadow:none; resize:none; }
.win-bar-launcher[hidden] { display:none !important; }

/* Accepted launcher choices keep a static confirmation until dismissal. */
[data-launcher] [data-launcher-selected] {
  background: var(--raise-active-bg);
  box-shadow: inset 3px 0 var(--accent);
}
@media (forced-colors: active) {
  [data-launcher] [data-launcher-selected] { background: Highlight; color: HighlightText; outline: 1px solid Highlight; }
  [data-launcher] [data-launcher-selected] :is(.md-item, .md-meta) { color: HighlightText; }
}
