/* IndianOne design system — implements docs/ui-guidelines.md for people who use the system 8 hours a day.
   Every color is a token. Neutral base is ~90% of pixels, the single accent ≤5%, status colors ≤5%. */

/* IBM Plex, served from this site (the CSP allows fonts from 'self' only). Only the Latin 1, Latin 2
   (Polish) and Pi (arrows, check mark) subsets ship; any other character falls back to the system font. */
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/IBMPlexSans-Regular-Latin1.woff2") format("woff2"); unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/IBMPlexSans-Regular-Latin2.woff2") format("woff2"); unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/IBMPlexSans-Regular-Pi.woff2") format("woff2"); unicode-range: U+0E3F, U+2000-200D, U+2015, U+2028-2029, U+202F, U+2032-2033, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+ECE0, U+EFCC, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/IBMPlexSans-Medium-Latin1.woff2") format("woff2"); unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/IBMPlexSans-Medium-Latin2.woff2") format("woff2"); unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/IBMPlexSans-Medium-Pi.woff2") format("woff2"); unicode-range: U+0E3F, U+2000-200D, U+2015, U+2028-2029, U+202F, U+2032-2033, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+ECE0, U+EFCC, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap; src: url("fonts/IBMPlexSans-SemiBold-Latin1.woff2") format("woff2"); unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap; src: url("fonts/IBMPlexSans-SemiBold-Latin2.woff2") format("woff2"); unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600; font-display: swap; src: url("fonts/IBMPlexSans-SemiBold-Pi.woff2") format("woff2"); unicode-range: U+0E3F, U+2000-200D, U+2015, U+2028-2029, U+202F, U+2032-2033, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+ECE0, U+EFCC, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap; src: url("fonts/IBMPlexSans-Bold-Latin1.woff2") format("woff2"); unicode-range: U+0000, U+000D, U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap; src: url("fonts/IBMPlexSans-Bold-Latin2.woff2") format("woff2"); unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Sans"; font-style: normal; font-weight: 700; font-display: swap; src: url("fonts/IBMPlexSans-Bold-Pi.woff2") format("woff2"); unicode-range: U+0E3F, U+2000-200D, U+2015, U+2028-2029, U+202F, U+2032-2033, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+221A, U+221E, U+222B, U+2248, U+2260, U+2264-2265, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+ECE0, U+EFCC, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/IBMPlexMono-Regular-Latin1.woff2") format("woff2"); unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/IBMPlexMono-Regular-Latin2.woff2") format("woff2"); unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/IBMPlexMono-Regular-Pi.woff2") format("woff2"); unicode-range: U+03C0, U+0E3F, U+2000-200D, U+2010-2012, U+2015, U+2028-2029, U+202F, U+2032-2033, U+203E, U+205F, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+2219-221A, U+221E, U+222B, U+2236, U+2248, U+2260, U+2264-2265, U+2400-2421, U+2500-259F, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+3000, U+FEFF, U+FFFD; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/IBMPlexMono-Medium-Latin1.woff2") format("woff2"); unicode-range: U+0020-007E, U+00A0-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2013-2014, U+2018-201A, U+201C-201E, U+2020-2022, U+2026, U+2030, U+2039-203A, U+2044, U+20AC, U+2122, U+2212, U+FB01-FB02; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/IBMPlexMono-Medium-Latin2.woff2") format("woff2"); unicode-range: U+0100-0101, U+0104-0130, U+0132-0151, U+0154-017F, U+018F, U+0192, U+01A0-01A1, U+01AF-01B0, U+01FA-01FF, U+0218-021B, U+0237, U+0259, U+1E80-1E85, U+1E9E, U+20A1, U+20A4, U+20A6, U+20A8-20AA, U+20AD-20AE, U+20B1-20B2, U+20B4-20B5, U+20B8-20BA, U+20BD, U+20BF; }
@font-face { font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/IBMPlexMono-Medium-Pi.woff2") format("woff2"); unicode-range: U+03C0, U+0E3F, U+2000-200D, U+2010-2012, U+2015, U+2028-2029, U+202F, U+2032-2033, U+203E, U+205F, U+2070, U+2074-2079, U+2080-2089, U+2113, U+2116, U+2126, U+212E, U+2150-2151, U+2153-215E, U+2190-2199, U+21A9-21AA, U+21B0-21B3, U+21B6-21B7, U+21BA-21BB, U+21C4, U+21C6, U+2202, U+2206, U+220F, U+2211, U+2215, U+2219-221A, U+221E, U+222B, U+2236, U+2248, U+2260, U+2264-2265, U+2400-2421, U+2500-259F, U+25CA, U+2713, U+274C, U+2B0E-2B11, U+3000, U+FEFF, U+FFFD; }

/* While Plex loads, font-display: swap shows a fallback font. The swap used to move the text: the
   layout-shift check measured 0.064 on the item list. These faces make Arial and Courier New take the
   same room as Plex. size-adjust matches the average glyph width on English text and figures. The
   overrides are Plex's own ascent and descent, divided by that scale. Liberation and Arimo share
   Arial's metrics, and Cousine shares Courier New's, so the same numbers hold on Linux. People still
   see Plex once it arrives. The numbers come from each pair's hmtx, hhea and OS/2 font tables. */
@font-face { font-family: "IBM Plex Sans Fallback"; font-weight: 400; src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans"), local("Arimo"); size-adjust: 101.46%; ascent-override: 101.03%; descent-override: 27.11%; line-gap-override: 0%; }
@font-face { font-family: "IBM Plex Sans Fallback"; font-weight: 500; src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans"), local("Arimo"); size-adjust: 103.51%; ascent-override: 99.02%; descent-override: 26.57%; line-gap-override: 0%; }
@font-face { font-family: "IBM Plex Sans Fallback"; font-weight: 600; src: local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold"), local("LiberationSans-Bold"), local("Arimo Bold"); size-adjust: 97.53%; ascent-override: 105.09%; descent-override: 28.20%; line-gap-override: 0%; }
@font-face { font-family: "IBM Plex Sans Fallback"; font-weight: 700; src: local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold"), local("LiberationSans-Bold"), local("Arimo Bold"); size-adjust: 98.79%; ascent-override: 103.76%; descent-override: 27.84%; line-gap-override: 0%; }
@font-face { font-family: "IBM Plex Mono Fallback"; font-weight: 400 500; src: local("Courier New"), local("CourierNewPSMT"), local("Liberation Mono"), local("LiberationMono"), local("Cousine"); size-adjust: 99.98%; ascent-override: 102.52%; descent-override: 27.50%; line-gap-override: 0%; }

:root {
  --bg-app: #F7F7F5;
  --bg-surface: #FCFCFB;
  --bg-elevated: #FFFFFF;
  --bg-subtle: #EFEFEC;
  --border: #E3E3DF;
  --border-strong: #8A8A84;
  --text-primary: #1F2329;
  --text-secondary: #5E646E;
  --text-muted: #676C75;
  --text-inverse: #FFFFFF;

  /* One neutral accent for everything interactive (panel ruling: brand red stays on the wordmark and printed documents). */
  /* Every colour below clears 4.5:1 against every surface above it, including --bg-subtle, which is the
     darkest of them. StylesheetContrastTests measures all of it; an eye cannot tell 4.49 from 4.51. */
  --accent: #1D5DE8;
  --accent-hover: #1D4ED8;
  --accent-soft: #EAF1FE;
  --accent-ink: #FFFFFF;
  --brand: #9B1C31;
  /* The letters on the wordmark badge. The badge is brand-coloured in both themes, and the two brand
     reds are far enough apart in lightness that one ink cannot serve both. */
  --brand-ink: #FFFFFF;

  --ok: #147A3A;        --ok-bg: #ECFDF3;
  --warn: #A94E08;      --warn-bg: #FFFBEB;
  --danger: #B91C1C;    --danger-bg: #FEF2F2;
  --info: #0E7490;      --info-bg: #ECFEFF;
  --neutral: #5E646E;   --neutral-bg: #EFEFEC;

  --fs-hero: 20px; --fs-title: 16px; --fs-body: 14px; --fs-table: 13px; --fs-meta: 12px; --fs-caption: 11px;
  /* Three corners only: small inline marks, blocks and controls, and fully round pills and dots. */
  --rail-w: 224px; --topbar-h: 46px; --radius: 6px; --radius-sm: 3px; --radius-round: 999px; --row-pad: 7px 10px; --gap: 16px; --control-h: 32px;
  --font-ui: "IBM Plex Sans", "IBM Plex Sans Fallback", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "IBM Plex Mono", "IBM Plex Mono Fallback", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --shadow-pop: 0 8px 24px rgba(15, 20, 30, .12), 0 2px 6px rgba(15, 20, 30, .08);
  --shadow-card: 0 1px 2px rgba(15, 20, 30, .05), 0 2px 8px rgba(15, 20, 30, .05);
  --shadow-lift: 0 2px 4px rgba(15, 20, 30, .07), 0 6px 16px rgba(15, 20, 30, .10);
  /* What dims the page behind a dialog, and the shadow at the edge of a table that scrolls. Tokens
     like any other, so the rule that every colour is a token has no exceptions and the stylesheet
     test can hold it. The edge shadow has to be a token for a second reason: black on a dark surface
     is not a shadow, it is nothing. */
  --scrim: rgba(0, 0, 0, .35);
  --edge-shadow: rgb(0 0 0 / .2);
  --fade: rgb(0 0 0 / 0);
  color-scheme: light;
}

[data-theme="dark"] {
  --bg-app: #16171A; --bg-surface: #1E1F23; --bg-elevated: #26272C; --bg-subtle: #28292E;
  --border: #34363C; --border-strong: #6B6E76;
  --text-primary: #E8E9EB; --text-secondary: #A2A6AE; --text-muted: #9096A0; --text-inverse: #1F2329;
  --accent: #6E99F2; --accent-hover: #7AA3F3; --accent-soft: #202B43; --accent-ink: #0D1B3A; --brand: #E08A96; --brand-ink: #1F2329;
  --ok: #4ADE80; --ok-bg: #1A2F24; --warn: #FBBF24; --warn-bg: #33291A; --danger: #F87171; --danger-bg: #3A1D1D; --info: #22D3EE; --info-bg: #0E2A33; --neutral: #A2A6AE; --neutral-bg: #28292E;
  --shadow-pop: 0 8px 24px rgba(0, 0, 0, .5), 0 2px 6px rgba(0, 0, 0, .35);
  --shadow-card: 0 1px 2px rgba(0, 0, 0, .3), 0 2px 8px rgba(0, 0, 0, .25);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, .35), 0 6px 16px rgba(0, 0, 0, .4);
  --scrim: rgba(0, 0, 0, .6);
  --edge-shadow: rgb(0 0 0 / .55); --fade: rgb(0 0 0 / 0);
  color-scheme: dark;
}

[data-density="comfortable"] { --row-pad: 10px 12px; --gap: 20px; --control-h: 36px; }
[data-density="compact"]     { --row-pad: 5px 10px; --gap: 12px; }
[data-density="ultra"]       { --row-pad: 2px 8px; --fs-table: 12px; --gap: 8px; --control-h: 28px; }
[data-scale="large"] { --fs-hero: 22px; --fs-title: 18px; --fs-body: 16px; --fs-table: 15px; --fs-meta: 14px; --fs-caption: 13px; }
[data-contrast="high"] { --text-secondary: #4A4F58; --text-muted: #5A5F68; --border: #C9C9C4; --border-strong: #6E6E68; }
[data-theme="dark"][data-contrast="high"] { --text-secondary: #C2C6CC; --text-muted: #A6ABB3; --border: #4A4D55; --border-strong: #8A8E96; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg-app); color: var(--text-primary); font: 400 var(--fs-body)/1.5 var(--font-ui); font-variant-numeric: tabular-nums; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }
/* A link inside a sentence has to be told apart from the sentence without relying on colour
   (WCAG 1.4.1), so in prose it is underlined. A link that is a control — the rail, a tab, a button, a
   crumb, a tile, the key cell of a table — is not inside a block of text and keeps the plain
   treatment, which is what keeps a dense queue screen readable. */
p a, li a, dd a, .sub a, .help a, .meta a, .empty a, .callout a, .wrap a, .note a, .kv a,
/* Message boxes hold sentences too: a flash, a banner, a notice, a warn-edge box, a pre-note and the
   form's error list. A next-action link is always the end of a sentence that says what to do. A
   button stays a button. */
:is(.flash, .banner, .notice, .warn-edge, .pre-note, .validation-summary-errors) a:not(.btn), a[data-next-action]:not(.btn),
/* A link that shares its table cell with anything else — a pill, a note, a second link — is read as
   part of a sentence. One that is the whole cell is a row identifier and stays plain. */
td a:not(:only-child) {
  text-decoration: underline; text-underline-offset: 2px;
}
::selection { background: var(--accent-soft); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: var(--radius-sm); }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; animation: none !important; } }
h1, h2, h3 { margin: 0; font-weight: 600; line-height: 1.3; }
h1 { font-size: var(--fs-hero); letter-spacing: -0.015em; } h2 { font-size: var(--fs-title); letter-spacing: -0.01em; } h3 { font-size: var(--fs-body); }
p { margin: 0 0 8px; } code, .mono { font-family: var(--font-mono); font-size: 0.93em; }
a { transition: color .12s ease; }
.dim { color: var(--text-secondary); } .muted { color: var(--text-muted); } .meta { font-size: var(--fs-meta); color: var(--text-secondary); }
.right { text-align: right; } .num { text-align: right; font-variant-numeric: tabular-nums; } .nowrap { white-space: nowrap; }
.inline { display: inline; margin: 0; } .mb0 { margin-bottom: 0; } .hidden { display: none !important; }
/* A row wraps rather than pushing its last control past the card's edge. A two-control row never
   notices the difference; a row of four inputs and a button stops drawing over its neighbour. */
.row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .wrap { flex-wrap: wrap; } .spacer { flex: 1; }
kbd { font: 500 var(--fs-caption)/1 var(--font-mono); padding: 2px 5px; border: 1px solid var(--border-strong); border-bottom-width: 2px; border-radius: var(--radius-sm); color: var(--text-secondary); background: var(--bg-surface); }

/* ── Frame: rail + topbar + content ─────────────────────────────────────── */
.frame { display: flex; min-height: 100vh; }
.rail { width: var(--rail-w); flex: 0 0 var(--rail-w); background: var(--bg-surface); border-right: 1px solid var(--border); position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; padding: 10px 8px; overflow-y: auto; scrollbar-width: thin; }
/* A collapsed rail stays on screen as an icon strip; labels hide and every link keeps its tooltip. */
[data-rail="closed"] .rail { width: 52px; flex-basis: 52px; padding: 10px 6px; }
[data-rail="closed"] .rail .brand { justify-content: center; padding: 0; }
/* The beta stamp goes with the wordmark. A 52px rail has no room for it, and leaving it in gave the
   rail a horizontal scrollbar at large text. */
[data-rail="closed"] .rail .brand-text, [data-rail="closed"] .rail .nav-text, [data-rail="closed"] .rail .grp, [data-rail="closed"] .rail .beta { display: none; }
[data-rail="closed"] .rail a.nav-link { justify-content: center; padding: 6px; }
[data-rail="closed"] .rail .nav-icon { opacity: .8; }
[data-rail="closed"] .rail .rail-foot { display: none; }
[data-rail="closed"] .rail-open { display: inline-flex; }
.rail-open { display: none; }
.brand { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 4px 8px 12px; font-size: var(--fs-title); font-weight: 600; color: var(--text-primary); }
.brand:hover { text-decoration: none; }
/* The release stamp. It wraps to its own line rather than overflowing the rail at large text. */
.beta { border: 1px solid currentColor; border-radius: var(--radius-sm); padding: 0 4px; font-size: var(--fs-caption); font-weight: 700; letter-spacing: .8px; text-transform: uppercase; line-height: 1.5; color: var(--warn); background: var(--warn-bg); }
.brand-mark { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: var(--radius); background: var(--brand); color: var(--brand-ink); font-size: 12px; font-weight: 700; letter-spacing: .5px; }
.brand small { color: var(--text-muted); font-weight: 500; font-size: var(--fs-meta); margin-left: 4px; }
.rail nav { display: flex; flex-direction: column; gap: 1px; }
.rail .grp { margin: 10px 10px 3px; font-size: var(--fs-caption); font-weight: 500; letter-spacing: .4px; text-transform: uppercase; color: var(--text-muted); }
.rail a.nav-link { display: flex; align-items: center; gap: 9px; padding: 5px 10px; border-radius: var(--radius); color: var(--text-secondary); font-size: var(--fs-body); transition: background-color .12s ease, color .12s ease; }
.rail a.nav-link:hover { background: var(--bg-subtle); color: var(--text-primary); text-decoration: none; }
.rail a.nav-link.active { background: var(--accent-soft); color: var(--accent); font-weight: 500; box-shadow: inset 2px 0 0 var(--accent); }
/* Rail and button icons. Each glyph is an SVG mask so it takes the current text colour and stays
   crisp at every text scale. An unknown name falls back to a dot, never a blank square. */
.nav-icon, [data-icon] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M8 5.2a2.8 2.8 0 1 0 0 5.6 2.8 2.8 0 0 0 0-5.6z'/%3E%3C/svg%3E"); }
.nav-icon { width: 15px; height: 15px; background: currentColor; opacity: .62; flex: none;
  mask: var(--icon-mask) center / contain no-repeat; -webkit-mask: var(--icon-mask) center / contain no-repeat; }
.rail a.nav-link:hover .nav-icon, .rail a.nav-link.active .nav-icon { opacity: 1; }
[data-icon="home"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M8 1 1 7v1h2v7h4v-4h2v4h4V8h2V7zM4 7.3 8 4l4 3.3V13h-2V9H6v4H4z'/%3E%3C/svg%3E"); }
[data-icon="bell"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M8 1a4 4 0 0 0-4 4v2.6L2.4 11v1h11.2v-1L12 7.6V5a4 4 0 0 0-4-4zm0 14a2 2 0 0 0 2-2H6a2 2 0 0 0 2 2z'/%3E%3C/svg%3E"); }
[data-icon="cases"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M6 1h4a2 2 0 0 1 2 2v1h2.5a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5H4V3a2 2 0 0 1 2-2zm0 3h4V3H6zM2.5 6v6.5h11V6z'/%3E%3C/svg%3E"); }
[data-icon="book"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M3.5 1H13a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H3.5A2.5 2.5 0 0 1 1 12.5v-9A2.5 2.5 0 0 1 3.5 1zm0 1.5A1 1 0 0 0 2.5 3.5v8.2a2.4 2.4 0 0 1 1-.2h9V2.5z'/%3E%3C/svg%3E"); }
[data-icon="users"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M6 2a2.6 2.6 0 1 1 0 5.2A2.6 2.6 0 0 1 6 2zM1.2 14a4.8 4.8 0 0 1 9.6 0zm10.6-11a2.2 2.2 0 1 1 0 4.4 2.2 2.2 0 0 1 0-4.4zM12 8.6a4 4 0 0 1 2.9 5.4h-2.6a6.3 6.3 0 0 0-1.8-4.6A4 4 0 0 1 12 8.6z'/%3E%3C/svg%3E"); }
[data-icon="people"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M6 2a2.6 2.6 0 1 1 0 5.2A2.6 2.6 0 0 1 6 2zM1.2 14a4.8 4.8 0 0 1 9.6 0zm10.6-11a2.2 2.2 0 1 1 0 4.4 2.2 2.2 0 0 1 0-4.4zM12 8.6a4 4 0 0 1 2.9 5.4h-2.6a6.3 6.3 0 0 0-1.8-4.6A4 4 0 0 1 12 8.6z'/%3E%3C/svg%3E"); }
[data-icon="audit"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M8 1 2 3.2v4.6c0 3.7 2.5 6.3 6 7.2 3.5-.9 6-3.5 6-7.2V3.2zm3.1 4.3 1.1 1.1-4.6 4.6-2.8-2.8 1.1-1.1 1.7 1.7z'/%3E%3C/svg%3E"); }
[data-icon="outbox"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M2 2h12v6.5h-3.6a2.5 2.5 0 0 1-4.8 0H2zm0 8h2.7a4 4 0 0 0 6.6 0H14v4H2z'/%3E%3C/svg%3E"); }
[data-icon="play"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1zm0 1.6a5.4 5.4 0 1 1 0 10.8A5.4 5.4 0 0 1 8 2.6zM6.6 4.9 11 8l-4.4 3.1z'/%3E%3C/svg%3E"); }
[data-icon="jobs"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1zm0 1.6a5.4 5.4 0 1 1 0 10.8A5.4 5.4 0 0 1 8 2.6zM7.2 4h1.6v4.1l2.7 1.6-.8 1.4-3.5-2.1z'/%3E%3C/svg%3E"); }
[data-icon="settings"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M6.9 1h2.2l.3 1.9 1.4.8 1.8-.7 1.1 1.9-1.4 1.2v1.7l1.4 1.2-1.1 1.9-1.8-.7-1.4.8-.3 1.9H6.9l-.3-1.9-1.4-.8-1.8.7L2.3 9l1.4-1.2V6.1L2.3 4.9l1.1-1.9 1.8.7 1.4-.8zM8 5.8A2.2 2.2 0 1 0 8 10.2 2.2 2.2 0 0 0 8 5.8z'/%3E%3C/svg%3E"); }
[data-icon="org"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M1.5 1h7v14h-7zm8 5h5v9h-5zM3 3h1.6v1.8H3zm2.8 0h1.6v1.8H5.8zM3 6h1.6v1.8H3zm2.8 0h1.6v1.8H5.8zM3 9h1.6v1.8H3zm2.8 0h1.6v1.8H5.8zm5.2-1h2v1.8h-2zm0 3h2v1.8h-2z'/%3E%3C/svg%3E"); }
[data-icon="box"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M8 1 1.6 4v8L8 15l6.4-3V4zm0 1.7 4.1 1.9L8 6.5 3.9 4.6zM3.1 5.9 7.2 7.8v5.3l-4.1-1.9zm9.8 0v5.3l-4.1 1.9V7.8z'/%3E%3C/svg%3E"); }
[data-icon="tree"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M1 1h5.5v3.6H1zm8.5 0H15v3.6H9.5zm0 5.2H15v3.6H9.5zm0 5.2H15V15H9.5zM3 4.6h1.4v8.6H8v1.4H3z'/%3E%3C/svg%3E"); }
[data-icon="badge"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M2 2.6h12v10.8H2zm3.2 2a1.9 1.9 0 1 0 0 3.8 1.9 1.9 0 0 0 0-3.8zM2.6 11.8c.4-1.5 1.5-2.3 2.6-2.3s2.2.8 2.6 2.3zM9.6 5.4h3.2v1.2H9.6zm0 2.6h3.2v1.2H9.6z'/%3E%3C/svg%3E"); }
[data-icon="coin"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1zm0 1.6a5.4 5.4 0 1 1 0 10.8A5.4 5.4 0 0 1 8 2.6zM7.2 3.9h1.6v.9h1.6v1.5H6.9a.6.6 0 0 0 0 1.2h1.6a2.1 2.1 0 0 1 .3 4.2v.9H7.2v-.9H5.6V10.2h3.1a.6.6 0 0 0 0-1.2H7.1a2.1 2.1 0 0 1-.3-4.2z'/%3E%3C/svg%3E"); }
[data-icon="ruler"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M1 5.6h14v4.8H1zm1.5 1.5v1.8h1V7.1zm2.6 0v2.6h1V7.1zm2.6 0v1.8h1V7.1zm2.6 0v2.6h1V7.1zm2.6 0v1.8h1V7.1z'/%3E%3C/svg%3E"); }
[data-icon="grid"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M1 1h6v6H1zm8 0h6v6H9zM1 9h6v6H1zm8 0h6v6H9z'/%3E%3C/svg%3E"); }
[data-icon="pencil"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M11.4 1.6a1.4 1.4 0 0 1 2 0l1 1a1.4 1.4 0 0 1 0 2L5.6 13.4 1.5 14.5l1.1-4.1zM3.9 10.9l-.5 1.7 1.7-.5 6.7-6.7-1.2-1.2z'/%3E%3C/svg%3E"); }
[data-icon="cross"] { --icon-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M4.2 3 8 6.8 11.8 3 13 4.2 9.2 8l3.8 3.8-1.2 1.2L8 9.2 4.2 13 3 11.8 6.8 8 3 4.2z'/%3E%3C/svg%3E"); }
.rail .rail-foot { padding: 8px 4px 0; border-top: 1px solid var(--border); display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: flex-end; }
.pref { display: flex; flex-direction: column; gap: 3px; }
.pref-label { font-size: 9px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--text-muted); padding-left: 2px; }
.rail-toggle { margin-left: auto; }
.seg { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius); overflow: hidden; }
.seg button, .rail-toggle, .rail-open, .icon-btn { font: 500 var(--fs-caption)/1 var(--font-ui); padding: 5px 7px; border: 0; background: var(--bg-surface); color: var(--text-secondary); cursor: pointer; }
.seg button + button { border-left: 1px solid var(--border); }
.seg button.on { background: var(--accent-soft); color: var(--accent); }
.rail-toggle, .rail-open { border: 1px solid var(--border-strong); border-radius: var(--radius); }
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar { display: flex; align-items: center; gap: 8px; padding: 8px 16px; border-bottom: 1px solid var(--border); background: var(--bg-surface); position: sticky; top: 0; z-index: 20; min-height: 46px; }
/* The breadcrumb is one line that never breaks a word. The last crumb names the record on screen,
   such as MO-000001, so it never shrinks. The middle crumbs shrink first and end in dots. "IndianOne"
   keeps its width too. The crumbs sit at the right end of their box. So if even the floors do not
   fit, the box cuts from the left and the record code stays whole. A long record name stops at 24em
   and ends in dots. With a floor of 4.5em on every crumb, the box cut the last one off (IND-00G55).
   The box shrinks at a fiftieth of the rate of the scope lists and the search box. So they give up
   their width first. The crumbs keep their words while there is room. */
.crumbs { display: flex; flex-wrap: nowrap; flex: 0 .02 auto; min-width: 0; justify-content: flex-end; align-items: center; gap: 5px; white-space: nowrap; overflow: hidden; font-size: var(--fs-meta); color: var(--text-secondary); }
.crumbs > * { flex: 0 1 auto; min-width: 2.4em; overflow: hidden; text-overflow: ellipsis; }
.crumbs > :first-child { flex-shrink: 0; min-width: auto; }
.crumbs > :last-child:not(:first-child) { flex-shrink: 0; min-width: auto; max-width: 24em; }
/* Slack at the end of the box. Any shrink of the box, even half a pixel, put dots on a middle crumb
   that had room. This empty part gives up its 12px first. A crumb only shrinks when it must. */
.crumbs::after { content: ""; flex: 0 100000 12px; width: 12px; min-width: 0; }
.crumbs a { color: var(--text-secondary); } .crumbs > :not(:first-child)::before { content: "/"; margin: 0 5px 0 0; color: var(--text-muted); }
.search-trigger { display: inline-flex; flex: 0 1 260px; align-items: center; gap: 12px; height: var(--control-h); padding: 0 8px 0 12px; min-width: 96px; white-space: nowrap; overflow: hidden; border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--bg-app); color: var(--text-muted); cursor: text; font: inherit; }
.icon-btn { position: relative; display: inline-grid; place-items: center; width: var(--control-h); height: var(--control-h); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text-secondary); text-decoration: none; }
.icon-btn [data-icon] { width: 15px; height: 15px; background: currentColor; opacity: .75;
  mask: var(--icon-mask) center / contain no-repeat; -webkit-mask: var(--icon-mask) center / contain no-repeat; }
.badge { position: absolute; top: -6px; right: -6px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: var(--radius-round); background: var(--accent); color: var(--accent-ink); font-size: 10px; font-weight: 600; display: grid; place-items: center; }
.user-menu { display: flex; align-items: center; gap: 8px; } .user-name { font-size: var(--fs-meta); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; min-width: 5em; }
.content { padding: 16px 20px 40px; max-width: 1800px; width: 100%; }
.flash { padding: 8px 12px; border-radius: var(--radius); margin-bottom: 12px; border: 1px solid transparent; font-size: var(--fs-body); }
.flash.ok { background: var(--ok-bg); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.flash.err { background: var(--danger-bg); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.flash.warn { background: var(--warn-bg); color: var(--warn); }
.flash.info { background: var(--info-bg); color: var(--info); }

/* ── Page head, cards, tiles ─────────────────────────────────────────────── */
.page-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: var(--gap); flex-wrap: wrap; }
/* A record page's actions never scroll away: the header pins under the topbar (UX-040). */
.page-head.stick { position: sticky; top: var(--topbar-h); z-index: 15; background: var(--bg-app); padding: 8px 0; margin-bottom: 8px; border-bottom: 1px solid var(--border); }
.page-head .titles { min-width: 0; } .page-head .sub { color: var(--text-secondary); font-size: var(--fs-meta); margin-top: 2px; }
.page-head .actions { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; box-shadow: var(--shadow-card); }
.card + .card { margin-top: var(--gap); }
.card h2 { margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }
.card h2 .meta { font-weight: 400; }
.grid { display: grid; gap: var(--gap); } .grid.two { grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); } .grid.three { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; margin-bottom: var(--gap); }
.tile { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; display: block; color: inherit; box-shadow: var(--shadow-card); transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease; }
.tile:hover { text-decoration: none; border-color: var(--border-strong); box-shadow: var(--shadow-lift); transform: translateY(-1px); }
.tile .label { font-size: var(--fs-meta); color: var(--text-secondary); } .tile .value { font-size: 22px; font-weight: 600; line-height: 1.2; margin-top: 2px; }
.tile .value.sm { font-size: var(--fs-body); font-weight: 500; }

/* Layout utilities. Pages never carry a style attribute: the content security policy blocks inline
   styles, and a named class keeps the same field the same width on every screen. */
.w-tiny { max-width: 80px; } .w-code { max-width: 110px; } .w-short { max-width: 150px; }
.w-med { max-width: 200px; } .w-long { max-width: 320px; }
/* On a form box the class is the width, not only a cap. A list box grows to fit its longest choice.
   With a cap alone, Incoterms came out half as wide as Currency, though both said w-med (IND-00FPY).
   A text box stayed at the browser's 20 letters, so w-long did nothing.
   Now two boxes with one class are one width on every screen. A narrow column still shrinks them. */
:is(input:not([type="checkbox"], [type="radio"]), select, textarea):is(.w-tiny, .w-code, .w-short, .w-med, .w-long) { max-width: 100%; }
:is(input:not([type="checkbox"], [type="radio"]), select, textarea).w-tiny { width: 80px; }
:is(input:not([type="checkbox"], [type="radio"]), select, textarea).w-code { width: 110px; }
:is(input:not([type="checkbox"], [type="radio"]), select, textarea).w-short { width: 150px; }
:is(input:not([type="checkbox"], [type="radio"]), select, textarea).w-med { width: 200px; }
:is(input:not([type="checkbox"], [type="radio"]), select, textarea).w-long { width: 320px; }
/* A VIN has 17 characters. A reader needs all of them. So a VIN cell takes no cap, and the column grows
   to fit it. With a cap of 110px, the VIN ran over the dealer code beside it (IND-00FKS). */
.w-vin { max-width: none; white-space: nowrap; }
.mt { margin-top: 10px; } .mb { margin-bottom: 10px; } .my { margin: 10px 0; }
.wrap { flex-wrap: wrap; }
.text-table { font-size: var(--fs-table); } .text-caption { font-size: var(--fs-caption); }
.warn-edge { border-color: var(--warn); }
/* Stored text such as a JSON payload can be one long line with no spaces. It may break anywhere.
   That keeps it inside its card, so the page never scrolls sideways. */
.pre-note { white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--fs-caption); margin: 4px 0; }
.bullets { margin: 0; padding-left: 16px; font-size: var(--fs-table); }
/* A callout is a note that needs attention, such as a step still missing. The colour says how
   urgent it is. */
.callout { padding: 8px 12px; border-radius: var(--radius); border: 1px solid var(--border); border-left-width: 3px; background: var(--bg-subtle); font-size: var(--fs-table); }
.callout.warn { background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 35%, transparent); border-left-color: var(--warn); }
.callout.err { background: var(--danger-bg); border-color: color-mix(in srgb, var(--danger) 35%, transparent); border-left-color: var(--danger); }
/* Indented tree cell: the depth is data, not a style attribute. */
.indent { padding-left: 8px; }
.indent[data-level="2"] { padding-left: 26px; } .indent[data-level="3"] { padding-left: 44px; }
.indent[data-level="4"] { padding-left: 62px; } .indent[data-level="5"] { padding-left: 80px; }
.indent[data-level="6"] { padding-left: 98px; } .indent[data-level="7"] { padding-left: 116px; }
.indent[data-level="8"] { padding-left: 134px; } .indent[data-level="9"] { padding-left: 152px; }
.tile .trend { font-size: var(--fs-caption); color: var(--text-muted); }
.tile.warn .value { color: var(--warn); } .tile.danger .value { color: var(--danger); } .tile.ok .value { color: var(--ok); }
/* The module launcher: one tile per module, large enough to pick from without reading a list of links.
   A tile is a plain link, so the whole card is the target and no script is needed to open a module. */
.tiles.module-tiles { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; margin-bottom: 0; }
.tile.module { display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px; }
.tile.module:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tile.module[aria-current="true"] { border-color: var(--accent); background: var(--accent-soft); }
.tile.module .nav-icon { flex: none; width: 18px; height: 18px; margin-top: 2px; background: var(--accent); opacity: .85;
  mask: var(--icon-mask) center / contain no-repeat; -webkit-mask: var(--icon-mask) center / contain no-repeat; }
.tile.module .body { min-width: 0; display: grid; gap: 1px; }
.tile.module .label { font-size: var(--fs-body); font-weight: 500; color: var(--text-primary); }
.tile.module .value.sm { margin-top: 2px; font-weight: 400; color: var(--text-secondary); }
/* The chain: the journey a motorcycle takes, one card per step, for somebody who has never seen the
   product. The strip at the top is the same flow the process map uses, so the two read alike. */
.flowmap.chain { margin-bottom: var(--gap); position: sticky; top: var(--topbar-h); z-index: 12; background: var(--bg-app); padding: 10px 0 8px; }
.flowmap.chain a.state { transition: border-color .15s ease, color .15s ease, background-color .15s ease; }
.flowmap.chain a.state.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
/* One line down the left, a dot per step: the story reads as a journey, not a pile of cards. */
.chain-steps { position: relative; padding-left: 28px; }
.chain-steps::before { content: ""; position: absolute; left: 8px; top: 14px; bottom: 14px; width: 2px; background: var(--border); }
.card.step { position: relative; }
.card.step::before { content: ""; position: absolute; left: -25px; top: 18px; width: 9px; height: 9px; border-radius: var(--radius-round); background: var(--bg-app); border: 2px solid var(--accent); }
/* Steps rise into view as the reader reaches them. The .rv class is added by script only, so without
   JavaScript the page renders complete; prefers-reduced-motion kills the transition, not the content. */
.rv .card.step { opacity: 0; transform: translateY(14px); transition: opacity .45s ease, transform .45s ease; }
.rv .card.step.in { opacity: 1; transform: none; }
/* The motorcycle from the register is the protagonist; its card gets the one tinted panel. */
.card.machine { background: var(--accent-soft); }
.flowmap.chain .state { color: inherit; }
.flowmap.chain .node:not(:last-child)::after { content: " \2192"; color: var(--text-muted); margin-left: 2px; }
.flowmap.chain .node:last-child .state { border-color: var(--border-strong); color: inherit; }
.card.machine .machine-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; flex-wrap: wrap; }
.card.machine .vin { font-size: 20px; font-weight: 600; letter-spacing: .5px; }
.card.step h2 { align-items: baseline; }
.step-no { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: var(--radius-round);
  background: var(--accent); color: var(--accent-ink); font-size: var(--fs-caption); font-weight: 600; flex: none; }
/* What makes ten cards a chain: each one says what it waits on and what it leaves behind. */
.handoff { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; margin: 0 0 12px; font-size: var(--fs-table); line-height: 1.5; max-width: 100ch; }
.handoff dt { color: var(--text-secondary); white-space: nowrap; }
.handoff dt::before { content: "\2190 "; color: var(--text-muted); }
.handoff dt + dd + dt::before { content: "\2192 "; }
.handoff dd { margin: 0; color: var(--text-primary); }
.card.step .tiles { margin-bottom: 4px; }
.tbl.controls td { vertical-align: top; }
.step-screens { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 12px; }
.step-screens .label { font-size: var(--fs-meta); color: var(--text-secondary); margin-right: 2px; }
.chip { border: 1px solid var(--border-strong); border-radius: var(--radius-round); padding: 2px 10px; font-size: var(--fs-caption); color: var(--text-secondary); }
.chip:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
/* The process catalogue: a written specification and the lifecycle the system enforces for it.
   These pages carry eleven sections of prose each, so the shape of the text is what makes them
   readable. The page had its own <style> block, which the content security policy dropped without
   telling anyone, which is why its line breaks never appeared. */
.badges { display: flex; flex-wrap: wrap; gap: 6px; margin: -4px 0 var(--gap); }
.lede { font-size: var(--fs-body); color: var(--text-primary); margin-bottom: 10px; max-width: 80ch; }
.muted { color: var(--text-muted); }
.card h3 { font-size: var(--fs-body); font-weight: 600; margin: 14px 0 6px; }
.card .note { font-size: var(--fs-meta); color: var(--text-secondary); margin-top: 10px; max-width: 80ch; }
.spec { max-width: 92ch; }
.points, .steps { margin: 0; padding-left: 18px; font-size: var(--fs-table); line-height: 1.55; }
.points > li, .steps > li { margin-bottom: 6px; }
.points > li::marker { color: var(--text-muted); }
.steps > li { margin-bottom: 12px; }
.steps > li::marker { color: var(--accent); font-weight: 600; }
.steps .lead { font-weight: 500; color: var(--text-primary); margin: 0 0 4px; }
.steps .bullets { color: var(--text-secondary); }
.scenario { border-left: 2px solid var(--border-strong); padding-left: 12px; margin-bottom: 12px; font-size: var(--fs-table); line-height: 1.55; }
.scenario:last-child { margin-bottom: 0; }
.scenario p { margin: 0 0 2px; color: var(--text-secondary); }
.scenario p.clause { color: var(--text-primary); display: grid; grid-template-columns: 46px 1fr; }
.scenario b { color: var(--text-muted); font-weight: 600; }
.notice { border-left: 3px solid var(--warn); background: var(--warn-bg); border-radius: var(--radius); padding: 10px 12px; font-size: var(--fs-table); line-height: 1.55; max-width: 86ch; }
.notice strong { display: block; margin-bottom: 4px; }
.notice p { margin: 0 0 4px; color: var(--text-secondary); }
.notice p:last-child { margin-bottom: 0; }
.jump { list-style: none; margin: 0; padding: 0; font-size: var(--fs-table); }
.jump li { padding: 3px 0; border-bottom: 1px solid var(--border); }
.jump li:last-child { border-bottom: 0; }
.jump a { display: block; color: var(--text-secondary); border-left: 2px solid transparent; padding-left: 8px; transition: color .12s ease, border-color .12s ease; }
.jump a:hover { color: var(--text-primary); text-decoration: none; }
.jump a.on { color: var(--accent); font-weight: 500; border-left-color: var(--accent); }
/* The lifecycle, read left to right: a state, the action that leaves it, then the next state. It wraps
   on a narrow window rather than scrolling sideways, because the order still reads down the rows. */
.flowmap { list-style: none; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; padding: 0; }
.flowmap .node { display: flex; align-items: center; gap: 6px; }
.flowmap .state { border: 1px solid var(--border-strong); border-radius: var(--radius); padding: 4px 10px; font-size: var(--fs-table); font-weight: 500; background: var(--bg-subtle); white-space: nowrap; }
.flowmap .node:first-child .state { border-color: var(--accent); color: var(--accent); }
.flowmap .node:last-child .state { border-color: var(--ok); color: var(--ok); }
.flowmap .act { font: 500 var(--fs-caption)/1 var(--font-mono); color: var(--text-secondary); white-space: nowrap; }
.flowmap .act::after { content: " \2192"; color: var(--text-muted); }
.act.checked { color: var(--accent); }
.act.checked::before { content: "\2713 "; }
.act.sample::after { content: none; }
.split { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: var(--gap); align-items: start; }
/* Either column may be narrower than its contents; both are told they may shrink. */
.split > * { min-width: 0; }
/* The reader's rail (contents, cases, provenance) stays on screen while the specification scrolls.
   It carries no height cap and no scroll of its own: the nested scroller clipped the rail's last
   controls — a select and its save button, or a short history — below the fold, and a control you
   cannot reach is a control that does not exist. A rail taller than the window now scrolls with the
   page like every other block, so the whole of it stays reachable. */
.split > div:last-child { position: sticky; top: calc(var(--topbar-h) + 16px); }
section.card[id] { scroll-margin-top: 62px; }
/* Two columns need room for a 380px rail and a working table beside it. 1280 rather than 1100:
   between those two widths the rail left the content column about 600px, which is narrower than the
   rows a record page draws (inputs plus a button) and forced every table into a sideways scroll. */
@media (max-width: 1280px) { .split { grid-template-columns: minmax(0, 1fr); } .split > div:last-child { position: static; } }
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; margin: 0; font-size: var(--fs-table); }
.kv dt { color: var(--text-secondary); } .kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.empty { padding: 24px; text-align: center; color: var(--text-secondary); border: 1px dashed var(--border-strong); border-radius: var(--radius); }
.empty strong { display: block; color: var(--text-primary); margin-bottom: 4px; }
.timeline { list-style: none; margin: 0; padding: 0; } .timeline li { padding: 6px 0 6px 14px; border-left: 2px solid var(--border); position: relative; font-size: var(--fs-table); }
.timeline li::before { content: ""; position: absolute; left: -5px; top: 12px; width: 8px; height: 8px; border-radius: var(--radius-round); background: var(--border-strong); }
/* A long history scrolls inside its card, so the card keeps its size and the page stays short. */
.timeline.capped { max-height: 420px; overflow-y: auto; padding-left: 6px; scrollbar-width: thin; }
/* Some History cards use a plain list or a table. They scroll the same way. A table's header row
   is sticky, so it stays in view as the rows scroll. */
.bullets.capped, .tbl-wrap.capped { max-height: 420px; overflow-y: auto; scrollbar-width: thin; }
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: var(--gap); }
.tabs a { padding: 6px 12px; color: var(--text-secondary); border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tabs a.on { color: var(--accent); border-color: var(--accent); font-weight: 500; } .tabs a:hover { text-decoration: none; color: var(--text-primary); }

/* ── Status pills: icon + color + text, never color alone ───────────────── */
.pill { display: inline-flex; align-items: center; gap: 5px; padding: 1px 8px; border-radius: var(--radius-round); font-size: var(--fs-caption); font-weight: 500; white-space: nowrap; line-height: 1.7; }
.pill::before { content: ""; width: 6px; height: 6px; border-radius: var(--radius-round); background: currentColor; }
.pill.ok { color: var(--ok); background: var(--ok-bg); } .pill.warn { color: var(--warn); background: var(--warn-bg); }
/* Pages and page models name a failed or refused state "err" or "bad" as often as "danger". Neither
   had a rule, so a check that blocked release drew as a plain grey label (IND-00G26). All three are
   red now. "dim" is grey for the same reason. */
.pill.danger, .pill.err, .pill.bad { color: var(--danger); background: var(--danger-bg); } .pill.info { color: var(--info); background: var(--info-bg); }
.pill.neutral, .pill.dim { color: var(--neutral); background: var(--neutral-bg); }
/* The key under a list of checks: each badge, then what it means for the next step. */
.check-legend { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 4px; }
.check-legend .pill { margin-right: 6px; }

/* ── Buttons ───────────────────────────────────────────────────────────── */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: var(--control-h); padding: 0 12px; border-radius: var(--radius); border: 1px solid var(--border-strong); background: var(--bg-surface); color: var(--text-primary); font: 500 var(--fs-body)/1 var(--font-ui); cursor: pointer; white-space: nowrap; transition: background-color .12s ease, border-color .12s ease, color .12s ease; }
.btn:hover { background: var(--bg-subtle); text-decoration: none; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); } .btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn.ghost { border-color: transparent; background: transparent; color: var(--text-secondary); } .btn.ghost:hover { background: var(--bg-subtle); color: var(--text-primary); }
.btn.danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 50%, transparent); } .btn.danger:hover { background: var(--danger-bg); }
.btn.sm { height: 26px; padding: 0 8px; font-size: var(--fs-meta); }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ── Tables: the heart of the system ────────────────────────────────────── */
.tbl-wrap { overflow: auto; border: 1px solid var(--border); border-radius: var(--radius); scrollbar-width: thin; box-shadow: var(--shadow-card);
  /* Covers ride the content and hide the edge shadow until there is something to scroll to. */
  background:
    linear-gradient(to right, var(--bg-surface) 30%, var(--fade)) local,
    linear-gradient(to left, var(--bg-surface) 30%, var(--fade)) local,
    radial-gradient(farthest-side at 0 50%, var(--edge-shadow), var(--fade)) scroll,
    radial-gradient(farthest-side at 100% 50%, var(--edge-shadow), var(--fade)) scroll,
    var(--bg-surface);
  background-repeat: no-repeat;
  background-size: 32px 100%, 32px 100%, 12px 100%, 12px 100%, 100% 100%;
  background-position: 0 0, 100% 0, 0 0, 100% 0, 0 0; }
table.tbl { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-table); }
.tbl th, .tbl td { padding: var(--row-pad); border-bottom: 1px solid var(--border); text-align: left; vertical-align: middle; white-space: nowrap; }
.tbl th { position: sticky; top: 0; z-index: 2; background: var(--bg-subtle); font-weight: 500; font-size: var(--fs-meta); color: var(--text-secondary); user-select: none; }
.tbl th.sorted { color: var(--text-primary); }
.tbl th[data-sort-state="asc"]::after { content: " ↑"; } .tbl th[data-sort-state="desc"]::after { content: " ↓"; }
.tbl th.sortable { cursor: pointer; } .tbl th.sortable:hover { color: var(--text-primary); }
/* Which columns sort, before the pointer arrives: a faint glyph that steps aside for the ↑/↓ arrow. */
.tbl th.sortable:not(.sorted)::after { content: "⇅"; opacity: .25; margin-left: 4px; }
.tbl td.key, .tbl th.key { position: sticky; left: 0; z-index: 1; background: var(--bg-surface); font-weight: 500; }
.tbl th.key { z-index: 3; background: var(--bg-subtle); }
.tbl tbody tr { transition: background-color .1s ease; }
.tbl tbody tr:hover td { background: var(--bg-subtle); } .tbl tbody tr:hover td.key { background: var(--bg-subtle); }
.tbl tbody tr.on td { background: var(--accent-soft); }
.tbl tbody tr.selected td { background: color-mix(in srgb, var(--accent-soft) 60%, var(--bg-surface)); }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl td.wrap, .tbl th.wrap { white-space: normal; min-width: 240px; }
.tbl td.wrap.narrow, .tbl th.wrap.narrow { min-width: 8rem; }
/* A width cap is for a form field. Text in a table cell does not wrap. So a cap on a cell cannot shrink
   the text, and a long code ran over the next column. Here a cap does not apply to a cell. The column
   grows to fit. The table scrolls when it must. A reader can still drag a column narrower. We tried
   wrapping instead. It broke short codes such as FORK-01 at the dash. */
td:is(.w-tiny, .w-code, .w-short, .w-med, .w-long) { max-width: none; }
/* A cell marked shown keeps its buttons in view without hover. It is for a decision that needs a field
   filled first, such as the bank call-back tick: a box beside an invisible button reads as a broken
   form, and a touch screen has no hover to reveal it. */
.tbl td.actions { text-align: right; } .tbl td.actions .btn { visibility: hidden; } .tbl tr:hover td.actions .btn, .tbl tr.on td.actions .btn, .tbl td.actions.shown .btn { visibility: visible; }
.tbl td.empty-cell { text-align: center; color: var(--text-muted); padding: 18px 10px; }
/* A home panel row that needs attention colours its name, so a leader finds it without reading every number. */
.tbl td.tone-bad, .tbl td.tone-bad a { color: var(--danger); } .tbl td.tone-warn, .tbl td.tone-warn a { color: var(--warn); } .tbl td.tone-good, .tbl td.tone-good a { color: var(--ok); }
.tbl td .sub { display: block; font-size: var(--fs-caption); color: var(--text-muted); }
.tbl input[type="checkbox"] { margin: 0; }
.tbl-foot { display: flex; align-items: center; gap: 12px; padding: 8px 10px; font-size: var(--fs-meta); color: var(--text-secondary); }
.pager { display: inline-flex; gap: 2px; align-items: center; } .pager a, .pager span { padding: 3px 8px; border-radius: var(--radius-sm); }
.pager a:hover { background: var(--bg-subtle); text-decoration: none; } .pager .on { background: var(--accent-soft); color: var(--accent); font-weight: 500; }
.pager input { width: 56px; }
/* A row on another page of a grid paged in the browser: still in the form, just not drawn. */
.tbl tbody tr.off-page { display: none; }
.filters { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
.filters input[type="search"] { min-width: 260px; }
.filters .result-count { color: var(--text-muted); font-size: var(--fs-meta); }
/* A list reloading after a search, a filter, a sort or a page change (IND-00GH3). The dim waits a
   moment, so a fast answer does not flicker. */
.tbl-wrap[aria-busy="true"] { opacity: .55; pointer-events: none; transition: opacity .15s ease .12s; }
.filters.busy :is(button[type="submit"], button:not([type]))::after {
  content: ""; display: inline-block; width: .8em; height: .8em; margin-left: .45em; vertical-align: -.1em;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: var(--radius-round); animation: list-busy .7s linear infinite; }
@keyframes list-busy { to { transform: rotate(360deg); } }
.saved-views { display: inline-flex; gap: 4px; align-items: center; flex-wrap: wrap; }
/* A saved view is a pill. The link opens it. The pencil changes it and the cross deletes it. Both
   open a small panel under the pill. A view someone shared carries a people mark. */
.saved-views .view-pill { position: relative; display: inline-flex; align-items: center; border-radius: var(--radius-round); background: var(--bg-subtle); }
.saved-views .view-pill.on { background: var(--accent-soft); }
.saved-views .view-pill > a { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: var(--radius-round); color: var(--text-secondary); font-size: var(--fs-caption); }
.saved-views .view-pill > a.on { color: var(--accent); }
.saved-views .view-pill > button { display: inline-grid; place-items: center; width: 20px; height: 20px; padding: 0; border: 0; border-radius: var(--radius-round); background: transparent; color: var(--text-secondary); cursor: pointer; }
.saved-views .view-pill > button:hover, .saved-views .view-pill > button:focus-visible { background: var(--accent-soft); color: var(--accent); }
.saved-views .view-pill [data-icon] { width: 11px; height: 11px; flex: none; background: currentColor;
  mask: var(--icon-mask) center / contain no-repeat; -webkit-mask: var(--icon-mask) center / contain no-repeat; }
.view-panel { position: absolute; top: calc(100% + 4px); left: 0; z-index: 10; display: grid; gap: 8px; min-width: 320px; padding: 10px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-elevated); color: var(--text-primary); box-shadow: var(--shadow-pop); font-size: var(--fs-meta); }
.view-panel p { margin: 0; }
.view-panel-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.view-panel-row input, .view-panel-row select { flex: 1 1 220px; }

/* ── Forms: built for repetition ────────────────────────────────────────── */
/* A control never draws past the box that holds it: a width utility caps it from above, the container
   caps it otherwise, so the frame keeps its shape at any window width. */
input, select, textarea { font: inherit; color: var(--text-primary); background: var(--bg-elevated); border: 1px solid var(--border-strong); border-radius: var(--radius); padding: 0 8px; height: var(--control-h); max-width: 100%; }
textarea { height: auto; min-height: 72px; padding: 6px 8px; line-height: 1.45; resize: vertical; }
/* A list box shows the rows its size attribute asks for. The fixed control height above cut every
   list box to under two rows, so a second choice was half hidden (IND-00FLO). */
select[multiple], select[size]:not([size="0"], [size="1"]) { height: auto; padding: 4px; }
input[type="checkbox"], input[type="radio"] { width: 15px; height: 15px; accent-color: var(--accent); }
input:disabled, select:disabled, textarea:disabled { opacity: .6; background: var(--bg-subtle); }
/* An id field types in the code font, but its hint is words. Without this the hint came out in the
   code font and smaller, unlike every other hint in the same filter row. */
input.mono::placeholder, textarea.mono::placeholder { font-family: var(--font-ui); font-size: var(--fs-body); }
/* A long list opened by the system on a Mac covers the whole screen, header and menu too (IND-00G9D).
   A browser that lets a page style its list now keeps a long one in a box that scrolls. Short lists
   and other browsers keep the system list. 16 options is about where the system list outgrows a box.
   A page can ask for the box on a shorter list too, with class="solid-list". The Mac system list is
   see-through, so a blue button under it showed through (IND-00G7R). The page's own box is solid. */
@supports (appearance: base-select) {
  select:not([multiple], [size]):is(:has(option:nth-of-type(16)), .solid-list),
  select:not([multiple], [size]):is(:has(option:nth-of-type(16)), .solid-list)::picker(select) { appearance: base-select; }
  select:not([multiple], [size]):is(:has(option:nth-of-type(16)), .solid-list) { display: inline-flex; align-items: center; }
  select:not([multiple], [size]):is(:has(option:nth-of-type(16)), .solid-list)::picker(select) {
    max-block-size: min(22rem, 60vh); overflow-y: auto; padding: 4px 0;
    color: var(--text-primary); background: var(--bg-elevated); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-pop);
  }
  select:not([multiple], [size]):is(:has(option:nth-of-type(16)), .solid-list) option { padding: 4px 10px; }
  select:not([multiple], [size]):is(:has(option:nth-of-type(16)), .solid-list) option:is(:hover, :focus-visible) { background: var(--accent-soft); }
}
.form { display: grid; gap: 10px; max-width: 680px; } .form > * { min-width: 0; } .form.wide { max-width: none; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
/* Many forms wrap the box in its label. The label words go on their own line, with the box under
   them and a small gap. That matches a .field, and the words never touch the box. A tick box keeps
   its words beside it. */
.form > label:not(.check, .field) { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px; font-size: var(--fs-meta); color: var(--text-secondary); font-weight: 500; }
.form > label:not(.check, .field) > :is(input:not([type="checkbox"]):not([type="radio"]), select, textarea) { flex: 1 0 100%; font-size: var(--fs-body); font-weight: 400; }
/* Opt-in for entry forms whose rows contain several long lookups. Keep the form within its
   container, wrap fields when space runs out, and leave wide line tables scrolling inside it. */
.form.responsive { width: 100%; min-width: 0; grid-template-columns: minmax(0, 1fr); }
.form.responsive > *, .form.responsive fieldset, .form.responsive .field { min-width: 0; }
.form.responsive .row { flex-wrap: wrap; align-items: start; }
.form.responsive .row > .field { flex: 1 1 180px; }
.form.responsive .field > input:not([type="checkbox"]):not([type="radio"]),
.form.responsive .field > select, .form.responsive .field > textarea { width: 100%; min-width: 0; }
.form.responsive .form-actions { flex-wrap: wrap; }
/* Opt-in for a longer entry form laid out two fields to a row. Both fields get the same width, so
   they line up down the page. The form may grow wider than the usual 680px. On a narrow screen,
   each field drops to its own line. */
.form.responsive.two-col { max-width: 960px; }
.form.responsive.two-col .row > .field { flex: 1 1 240px; }
/* A row of labelled fields packs them at the top. Say one field has a help line and its neighbour has
   none. A centred row drops the neighbour half a line, so labels and boxes no longer line up. This
   rule holds for every form row on every screen, so no page needs its own fix. It has to live here:
   the security policy refuses a style attribute on the row. "top" still works for other rows. */
.row:has(> .field), .row.top { align-items: start; }
/* A tick box alone in a field has no label above it. Next to a labelled box it would sit level with
   that label. A blank line the height of a label, and a box's height, put it level with the box. */
.row:has(> .field > :is(input:not([type="checkbox"]):not([type="radio"]), select, textarea))
  > .field:not(:has(> :is(input, select, textarea))):has(> label > :is(input[type="checkbox"], input[type="radio"]))::before {
  content: "\00a0"; font-size: var(--fs-meta); font-weight: 500; }
.row:has(> .field > :is(input:not([type="checkbox"]):not([type="radio"]), select, textarea))
  > .field:not(:has(> :is(input, select, textarea))) > label:has(> :is(input[type="checkbox"], input[type="radio"])) {
  display: flex; align-items: center; gap: 6px; min-height: var(--control-h); }
/* A form with a table of lines. The fields keep their width. The table takes the page width, because
   eight columns of boxes do not fit in the form's width. */
.form.with-lines, .form.responsive.two-col.with-lines { max-width: none; }
.form.with-lines > * { max-width: 680px; }
.form.two-col.with-lines > * { max-width: 960px; }
.form.with-lines > .line-grid { max-width: none; display: grid; gap: 6px; }
.line-grid > .field, .line-grid > p { max-width: 680px; margin: 0; }
/* An entry grid has boxes in its cells, so its cells are tighter than a list's. */
.tbl.entry td { padding: 3px 6px; }
.tbl.entry tfoot td { font-weight: 500; background: var(--bg-subtle); border-bottom: 0; }
/* The script shows some buttons only once it can run them, and they stay hidden until then. The btn
   class sets its own display, which would beat the hidden attribute. */
.btn[hidden] { display: none; }
/* A wide form is a grid, and each field stretches to the tallest field in its grid row. A field
   with no help line spread the spare height into its label and box. So its box sat lower.
   Packing each field at the top keeps every box in a grid row level. A tick box gets a box's
   height, so its words stay level with the box beside it. */
.form.wide > .field { align-content: start; }
.form.wide > .field > label.inline-form { min-height: var(--control-h); }
.field { display: grid; gap: 3px; min-width: 0; } .field label { font-size: var(--fs-meta); color: var(--text-secondary); font-weight: 500; }
.field .help { font-size: var(--fs-caption); color: var(--text-muted); } .field .err, .validation-summary-errors { color: var(--danger); font-size: var(--fs-meta); }
.field.invalid input, .field.invalid select, .field.invalid textarea, .input-validation-error { border-color: var(--danger); }
/* The rule above needs a labelled field block. So a plain inline row, such as Add site, got no outline
   when the browser refused a box in it (IND-00FOU). :user-invalid marks any box the browser refuses.
   It waits until the user has typed in the box or tried to submit, so a fresh form does not open in red. */
input:user-invalid, select:user-invalid, textarea:user-invalid { border-color: var(--danger); outline-color: var(--danger); }
.field.span2 { grid-column: span 2; }
/* A box the form needs shows a red star after its label. A person sees what to fill before saving.
   The star keys off the required attribute, so it always agrees with the check the browser makes. */
.field:has(> :is(input, select, textarea)[required]) > label::after,
.form > label:not(.field):has(> :is(input, select, textarea)[required])::after,
label.field:has(> :is(input, select, textarea)[required]) > .cap::after { content: " *"; color: var(--danger); font-weight: 600; }
/* A caption can sit inside the label that wraps its box: label.field > span.cap. It reads like the label
   of a .field. A placeholder or hover text alone is gone once the box holds a value (IND-00FNP). */
label.field > .cap { font-size: var(--fs-meta); color: var(--text-secondary); font-weight: 500; }
.form-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding-top: 4px; }
/* The resolve-a-definition row: four inputs and a button on one line when the column is wide enough,
   two lines when it is not. It was the widest fixed row in the system and the one that overflowed. */
.inline-form { display: inline-flex; gap: 6px; align-items: center; flex-wrap: wrap; }
/* A one-line form whose boxes carry captions above them. Its boxes, buttons and tick boxes line up
   along their bottom edge. A caption then never pushes its box out of line with the button. */
.inline-form:has(> .field) { align-items: flex-end; }
.inline-form:has(> .field) > label.inline-form { min-height: var(--control-h); }
/* "even" gives each captioned box in the row an equal share of the card. So a set of boxes that
   belong together, such as a party's role, currency, terms and Incoterms, is one width. The boxes
   grow on a wide window and wrap on a narrow one (IND-00FPY). */
.inline-form.even { display: flex; }
.inline-form.even > .field { flex: 1 1 150px; }
.inline-form.even > .field > :is(input:not([type="checkbox"], [type="radio"]), select, textarea) { width: 100%; }
fieldset { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; margin: 0; } legend { font-size: var(--fs-meta); color: var(--text-secondary); padding: 0 4px; }

/* ── Toasts, palette, bulk bar ─────────────────────────────────────────── */
#toasts { position: fixed; right: 16px; bottom: 16px; z-index: 100; display: flex; flex-direction: column; gap: 6px; }
.toast { display: flex; align-items: center; gap: 12px; padding: 8px 12px; border-radius: var(--radius); background: var(--bg-elevated); border: 1px solid var(--border-strong); box-shadow: var(--shadow-pop); font-size: var(--fs-body); max-width: 480px; }
.toast.ok { border-color: var(--ok); } .toast.err { border-color: var(--danger); } .toast.warn { border-color: var(--warn); }
.toast .act { color: var(--accent); font-weight: 500; cursor: pointer; margin-left: auto; }
.cmdk-back { position: fixed; inset: 0; background: var(--scrim); z-index: 90; display: flex; align-items: flex-start; justify-content: center; padding-top: 12vh; }
.cmdk-back[hidden] { display: none; }

/* ── Architecture chat: a transcript that fills the card, newest at the bottom ─────────────────────
   Classes rather than style attributes, because the content security policy refuses inline styles
   (ADR-014) and the page rendered unstyled with a console error on every visit. Colours are tokens. */
.card.chat { display: flex; flex-direction: column; height: 65vh; padding: 0; }
.chat-window { flex-grow: 1; overflow-y: auto; padding: 16px; border-bottom: 1px solid var(--border); }
.chat-msg { display: flex; flex-direction: column; align-items: flex-start; margin-bottom: 12px; }
.chat-msg.from-user { align-items: flex-end; }
.chat-bubble { max-width: 80%; padding: 10px 14px; border-radius: var(--radius); background: var(--bg-app); border: 1px solid var(--border); color: var(--text-primary); }
.chat-msg.from-user .chat-bubble { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.chat-role { font-size: var(--fs-meta); opacity: .8; margin-bottom: 4px; font-weight: 600; }
.chat-text { white-space: pre-wrap; font-size: var(--fs-body); }
.chat-compose { padding: 16px; background: var(--bg-surface); }
.chat-form { display: flex; gap: 8px; margin: 0; }
.chat-form { align-items: flex-end; }
.chat-form > .field { flex-grow: 1; min-width: 0; } .chat-input { width: 100%; min-width: 0; }
.markdown-body p { margin-top: 0; margin-bottom: .5rem; }
.markdown-body p:last-child { margin-bottom: 0; }
.markdown-body pre { background: var(--bg-surface); padding: 8px; border-radius: var(--radius); overflow-x: auto; }
.markdown-body code { font-family: var(--font-mono); font-size: .9em; background: var(--bg-subtle); padding: 2px 4px; border-radius: var(--radius-sm); }
.markdown-body ul, .markdown-body ol { margin-top: 0; margin-bottom: .5rem; padding-left: 1.5rem; }
.markdown-body ul:last-child, .markdown-body ol:last-child { margin-bottom: 0; }
.cmdk { width: min(680px, 92vw); background: var(--bg-elevated); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-pop); overflow: hidden; }
.cmdk-foot { display: flex; gap: 14px; padding: 6px 12px; border-top: 1px solid var(--border); font-size: var(--fs-caption); color: var(--text-muted); }
/* The shortcut sheet reuses the palette's frame; the body is three columns of key → meaning. */
.sheet { padding: 16px 18px; }
.sheet-body { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 18px; }
.sheet-col h3 { font-size: var(--fs-meta); text-transform: uppercase; letter-spacing: .07em; color: var(--text-muted); margin-bottom: 8px; }
.keys { margin: 0; display: grid; gap: 6px; }
.keys > div { display: flex; align-items: center; gap: 10px; }
.keys dt { min-width: 52px; text-align: right; } .keys dt kbd + kbd { margin-left: 2px; }
.keys dd { margin: 0; font-size: var(--fs-meta); color: var(--text-secondary); }
.cmdk input { width: 100%; height: 44px; border: 0; border-bottom: 1px solid var(--border); border-radius: 0; padding: 0 14px; font-size: var(--fs-title); background: transparent; }
.cmdk ul { list-style: none; margin: 0; padding: 6px; max-height: 50vh; overflow: auto; }
.cmdk li { display: flex; align-items: center; gap: 10px; padding: 6px 10px; border-radius: var(--radius); cursor: pointer; font-size: var(--fs-body); }
.cmdk li.on { background: var(--accent-soft); } .cmdk li .kind { margin-left: auto; font-size: var(--fs-caption); color: var(--text-muted); }
.cmdk li .code { font-family: var(--font-mono); color: var(--text-secondary); font-size: var(--fs-meta); }
.cmdk li.mute { color: var(--text-muted); cursor: default; }
.bulkbar { position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px; z-index: 80; display: flex; gap: 10px; align-items: center; padding: 8px 12px; background: var(--bg-elevated); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-pop); }
.bulkbar[hidden] { display: none; }
.bulkbar .count { font-weight: 500; }
.hovercard { position: absolute; z-index: 70; width: 260px; padding: 10px 12px; background: var(--bg-elevated); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-pop); font-size: var(--fs-meta); }
/* The peek drawer: a record slides in beside the list, never instead of it. */
.peek-back { position: fixed; inset: 0; background: var(--scrim); z-index: 85; display: flex; justify-content: flex-end; }
.peek { width: min(760px, 94vw); background: var(--bg-app); border-left: 1px solid var(--border-strong); box-shadow: var(--shadow-pop); display: flex; flex-direction: column; animation: peek-in .18s ease; }
@keyframes peek-in { from { transform: translateX(24px); opacity: .6; } to { transform: none; opacity: 1; } }
.peek-head { display: flex; align-items: center; gap: 10px; padding: 10px 16px; border-bottom: 1px solid var(--border); background: var(--bg-surface); flex: none; }
.peek-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.peek-body { overflow-y: auto; padding: 16px 18px 32px; flex: 1; }

/* ── Sign-in and error pages ────────────────────────────────────────────── */
/* 420px rather than 380px: the sign-in card carries the release notice, and two sentences of it
   wrapped to eleven lines at the narrower width. The other cards here hold one short line each. */
.center-card { max-width: 420px; margin: 10vh auto 0; }
.center-card .brand { justify-content: center; margin-bottom: 8px; }
.center-card .notice { margin-bottom: 14px; }

/* ── Scope switcher: which entity and site the page is showing ───────────── */
.scope-switcher { display: flex; gap: 6px; align-items: center; flex: 0 1 auto; min-width: 240px; overflow: clip; overflow-clip-margin: 4px; }
.scope-switcher label { font-size: var(--fs-caption); font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted); }
.scope-switcher select { flex: 0 1 180px; min-width: 76px; max-width: 180px; height: var(--control-h); padding: 0 8px; font-size: var(--fs-meta); background: var(--bg-app); border: 1px solid var(--border-strong); border-radius: var(--radius); color: var(--text-primary); cursor: pointer; }
.scope-switcher select:hover { background: var(--bg-subtle); }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* A narrow window has no room for everything in the top bar. The breadcrumb matters most, so the
   other parts give way first. Below 1180px the user name hides. The scope labels stay for screen
   readers only. The search box drops its key hint. Below 900px the breadcrumb takes a line of its
   own, so the bar never makes the page scroll sideways. These rules sit after the scope lists'
   own rules on purpose. They must win over them. */
@media (max-width: 1180px) {
  .user-name { display: none; }
  .scope-switcher { min-width: 0; }
  .scope-switcher label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .search-trigger { flex-basis: 160px; min-width: 80px; } .search-trigger kbd { display: none; }
}
@media (max-width: 900px) {
  :root { --topbar-h: 80px; }
  .topbar { flex-wrap: wrap; row-gap: 6px; min-height: var(--topbar-h); }
  .crumbs { order: -1; flex: 1 1 100%; justify-content: flex-start; }
  .scope-switcher select { flex-basis: 120px; } .search-trigger { flex-basis: 110px; }
}

/* ── Transitions, effect preview, approval card, error banner ────────────
   A step you cannot take now is shown disabled with the reason, not hidden: a hidden button
   teaches nobody what to do next (UX-042). */
.transitions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.transitions .btn[disabled] { opacity: 0.55; cursor: not-allowed; }
.blocked-why { margin-left: 4px; }
.blocked-why a { text-decoration: underline; }

.effect-preview .tbl { margin-top: 4px; }
.effect-preview .flash { margin-top: 8px; margin-bottom: 0; }

.approval-card .kv { margin-top: 10px; }
.approval-card ul { margin: 0; padding-left: 18px; }
.approval-card .transitions { margin-top: 12px; }

.effective-versions { margin-top: 6px; }

.error-banner { display: grid; gap: 6px; }
.error-banner .error-title { font-size: var(--fs-body); }
.error-banner .error-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.error-banner .error-meta { display: flex; flex-wrap: wrap; gap: 10px; }

/* The server said a business rule refuses this input; the browser's own check could not know. */
[data-validate-message] { color: var(--danger); font-size: var(--fs-meta); }
[data-validate-message][hidden] { display: none; }

/* ── Process map: how a module works, as a traced diagram ────────────────────
   Lanes of step boxes in one SVG. Tracing dims what is not connected to the selected box;
   without script every box is still a plain link to its stream section below. */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
.pmap-card { overflow: hidden; }
.pmap-scroll { overflow-x: auto; scrollbar-width: thin; }
.pmap { display: block; width: 100%; min-width: 880px; height: auto; font-family: var(--font-ui); }
.pmap text { text-anchor: middle; }
.lane-seps line { stroke: var(--border); stroke-width: 1; }
.axis-note { font-size: 11px; font-style: italic; fill: var(--text-muted); text-anchor: start; }
.lane-no { fill: var(--accent); }
.lane-no-t { font-size: 11px; font-weight: 600; fill: var(--accent-ink); }
.lane-t { font-size: 13px; font-weight: 600; fill: var(--text-primary); text-anchor: start; }
.lane-s { font-size: 11px; fill: var(--text-muted); text-anchor: start; }
.pmap-node { cursor: pointer; }
.pmap-node .box { fill: var(--bg-elevated); stroke: var(--border-strong); stroke-width: 1.25; }
.pmap-node:hover .box, .pmap-node:focus-visible .box { stroke: var(--accent); stroke-width: 2; }
.pmap-node .t { font-size: 13px; font-weight: 600; fill: var(--text-primary); }
.pmap-node:hover .t { fill: var(--accent); }
.pmap-node .s { font-family: var(--font-mono); font-size: 10.5px; fill: var(--text-muted); }
.pmap-node:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius); }
.stub.in { fill: none; stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 5 4; }
.stub.out { fill: var(--ok-bg); stroke: var(--ok); stroke-width: 1.25; }
a.pmap-node.out:hover .stub.out, a.pmap-node.out:focus-visible .stub.out { stroke-width: 2; }
.stub-t { font-size: 11px; fill: var(--text-secondary); }
.edge { fill: none; stroke: var(--border-strong); stroke-width: 1.5; marker-end: url(#pmap-arrow); }
.edge.ext { stroke: var(--text-muted); stroke-dasharray: 5 4; }
.edge.cross { stroke: var(--info); }
.edge.out { stroke: var(--ok); }
.arrow-head { fill: var(--border-strong); }
.arrow-head-lit { fill: var(--accent); }
.edge-t { font-size: 11px; fill: var(--text-secondary); paint-order: stroke; stroke: var(--bg-surface); stroke-width: 4px; }
[data-node], .edge { transition: opacity .15s ease; }
[data-trace].tracing [data-node] { opacity: .3; }
[data-trace].tracing [data-node].lit { opacity: 1; }
[data-trace].tracing .edge { opacity: .15; }
[data-trace].tracing .edge.lit { opacity: 1; stroke: var(--accent); stroke-width: 2.25; marker-end: url(#pmap-arrow-lit); }
[data-trace].tracing .edge-t { opacity: .2; }
[data-trace].tracing .edge-t.lit { opacity: 1; fill: var(--accent); }
.pmap-stream { scroll-margin-top: 60px; }
.pmap-stream:target { border-color: var(--accent); }

/* ── Print ──────────────────────────────────────────────────────────────── */
@media print { .rail, .topbar, #toasts, .bulkbar, .filters, .page-head .actions { display: none !important; } .content { padding: 0; } .tbl-wrap { border: 0; } .pmap-scroll { overflow: visible; } .pmap { min-width: 0; } }
.filters input.view-name { width: 180px; }
