/* Butterpie design tokens + skin — SC-013 source of truth. Semantic tokens only in components.
   ================================================================================================
   LEMON BERRY (brand kit v1.0). Lemon #F4E68A (butter) · Berry #A32142 (filling) · Plum #3E2838
   (crust) — the three permanent colours of the approved mark, and the three roles the kit gives
   them: lemon for small moments of emphasis and the selected navigation item, berry for primary
   actions and links, plum for the navigation shell and brand surfaces. Neutrals are plum-tinted so
   a dense table still reads as part of the same object; the kit asks for ~85–90% neutral surface
   across a workspace, and this file is what makes that the default rather than a discipline.

   EVERY VALUE BELOW IS THE KIT'S, COPIED, NOT DERIVED. Its contrast report checks 134 pairs and the
   estate re-checks the 74 pairs THIS product actually renders in
   `tests/unit/palette-meets-its-own-contrast-floor.test.ts`, reading the values out of this file —
   so a hand-tuned hex that looks nicer and fails AA cannot land quietly. Add a token here, add its
   pairs there.

   THE NAMES ARE THE OLD NAMES ON PURPOSE. `--brand`, `--ink`, `--line`, `--surface-2` are consumed
   by ~120 components; re-pointing them is a palette change, while renaming them would be a palette
   change PLUS a 120-file refactor whose diff nobody can read. The kit's own `--bp-*` names are
   published alongside as aliases, so new code can speak the kit's vocabulary without a second
   source of truth for the values.
   ================================================================================================ */

/* ---- Light theme (default) ---- */
:root {
  --bg: #F8F7F8; --surface: #FFFFFF; --surface-2: #F1EDF0; --surface-raised: #FFFFFF;
  --ink: #2E202A; --ink-muted: #6B5D67; --ink-subtle: #776772;
  --line: #E6DFE4; --line-strong: #91818B;
  --brand: #A32142; --brand-strong: #8A1A38; --brand-pressed: #71132D;
  --brand-mark: #F4E68A; --on-brand: #FFFFFF;
  --accent: #F4E68A; --on-accent: #3E2838;
  --highlight: #FCF8DD; --on-highlight: #3E2838;
  --selection: #F8EAEF; --on-selection: #A32142; --selection-indicator: #A32142;
  --disabled: #F1EDF0; --on-disabled: #6B5D67;
  --focus: #285F91;
  --ok: #26734D; --ok-bg: #EAF5EE;
  --warn: #875600; --warn-bg: #FFF3D6;
  --danger: #B42318; --danger-bg: #FEECE9; --danger-strong: #951C13; --on-danger: #FFFFFF;
  --info: #285F91; --info-bg: #EBF3FA;
  --overlay: #1B151BA6;
  /* THE DATA PALETTE (chart kit v2). Six categorical slots, used in this order and never cycled,
     then the chart neutrals and a one-hue ageing ramp for overdue severity. `--brand` never paints
     data: berry is 1.12:1 from --danger, so a neutral series drawn in it read as an alarm. The
     neutral single-series colour is --chart-1. Values are the same in the three consoles, and
     chart-palette-is-distinguishable.test.ts measures them (CVD and normal-vision separation). */
  --chart-1: #3D43A8; --chart-2: #04A19B; --chart-3: #CF6F19;
  --chart-4: #7D2A79; --chart-5: #A68003; --chart-6: #006C93;
  --chart-muted: #8A7C86; --chart-track: #ECE6EA; --chart-grid: #EDE8EC; --chart-axis: #91818B;
  --chart-age-1: #F0986F; --chart-age-2: #DD6D45; --chart-age-3: #C44323; --chart-age-4: #9E2418;

  /* THE NAVIGATION SHELL IS PLUM IN BOTH THEMES, and that is the kit's instruction rather than an
     oversight: the shell is a brand surface, not a light-mode surface, so it does not invert. Which
     is also why the nav carries the WHITE logo in both themes — on plum the full-colour crust loses
     its separation, and the kit says so in as many words. */
  --nav: #3E2838; --on-nav: #F8F3F6; --nav-muted: #C5B8C1;
  --nav-hover: #52374A; --nav-active: #F4E68A; --on-nav-active: #3E2838;

  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px;
  --space-6: 32px; --space-8: 48px; --space-10: 64px;
  --radius-sm: 6px; --radius-md: 10px; --radius-lg: 14px; --radius-pill: 999px;
  /* Plum-tinted shadow. A neutral-black shadow over plum-tinted neutrals reads as dirt; the tint is
     the same hue family as the surfaces it falls on. */
  --elev-1: 0 1px 2px rgba(46,32,42,.08), 0 1px 1px rgba(46,32,42,.04);
  --elev-2: 0 4px 12px rgba(46,32,42,.10);
  --elev-3: 0 12px 28px rgba(46,32,42,.16);

  --fs-caption: 12px; --fs-small: 13px; --fs-body: 15px; --fs-body-lg: 17px;
  --fs-h4: 19px; --fs-h3: 22px; --fs-h2: clamp(24px,3vw,28px); --fs-h1: clamp(28px,4vw,34px);
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, "Noto Sans", sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;

  --dur-1: 120ms; --dur-2: 180ms; --dur-3: 240ms; --dur-4: 200ms;
  --ease-standard: cubic-bezier(.2,0,0,1); --ease-emphasized: cubic-bezier(.2,0,0,1);

  /* The three permanent logo colours. They are the ARTWORK's, never a UI surface: nothing in this
     file may recolour the mark, and the kit is explicit that dark mode uses a lighter berry for
     CONTROLS without touching the logo. */
  --bp-brand-lemon: #F4E68A; --bp-brand-berry: #A32142; --bp-brand-plum: #3E2838;
}

/* ---- Dark theme ----------------------------------------------------------------------------
   THE MEDIA QUERY WRAPS THE SELECTOR, NOT THE OTHER WAY ROUND. This block used to be a nested
   `@media` INSIDE `:root:not([data-theme="light"])`, which is CSS Nesting — valid in current
   engines and silently DROPPED by anything older, taking the whole system-dark theme with it and
   leaving a light app on a dark OS with every gate green. The standard form below needs no nesting
   support at all.

   Three states, not two: an explicit `light` choice must beat a dark OS (hence the `:not`), an
   explicit `dark` choice must beat a light OS (hence the second block), and the un-stamped default
   follows the system. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #1B151B; --surface: #261E26; --surface-2: #312731; --surface-raised: #312731;
    --ink: #F8F3F6; --ink-muted: #C5B8C1; --ink-subtle: #AB9BA6;
    --line: #483B47; --line-strong: #8D7989;
    --brand: #F28AA5; --brand-strong: #F6A1B6; --brand-pressed: #E87394;
    --brand-mark: #F4E68A; --on-brand: #2A1420;
    --accent: #F4E68A; --on-accent: #3E2838;
    --highlight: #39331E; --on-highlight: #F4E68A;
    --selection: #4A2738; --on-selection: #FFD9E4; --selection-indicator: #F28AA5;
    --disabled: #352B34; --on-disabled: #AB9BA6;
    --focus: #F4E68A;
    --ok: #8ED5AD; --ok-bg: #18382B;
    --warn: #F4D48C; --warn-bg: #40321A;
    --danger: #FFAAA0; --danger-bg: #482925; --danger-strong: #FFC1B9; --on-danger: #351713;
    --info: #9DCCF2; --info-bg: #21384B;
    --overlay: #000000B3;
    --chart-1: #6674DE; --chart-2: #18A7A1; --chart-3: #C16302;
    --chart-4: #B05BAA; --chart-5: #9E7B03; --chart-6: #0C8BBB;
    --chart-muted: #93818F; --chart-track: #3A2F39; --chart-grid: #372D36; --chart-axis: #8D7989;
    --chart-age-1: #A64831; --chart-age-2: #CC5D3A; --chart-age-3: #E67E54; --chart-age-4: #F8A87E;
    --nav: #211820; --on-nav: #F8F3F6; --nav-muted: #C5B8C1;
    --nav-hover: #3E2838; --nav-active: #F4E68A; --on-nav-active: #3E2838;
    --elev-1: 0 1px 2px rgba(0,0,0,.40), 0 1px 1px rgba(0,0,0,.24);
    --elev-2: 0 4px 12px rgba(0,0,0,.44);
    --elev-3: 0 12px 28px rgba(0,0,0,.52);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #1B151B; --surface: #261E26; --surface-2: #312731; --surface-raised: #312731;
  --ink: #F8F3F6; --ink-muted: #C5B8C1; --ink-subtle: #AB9BA6;
  --line: #483B47; --line-strong: #8D7989;
  --brand: #F28AA5; --brand-strong: #F6A1B6; --brand-pressed: #E87394;
  --brand-mark: #F4E68A; --on-brand: #2A1420;
  --accent: #F4E68A; --on-accent: #3E2838;
  --highlight: #39331E; --on-highlight: #F4E68A;
  --selection: #4A2738; --on-selection: #FFD9E4; --selection-indicator: #F28AA5;
  --disabled: #352B34; --on-disabled: #AB9BA6;
  --focus: #F4E68A;
  --ok: #8ED5AD; --ok-bg: #18382B;
  --warn: #F4D48C; --warn-bg: #40321A;
  --danger: #FFAAA0; --danger-bg: #482925; --danger-strong: #FFC1B9; --on-danger: #351713;
  --info: #9DCCF2; --info-bg: #21384B;
  --overlay: #000000B3;
  --chart-1: #6674DE; --chart-2: #18A7A1; --chart-3: #C16302;
  --chart-4: #B05BAA; --chart-5: #9E7B03; --chart-6: #0C8BBB;
  --chart-muted: #93818F; --chart-track: #3A2F39; --chart-grid: #372D36; --chart-axis: #8D7989;
  --chart-age-1: #A64831; --chart-age-2: #CC5D3A; --chart-age-3: #E67E54; --chart-age-4: #F8A87E;
  --nav: #211820; --on-nav: #F8F3F6; --nav-muted: #C5B8C1;
  --nav-hover: #3E2838; --nav-active: #F4E68A; --on-nav-active: #3E2838;
  --elev-1: 0 1px 2px rgba(0,0,0,.40), 0 1px 1px rgba(0,0,0,.24);
  --elev-2: 0 4px 12px rgba(0,0,0,.44);
  --elev-3: 0 12px 28px rgba(0,0,0,.52);
}
:root[data-theme="light"] { color-scheme: light; }

/* ---- The kit's own vocabulary, as ALIASES of the tokens above ------------------------------
   Published so new code can write `var(--bp-surface)` and match the brand kit's documentation
   verbatim. They are `var()` references and not copies: a second set of literals would be a second
   source of truth, and the two would agree until the day somebody edited one. */
:root {
  --bp-canvas: var(--bg); --bp-surface: var(--surface); --bp-surface-subtle: var(--surface-2);
  --bp-surface-raised: var(--surface-raised);
  --bp-text: var(--ink); --bp-text-secondary: var(--ink-muted); --bp-text-muted: var(--ink-subtle);
  --bp-border: var(--line); --bp-border-control: var(--line-strong);
  --bp-primary: var(--brand); --bp-primary-hover: var(--brand-strong);
  --bp-primary-pressed: var(--brand-pressed); --bp-on-primary: var(--on-brand);
  --bp-link: var(--brand); --bp-link-hover: var(--brand-strong);
  --bp-selection: var(--selection); --bp-on-selection: var(--on-selection);
  --bp-selection-indicator: var(--selection-indicator);
  --bp-highlight: var(--highlight); --bp-on-highlight: var(--on-highlight);
  --bp-accent: var(--accent); --bp-on-accent: var(--on-accent);
  --bp-focus: var(--focus); --bp-disabled: var(--disabled); --bp-on-disabled: var(--on-disabled);
  --bp-nav: var(--nav); --bp-on-nav: var(--on-nav); --bp-nav-muted: var(--nav-muted);
  --bp-nav-hover: var(--nav-hover); --bp-nav-active: var(--nav-active);
  --bp-on-nav-active: var(--on-nav-active);
  --bp-success: var(--ok); --bp-success-soft: var(--ok-bg);
  --bp-warning: var(--warn); --bp-warning-soft: var(--warn-bg);
  --bp-danger: var(--danger); --bp-danger-soft: var(--danger-bg);
  --bp-danger-hover: var(--danger-strong); --bp-on-danger: var(--on-danger);
  --bp-info: var(--info); --bp-info-soft: var(--info-bg);
  --bp-overlay: var(--overlay);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg); color: var(--ink);
  font-family: var(--font-sans); font-size: var(--fs-body); line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
h1 { font-size: var(--fs-h1); line-height: 1.15; letter-spacing: -.02em; font-weight: 800; margin: 0 0 var(--space-4); }
h2 { font-size: var(--fs-h2); line-height: 1.15; letter-spacing: -.02em; font-weight: 700; margin: 0 0 var(--space-3); }
h3 { font-size: var(--fs-h3); line-height: 1.2; font-weight: 700; margin: 0 0 var(--space-2); }
/* JAPANESE (docs/i18n/FIVE-LANGUAGES.md). The Latin stack stays FIRST and unchanged, so every Latin
   letter and digit renders exactly as it does in the other four languages; a glyph none of those fonts
   has (kana, kanji) falls through, per character, to the CJK faces each platform ships — macOS
   Hiragino, Windows Yu Gothic UI / Meiryo, Linux and Android Noto Sans JP. Scoped to `:lang(ja)` on the
   root, which `<html lang="ja">` sets at SSR and ClientI18n.setLocale keeps in step on a switch.
   Japanese has no letter case and no word-level tracking: uppercasing is a no-op at best, and the
   negative/positive letter-spacing tuned for Latin headings and caption labels crowds or scatters
   kanji, so both are reset here. */
:root:lang(ja) {
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, "Noto Sans",
    "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", Meiryo, "Noto Sans JP", sans-serif;
}
:lang(ja) h1, :lang(ja) h2 { letter-spacing: normal; }
:lang(ja) .us-kpi-label, :lang(ja) .us-deep-label { text-transform: none; letter-spacing: normal; }
:lang(ja) .us-chart-empty { font-style: normal; }
a { color: var(--brand); text-underline-offset: 2px; }
a:hover { color: var(--brand-strong); }

/* Focus — single global rule, never removed. */
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 3px; }

/* Reduced motion: collapse every duration, disable transitions/animations. */
@media (prefers-reduced-motion: reduce) {
  :root { --dur-1: 0ms; --dur-2: 0ms; --dur-3: 0ms; --dur-4: 0ms; }
  *, *::before, *::after { transition-duration: 0ms !important; animation-duration: 0ms !important; scroll-behavior: auto !important; }
}

.us-visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.us-skip-link {
  position: absolute; left: var(--space-3); top: -60px; z-index: 100;
  background: var(--surface); color: var(--ink); padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md); box-shadow: var(--elev-2); text-decoration: none; font-weight: 700;
  transition: top var(--dur-1) var(--ease-standard);
}
.us-skip-link:focus { top: var(--space-3); }

/* ---- Brand wordmark ---- */
/* ---- The mark -------------------------------------------------------------------------------
   `width`/`height` on the <img> are the artwork's own 672x430 so the browser reserves the right box
   before the SVG arrives (no layout shift); the rendered size is set here, in one place. */
/* THE LOCKUP IS MARK + NAME, and the name is not decoration. The approved artwork is a wide, shallow
   slice (672x430 with most of its mass in a thin wedge); at the 24-28px a header gives it, the crust
   and the butter collapse into one blob and it reads as an unidentifiable smudge. The kit ships no
   wordmark, so the product name is set in the UI face beside the mark — which is also what makes the
   header identifiable to somebody who has never seen the mark before. */
.us-wordmark { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: 800; letter-spacing: -.02em; font-size: var(--fs-body-lg); }
.us-wordmark-name { white-space: nowrap; }
.us-wordmark-surface { color: var(--ink); }
.us-wordmark-nav { color: var(--on-nav); }
.us-mark { display: block; width: auto; height: 34px; }
.us-wordmark-nav .us-mark { height: 30px; }
/* WHICH FILE IS VISIBLE IS A THEME DECISION, and it is made three times for the three theme states
   the app has: the un-stamped default follows the system, and either explicit choice beats it. The
   shell (`us-wordmark-nav`) is exempt — it renders only the white mark, because plum does not
   invert. */
.us-wordmark-surface .us-mark-white { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .us-wordmark-surface .us-mark-colour { display: none; }
  :root:not([data-theme="light"]) .us-wordmark-surface .us-mark-white { display: block; }
}
:root[data-theme="dark"] .us-wordmark-surface .us-mark-colour { display: none; }
:root[data-theme="dark"] .us-wordmark-surface .us-mark-white { display: block; }
:root[data-theme="light"] .us-wordmark-surface .us-mark-colour { display: block; }
:root[data-theme="light"] .us-wordmark-surface .us-mark-white { display: none; }

/* ---- Buttons ---- */
.us-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  font-family: inherit; font-size: var(--fs-body); font-weight: 700; line-height: 1;
  padding: 10px var(--space-4); border-radius: var(--radius-md); border: 1px solid transparent;
  cursor: pointer;
  /* BOX-SHADOW ONLY. THE BACKGROUND USED TO FADE, AND THAT WAS A MEASURED CONTRAST FAILURE.
     `:disabled` is a colour change now (the kit's `disabled`/`on-disabled` at full opacity, since a
     55% fade drags every pairing under it off its checked value invisibly). But the enabled and
     disabled states are deliberately DIFFERENT pairs — #2A1420 on #F28AA5 and #AB9BA6 on #352B34 —
     and `background` was in the transition list while `color` was not. So for the ~120 ms after a
     primary button became enabled, the fill crossed from one state's ground to the other's while the
     label had already snapped: measured worst 1.27:1, and the a11y harness caught it on the settle
     composer the moment a staged row enabled Save.
     Transitioning `color` as well is WORSE, not better — the two ramps cross and the blend bottoms
     out at 1.03:1. There is no safe interpolation between two deliberately different pairs, so the
     answer is not to interpolate: an enabledness change is instant. Hover stays instant with it,
     which is what a button does anyway; the focus ring keeps its transition because a ring's two
     states share their ground. */
  transition: box-shadow var(--dur-1) var(--ease-standard);
}
/* DISABLED IS A COLOUR, NOT AN OPACITY. `opacity: .55` fades the whole control, which drags every
   pairing under it off its checked contrast — a 55%-opaque berry button computes about 3.0:1 against
   the canvas and the label with it — and it does so invisibly, because nothing measures a composited
   colour. The kit's instruction is explicit: use `disabled` + `on-disabled` at FULL opacity and
   expose the state programmatically (which `:disabled` already does). 5.35:1 in light, 5.15:1 in
   dark, both checked. */
.us-btn:disabled { background: var(--disabled); color: var(--on-disabled); border-color: transparent; cursor: not-allowed; }
/* A BUSY button is `aria-disabled`, not `disabled` (month-end's close, the correction's reopen, the help
   forms' send): a natively disabled control drops the keyboard focus on <body>. A primary or secondary
   one wears the SAME checked pair as :disabled, and the hover/active fills exclude it as they exclude
   :disabled (the staff console's rule, apps/us-staff-web/src/tokens.css). A GHOST button is left to its
   own inline look: the step rail's unreachable steps and the greyed microphone are aria-disabled ghosts
   that are not busy, and a grey fill would change what they say. */
.us-btn-primary[aria-disabled="true"], .us-btn-secondary[aria-disabled="true"] { background: var(--disabled); color: var(--on-disabled); border-color: transparent; cursor: not-allowed; }
.us-btn-primary { background: var(--brand); color: var(--on-brand); }
.us-btn-primary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--brand-strong); }
.us-btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
.us-btn-secondary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--surface-2); }
.us-btn-ghost { background: transparent; color: var(--ink); }
.us-btn-ghost:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--surface-2); }

/* ---- Fields ---- */
.us-field { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
/* ONE DECLARATION, TWO SELECTORS. `.us-field label` styles the labels `TextField`/`SelectField`
   render; `.us-label` is the class seven hand-rolled label+select pairs already used
   (fiscal.tsx x4, statutory.tsx, agent-proposals.tsx) and which no stylesheet defined — so those
   seven inherited `body` and sat in the SAME `.us-field-row` as component labels that did not,
   visibly heavier and larger than their neighbours. Sharing one rule is what stops the two from
   drifting apart again; a separate `.us-label` block would be a second answer to one question. */
.us-field label, .us-label { font-weight: 600; font-size: var(--fs-small); color: var(--ink); }
.us-input {
  font-family: inherit; font-size: var(--fs-body); color: var(--ink); background: var(--surface);
  padding: 10px var(--space-3); border: 1px solid var(--line-strong); border-radius: var(--radius-md); width: 100%;
}
.us-input:focus-visible { border-color: var(--focus); }
.us-input[aria-invalid="true"] { border-color: var(--danger); }
.us-field-error { color: var(--danger); font-size: var(--fs-small); }
.us-field-hint { color: var(--ink-subtle); font-size: var(--fs-small); }

/* P-025 (ADR-US-076) — A REFUSED FIELD, AND THE SUMMARY ABOVE THE FORM (ui/form-errors.tsx).
   A field the form marks gets a danger rule down its left edge, beside the input's own danger border
   above: two shapes plus the words under it, so colour is never the only signal (WCAG 1.4.1). A group
   (`fieldset`) takes the same rule on its frame, and its error line sits on its own row under the
   legend. The summary is a plain surface with a danger rule, the same family; its links are in the
   danger ink, which holds 4.5:1 on --surface in both themes. Theme tokens only, no animation.
   THE MARK MOVES NOTHING (review: the first version indented a marked field by its rule and padding,
   15 px, so the label and input jumped right when the error appeared and back under the caret on the
   first keystroke, and a marked input no longer lined up with its neighbours). The field's rule is a
   pseudo-element hung in the gutter to the LEFT of the field, out of the layout; it is a border, not a
   background, so forced-colors mode keeps it. A fieldset's thicker left frame takes back the 2 px from
   its own padding, so its contents stay put.
   THE NARROWEST GUTTER DECIDES WHERE IT HANGS (review forms-05). Fields share a row with a 12 px gap
   (`.us-field-row`, and the `us-row` rows with `gap: var(--space-3)`), and the neighbour on the left
   draws its border at the edge of that gap and its focus ring 4 px beyond it. Hung 15 px out, the rule
   cut through the neighbour's border and ring and read as the NEIGHBOUR's mark. It hangs 7 px out: 3 px
   wide, it fills -7..-4 px, clear of the neighbour's ring (which ends at -8 px) and of its own field's
   ring (which starts at -4 px), and still inside the 16 px grid gap and the card's 24 px padding.
   `scroll-margin` keeps a summary scrolled to the top clear of the sticky degraded-locale banner; a
   refused field (and a line card) keep the same room when the focus step brings them in (forms-03). */
.us-field.us-field-invalid { position: relative; }
.us-field.us-field-invalid::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: calc(-3px - var(--space-1));
  border-left: 3px solid var(--danger); pointer-events: none;
}
.us-field.us-field-invalid, .us-fieldset.us-field-invalid, .us-line-card { scroll-margin-top: var(--space-8); }
.us-fieldset.us-field-invalid { border-color: var(--danger); border-left-width: 3px; padding-left: calc(var(--space-4) - 2px); }
.us-fieldset > .us-field-error { display: block; margin: 0 0 var(--space-3); }
.us-error-summary {
  background: var(--surface); color: var(--ink); border: 1px solid var(--line); border-left: 4px solid var(--danger);
  border-radius: var(--radius-md); padding: var(--space-4) var(--space-5); scroll-margin: var(--space-8);
}
.us-error-summary:focus { outline: 2px solid var(--focus); outline-offset: 2px; }
.us-error-summary-title { font-size: var(--fs-body-lg); line-height: 1.3; margin: 0 0 var(--space-2); color: var(--ink); }
.us-error-summary-list { margin: 0; padding-left: var(--space-5); display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--fs-small); }
.us-error-summary-list a { color: var(--danger); font-weight: 600; text-decoration: underline; }
.us-error-summary-list a:hover { color: var(--danger-strong); }

/* A row of fields that must line up on their INPUTS (ADR-US-055 wave).
   `align-items:flex-end` — the convention this replaces — CANNOT do it, and reads as a layout bug on
   any row with mixed hints: the hint and error lines sit BELOW the input inside .us-field, so a
   hinted field's bottom edge is its HINT, and bottom-aligning lifts its input above an unhinted
   neighbour's. The fields stagger. Top-aligning works because every field opens with a label line of
   the same size, so the inputs land on one baseline and an optional hint just hangs below where it
   belongs. A `label.us-stack` wrapping a <select> adopts the field's own gap and bottom margin so it
   sits on that baseline too. Submit actions do NOT belong in a field row — give them their own row,
   or they align to nothing. */
.us-field-row { display: flex; flex-wrap: wrap; gap: 0 var(--space-3); align-items: flex-start; }
.us-field-row > label.us-stack { gap: var(--space-2); margin-bottom: var(--space-4); }

/* A <select> is a form control like any other. Left bare it renders as an unstyled browser default
   beside a bordered .us-input — different height, different border, different type size — which is
   why the mixed rows above looked broken even where they aligned. Same box as .us-input. */
.us-select {
  font-family: inherit; font-size: var(--fs-body); color: var(--ink); background: var(--surface);
  padding: 10px var(--space-3); border: 1px solid var(--line-strong);
  border-radius: var(--radius-md); width: 100%;
}
.us-select:focus-visible { border-color: var(--focus); }
/* P-025 part 2 (ADR-US-076): a refused <select> takes the same danger border as a refused .us-input. */
.us-select[aria-invalid="true"] { border-color: var(--danger); }

/* Form grouping (order 6, transactional depth): fieldsets + a two-column grid so a thirty-field
   master form stays scannable, collapsing to one column under 40rem. */
.us-fieldset { border: 1px solid var(--line); border-radius: var(--radius-md); padding: var(--space-4); margin: 0 0 var(--space-4); }
.us-fieldset > legend { font-weight: 600; font-size: var(--fs-small); color: var(--ink-subtle); padding: 0 var(--space-2); }
.us-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--space-4); }
.us-form-grid > .us-field-wide { grid-column: 1 / -1; }
@media (max-width: 40rem) { .us-form-grid { grid-template-columns: minmax(0, 1fr); } }
.us-check-row { display: flex; align-items: center; gap: var(--space-2); }
.us-check-row label { font-weight: 400; }
.us-check { margin-bottom: var(--space-3); }

/* Sales line editor (order 6): a card per line inside the lines fieldset, and the derived
   read-only values that sit beside the inputs. */
.us-line-card { margin-bottom: var(--space-3); padding: var(--space-4); }
.us-line-head { justify-content: space-between; margin-bottom: var(--space-3); }
.us-field-label-static { font-weight: 600; font-size: var(--fs-small); color: var(--ink); }
.us-line-net { font-variant-numeric: tabular-nums; padding: 10px 0; color: var(--ink); }
.us-totals { width: auto; min-width: 16rem; margin-left: auto; }
.us-totals td.num { text-align: right; font-variant-numeric: tabular-nums; }
.us-card-title { font-size: var(--fs-body); margin: 0 0 var(--space-3); }

/* Notification bell, popover, unread rows, and the shortcut help sheet (order 6). */
.us-bell { position: relative; display: inline-flex; }
.us-badge { display: inline-block; min-width: 1.25rem; margin-left: var(--space-2); padding: 0 5px;
  border-radius: 999px; background: var(--brand); color: var(--on-brand); font-size: var(--fs-small);
  font-variant-numeric: tabular-nums; text-align: center; }
/* z 39: above the assistant panel (38), so the bell's popover is never clipped under it, and below the
   drawer and the help sheet's scrim (40) — the desktop header makes no stacking context (G1-R2-03). */
.us-popover { position: absolute; top: calc(100% + var(--space-2)); right: 0; z-index: 39; width: min(24rem, 90vw);
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  box-shadow: var(--elev-1); padding: var(--space-4); }
.us-popover-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.us-popover-list li.unread { font-weight: 600; }
.us-table tr.unread td { font-weight: 600; }
.us-sheet-backdrop { position: fixed; inset: 0; z-index: 40; background: var(--overlay);
  display: flex; align-items: center; justify-content: center; padding: var(--space-4); }
.us-sheet { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  box-shadow: var(--elev-1); padding: var(--space-5); width: min(30rem, 100%); max-height: 80vh; overflow: auto; }
.us-sheet kbd { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--fs-small);
  border: 1px solid var(--line-strong); border-bottom-width: 2px; border-radius: 4px; padding: 1px 5px; background: var(--surface-2); }
/* The focus-trapped alertdialog (administration wave step 1, §5.3): the dirty-exit guard the breach,
   authorisation and processing-activity forms raise; the box is the sheet's shape. Its BACKDROP is
   the one rule under "Dialog" below (z-index 80, above the sheet's 40 and the assistant's sheet's 45).
   A second `.us-dialog-backdrop` rule stood here at z-index 50 and was dead — the later rule won every
   property it set — which is exactly how a layer order reads right in one place and is wrong in
   effect. Removed with the assistant's layers (ADR-US-069), which had to be ordered against it. */
.us-dialog { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  box-shadow: var(--elev-1); padding: var(--space-5); width: min(28rem, 100%); max-height: 80vh; overflow: auto; }
.us-dialog p { margin: 0 0 var(--space-4); }

/* The shared history timeline (order 6). */
.us-timeline { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.us-timeline li { display: flex; gap: var(--space-3); align-items: baseline; }
.us-timeline-when { color: var(--ink-subtle); font-size: var(--fs-small); font-variant-numeric: tabular-nums; white-space: nowrap; }
textarea.us-input { resize: vertical; min-height: 6rem; }

/* ---- Card / surface ---- */
.us-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--elev-1); padding: var(--space-5); }

/* ---- Banner / alert (live region) ---- */
.us-banner { display: flex; gap: var(--space-3); align-items: flex-start; padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); border: 1px solid var(--line); font-size: var(--fs-small); }
.us-banner-info { background: var(--info-bg); border-color: color-mix(in srgb, var(--info) 30%, transparent); color: var(--ink); }
.us-banner-warn { background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 30%, transparent); color: var(--ink); }
.us-banner-danger { background: var(--danger-bg); border-color: color-mix(in srgb, var(--danger) 30%, transparent); color: var(--ink); }
.us-banner-ok { background: var(--ok-bg); border-color: color-mix(in srgb, var(--ok) 30%, transparent); color: var(--ink); }
.us-banner .us-btn { margin-left: auto; }
/* P-2DB06 — the demo banner at the top of a demo session's pages (demo-banner.tsx). */
.us-demo-banner { margin: 0 0 var(--space-4); overflow-wrap: break-word; }

/* ---- App frame (P-FRAME) ---- */
.us-frame { min-height: 100vh; display: grid; grid-template-columns: 248px 1fr; grid-template-rows: 56px 1fr; grid-template-areas: "header header" "nav main"; }
.us-frame-header { grid-area: header; display: flex; align-items: center; gap: var(--space-4); padding: 0 var(--space-4); background: var(--surface); border-bottom: 1px solid var(--line); box-shadow: var(--elev-1); }
.us-frame-nav { grid-area: nav; background: var(--surface); border-right: 1px solid var(--line); padding: var(--space-3); overflow-y: auto; }
.us-frame-main { grid-area: main; padding: var(--space-5); overflow-y: auto; }
.us-utility { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); }
/* The organization's NAME (workspace.tsx) — a legal name the customer chose, of any length ("… spółka
   z ograniczoną odpowiedzialnością spółka komandytowa"). It is the one header item allowed to shrink:
   cut with an ellipsis on one line rather than pushing the bell and sign-out off the edge. The whole
   name stays in the DOM and in its `title`. Where it shows (≥721 px; hidden below, see ≤720), the
   controls do not give way to it: `.us-utility` carries `min-width: 0` at ≤960 px, so without
   `flex-shrink: 0` a long name squeezed the utility and its last select spilled 56 px past a 721 px
   viewport (measured, tests/unit/shell-org-name-in-chromium.test.ts W5). The utility's content fits
   at every width ≥721 px with the label at zero, so not shrinking it costs nothing. */
.us-org-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (min-width: 721px) {
  .us-frame-header .us-utility { flex-shrink: 0; }
}

/* `.us-nav-toggle` — THE DRAWER'S ONLY OPENER, and until now it had no rule in any stylesheet.
   Below 720px the grid drops the `nav` area entirely and `.us-frame-nav` becomes an off-canvas panel
   that ONLY `[data-open="true"]` brings back — and only this button sets `data-open`
   (screens/workspace.tsx). The button carried an inline `style="display:none"`, so it was hidden at
   EVERY width and the drawer could never open: on a phone the primary navigation (102 links) was
   unreachable, with only Notifications and Sign out left in the header. Measured at 375x812 before
   the fix.
   The inline attribute is REMOVED rather than beaten with `!important`: no class selector can outrank
   an inline declaration, and the three `!important`s in this file are escape hatches for
   reduced-motion, `.us-visually-hidden` and print — not specificity workarounds. Visibility is a
   responsive concern, so it lives here beside `.us-frame`.
   Source order is the mechanism: `.us-btn` (:93) sets `display: inline-flex` at the same (0,1,0)
   specificity, so this rule must come after it — it does. */
.us-nav-toggle { display: none; }

/* See the note on `.us-nav-backdrop` inside the media query. This BASE rule is the half that does
   not depend on any script: the backdrop renders on `drawerOpen` alone, and rotating a phone to
   landscape with the drawer open drops every media-query rule while leaving the element in the tab
   order. Shipped without this on 2026-09-13; fixed the same day. */
.us-nav-backdrop { display: none; }

/* The header's compact icons (locale, theme, sign-out): absent at full width, where the words show.
   The media query below brings them in exactly where the words step aside. */
.us-compact-icon { display: none; }

/* THE HEADER FITS EVERY WIDTH, IN TWO STEPS — AND THE FIRST STEP IS 960 PX, NOT 720.
   With words on every control the header needs 773–869 px across the five languages (the bell's
   word, two worded selects and a worded sign-out). The compact block used to start at the drawer's
   720 px, so every portrait tablet scrolled sideways: 721/768 px in English (773 px wide), 721–820 px
   in Polish (839), French (844) and German (869), with "Abmelden" pushed past the right edge
   (W1 review, 2026-09-26, visual-check V1 at 721/768/820). So:
     ≤960 px — every header control keeps its WORDS as its accessible name and SHOWS an icon: the
               bell's and sign-out's labels are visually hidden (never `display: none`), and the two
               selects stay the NATIVE controls, transparent over their icon, so a tap opens the same
               picker with every language named in its own words;
     ≤720 px — where the sidebar becomes the drawer: the ☰ opener, the drawer, its backdrop, the
               org name hidden (the page names it), the 16 px page gutter and the tighter header gaps;
     ≤374 px — a 360 px Android phone: the header's side padding gives up the 2 px it overflowed by. */
@media (max-width: 960px) {
  .us-bell-label,
  .us-signout-label { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
  .us-utility { gap: var(--space-1); min-width: 0; }
  .us-utility .us-btn { padding: 8px 10px; font-size: 13px; }
  /* Each select shows an icon (a globe, a sun-moon) and keeps its words for assistive tech. The
     select is not replaced: the NATIVE control, transparent, covers its icon — the same id, label and
     picker. Its type is 16 px because iOS zooms the page when a smaller field takes focus; it is
     invisible, so the size costs nothing. An invisible control draws an invisible outline, so the
     keyboard ring is drawn on the box around it, in the shell's own ring colour. */
  .us-utility .us-ltb-compact { gap: var(--space-1); }
  .us-ltb-compact .us-field { position: relative; display: inline-grid; place-items: center; width: 36px; height: 36px;
    color: var(--on-nav); border: 1px solid color-mix(in srgb, var(--on-nav) 38%, transparent); border-radius: var(--radius-md); }
  .us-ltb-compact .us-field:hover { background: var(--nav-hover); }
  .us-ltb-compact .us-field .us-input { position: absolute; inset: 0; width: 100%; max-width: none; height: 100%; padding: 0;
    font-size: 16px; opacity: 0; cursor: pointer; }
  .us-ltb-compact .us-field:has(.us-input:focus-visible) { outline: 2px solid var(--nav-active); outline-offset: 2px; box-shadow: 0 0 0 4px var(--nav); }
  .us-compact-icon { display: block; flex: none; pointer-events: none; }
  /* EVERY HEADER CONTROL IS A 36 PX SQUARE, and the unread count sits ON the bell's corner — outside
     the glyph, not over it. At `top: 0; right: 0` a three-digit pill ("805") sat inside the 36 px
     button and covered the bell's whole dome (measured at 390 px: badge 227–257 × 11–27 over a
     231–249 × 19–37 glyph). Nudged up and right past the button's corner, with a ring in the header's
     own colour so it reads as a separate mark where it overlaps the button's border. A count above
     99 reads "99+" (notifications.tsx); the exact number stays in the unread sentence. */
  .us-utility .us-signout { width: 36px; height: 36px; padding: 0; }
  .us-utility .us-bell > .us-btn { position: relative; width: 36px; height: 36px; padding: 0; }
  .us-utility .us-bell .us-badge { position: absolute; top: -8px; right: -6px; min-width: 16px; margin: 0; padding: 0 4px;
    font-size: 11px; line-height: 16px; box-shadow: 0 0 0 2px var(--nav); pointer-events: none; }
}

@media (max-width: 720px) {
  .us-frame { grid-template-columns: 1fr; grid-template-areas: "header" "main"; }
  /* `visibility: hidden` ON THE CLOSED DRAWER, AND IT IS NOT COSMETIC. `transform` moves the panel
     off-screen for the EYES and leaves it in the TAB ORDER: measured at 375x812 with the drawer
     closed, the eighth Tab landed on the "Home" nav link at x=-236px, and a keyboard user then walked
     102 invisible links. `visibility: hidden` removes it from the tab order; no transform can.
     The zero-length visibility transition is DELAYED by the slide duration on close, so the panel is
     still painted while it slides out; the open rule resets that delay so it is painted before it
     slides in. Under `prefers-reduced-motion` (:71) `--dur-3` is 0ms, so the delay collapses with the
     animation and the panel cannot get stuck hidden. */
  .us-frame-nav { position: fixed; top: 56px; bottom: 0; left: 0; width: 248px; visibility: hidden; transform: translateX(-100%); transition: transform var(--dur-3) var(--ease-emphasized), visibility 0s linear var(--dur-3); z-index: 40; box-shadow: var(--elev-3); }
  /* `transition-delay` as the LONGHAND, so the shorthand above is not written twice — a duplicated
     shorthand drifts the first time somebody edits the duration in one copy and not the other. */
  .us-frame-nav[data-open="true"] { visibility: visible; transform: translateX(0); transition-delay: 0s; }
  /* ...and the opener appears exactly where the sidebar disappears. */
  .us-nav-toggle { display: inline-flex; width: 36px; height: 36px; padding: 0; }
  /* THE HEADER PAINTS ABOVE THE OPEN DRAWER. The drawer is `top: 56px`, which assumes the header
     starts at the viewport top — false whenever the degraded-locale banner (app.tsx, `position:
     sticky`) is present, because it pushes the header down and the drawer then covers the very ☰ that
     opened it. Measured with the banner up: header 46-102, drawer top 56, toggle covered.
     Raising the header instead of chasing the offset keeps the toggle clickable in BOTH cases and
     needs no coordination between a banner and a drawer that know nothing about each other; the
     drawer simply slides under the header, which is what `top: 56px` was reaching for anyway.
     50 sits above the drawer (40) and below the banner (90), so the banner still wins the top strip. */
  .us-frame-header { position: relative; z-index: 50; }
  /* The drawer covers 248 of 375 CSS px on a phone, so a tap outside it must dismiss. The backdrop
     sits one layer BELOW the drawer (z-index 39 against the drawer's 40) so the nav stays clickable,
     and it is only rendered while the drawer is open. */
  .us-nav-backdrop { display: block; position: fixed; inset: 56px 0 0 0; z-index: 39; background: var(--overlay); border: 0; padding: 0; margin: 0; width: 100%; cursor: default; }
  /* THE HEADER FITS THE PHONE: the org name goes (the page names it), the header's own gaps tighten,
     and the page gutter is 16 px. The wordmark keeps its name — the mark alone reads as a smudge. */
  .us-frame-header { gap: var(--space-2); padding: 0 var(--space-3); }
  .us-frame-main { padding: var(--space-4); }
  .us-org-label { display: none; }
}

/* A 360 PX ANDROID PHONE: the header measured 362 px in every language. The side padding gives up
   2 px a side rather than the wordmark its name. */
@media (max-width: 374px) {
  .us-frame-header { padding: 0 10px; }
}

/* ---- Side nav (P-NAV) ---- */
.us-nav-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.us-nav-item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); color: var(--ink); text-decoration: none; font-weight: 600; font-size: var(--fs-small); }
.us-nav-item:hover { background: var(--surface-2); }
.us-nav-item[aria-current="page"] { background: color-mix(in srgb, var(--brand) 14%, transparent); color: var(--brand-strong); }
.us-nav-item[aria-disabled="true"] { color: var(--ink-subtle); cursor: not-allowed; }

/* ---------------------------------------------------------------------------------------------
   THE NAV HIERARCHY (nav-groups.ts).

   Each group is a native <details>/<summary>. The marker is removed in BOTH spellings — the
   standard `::marker` on the summary and WebKit's legacy `::-webkit-details-marker` — because
   Safari still honours the second, and a page that removes only one ships a stray triangle beside
   the chevron on every Apple device. The chevron is drawn instead, and rotated from the parent's
   [open] state so it always agrees with what the group is actually doing.
   --------------------------------------------------------------------------------------------- */
.us-nav-group { border: 0; margin: 0; }
.us-nav-group + .us-nav-group { margin-top: 2px; }
.us-nav-group-summary {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--ink); font-weight: 700; font-size: var(--fs-small);
  cursor: pointer; list-style: none; user-select: none;
}
.us-nav-group-summary::-webkit-details-marker { display: none; }
.us-nav-group-summary::marker { content: ''; }
.us-nav-group-summary:hover { background: var(--surface-2); }
/* The keyboard ring is the SUMMARY's, not a child's: <summary> is the focusable element, and a
   ring drawn on the label would sit inside the control that actually has focus. */
.us-nav-group-summary:focus-visible { outline: 2px solid var(--brand-strong); outline-offset: 2px; }
.us-nav-group-label { flex: 1 1 auto; }

.us-nav-icon { flex: 0 0 auto; opacity: .72; }
.us-nav-group-summary:hover .us-nav-icon,
.us-nav-group[open] > .us-nav-group-summary .us-nav-icon { opacity: 1; }

.us-nav-chevron { transition: transform var(--dur-2, 160ms) var(--ease-standard, ease); }
.us-nav-group[open] > .us-nav-group-summary .us-nav-chevron { transform: rotate(180deg); }

/* The links inside a group are indented to the group icon's text column, so the hierarchy reads as
   a hierarchy rather than as two lists that happen to be adjacent. */
.us-nav-group > .us-nav-list { padding-left: calc(18px + var(--space-3)); margin-top: 2px; }

/* A group holding the current page stays open (the `open` attribute is set from the active route),
   and its heading is marked so the eye can find where it is without expanding anything else. */
.us-nav-group[open] > .us-nav-group-summary { color: var(--brand-strong); }

@media (prefers-reduced-motion: reduce) {
  .us-nav-chevron { transition: none; }
}
/* `.us-pill` — a state chip, and `.us-pill-danger` its tone, following this estate's own modifier
   pattern (`.us-btn` + `.us-btn-primary`, and the staff console's `.us-sev` + `.us-sev-4`).
   The BASE carries geometry only, so the generic name stays generic: the next person who wants a
   neutral chip gets one instead of a danger chip with a misleading name. That matters here — this app
   grows classes by analogy, which is how `.us-num` reached 398 unstyled elements.
   It had no rule at all, so the sole site — the "revoked" marker on the share-link list
   (screens/extras.tsx) — rendered as bare text beside the `.us-meta` expiry line it needs to be
   distinguishable FROM. It is the only signal a share link is dead: the Revoke button simply
   disappears once it has been used.
   NOT `.us-badge`, which is a COUNT pill (min-width, tabular-nums, its own left margin that would
   double this flex row's gap on one side) in the affirmative brand gold — wrong shape and wrong tone
   for a dead link. Geometry mirrors the staff console's `.us-state-danger` and this app's own
   `CHIP_STALE` (screens/assistant.tsx).
   CONTRAST, computed from the real tokens: `--danger` on `--danger-bg` is 5.58:1 in light and 7.57:1
   in dark — AA for normal text in both themes, which a tone-on-surface chip would also pass but with
   less separation from the metadata beside it. */
.us-pill { display: inline-block; padding: 1px var(--space-2); border-radius: var(--radius-pill);
  font-size: var(--fs-small); font-weight: 700; letter-spacing: .02em;
  background: var(--surface-2); color: var(--ink-muted); border: 1px solid var(--line-strong); }
.us-pill-danger { background: var(--danger-bg); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 35%, transparent); }

.us-nav-deferred-tag { margin-left: auto; font-size: 10px; font-weight: 700; color: var(--ink-subtle); border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 1px var(--space-2); }

/* ---- List (P-LIST) states ---- */
.us-skeleton { background: linear-gradient(90deg, var(--surface-2), var(--surface), var(--surface-2)); background-size: 200% 100%; animation: us-shimmer 1.2s infinite linear; border-radius: var(--radius-sm); height: 14px; }
@keyframes us-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.us-empty, .us-error-state { text-align: center; padding: var(--space-8) var(--space-4); color: var(--ink-muted); }
.us-empty h3 { color: var(--ink); }
.us-table { width: 100%; border-collapse: collapse; }
.us-table th, .us-table td { text-align: left; padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--line); font-size: var(--fs-small); }
.us-table th { color: var(--ink-subtle); font-weight: 600; }
.us-table td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }

/* ---- Login / centered auth ---- */
.us-auth { min-height: 100vh; display: grid; place-items: center; padding: var(--space-5); }
/* CLEAR OF THE CORNER'S ROW (G3-REGRESSION-01). The Help & feedback circle is fixed in the bottom-right corner
   on these screens too (sign-in, sign-up, two-step, the e-mail landings), and on a narrow phone in Polish or
   Japanese the card's last row — the language and theme pickers — ran under its 44 px target. The page keeps
   the frame's clearance below the card (.us-frame-main): the row's gutter, the circle's target and a gap, so a
   card taller than the window scrolls its last control clear of the circle, and one that fits never meets it. */
.us-auth { padding-bottom: calc(var(--space-5) + 44px + var(--space-4) + env(safe-area-inset-bottom)); }
.us-auth-card { width: 100%; max-width: 380px; }
.us-auth-brand { display: flex; justify-content: center; margin-bottom: var(--space-5); font-size: var(--fs-h3); }

/* ---- Toast (polite live region) ---- */
.us-toasts { position: fixed; bottom: var(--space-4); right: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); z-index: 60; }
.us-toast { background: var(--surface); border: 1px solid var(--line); box-shadow: var(--elev-2); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); font-size: var(--fs-small); }

/* ---- Dialog (P-DOCFORM conflict, etc.) ---- */
.us-dialog-backdrop { position: fixed; inset: 0; background: var(--overlay); display: grid; place-items: center; padding: var(--space-4); z-index: 80; }
.us-dialog { background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--elev-3); padding: var(--space-5); max-width: 440px; width: 100%; }

.us-row { display: flex; gap: var(--space-3); align-items: center; }
.us-stack { display: flex; flex-direction: column; gap: var(--space-3); }
.us-muted { color: var(--ink-muted); }
/* `.us-num` — used on 398 elements in this app and defined on NONE of them until now. The staff
   console has carried this rule since its charts landed; this app grew the same class by analogy and
   never got the rule, so every figure that asked for tabular numerals rendered proportional ones and
   columns of numbers did not line up. Nothing could have reported it: an unstyled class is valid
   markup, valid TypeScript, and accessible. Mirrors apps/us-staff-web/src/tokens.css. */
/* …AND A FIGURE NEVER BREAKS ACROSS LINES. Polish, French and German group digits with a space
   (`1 234 567,50`, `1 234,50 PLN`), and a space is a line-break opportunity: in a narrow cell a Polish
   amount split into `1 234` over `567,50`, which reads as two numbers. The currency code stays on the
   figure's line for the same reason. `.us-num-wrap` is for the few `.us-num` values that are
   identifiers rather than figures (an IBAN in a detail row, a principal id), which may still wrap
   at their own spaces or hyphens on a phone. */
.us-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* …BUT A COLUMN HEADER IS WORDS, NOT A FIGURE. Some sixty `<th class="us-num">` (ledger, statutory,
   receivables, fiscal, accounting) mark a numeric COLUMN, and `nowrap` reached them with the class:
   "Wartość netto w dacie rozchodu" became one unbreakable line, and the longer German and French
   headers more so, widening the whole table past a phone and a printed page. The rule is for the
   DATA cells and inline figures; a header wraps between its words. Higher specificity than
   `.us-num` (0,1,1 over 0,1,0), so it wins wherever it sits. */
th.us-num { white-space: normal; }
.us-num-wrap { white-space: normal; overflow-wrap: anywhere; }
.us-meta { color: var(--ink-subtle); font-size: var(--fs-small); }

/* ---- THE CONTRAST HARDENING THIS BLOCK USED TO BE, AND WHY IT IS GONE --------------------------
   Until the Lemon Berry palette landed, `--brand` was gold `#b9791a`: white on it is **3.61:1**, so
   a primary button, a bare table link and a badge all failed the 4.5:1 AA floor for normal text.
   The remedy was to fill them with the DARKER `--brand-strong` and fake a hover with
   `filter: brightness(0.9)` — which worked, and cost the palette its hover step: the resting state
   had already spent it.

   Berry does not need the workaround. White on `#A32142` is **7.36:1**, and the kit ships a real
   three-step ramp — primary / hover / pressed — that holds in both themes:

       light   #FFFFFF on #A32142  7.36:1   hover #8A1A38  9.19:1   pressed #71132D
       dark    #2A1420 on #F28AA5  7.34:1   hover #F6A1B6  8.77:1   pressed #E87394

   So the resting fill goes back to `--brand`, hover and pressed become the tokens they always
   should have been, and `filter` stops being a colour decision. Bare links get the same: `--brand`
   on `--surface` is 7.36:1 where the gold was 3.61:1 — the failure the old block measured against
   real receivables and purchases rows is arithmetically absent, not merely re-tuned.

   The one rule kept from the old block is the ACTIVE NAV LABEL, and it is kept because the reason
   changed rather than disappeared: the selected navigation item is now lemon `--nav-active` with
   `--on-nav-active` plum on it (10.56:1), which is the kit's own pairing, so the label takes the
   nav's token and not the page's `--ink`. `tests/unit/palette-meets-its-own-contrast-floor.test.ts`
   drives all of these out of this file, so the claim above is checked rather than asserted. */
.us-btn-primary { background: var(--brand); color: var(--on-brand); }
.us-btn-primary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--brand-strong); }
.us-btn-primary:active:not(:disabled):not([aria-disabled="true"]) { background: var(--brand-pressed); }
a { color: var(--brand); }
a:hover { color: var(--brand-strong); }
.us-badge { background: var(--brand); color: var(--on-brand); }

/* ---- THE NAVIGATION SHELL IS A BRAND SURFACE (kit: "Navigation") -------------------------------
   Plum ground, light text, plum-hover, and the SELECTED item in lemon with plum on it. The active
   item carries `aria-current="page"` already; this gives it the visual cue the kit prescribes, and
   the pairing is the kit's 10.56:1 one rather than a tint of the page palette. */
/* THE SHELL. `.us-frame-header` and `.us-frame-nav` are ONE brand surface in plum, in BOTH themes —
   the kit is explicit that navigation is a brand surface and does not invert with the page.

   THE HALF-APPLIED VERSION OF THIS CHANGE IS THE DANGEROUS ONE. Repainting the two containers and
   leaving their CONTENTS on page tokens gives plum-on-plum text: an invisible org name, an invisible
   ☰ on a phone, an unreadable locale select. This estate has shipped exactly that shape before — a
   nav toggle with no rule and an inline `display:none`, 102 links unreachable on a phone, live, past
   a green axe run. So every control that can appear in the shell is repainted here, by name, and the
   list below is the complete inventory of what `AppFrame` renders into it:
   the ☰ toggle (.us-btn-ghost), the wordmark link, the org label (.us-meta), the notification bell
   and its badge, the locale and theme selects (.us-input inside .us-utility), and sign-out
   (.us-btn-secondary). Adding a control to the header means adding it here. */
.us-frame-header,
.us-frame-nav {
  background: var(--nav);
  color: var(--on-nav);
  border-color: color-mix(in srgb, var(--on-nav) 16%, transparent);
}
.us-frame-header { box-shadow: none; }

/* Text and links directly on the shell. */
.us-frame-header .us-meta,
.us-frame-nav .us-meta { color: var(--nav-muted); }
.us-frame-header a { color: var(--on-nav); }
.us-frame-header a:hover { color: var(--nav-active); }

/* Every button that sits on the shell: ghost and secondary both become outlined-on-plum, because a
   `--surface` fill would be a white chip on a plum bar and a bare `--ink` label would vanish. */
.us-frame-header .us-btn-ghost,
.us-frame-header .us-btn-secondary {
  background: transparent;
  color: var(--on-nav);
  border-color: color-mix(in srgb, var(--on-nav) 38%, transparent);
}
.us-frame-header .us-btn-ghost:hover:not(:disabled):not([aria-disabled="true"]),
.us-frame-header .us-btn-secondary:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--nav-hover); color: var(--on-nav); }

/* The locale and theme selects. `.us-input` is a light form control by default; on plum it needs the
   shell's own ground, its own outline and an arrow the browser draws in the right colour — which is
   what `color-scheme: dark` on the control does, since the shell is dark in BOTH page themes. */
.us-frame-header .us-input {
  background: var(--nav-hover);
  color: var(--on-nav);
  border-color: color-mix(in srgb, var(--on-nav) 38%, transparent);
  color-scheme: dark;
}
.us-frame-header .us-input:hover { border-color: color-mix(in srgb, var(--on-nav) 60%, transparent); }

/* The notification badge. `.us-badge` is berry-on-white elsewhere; on plum the kit's own emphasis
   colour is lemon, and plum-on-lemon is the checked 10.56:1 pair. */
.us-frame-header .us-badge { background: var(--nav-active); color: var(--on-nav-active); }

/* The nav list itself. */
.us-frame-nav .us-nav-item { color: var(--nav-muted); }
.us-frame-nav .us-nav-item:hover { background: var(--nav-hover); color: var(--on-nav); }
.us-frame-nav .us-nav-item[aria-current="page"] { background: var(--nav-active); color: var(--on-nav-active); font-weight: 700; }
.us-frame-nav .us-nav-item[aria-disabled="true"] { color: color-mix(in srgb, var(--nav-muted) 62%, transparent); }
.us-frame-nav .us-nav-group-summary { color: var(--on-nav); }
.us-frame-nav .us-nav-group-summary:hover { background: var(--nav-hover); }
.us-frame-nav .us-nav-group[open] > .us-nav-group-summary { color: var(--nav-active); }
.us-frame-nav .us-nav-group-summary:focus-visible { outline-color: var(--nav-active); }
.us-frame-nav .us-nav-deferred-tag { color: var(--nav-muted); border-color: color-mix(in srgb, var(--on-nav) 32%, transparent); }

/* FOCUS ON A COLOURED SURFACE NEEDS ITS OWN RING. The page's focus colour is a mid blue in light
   mode — 1.6:1 on plum, i.e. invisible exactly where a keyboard user is navigating. Lemon on plum is
   10.56:1, and the surface-coloured offset the kit asks for is what keeps the ring readable where it
   crosses a lemon active item. */
.us-frame-header :focus-visible,
.us-frame-nav :focus-visible { outline: 2px solid var(--nav-active); outline-offset: 2px; box-shadow: 0 0 0 4px var(--nav); }

/* ================================================================================================
   Channel chart kit v2 (scene-based); the staff console keeps kit v1. Colour is proven by
   chart-palette-is-distinguishable.test.ts and palette-meets-its-own-contrast-floor.test.ts.

   THE RULES IN THIS BLOCK ARE KIT V1's, the copy of the staff console's kit this app started from.
   They stay only while a v1 component still renders (`BarList`, `ColumnChart`, the v1 `Donut`,
   `Sparkline` and `Kpi`, all `@deprecated`) and go in the change that deletes the last of them. Do
   not build on them. The v2 rules follow the tile system below, and the staff console's copy is a
   different kit on purpose now, so nothing here is kept "in step" with it any more.
   ================================================================================================ */

/* KIT V1 IS GONE (decisions §1): `BarList`, `ColumnChart`, the v1 sparkline, donut and `Kpi` were
   deleted with nothing importing them, and their rules with them — the bar list, the stretched-viewBox
   SVGs, the v1 donut legend and the v1 KPI box. The report's `KpiCard` keeps its own rules below. */

/* ================================================================================================
   THE CATEGORY-DASHBOARD TILE (docs/architecture/category-dashboards.md §2.2).

   One grid, one tile, one report view — for thirteen screens. The grid is a class, not an inline
   string: thirteen screens copying a `grid-template-columns` literal is how this estate grew 398
   `.us-num` elements with no rule behind them.

   A TILE IS A FIVE-ROW GRID — head / figure / sub / viz / foot — and every row says what happens when
   its text is longer than the column, which is the whole answer to "text beyond the tile":
     - the figure is SIZED to fit, by measurement (`fitFigure`: 28 / 24 / 20 / 17 px), and a number is
       never cut with an ellipsis; below 17 px the unit wraps under it;
     - any state word is a chip, which wraps as a tidy two-line box instead of a 28 px sentence
       (`books.kpir` drew "Prowadzona jest inna księga" 145 px past its card);
     - the title and the sub-line are clamped to two lines, with the full text in `title`;
     - a bar's label has a line of its own, so it is not squeezed into a 110 px column.
   The viz row takes the slack (1fr) and the cards stretch to the row's height, so the feet line up
   across a row. Every row is placed by NUMBER, not by source order: a tile with no sub-line must not
   pull its chart up into row 3 and leave the slack between the chart and the foot.

   `min(100%, 264px)` so a phone gets one column and never a sideways scroll; `auto-fill` rather than
   `auto-fit` so a dashboard with two tiles does not stretch them across the page.
   ================================================================================================ */
.us-tilegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 264px), 1fr)); gap: var(--space-4); align-items: stretch; }

/* THE CARD'S 20 PX SIDES ARE SPLIT 12 + 8, and the rendered tile is the same 20 px inset. The Details
   button is a ghost button whose TEXT aligns with the tile's text, so its hover background reaches
   8 px left of that edge. With the whole 20 px on the card, that 8 px lies outside `.us-tile` — which
   is exactly what the visual check's V2 ("nothing escapes the tile") measures. Giving the tile 8 px
   of its own padding keeps the look and keeps the measurement honest. */
.us-card.us-tile-card { padding: 18px 12px 14px; height: 100%; min-width: 0; }
.us-tile { display: grid; grid-template-rows: auto auto auto 1fr auto; row-gap: 6px; height: 100%; min-width: 0;
  padding: 0 8px; container-type: inline-size; }

/* Row 1 — the head. The badge wraps to its own line before the title is squeezed to a third one:
   the title keeps 12ch, the badge never shrinks. */
.us-tile-head { grid-row: 1; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 4px 8px; min-width: 0; }
/* The tile heading is an h2 and must NOT be h2-sized: at --fs-h2 it would outweigh the figure it
   labels, and the figure is what the reader came for. `letter-spacing` is reset because the h2's
   tight tracking is tuned for display sizes and crowds a 14 px label (and kanji). */
.us-tile-title { flex: 1 1 12ch; min-width: 0; margin: 0; font-size: 14px; line-height: 1.3; font-weight: 700;
  letter-spacing: normal; color: var(--ink-muted); overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.us-tile-badges { margin-left: auto; flex: 0 0 auto; display: flex; align-items: center; gap: 4px; }
/* THE BADGE ON ITS OWN LINE — set by `Tile` when the kit's text measure says the title would need a
   third line beside it (visual spec §3: "the title is never squeezed to three lines"). */
.us-tile-head-stack .us-tile-title { flex-basis: 100%; }
.us-tile-head-stack .us-tile-badges { margin-left: 0; }
/* THE HEAD RESERVES TWO TITLE LINES WHEREVER TILES SIT SIDE BY SIDE, so every headline figure in a row
   starts at one height. Each tile is its own grid, and a one-line title put its figure 18 px above
   its neighbour's under a two-line title (69 of 112 multi-tile rows at 1440 px). Phones — one column —
   keep the compact head. */
@media (min-width: 721px) {
  .us-tile-head { min-height: calc(2 * 1.3 * 14px); }
  /* …and two title lines PLUS a badge line (4 px gap, an 18.5 px badge) in a grid where one tile's badge
     had to wrap under its title (`TileGrid`), so that tile's figure does not sit a line below the rest. */
  .us-tilegrid-tall-heads .us-tile-head { min-height: calc(2 * 1.3 * 14px + 23px); }
  /* JAPANESE LINES ARE TALLER THAN 1.3 em. A kanji face and the Latin face beside it share each line box
     on their own baselines, so a two-line Japanese title measured 41 px against the 36.4 px reserved
     and pushed its figure 4 px below its row's (purchases `cost_unbooked` beside its scope tag). The
     title is set at a line height that holds both faces, and the head reserves exactly that. */
  :lang(ja) .us-tile-title { line-height: 1.5; }
  :lang(ja) .us-tile-head { min-height: calc(2 * 1.5 * 14px); }
  :lang(ja) .us-tilegrid-tall-heads .us-tile-head { min-height: calc(2 * 1.5 * 14px + 23px); }
}
/* The LIVE marker and the scope tag ("Today", or the chosen month). The WORD carries the meaning;
   the dot is aria-hidden decoration. --ok on --ok-bg and --ink-muted on --surface-2 are checked
   pairs in both themes. */
.us-live { display: inline-flex; align-items: center; gap: 5px; padding: 1px 7px; border-radius: var(--radius-pill);
  background: var(--ok-bg); color: var(--ok); font-size: 11px; font-weight: 700; line-height: 1.5; white-space: nowrap; }
.us-live-dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--ok); }
.us-tile-scope { display: inline-flex; align-items: center; padding: 1px 7px; border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--ink-muted); font-size: 11px; font-weight: 700; line-height: 1.5; white-space: nowrap; }

/* Row 2 — the figure. The size class comes from `fitFigure` against the measured column; the rule
   here is only the default. A figure is read, not compared down a column, so its digits are
   PROPORTIONAL — which has to be said on the `.us-num` inside it too, or that class's tabular
   numerals win. */
.us-tile-figure { grid-row: 2; display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 6px; min-width: 0;
  font-size: 28px; font-weight: 700; line-height: 1.15; color: var(--ink); font-variant-numeric: normal; }
.us-tile-figure .us-num { font-variant-numeric: normal; }
.us-fig-xl { font-size: 28px; }
.us-fig-lg { font-size: 24px; }
.us-fig-md { font-size: 20px; }
.us-fig-sm { font-size: 17px; }
.us-tile-unit { font-size: var(--fs-small); font-weight: 600; color: var(--ink-subtle); }
/* Tone on a FIGURE, and every tone sits beside a word (§2.5: nothing encodes meaning by colour
   alone). The brand tone means NEUTRAL: --ink, never a berry — berry is 1.12:1 from --danger, which
   is why a neutral figure used to read as an alarm. */
.us-tile-ok     { color: var(--ok); }
.us-tile-warn   { color: var(--warn); }
.us-tile-danger { color: var(--danger); }
.us-tile-brand  { color: var(--ink); }
/* A STATE IS A CHIP, whatever the tile's visual. An 8 px radius, not a pill's 999 px, because a chip
   that wraps must stay a tidy box — a two-line pill is a lozenge with its text pressed into the
   ends. `.us-pill` gives the border; each tone gives the border its colour at 35 %. */
.us-tile-figure .us-pill { max-width: 100%; padding: 3px 10px; border-radius: 8px; font-size: var(--fs-small); font-weight: 700;
  line-height: 1.35; letter-spacing: normal; white-space: normal; overflow-wrap: anywhere; }

/* Row 3 — the supporting line, clamped to THREE lines. Two could not hold the spec's own 90-character
   sentence at 13 px in a 212–232 px column (about 35 characters a line), and the cut landed inside
   figures and units ("17.827,40 PL…") while the stretched card below it stood empty; 62 of the 67
   clamped sub-lines measured across five languages need exactly three. */
.us-tile-sub { grid-row: 3; margin: 0; min-width: 0; font-size: var(--fs-small); line-height: 1.4; color: var(--ink-muted);
  overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
/* A FIGURE OR A DATE NEVER BREAKS ACROSS TWO LINES (`keepWhole` in patterns.tsx): a Polish amount is
   grouped with plain spaces, and "5 970" / "453,69" on two lines reads as two numbers. */
.us-nowrap { white-space: nowrap; }

/* Row 4 — the visual. It holds its height only on the three visuals that draw something, so a
   FIGURE or STATE tile does not carry 76 px of nothing. A LIVE tile's as-of line sits last in it.
   THE VISUAL FOLLOWS THE SUB-LINE. The row takes the slack (1fr) so the feet of a row line up, and
   with its content pushed to the END the chart sat on the foot and left a tall empty band between the
   figure and the picture of it ("Owed to you", "Cash" at 1440 px). The content now starts at the top
   of the row; the slack sits under it, above the foot. */
.us-tile-viz { grid-row: 4; display: flex; flex-direction: column; justify-content: flex-start; gap: 6px; min-width: 0; min-height: 0; }
.us-tile[data-visual="SHARE"] .us-tile-viz,
.us-tile[data-visual="TREND"] .us-tile-viz,
.us-tile[data-visual="BREAKDOWN"] .us-tile-viz { min-height: 76px; }
.us-tile-viz [data-us^="tile-asof-"] { margin: 0; font-size: var(--fs-caption); color: var(--ink-subtle); }

/* Row 5 — the foot. The Details control is a ghost button that must not look like a primary action,
   and its text lines up with the tile's own text (see the card's padding above). */
.us-tile-foot { grid-row: 5; display: flex; flex-wrap: wrap; align-items: center; gap: 12px; min-width: 0;
  margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--line); }
.us-tile-foot a { font-size: var(--fs-small); color: var(--ink-muted); }
.us-tile-deep { font-size: var(--fs-small); font-weight: 700; padding: 4px var(--space-2); margin-left: calc(var(--space-2) * -1); }

/* DEGRADED STATES — no access, not configured, not recorded, does not apply, could not load — sit at
   the TOP, across rows 2–4, as an icon and a sentence. They used to be the figure: a paragraph in the
   28 px slot. The reason a tile does not apply is clamped to three lines; the report repeats it. */
.us-tile-state { grid-row: 2 / 5; align-self: start; display: flex; align-items: flex-start; gap: var(--space-2); min-width: 0;
  font-size: var(--fs-small); line-height: 1.4; color: var(--ink-subtle); overflow-wrap: anywhere; }
.us-tile-state-icon { flex: 0 0 18px; width: 18px; height: 18px; display: inline-grid; place-items: center;
  border: 1.5px solid var(--ink-subtle); border-radius: 50%; color: var(--ink-subtle); font-size: 11px; font-weight: 700; line-height: 1; }
/* The glyph FILLS the ring: at 11 px the "!" of "Could not load" was a 2 px speck (a 1.8 px bar at a
   0.8 px stroke) while the lock read clearly. The state glyphs are drawn tall on their 24 grid
   (icons.tsx) and stroked heavier here. */
.us-tile-state-icon svg { width: 12px; height: 12px; opacity: 1; }
.us-tile-state-icon .us-state-glyph-bare { stroke-width: 2.75; }
/* Retry is a ghost button, and its TEXT lines up with the sentence above it — the button's own
   10 px padding indented it, so it read as loose bold text rather than a control under the sentence.
   The underline on hover and the focus ring say it is a control. */
.us-tile-state .us-btn { margin-top: var(--space-2); margin-left: -10px; padding: 6px 10px; font-size: var(--fs-small); }
.us-tile-state .us-tile-retry { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); }
.us-tile-state .us-tile-retry:hover { text-decoration-color: currentColor; }

/* LOADING HAS THE LOADED SHAPE: a figure bar, a sub-line bar and, on a drawing visual, a viz block —
   so `[data-visual]`'s viz height holds and nothing moves when the answer lands. */
.us-skeleton-figure { display: block; width: 55%; height: 28px; margin: 2px 0; }
.us-skeleton-line { display: block; width: 80%; height: 13px; margin-top: 3px; }
.us-skeleton-short { width: 55%; }
/* The viz block is about as tall as what each visual draws: a 64 px ring; a 60 px chart and its delta
   line; three bars (35 px each) and their note. */
.us-skeleton-viz { display: block; width: 100%; height: 64px; }
.us-tile[data-visual="TREND"] .us-skeleton-viz { height: 92px; }
.us-tile[data-visual="BREAKDOWN"] .us-skeleton-viz { height: 124px; }
/* A REFRESH OVER A HELD VALUE: the figure and its picture stay, dimmed, until the new answer lands
   (`aria-busy` says the same to assistive technology). The head and foot stay at full strength. */
.us-tile[aria-busy="true"] > .us-tile-figure,
.us-tile[aria-busy="true"] > .us-tile-sub,
.us-tile[aria-busy="true"] > .us-tile-viz { opacity: .55; transition: opacity var(--dur-2) linear; }
.us-clamp-3 { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }

/* SHARE — a 64 px ring meter and, beside it, three lines that make its percentage checkable:
   "17 of 52", then what the part is, then what the whole is. */
.us-share { display: grid; grid-template-columns: 64px minmax(0, 1fr); gap: 12px; align-items: center; min-width: 0; }
.us-share-caption { display: flex; flex-direction: column; min-width: 0; font-size: var(--fs-caption); line-height: 1.35; color: var(--ink-muted); }
.us-share-caption > * { margin: 0; overflow-wrap: anywhere; }
.us-share-fraction { font-size: 14px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
/* The part's and the whole's nouns: 12 / 1.35 `--ink-muted` (visual spec §3), one per line. */
.us-share-term { display: block; font-size: var(--fs-caption); line-height: 1.35; color: var(--ink-muted); }

/* BREAKDOWN — at most three compact bars (the chart kit draws them) and the note that says how many
   parts there are and how many are not drawn. */
.us-parts-note { margin: 0; font-size: var(--fs-caption); color: var(--ink-subtle); }

/* TREND — the delta under the sparkline: the direction and amount in bold, in the tone, then the
   period in muted text. The arrow is aria-hidden decoration; the words name the direction. Two lines
   at most, with the full sentence in `title`. The brand (neutral) tone is --ink on --ink-muted: a
   trend that is neither good nor bad is not coloured as either. */
.us-delta { margin: 0; min-width: 0; font-size: var(--fs-caption); line-height: 1.35; color: var(--ink-muted); overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.us-delta b { font-weight: 700; }
.us-delta-period { color: var(--ink-muted); font-weight: 400; }
.us-delta-ok b     { color: var(--ok); }
.us-delta-warn b   { color: var(--warn); }
.us-delta-danger b { color: var(--danger); }
.us-delta-brand { color: var(--ink-muted); }
.us-delta-brand b { color: var(--ink); }

/* STATE tones on the chip. `.us-pill` carries geometry and a `-danger` tone (extras.tsx's revoked
   marker); the other tones are added here rather than a second chip class being invented. Each
   tone/background pair is the one the banners use, so both themes hold. The brand chip is NEUTRAL. */
.us-pill-ok    { background: var(--ok-bg);    color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 35%, transparent); }
.us-pill-warn  { background: var(--warn-bg);  color: var(--warn); border-color: color-mix(in srgb, var(--warn) 35%, transparent); }
.us-pill-brand { background: var(--surface-2); color: var(--ink); border-color: var(--line-strong); }

/* A NARROW TILE (four columns at 1440 px leave about 234 px of content) tightens the two gaps that
   are sized for a wider one. The tile is its own container, so this follows the COLUMN, not the
   viewport — the same tile is narrow at 1440 and wide at 1024. */
@container (max-width: 240px) {
  .us-share { column-gap: 10px; }
  .us-tile-foot { column-gap: var(--space-2); }
}

.us-parts { display: flex; flex-direction: column; gap: var(--space-2); }

/* THE DEEP DIVE — A REGION, NOT A MODAL (§5).
   No backdrop, no focus trap, no fourth dialog implementation: the dashboard stays visible and
   operable behind it, `Escape` and Close clear `?tile=` and focus moves to the panel's heading on
   open. It is a card with a stronger edge so it reads as "opened from" the grid above it. */
.us-deep { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-lg);
  padding: var(--space-5); box-shadow: var(--elev-2); display: flex; flex-direction: column; gap: var(--space-3); }
.us-deep-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
/* The heading takes focus on open, so it must SHOW that it has it — and it must not show a ring when
   the panel was opened by a plain mouse click.

   THE FIRST ATTEMPT DID THE OPPOSITE. `outline: none` sat on the base rule, and the base rule and
   the global `:focus-visible { outline: 2px solid var(--focus) }` have the SAME specificity (0,1,0),
   so source order decided and the LATER `outline: none` won in every state — including
   `:focus-visible`. Driven: open the panel from the keyboard and the heading reports
   {"matchesFocusVisible":true,"outlineStyle":"none"} — it matches the rule and paints no ring. So a
   keyboard user's focus was moved silently to an invisible target (WCAG 2.4.7) and then silently
   dropped to <body> on close. The suppression is now SCOPED to the case it was written for: focused,
   but not focus-visible. `:not()` takes its argument's specificity, so this is (0,3,0) and it beats
   the global rule — in the one state where the global rule does not apply anyway. */
.us-deep-title { font-size: var(--fs-h3); font-weight: 700; margin: 0; }
.us-deep-title:focus:not(:focus-visible) { outline: none; }
.us-deep-section { display: flex; flex-direction: column; gap: var(--space-1); }
.us-deep-label { font-size: var(--fs-caption); color: var(--ink-muted); text-transform: uppercase; letter-spacing: .06em; }

/* ================================================================================================
   CHART KIT V2 (ui/chart-scene.ts lays a chart out as a scene; ui/charts.tsx paints it as SVG, and
   export/pdf-paint.ts paints the same scene as PDF).

   AN SVG IN REAL PIXELS. The painter writes `viewBox="0 0 W H"` with `width=W height=H` at the width
   it measured, so the box is never stretched and text can live inside the SVG. `max-width: 100%;
   height: auto` is only the safety net for the frame before the first measurement (SSR renders at a
   fallback width): the chart shrinks in proportion rather than overflowing its panel.

   THE PAINTER OWNS SIZE, WEIGHT AND COLOUR, as attributes (`font-size`, `font-weight`,
   `fill="var(--<paint>)"`), because the PDF painter has to know them too and CSS cannot reach it.
   These rules therefore never set `fill` or `font-size` on a mark or a text node — a class rule beats
   a presentation attribute, and would silently repaint what the scene decided. What the classes carry
   is what the scene cannot: the font family, the halo behind a value label, crisp hairlines, and the
   forced-colours mapping at the end of this block.

   COLOUR IS NEVER THE ONLY CARRIER: every series is named in a legend or a title, partial buckets are
   hatched, and every chart has a hidden digits table.
   ================================================================================================ */
.us-chart { position: relative; display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.us-chart-svg, .us-spark, .us-ring, .us-donut { display: block; max-width: 100%; height: auto; }
/* EXCEPT the status grid, which the kit lets SCROLL (months × kinds can outgrow a phone) rather than
   shrink its 26 × 20 cells and 11 px glyphs to unreadable: the wrapper scrolls, the SVG keeps its size. */
.us-status-grid { max-width: 100%; }
.us-status-grid .us-chart-svg { max-width: none; }
.us-ring, .us-donut { flex: none; }
.us-chart-empty { margin: 0; font-style: italic; font-size: var(--fs-small); color: var(--ink-subtle); }

/* The panel head: a title and, for two or more series, the legend. */
.us-chart-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 6px 16px;
  margin-bottom: 10px; min-width: 0; }
.us-chart-title { margin: 0; font-size: var(--fs-body); font-weight: 700; line-height: 1.3; letter-spacing: normal; color: var(--ink); overflow-wrap: anywhere; }

/* THE LEGEND IS REAL TEXT, and on a time chart with two or more series each entry is a toggle
   (`button[aria-pressed]`). A hidden series keeps its words — struck through and dimmed to a checked
   text colour — so the reader can still see what they hid; the swatch fades, the label stays legible. */
.us-chart-legend { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px;
  font-size: var(--fs-caption); line-height: 1.35; color: var(--ink-muted); }
.us-chart-legend-item { display: inline-flex; align-items: center; gap: 6px; min-width: 0; overflow-wrap: anywhere; }
.us-chart-legend button { display: inline-flex; align-items: center; gap: 6px; min-height: 24px; margin: -2px -4px; padding: 2px 4px;
  font: inherit; color: inherit; text-align: left; background: none; border: 0; border-radius: var(--radius-sm); cursor: pointer; }
.us-chart-legend button:hover:not(:disabled) { color: var(--ink); }
.us-chart-legend button:disabled { cursor: default; }
.us-chart-legend button[aria-pressed="false"] { color: var(--ink-subtle); text-decoration: line-through; }
.us-chart-legend button[aria-pressed="false"] .us-swatch,
.us-chart-legend button[aria-pressed="false"] .us-swatch-line { opacity: .35; }
/* Swatches. The paint arrives inline (`background: var(--chart-n)`, or the kit's stripes for the
   hatched "to date" key); --chart-1 is only the default. A hatch is mostly empty, so it gets a faint
   frame to read as a key rather than as three loose lines. */
.us-swatch { flex: none; display: inline-block; width: 10px; height: 10px; border-radius: 3px; background: var(--chart-1); }
.us-swatch-line { flex: none; display: inline-block; width: 14px; height: 2px; border-radius: 1px; background: var(--chart-1); }
.us-swatch-hatch { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink-subtle) 45%, transparent); }
/* A line's "to date" point is HOLLOW, so its key is a hollow dot, not a hatched block. */
.us-swatch-hollow { flex: none; display: inline-block; width: 8px; height: 8px; border-radius: 50%; border: 2px solid var(--chart-1); background: var(--surface); }
/* A status grid's key carries the cell's GLYPH, the cue that is not colour (WCAG 1.4.1). */
.us-swatch-glyph { width: 14px; height: 14px; border-radius: 3px; color: var(--surface); font-size: 10px; font-weight: 700; line-height: 14px; text-align: center; }
.us-swatch-glyph-track { color: var(--ink-muted); }

/* THE PLOT IS ONE TAB STOP. `role=group` with arrow keys between points; its keyboard ring is the
   global `:focus-visible` rule, not a second one. `pan-y` keeps a vertical page scroll on a phone
   while a horizontal drag moves the active point. */
.us-chart-plot { position: relative; min-width: 0; border-radius: var(--radius-sm); touch-action: pan-y; -webkit-tap-highlight-color: transparent; }

/* SVG text. The family only; the scene sets size, weight, colour and (for ticks and values) tabular
   numerals, inline. A value label (weight ≥ 600) gets a halo in the card colour, so a gridline or the
   comparison line passing behind it cannot cut through its digits. */
.us-chart-text { font-family: var(--font-sans); }
.us-chart-text-ink { fill: var(--ink); }
.us-chart-text-strong { paint-order: stroke; stroke: var(--surface); stroke-width: 3px; stroke-linejoin: round; }
.us-ring-label { font-family: var(--font-sans); font-weight: 700; fill: var(--ink); font-variant-numeric: normal; }
.us-donut-total { font-family: var(--font-sans); font-weight: 700; fill: var(--ink); font-variant-numeric: tabular-nums; }
/* Hairlines sit on the half pixel (the scene places them at y + 0.5); crispEdges keeps them one
   device pixel instead of two half-tone ones. */
.us-grd, .us-axs { shape-rendering: crispEdges; }

/* THE TOOLTIP is HTML inside the panel, placed by the chart (12 px right of the crosshair, flipped
   and clamped so it never leaves the panel) and read out by a polite live region. It never takes
   the pointer: it would steal the hover it is describing. */
.us-chart-tip { position: absolute; top: 0; left: 0; z-index: 5; min-width: 160px; max-width: min(280px, 100%); padding: 8px 10px;
  background: var(--surface); color: var(--ink-muted); border: 1px solid var(--line-strong); border-radius: 8px; box-shadow: var(--elev-2);
  font-size: var(--fs-caption); line-height: 1.4; pointer-events: none; }
.us-chart-tip[hidden] { display: none; }
.us-chart-tip > :first-child:not(.us-chart-tip-row) { display: block; margin: 0 0 4px; font-size: var(--fs-small); font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
.us-chart-tip > .us-meta { margin-top: 2px; font-size: var(--fs-caption); }
.us-chart-tip-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 8px; min-height: 18px; }
/* The key is the swatch itself (the paint inline; a line key overrides the size inline to 14 × 2). */
.us-chart-tip-key { display: inline-block; width: 10px; height: 10px; border-radius: 3px; background: var(--chart-1); }
.us-chart-tip-value { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }

/* A ring meter or a donut with a legend: side by side, and the legend drops below when the panel is
   narrower than 480 px (the panel is the container, so a half-width panel at 1440 px counts). */
.us-chart:has(> .us-donut-legend),
.us-chart:has(> .us-ring + .us-chart-legend) { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 12px 24px; }
.us-chart:has(> .us-ring + .us-chart-legend) > .us-chart-legend { flex: 1 1 200px; flex-direction: column; align-items: flex-start; gap: 6px;
  max-width: 28rem; font-size: var(--fs-small); color: var(--ink); }
/* A LEGEND KEEPS ITS FIGURES BESIDE ITS WORDS: capped at 28rem, so a full-width panel at 1440 px does
   not put "16 · 5 %" 700 px from "Not yet due". */
.us-donut-legend { list-style: none; margin: 0; padding: 0; flex: 1 1 220px; max-width: 28rem; min-width: 0; display: grid; gap: 8px;
  font-size: var(--fs-small); line-height: 1.35; color: var(--ink); }
.us-donut-legend li { display: grid; grid-template-columns: 12px minmax(0, 1fr) auto 44px; align-items: center; gap: 8px; }
.us-donut-legend li > :nth-child(2) { min-width: 0; overflow-wrap: anywhere; }
.us-donut-legend li > :nth-child(3) { font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.us-donut-legend li > :nth-child(4) { color: var(--ink-subtle); text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
@container us-panel (max-width: 479px) {
  .us-chart:has(> .us-donut-legend),
  .us-chart:has(> .us-ring + .us-chart-legend) { flex-direction: column; align-items: flex-start; }
  .us-donut-legend, .us-chart:has(> .us-ring + .us-chart-legend) > .us-chart-legend { flex: none; width: 100%; max-width: 28rem; }
}

/* THE KPI CARD (report). Label, value, optional sub — and no uppercase or tracking on the label,
   which breaks German compounds and does nothing for Japanese. The value is fitted by `fitFigure`
   like a tile figure, and it is proportional for the same reason. It takes no colour of its own, so
   a declared tone class on it wins. */
.us-kpi-card { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 12px 14px; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius-md); }
.us-kpi-card .us-kpi-label { font-size: var(--fs-caption); font-weight: 700; line-height: 1.35; color: var(--ink-muted);
  text-transform: none; letter-spacing: normal; overflow-wrap: anywhere; }
.us-kpi-card .us-kpi-value { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 6px; min-width: 0;
  font-size: 22px; font-weight: 700; line-height: 1.2; font-variant-numeric: normal; }
.us-kpi-card .us-kpi-value .us-num { font-variant-numeric: normal; }
/* A WORD-VALUED KEY FIGURE WRAPS (two lines at most) instead of being clipped on one. */
.us-kpi-card .us-kpi-value .us-kpi-words { white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.us-kpi-card .us-kpi-sub { font-size: var(--fs-caption); line-height: 1.35; color: var(--ink-subtle); overflow-wrap: anywhere; }

/* ================================================================================================
   THE REPORT VIEW (deep dive v2). It opens IN PLACE of the tile grid, under the unchanged page head:
   axes, labels and a table need the full content width, and the old panel under the whole grid was
   "far from the tile". Order: head, controls, KPI strip, charts, table, "All figures", "What this
   does not include".

   A refetch keeps the frame: the panels drop to 55 % with `aria-busy` and nothing moves — no
   skeleton after the first paint, no layout jump.
   ================================================================================================ */
.us-report { container: us-report / inline-size; display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.us-report[aria-busy="true"] .us-report-panel { opacity: .55; }
.us-report-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: var(--space-4); }
.us-report-titles { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; flex: 1 1 20rem; min-width: 0; }
.us-report-back { display: inline-flex; align-items: center; gap: 4px; min-height: 24px; margin: 0 0 2px; padding: 2px 0;
  font: inherit; font-size: var(--fs-small); font-weight: 700; line-height: 1.3; color: var(--ink-muted); text-align: left;
  background: none; border: 0; cursor: pointer; }
.us-report-back:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; }
.us-report .us-deep-title { overflow-wrap: anywhere; }
.us-report-scope { margin: 0; font-size: var(--fs-small); line-height: 1.4; color: var(--ink-subtle); overflow-wrap: anywhere; }
.us-report-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }

/* The controls: each one a caption above a native control. */
.us-report-toolbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 16px; min-width: 0; padding: 12px 14px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); }
.us-ctl { display: flex; flex-direction: column; gap: 4px; min-width: 0; max-width: 100%; }
.us-ctl > span:first-child { font-size: var(--fs-caption); font-weight: 700; line-height: 1.35; color: var(--ink-muted); }
.us-ctl .us-input { width: auto; max-width: 100%; padding: 8px 10px; font-size: 14px; }

/* THE SEGMENTED CONTROL IS A FIELDSET OF RADIOS, drawn as joined segments. The radio covers its whole
   segment at zero opacity, so the segment is the hit area and the native control keeps the keyboard
   (arrows move the choice) and the semantics. The checked segment is --surface on --ink. Its
   keyboard ring is drawn on the SEGMENT, because an outline on a transparent input is transparent. */
.us-seg { display: inline-flex; flex-wrap: wrap; min-width: 0; margin: 0; padding: 0; border: 0; }
.us-seg > legend { margin: 0 0 4px; padding: 0; font-size: var(--fs-caption); font-weight: 700; line-height: 1.35; color: var(--ink-muted); }
.us-seg > label { position: relative; display: inline-flex; align-items: center; min-height: 34px; margin: 0 0 0 -1px; padding: 6px 12px;
  font-size: var(--fs-small); font-weight: 600; line-height: 1.2; white-space: nowrap; color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); cursor: pointer; }
.us-seg > label:first-of-type { margin-left: 0; border-radius: var(--radius-md) 0 0 var(--radius-md); }
.us-seg > label:last-of-type { border-radius: 0 var(--radius-md) var(--radius-md) 0; }
.us-seg > label:only-of-type { border-radius: var(--radius-md); }
.us-seg > label:hover { background: var(--surface-2); }
.us-seg input[type="radio"] { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.us-seg > label:has(> input:checked) { z-index: 1; color: var(--surface); background: var(--ink); border-color: var(--ink); }
.us-seg > label:has(> input:focus-visible) { z-index: 2; outline: 2px solid var(--focus); outline-offset: 2px; }
.us-seg > label:has(> input:disabled) { color: var(--on-disabled); background: var(--disabled); cursor: not-allowed; }

/* Filter chips. The remove control is a 24 × 24 hit area (WCAG 2.5.8) around a small glyph. */
.us-report-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; min-width: 0; }
.us-chip { display: inline-flex; align-items: center; gap: 2px; max-width: 100%; min-height: 28px; padding: 1px 2px 1px 10px;
  font-size: var(--fs-caption); font-weight: 600; line-height: 1.35; color: var(--ink); overflow-wrap: anywhere;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-pill); }
.us-chip:not(:has(.us-chip-x)) { padding-right: 10px; }
.us-chip-x { flex: none; display: inline-grid; place-items: center; width: 24px; height: 24px; margin: 0; padding: 0;
  font: inherit; font-size: 14px; line-height: 1; color: var(--ink-muted); background: none; border: 0; border-radius: 50%; cursor: pointer; }
.us-chip-x:hover { color: var(--ink); background: var(--line); }

/* KPI strip, then the charts: one column, two once the report itself is 900 px wide. The report is
   the container rather than the viewport, so the nav drawer's width is already accounted for. */
.us-report-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); gap: 12px; }
/* The strip's caption: what the cards are OF when the range above them does not move them. */
.us-report-kpiblock { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.us-report-kpinote { margin: 0; font-size: var(--fs-caption); line-height: 1.35; color: var(--ink-subtle); overflow-wrap: break-word; }
.us-report-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); align-items: start; }
@container us-report (min-width: 900px) {
  .us-report-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.us-report-panel { container: us-panel / inline-size; position: relative; min-width: 0; margin: 0; padding: 16px 18px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); }
.us-report-panel-wide { grid-column: 1 / -1; }
/* A section's scope sentence (a snapshot among ranged sections, a filter it cannot follow). */
.us-report-note { margin: 0 0 8px; font-size: var(--fs-caption); line-height: 1.35; color: var(--ink-subtle); overflow-wrap: break-word; }
.us-report-section-error { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: 0;
  padding: var(--space-3) 0; font-size: var(--fs-small); color: var(--ink-subtle); }

/* THE REPORT TABLE. Its own class, never `.us-table`: the harness counts `table.us-table` inside the
   report as the "All figures" facts. The wrap scrolls sideways inside itself, so a wide table never
   widens the page; it is a focusable region so a keyboard can scroll it. */
/* A TABLE THAT SCROLLS SAYS SO. On a phone the overlay scrollbar is hidden, so a column cut at the
   panel's edge read as a whole value ("114,401.7"). The wrap carries an edge shadow on the side that
   has more to show — two `scroll`-attached shadows under two `local`-attached covers in the surface
   colour, which slide away as the table reaches that end — and the first column stays put. */
.us-report-tablewrap { max-width: 100%; overflow-x: auto; border-radius: var(--radius-sm);
  background:
    linear-gradient(to right, var(--surface) 50%, transparent) left center / 40px 100% no-repeat local,
    linear-gradient(to left, var(--surface) 50%, transparent) right center / 40px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, color-mix(in srgb, var(--ink) 24%, transparent), transparent) left center / 14px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, color-mix(in srgb, var(--ink) 24%, transparent), transparent) right center / 14px 100% no-repeat scroll;
  background-color: var(--surface); }
.us-rtable { width: 100%; border-collapse: collapse; font-size: var(--fs-small); line-height: 1.4; }
.us-rtable th { padding: 8px 10px; text-align: left; vertical-align: bottom; white-space: nowrap; font-size: var(--fs-caption); font-weight: 700;
  color: var(--ink-muted); border-bottom: 1px solid var(--line-strong); }
.us-rtable th.n { text-align: right; }
.us-rtable td { padding: 8px 10px; vertical-align: top; color: var(--ink); border-bottom: 1px solid var(--line); }
.us-rtable td.n { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.us-rtable td.d { white-space: nowrap; font-variant-numeric: tabular-nums; }
/* `break-word`, not `anywhere`: `anywhere` drops a column's min-content to one glyph, so an auto table
   squeezed its text column first and broke every month name mid-word ("paździ|ernik", "2025年11|月").
   `break-word` still breaks a word too long for the whole table, and the table scrolls in its wrap. */
.us-rtable td.t { overflow-wrap: break-word; }
.us-rtable tfoot td, .us-rtable-total td, .us-rtable-total th { font-weight: 700; border-top: 1px solid var(--line-strong); border-bottom: 0; }
/* The first column is the row's name: it stays in view while the figures scroll under it. */
.us-rtable tr > :first-child { position: sticky; left: 0; z-index: 1; background: var(--surface); }
.us-rtable tr.is-active td { background: var(--surface-2); }
/* A header that does not sort sits on the same line as one that does: the sort button's 24 px. */
.us-rtable th:not([aria-sort]) { line-height: 24px; }
@container us-panel (max-width: 479px) {
  /* A date range ("2026-09-26 – 2026-10-02") may wrap at its dash in a phone's panel. */
  .us-rtable td.d { white-space: normal; }
  /* PRIORITY 2 IS "SHOWN FROM TABLET WIDTH" (TableColumn.priority): in a phone's panel it steps aside
     too, so the row's name and its key figure fit — the CSV, the PDF and a wider screen keep it. */
  .us-rtable .us-p2 { display: none; }
}
/* Sort headers: `th[aria-sort] > button`. The direction mark is drawn with borders, not a glyph in
   `content`, so it adds nothing to the button's accessible name — `aria-sort` already says it. */
.us-sort { display: inline-flex; align-items: center; gap: 4px; min-height: 24px; padding: 0; font: inherit; color: inherit;
  white-space: nowrap; background: none; border: 0; cursor: pointer; }
.us-sort:hover { color: var(--ink); }
th.n > .us-sort { flex-direction: row-reverse; }
th[aria-sort="ascending"] > .us-sort::after,
th[aria-sort="descending"] > .us-sort::after { content: ''; border: 4px solid transparent; }
th[aria-sort="ascending"] > .us-sort::after { border-top-width: 0; border-bottom: 5px solid currentColor; }
th[aria-sort="descending"] > .us-sort::after { border-bottom-width: 0; border-top: 5px solid currentColor; }
.us-report-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4);
  margin-top: var(--space-2); font-size: var(--fs-small); color: var(--ink-subtle); }
.us-report-foot label { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: 600; color: var(--ink); }
.us-report-foot .us-input { width: auto; padding: 6px 8px; font-size: 14px; }

/* "All figures" and "What this does not include": two collapsed disclosures. A sentence in the
   facts table gets a plain cell that wraps (a figure keeps `td.num`), which is what stops a long
   "Not tracked here — …" pushing the table past its panel. */
.us-disclosure.us-report-facts, .us-disclosure.us-report-notes { background: var(--surface); }
.us-disclosure.us-report-facts > summary, .us-disclosure.us-report-notes > summary { font-size: 14px; font-weight: 700; }
.us-report-facts .us-table td { overflow-wrap: anywhere; }
.us-report-facts .us-table td.num { overflow-wrap: normal; }
.us-report-notes p { margin: 0; max-width: 72ch; font-size: 14px; line-height: 1.55; color: var(--ink-muted); }

/* ================================================================================================
   THE DASHBOARD PAGE HEAD: title and actions, the intro, and the controls bar (the page period with
   previous/next steppers, the scope note, the as-of line). Export is a `details` menu.
   ================================================================================================ */
.us-dash-head { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; margin-bottom: var(--space-5); }
.us-dash-titlerow { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--space-2) var(--space-4); min-width: 0; }
.us-dash-titlerow h1 { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.us-dash-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.us-dash-intro { margin: 0; max-width: 72ch; font-size: var(--fs-small); line-height: 1.5; color: var(--ink-muted); }
.us-dash-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; min-width: 0; padding: 10px 12px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md); }
.us-dash-controls label { font-size: var(--fs-small); font-weight: 600; color: var(--ink); }
.us-dash-controls .us-input { width: auto; max-width: 100%; padding: 8px 10px; font-size: 14px; }
.us-dash-controls [data-us="dash-asof"] { margin-left: auto; }
.us-dash-scope { font-size: var(--fs-small); color: var(--ink-subtle); overflow-wrap: anywhere; }
.us-dash-status { margin: 0; font-size: var(--fs-small); color: var(--ink-subtle); }
/* A square icon button (the period steppers). Its name is its `aria-label`; the icon is full
   strength here, not the nav's resting 72 %, because it is the only thing that says what it does. */
.us-icon-btn { flex: none; width: 34px; height: 34px; padding: 0; justify-content: center; }
.us-icon-btn svg, .us-menu-item svg { width: 16px; height: 16px; opacity: 1; }

/* THE MENU IS A <details>: native open/close, a summary that is already a button to assistive tech.
   The marker is removed in both spellings, as the nav groups do. */
.us-menu { position: relative; display: inline-block; }
.us-menu > summary { list-style: none; cursor: pointer; }
.us-menu > summary::-webkit-details-marker { display: none; }
.us-menu > summary::marker { content: ''; }
.us-menu-list { position: absolute; top: calc(100% + 4px); right: 0; z-index: 30; display: flex; flex-direction: column; gap: 2px;
  min-width: 220px; max-width: calc(100vw - 32px); margin: 0; padding: 6px; list-style: none;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-md); box-shadow: var(--elev-2); }
.us-menu-item { display: flex; align-items: center; gap: var(--space-2); width: 100%; padding: 8px 10px; font: inherit; font-size: 14px;
  line-height: 1.3; text-align: left; text-decoration: none; color: var(--ink); background: none; border: 0; border-radius: var(--radius-sm); cursor: pointer; }
.us-menu-item:hover:not(:disabled) { color: var(--ink); background: var(--surface-2); }
.us-menu-item:disabled, .us-menu-item[aria-disabled="true"] { color: var(--on-disabled); cursor: not-allowed; }

/* Print-only content (the organisation, title, period and "Generated at" line, and prose the screen
   keeps collapsed) is absent on screen; the print rules below bring it back. */
.us-print-head, .us-print-only { display: none; }

@media (max-width: 720px) {
  .us-dash-controls [data-us="dash-asof"] { margin-left: 0; flex-basis: 100%; }
  /* THE PERIOD ROW TAKES THE WHOLE LINE on a phone, under its label, and the select grows into it: on
     the label's line it had 246 px and "Bieżący miesiąc (wrzesień 2026)" lost its ")" under the arrow. */
  .us-dash-period { flex: 1 1 100%; gap: 6px !important; }
  .us-dash-period > select { flex: 1 1 auto; padding-left: 8px; padding-right: 4px; font-size: 13px; }
  .us-report-kpis { grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr)); }
  /* Low-priority table columns step aside on a phone; the CSV and the wider screen keep them. */
  .us-p3 { display: none; }
}

/* FORCED COLOURS. The system palette replaces ours, so the chart falls back to what forced colours
   are for: marks in CanvasText, tracks and hairlines in GrayText, text in CanvasText, separators in
   Canvas. The selectors read the painter's attributes, so a line (`fill="none"`) is never filled and
   a hatch (`fill="url(…)"`) keeps its pattern. Status cells become outlines so their glyph — the
   word the cell stands for — stays readable. A legend key's paint is an INLINE style, which no
   selector outranks, so the keys take `!important` here — the same accessibility escape hatch as
   the reduced-motion rule — or they would keep chart colours beside CanvasText marks. */
@media (forced-colors: active) {
  .us-mk[fill]:not([fill="none"]):not([fill^="url("]) { fill: CanvasText; }
  .us-mk[stroke]:not([stroke="none"]):not([stroke="var(--surface)"]) { stroke: CanvasText; }
  .us-mk[stroke="var(--surface)"] { stroke: Canvas; }
  .us-trk[fill]:not([fill="none"]) { fill: GrayText; }
  .us-trk[stroke]:not([stroke="none"]) { stroke: GrayText; }
  .us-grd, .us-axs { stroke: GrayText; }
  .us-grd[fill]:not([fill="none"]), .us-axs[fill]:not([fill="none"]) { fill: GrayText; }
  .us-chart-text, .us-chart-text-ink, .us-ring-label, .us-donut-total { fill: CanvasText; }
  .us-chart-text-strong { stroke: Canvas; }
  .us-status-grid .us-mk[fill]:not([fill="none"]) { fill: Canvas; stroke: CanvasText; stroke-width: 1px; }
  .us-swatch, .us-chart-tip-key { forced-color-adjust: none; background: CanvasText !important; }
  .us-swatch-hatch { background: repeating-linear-gradient(45deg, CanvasText 0 2px, transparent 2px 5px) !important; }
  .us-seg > label:has(> input:checked) { forced-color-adjust: none; color: HighlightText; background: Highlight; border-color: Highlight; }
}

/* NO MOTION ON A CHART, in either preference: nothing here animates a mark, and under reduced motion
   nothing is allowed to start. */
@media (prefers-reduced-motion: reduce) {
  .us-chart, .us-chart *, .us-chart-plot, .us-chart-bucket, .us-chart-tip, .us-mk, .us-trk, .us-ring, .us-donut, .us-spark,
  .us-report-panel, .us-menu-list { transition: none; animation: none; }
}

/* ---- The assistant layer (ADR-US-069; UX spec §1–§3, §7–§8) ------------------------------------
   One button group on every signed-in screen and the panel it opens (src/assist/dock.tsx).

   THE LAYERS, bottom to top: page content 0–30 · the button 35 · the desktop panel 38 · the nav
   drawer's backdrop and the header's notifications popover 39 (the popover paints over the panel; on a
   phone it lives inside the header's own z-50 context) · the drawer and the help sheet 40 · the phone
   sheet 45 · the phone header 50
   · dialogs 80 · the degraded-language banner 90 · the navigation progress bar 95. So the drawer opens
   OVER the button, a dialog raised from the panel paints above it, and the button never covers the
   header.

   EVERY COLOUR IS AN EXISTING, TESTED PAIR (palette-meets-its-own-contrast-floor): --on-brand on the
   --brand ramp, --brand on --surface, --on-disabled on --disabled, --on-danger on --danger, --ink on
   --surface-2, --on-selection on --selection, and the tooltip's --surface on --ink (the body-text pair,
   inverted — contrast is symmetric).

   GREYED IS A COLOUR, NOT AN OPACITY. The owner's "low opacity" look lives only in DECORATION: the
   shadow is dropped and a 1px ring at 40% replaces it. The glyph is --on-disabled at full strength on
   --disabled (5.35:1 light, 5.15:1 dark) — the rule `.us-btn:disabled` states above, for the reason it
   states. */
.us-nav-progress { position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 95; overflow: hidden; pointer-events: none;
  background: color-mix(in srgb, var(--brand) 22%, transparent); }
.us-nav-progress::before { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 40%; background: var(--brand);
  animation: us-nav-progress 1.1s var(--ease-standard) infinite; }
@keyframes us-nav-progress { from { transform: translateX(-100%); } to { transform: translateX(250%); } }

/* THE LAST ROW OF EVERY PAGE SCROLLS CLEAR OF THE BUTTON: the page gutter, the phone button's 52 px, a
   16 px breath and the home indicator. After the ≤720 px `padding` shorthand, so it holds at every
   width. (Print resets it: the print block below comes later.) */
.us-frame-main { padding-bottom: calc(var(--space-5) + 52px + var(--space-4) + env(safe-area-inset-bottom)); }
/* The toast stack (unused today) would sit exactly where the button is; it moves above it. */
.us-toasts { bottom: calc(24px + 52px + 16px); }

.us-assist-fab { position: fixed; z-index: 35; display: flex; align-items: center; gap: 12px;
  right: max(24px, calc(env(safe-area-inset-right) + 16px)); bottom: max(24px, calc(env(safe-area-inset-bottom) + 16px)); }
.us-assist-fab-main, .us-assist-fab-mic { position: relative; display: inline-grid; place-items: center; margin: 0; padding: 0;
  border: 1px solid transparent; border-radius: 50%; font: inherit; cursor: pointer; }
.us-assist-fab-main { width: 48px; height: 48px; background: var(--brand); color: var(--on-brand); box-shadow: var(--elev-2); }
.us-assist-fab-main:hover { background: var(--brand-strong); }
.us-assist-fab-main:active { background: var(--brand-pressed); }
.us-assist-fab-mic { width: 36px; height: 36px; background: var(--surface); color: var(--brand); border-color: var(--line-strong); box-shadow: var(--elev-1); }
/* A 44 px target around the 36 px disc (WCAG 2.5.8 asks 24; the phone asks for a thumb). */
.us-assist-fab-mic::before { content: ''; position: absolute; inset: -4px; border-radius: 50%; }
.us-assist-fab-main .us-assist-glyph { width: 24px; height: 24px; }
.us-assist-fab-mic .us-assist-glyph { width: 20px; height: 20px; }
.us-assist-fab button:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: 50%; }
.us-assist-fab button[data-greyed="true"] { background: var(--disabled); color: var(--on-disabled); border-color: transparent; cursor: help;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--line-strong) 40%, transparent); }
/* LIVE VOICE (W3-C sets data-voice): the mic turns danger-red and a ring pulses every 1.6 s. It stays
   red with the panel closed — one of the three signals that the microphone is on. */
.us-assist-fab[data-voice="live"] .us-assist-fab-mic { background: var(--danger); color: var(--on-danger); border-color: transparent;
  animation: us-assist-pulse 1.6s ease-out infinite; }
@keyframes us-assist-pulse { from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--danger) 60%, transparent); } to { box-shadow: 0 0 0 8px transparent; } }

/* The reason, above-left of its button. `role=tooltip`, referenced by `aria-describedby` whether shown
   or not, so the reason is announced on focus even where the pointer never hovers. */
.us-assist-tip { position: absolute; bottom: calc(100% + 10px); right: 0; width: max-content; max-width: 260px; padding: 6px 10px;
  border-radius: var(--radius-sm); background: var(--ink); color: var(--surface); box-shadow: var(--elev-2);
  font-size: var(--fs-small); font-weight: 600; line-height: 1.35; text-align: left; overflow-wrap: anywhere; }
.us-assist-tip[hidden] { display: none; }
.us-assist-tip[data-for="mic"] { right: calc(48px + 12px); }

/* THE PANEL. Desktop: a non-modal region at the right, under the header. At EVERY desktop width it
   pushes the page (html[data-assist=open]) so nothing of the page sits under it — the figures being
   asked about, the fields the assistant fills and the page's own actions stay in view and reachable
   (G2-R2-04: at 721–1199 px it used to overlay, hiding the contractor form's right column and the list's
   "New" button while they stayed in the Tab order). ≥1200 px the page keeps its sidebar; 721–1199 px the
   sidebar becomes the drawer (the ☰ opener, as on a phone) while the panel is open, and the panel is
   narrowed so the page keeps at least about 400–560 px. */
.us-assist-panel { position: fixed; top: 56px; right: 0; bottom: 0; z-index: 38; width: var(--assist-w, min(400px, 100vw));
  display: flex; flex-direction: column; background: var(--surface); color: var(--ink);
  border-left: 1px solid var(--line); box-shadow: var(--elev-3); animation: us-assist-slide var(--dur-3) var(--ease-emphasized); }
@keyframes us-assist-slide { from { transform: translateX(100%); } to { transform: translateX(0); } }
@media (min-width: 721px) {
  html[data-assist="open"] { --assist-w: clamp(320px, calc(100vw - 560px), 400px); }
  html[data-assist="open"] .us-frame-main { margin-right: var(--assist-w); min-width: 0; }
}
@media (min-width: 721px) and (max-width: 1199px) {
  /* The sidebar steps aside as the phone's drawer does (the same rules as ≤720 px, keyed on the open
     panel): the grid drops the nav column, the ☰ opener appears, the drawer slides over the page with a
     backdrop, and the header paints above it. */
  html[data-assist="open"] .us-frame { grid-template-columns: minmax(0, 1fr); grid-template-areas: "header" "main"; }
  html[data-assist="open"] .us-frame-nav { position: fixed; top: 56px; bottom: 0; left: 0; width: 248px; visibility: hidden; transform: translateX(-100%);
    transition: transform var(--dur-3) var(--ease-emphasized), visibility 0s linear var(--dur-3); z-index: 40; box-shadow: var(--elev-3); }
  html[data-assist="open"] .us-frame-nav[data-open="true"] { visibility: visible; transform: translateX(0); transition-delay: 0s; }
  html[data-assist="open"] .us-nav-toggle { display: inline-flex; width: 36px; height: 36px; padding: 0; }
  html[data-assist="open"] .us-frame-header { position: relative; z-index: 50; }
  html[data-assist="open"] .us-nav-backdrop { display: block; position: fixed; inset: 56px 0 0 0; z-index: 39; background: var(--overlay);
    border: 0; padding: 0; margin: 0; width: 100%; cursor: default; }
}
.us-assist-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--space-2) var(--space-3);
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--line); }
.us-assist-head h2 { margin: 0; font-size: var(--fs-body-lg); }
.us-assist-head h2:focus:not(:focus-visible) { outline: none; }
.us-assist-actions { display: flex; align-items: center; gap: var(--space-1); }
.us-assist-actions .us-icon-btn { width: 36px; height: 36px; }
.us-assist-context { grid-column: 1 / -1; justify-self: start; max-width: 100%; margin: 0; padding: 2px var(--space-2);
  border-radius: var(--radius-pill); background: var(--surface-2); color: var(--ink); font-size: var(--fs-small); overflow-wrap: anywhere; }
.us-assist-low { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); margin: 0; font-size: var(--fs-small); color: var(--warn); }
.us-assist-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-3) var(--space-4); }
.us-assist-body:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.us-assist-explain .us-banner { flex-wrap: wrap; }
.us-assist-thread { flex: 1 1 auto; }
.us-assist-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.us-assist-msg { align-self: flex-start; max-width: 88%; padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  background: var(--surface-2); color: var(--ink); font-size: var(--fs-small); line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; }
.us-assist-msg[data-role="user"] { align-self: flex-end; background: var(--selection); color: var(--on-selection); }
.us-assist-streaming { align-self: flex-start; max-width: 88%; padding: var(--space-2) var(--space-3); border-radius: var(--radius-md);
  background: var(--surface-2); color: var(--ink); font-size: var(--fs-small); line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere; }
/* The model's own text (src/assist/answer-text.tsx): paragraphs and lists spaced like the thread, bullets
   and numbers back (the thread's list-style: none is inherited), the lists indented inside the bubble. */
.us-assist-answer > p { margin: 0; }
.us-assist-answer > * + * { margin-top: var(--space-2); }
.us-assist-answer ul, .us-assist-answer ol { margin: 0; padding-left: 1.25em; }
.us-assist-answer ul { list-style: disc; }
.us-assist-answer ol { list-style: decimal; }
.us-assist-answer li + li { margin-top: 2px; }
.us-assist-sources { margin-top: var(--space-2); font-size: var(--fs-caption); color: var(--ink-muted); }
.us-assist-sources > summary { cursor: pointer; font-weight: 600; }
.us-assist-suggestions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.us-assist-chip { min-height: 32px; padding: 4px var(--space-3); border: 1px solid var(--line-strong); border-radius: var(--radius-pill);
  background: var(--surface); color: var(--ink); font: inherit; font-size: var(--fs-small); text-align: left; cursor: pointer; }
.us-assist-chip:hover { background: var(--surface-2); }
.us-assist-chip[aria-disabled="true"] { background: var(--disabled); color: var(--on-disabled); border-color: transparent; cursor: not-allowed; }
.us-assist-composer { display: flex; align-items: flex-end; gap: var(--space-2); padding: var(--space-3) var(--space-4); border-top: 1px solid var(--line); }
.us-assist-composer textarea.us-input { flex: 1 1 auto; width: auto; min-height: 0; max-height: calc(6 * 1.4em + 22px); resize: none; line-height: 1.4; }
/* The panel ends at the screen's bottom edge; on a phone that is the home indicator (viewport-fit=cover),
   so the disclaimer clears the inset (0 on a desktop) (G1-R2-08). */
.us-assist-foot { padding: 0 var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom)); color: var(--ink-subtle); font-size: var(--fs-caption); line-height: 1.4; }
.us-assist-foot p { margin: 0; }

/* Voice (W3-C): the bar at the top of the thread, the recording pill, the captions, the consent sheet. */
/* THE LIVE CALL'S BAR STAYS IN VIEW (G5-OW-03). The sticky box is the bar's wrapper — a direct child of the
   scrolling .us-assist-body — not the bar itself, whose parent is only as tall as the bar and its captions
   (a sticky box never leaves its parent). The negative top and the matching padding cover the body's own
   top padding, so the thread does not show above the bar; the negative margin keeps the resting layout. */
.us-assist-body > [data-assist-voice-phase] { position: sticky; top: calc(-1 * var(--space-3)); z-index: 1;
  margin-top: calc(-1 * var(--space-3)); padding: var(--space-3) 0 var(--space-1); background: var(--surface); }
.us-assist-voicebar { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); background: var(--surface-2); color: var(--ink); font-size: var(--fs-small); }
.us-assist-rec { display: inline-flex; align-items: center; gap: 6px; padding: 1px var(--space-2); border-radius: var(--radius-pill);
  background: var(--danger-bg); color: var(--danger); font-size: var(--fs-caption); font-weight: 700; }
.us-assist-rec::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--danger); }
.us-assist-captions { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
  font-size: var(--fs-small); color: var(--ink-muted); }
/* THE MICROPHONE IS ON, ON EVERY SCREEN (G3R1-05, G3-R2UX-01): the voice client sets
   html[data-assist-voice=live] from whether the microphone captures, whatever renders. A thin red line
   along the top of the viewport is the fallback that no route, panel state or full page can hide; the
   pill, the red mic and the '● ' title prefix say the same in words. Decorative: pointer-events none. */
html[data-assist-voice="live"] body::after { content: ''; position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 70;
  background: var(--danger); pointer-events: none; }
.us-assist-composer .us-assist-mic { flex: none; }
/* MUTE, PRESSED (G3-R2UX-05): the struck-through mic on the greyed pair, so a muted call looks muted. */
.us-assist-mute[aria-pressed="true"] { background: var(--disabled); color: var(--on-disabled); }
.us-assist-consent { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--line-strong);
  border-radius: var(--radius-md); background: var(--surface); color: var(--ink); font-size: var(--fs-small); }
/* G5-OW-05: the consent text in its own scroll box, so Agree / Not now stay on screen under it. */
.us-assist-consent-text { max-height: min(50vh, 22rem); max-height: min(50dvh, 22rem); overflow-y: auto; overscroll-behavior: contain;
  padding: var(--space-2) var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2);
  line-height: 1.5; overflow-wrap: anywhere; }
.us-assist-consent-text p { margin: 0; white-space: pre-wrap; }
.us-assist-consent-text p + p { margin-top: var(--space-2); }
.us-assist-consent-text:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.us-assist-consent-actions { flex-wrap: wrap; gap: var(--space-2); }

/* Form filling (W2-F): a field the assistant filled, the field it is asking about, and the badge. */
.us-assist-filled { animation: us-assist-highlight 2s ease-out; border-radius: var(--radius-sm); }
@keyframes us-assist-highlight { from { background: var(--highlight); } to { background: transparent; } }
.us-assist-asking { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--radius-sm); }
.us-assist-badge { display: inline-block; padding: 1px var(--space-2); border-radius: var(--radius-pill); background: var(--info-bg); color: var(--info);
  font-size: var(--fs-caption); font-weight: 600; }
.us-assist-badge[data-tone="warn"] { background: var(--warn-bg); color: var(--warn); }

/* THE PHONE: a bigger button, and the panel becomes a bottom sheet — modal, under the header, the full
   dynamic height (so the composer sits above the on-screen keyboard), with the page locked behind it. */
@media (max-width: 720px) {
  .us-assist-fab-main { width: 52px; height: 52px; }
  .us-assist-fab-mic { width: 40px; height: 40px; }
  .us-assist-fab-mic::before { inset: -2px; }
  .us-assist-tip[data-for="mic"] { right: calc(52px + 12px); }
  .us-assist-panel { top: 56px; left: 0; right: 0; bottom: auto; z-index: 45; width: 100%; height: calc(100vh - 56px); height: calc(100dvh - 56px);
    border-left: 0; border-top: 1px solid var(--line); border-radius: var(--radius-lg) var(--radius-lg) 0 0; animation-name: us-assist-rise; }
  .us-assist-actions .us-icon-btn { width: 44px; height: 44px; }
  html[data-assist="open"] { overflow: hidden; }
}
@keyframes us-assist-rise { from { transform: translateY(100%); } to { transform: translateY(0); } }

/* Forced colours: every button keeps a visible edge; a greyed one reads as the system's disabled. */
@media (forced-colors: active) {
  .us-assist-fab button { border: 2px solid ButtonText; }
  .us-assist-fab button[data-greyed="true"] { border-color: GrayText; color: GrayText; }
  .us-assist-tip { border: 1px solid CanvasText; }
  .us-assist-panel { border-left: 1px solid CanvasText; }
  .us-assist-chip[aria-disabled="true"] { border-color: GrayText; color: GrayText; }
  .us-nav-progress::before { background: Highlight; }
}

/* Reduced motion: no slide, no pulse, no highlight fade, no running bar. */
@media (prefers-reduced-motion: reduce) {
  .us-assist-panel, .us-assist-fab-mic, .us-assist-fab[data-voice="live"] .us-assist-fab-mic, .us-assist-filled, .us-nav-progress::before { animation: none; }
}

/* ---- Help & feedback (ADR-US-070; spec §9) ----------------------------------------------------
   The circle beside the assistant's, the panel/sheet it opens (signed-in dialog and contact form),
   and the picture's lightbox (src/help/*.tsx).

   STAGED IN WAVE 1 as src/help/help.css and pasted here verbatim in wave 2 (the staging file is
   gone): the SPA serves ONE stylesheet, /assets/tokens.css, which build.mjs copies verbatim (no CSS
   bundler), and three gates read this file by path: spa-class-has-css, spa-style-var-is-defined and
   palette-meets-its-own-contrast-floor. tests/unit/help-css.test.ts pins these rules here.

   TOKENS ONLY, AND ONLY PAIRS THE PALETTE TEST ALREADY CHECKS: --brand on --surface and on
   --surface-2 (the glyph, the dot, links), --ink / --ink-muted / --ink-subtle on --surface and
   --surface-2, --line-strong on --surface (the ring), --focus on --surface, the tooltip's --surface on
   --ink (the body-text pair, inverted), and the buttons' own --on-brand/--brand ramp (.us-btn).
   No new colour, so no new pair.

   THE LAYERS (tokens.css, "The assistant layer"): the circle row 35 · the desktop panel 38 (the
   assistant panel's tier; the shell never shows both over the corner) · the phone scrim 44 and sheet
   45 (the assistant sheet's tier) · the lightbox 80 (dialogs). */

/* THE CIRCLE — the microphone's exact visual weight: 36 px, --surface, --brand glyph, 1 px ring, --elev-1. */
.us-help-circle-wrap { position: relative; display: inline-flex; }
.us-help-circle { position: relative; display: inline-grid; place-items: center; width: 36px; height: 36px; margin: 0; padding: 0;
  border: 1px solid var(--line-strong); border-radius: 50%; background: var(--surface); color: var(--brand); box-shadow: var(--elev-1);
  font: inherit; cursor: pointer; }
/* A 44 px target around the 36 px disc (WCAG 2.5.8 asks 24; the phone asks for a thumb). */
.us-help-circle::before { content: ''; position: absolute; inset: -4px; border-radius: 50%; }
.us-help-circle:hover { background: var(--surface-2); }
.us-help-circle:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: 50%; }
.us-help-glyph { width: 20px; height: 20px; }
/* The unread dot: decoration (the count is in the button's name); a --surface ring lifts it off the disc's edge. */
.us-help-dot { position: absolute; top: -2px; right: -2px; width: 10px; height: 10px; border-radius: 50%;
  background: var(--brand); box-shadow: 0 0 0 2px var(--surface); pointer-events: none; }
/* The tooltip can be HOVERED (WCAG 1.4.13, G2-UX-I18N-09): it takes pointer events, and a transparent
   bridge fills the 10 px gap down to the circle, so the pointer never leaves the wrap on its way up and
   the tip does not vanish under it. */
.us-help-tip { position: absolute; bottom: calc(100% + 10px); right: 0; z-index: 1; width: max-content; max-width: 260px; padding: 6px 10px;
  border-radius: var(--radius-sm); background: var(--ink); color: var(--surface); box-shadow: var(--elev-2);
  font-size: var(--fs-small); font-weight: 600; line-height: 1.35; text-align: left; overflow-wrap: anywhere; }
.us-help-tip::after { content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 10px; }
.us-help-tip[hidden] { display: none; }

/* THE PANEL (desktop): non-modal, anchored above the circle row at its right edge — the row's gutter,
   its tallest button (the assistant's 48 px) and the row's 12 px gap. */
.us-help-panel { position: fixed; z-index: 38; right: max(24px, calc(env(safe-area-inset-right) + 16px));
  bottom: calc(max(24px, calc(env(safe-area-inset-bottom) + 16px)) + 48px + 12px);
  width: min(400px, calc(100vw - 32px)); max-height: min(80vh, calc(100vh - 56px - 96px));
  display: flex; flex-direction: column; background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: var(--radius-lg); box-shadow: var(--elev-3); overflow: hidden; }
.us-help-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4); border-bottom: 1px solid var(--line); }
.us-help-head h2 { margin: 0; font-size: var(--fs-body-lg); line-height: 1.3; }
.us-help-head h2:focus:not(:focus-visible) { outline: none; }
.us-help-x { width: 18px; height: 18px; }
.us-help-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; display: flex; flex-direction: column;
  gap: var(--space-3); padding: var(--space-3) var(--space-4) var(--space-4); }
.us-help-body:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
.us-help-inbox { align-self: flex-start; font-size: var(--fs-small); font-weight: 600; }
.us-help-form { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; }
.us-help-form .us-field { margin-bottom: 0; }
.us-help-intro { margin: 0; font-size: var(--fs-small); color: var(--ink-muted); }
/* THE KIND CHOICE IS ALWAYS ONE ROW OF THREE EQUAL SEGMENTS (G2-UX-I18N-06). The shared .us-seg wraps a
   segment that does not fit onto a second row (its labels are sized to their text first), which left a
   stray third button under two joined ones in pl/de/fr on the desktop panel and in every language on a
   phone. A one-row grid of equal columns wraps the TEXT inside each segment instead, so the shared first
   and last radii and the -1 px border overlap always line up. Scoped here: other .us-seg groups keep
   their flow. */
.us-help-kind { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); max-width: 100%; }
.us-help-kind > label { justify-content: center; text-align: center; white-space: normal; }
.us-help-textarea { min-height: 7.5em; resize: vertical; line-height: 1.45; }
.us-help-count { align-self: flex-end; font-size: var(--fs-caption); color: var(--ink-subtle); font-variant-numeric: tabular-nums; }
.us-help-blocking { font-size: var(--fs-small); }

/* "WHAT WE WILL SEND": the picture, its control, and every detail. On --surface-2, whose text pairs are checked. */
.us-help-send { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; padding: var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface-2); color: var(--ink); }
.us-help-send h3 { margin: 0; font-size: var(--fs-small); font-weight: 700; line-height: 1.35; }
.us-help-shot { display: flex; align-items: flex-start; gap: var(--space-3); }
.us-help-thumb { flex: none; display: block; width: 128px; margin: 0; padding: 0; overflow: hidden; cursor: zoom-in;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm); background: var(--surface); }
.us-help-thumb img { display: block; width: 100%; height: auto; }
.us-help-thumb:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.us-help-shot-side { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); min-width: 0; }
.us-help-shot-text { margin: 0; font-size: var(--fs-small); color: var(--ink-muted); line-height: 1.45; }
.us-help-shot-toggle { padding: 6px var(--space-3); font-size: var(--fs-small); }
/* THE LABEL COLUMN IS CAPPED, SO THE VALUES KEEP THEIRS. It was `minmax(0, max-content)`: a grid gives a
   max-content track its whole max-content width before a `1fr` track gets anything, so one long label
   ("Recent errors (code · status · reference · time)", ja 最近のエラー（…）) took the panel and every value
   wrapped one character per line in what was left. `fit-content(40%)` sizes the column to its labels when
   they are short and wraps a long one inside 40 % of the list, so the values always have the rest —
   the desktop panel and the phone sheet alike (tests/unit/help-details-in-chromium.test.ts). */
.us-help-details { display: grid; grid-template-columns: fit-content(40%) minmax(0, 1fr); gap: 2px var(--space-3); margin: 0;
  font-size: var(--fs-small); line-height: 1.45; }
.us-help-details dt { color: var(--ink-muted); }
.us-help-details dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; }
.us-help-details ul { margin: 0; padding-left: 1.1em; }
.us-help-note { margin: 0; font-size: var(--fs-caption); color: var(--ink-muted); }
/* The identifiers sent with the details, one click away (everything sent is shown before sending). */
.us-help-tech { font-size: var(--fs-small); }
.us-help-tech summary { cursor: pointer; color: var(--ink-muted); }
.us-help-tech summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.us-help-tech .us-help-note, .us-help-tech .us-help-details { margin-top: var(--space-2); }
.us-help-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); }
/* SEND STAYS IN VIEW (G3-OWNER-02). The form's last row — a refusal when there is one, then Cancel and Send —
   is pinned to the bottom of the panel's scroller, so the primary action shows without scrolling past the
   details a person did not ask for (a 1366×768 laptop gives the panel at most 614 px). A sticky box stops at
   its scroller's padding, so a negative inset equal to the body's bottom padding puts it on the real edge; the
   negative margins let it span the panel's width with its own padding, and at the end of the scroll it sits
   exactly where it would have. The body's scroll padding is a little more than the row's height: a control
   reached with Tab and scrolled to the nearest edge (Firefox and Safari scroll focus that way; Chromium
   centres it) stops above the row, never under it (WCAG 2.4.11). The sent state's actions are not in a form. */
.us-help-foot { position: sticky; bottom: calc(-1 * var(--space-4)); z-index: 1; display: flex; flex-direction: column; gap: var(--space-2);
  margin: 0 calc(-1 * var(--space-4)) calc(-1 * var(--space-4)); padding: var(--space-3) var(--space-4) var(--space-4);
  border-top: 1px solid var(--line); background: var(--surface); }
.us-help-body { scroll-padding-bottom: calc(var(--space-3) + 44px + var(--space-4) + var(--space-3)); }
.us-help-done { display: flex; flex-direction: column; gap: var(--space-3); }
.us-help-done-text { margin: 0; }
.us-help-done-text:focus:not(:focus-visible) { outline: none; }

/* THE PHONE SHEET's scrim (the sheet itself is .us-help-panel under the media query below). */
.us-help-scrim { position: fixed; inset: 0; z-index: 44; background: var(--overlay); }

/* THE PICTURE, FULL SIZE: a modal over everything, the image fitted to the viewport. */
.us-help-lightbox { position: fixed; inset: 0; z-index: 80; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-3); padding: var(--space-4); background: var(--overlay); }
.us-help-lightbox-bar { display: flex; justify-content: flex-end; width: 100%; }
.us-help-lightbox img { display: block; max-width: 100%; max-height: calc(100vh - 120px); max-height: calc(100dvh - 120px); width: auto; height: auto;
  object-fit: contain; border: 1px solid var(--line-strong); border-radius: var(--radius-sm); background: var(--surface); box-shadow: var(--elev-3); }

/* THE PHONE: the circle grows with the microphone (40 px), and the panel becomes a bottom sheet —
   full width, under the header at most, above the home indicator. */
@media (max-width: 720px) {
  .us-help-circle { width: 40px; height: 40px; }
  .us-help-circle::before { inset: -2px; }
  .us-help-panel { left: 0; right: 0; bottom: 0; z-index: 45; width: 100%; max-height: calc(100vh - 56px); max-height: calc(100dvh - 56px);
    border-width: 1px 0 0; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
  .us-help-body { padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom)); }
  /* The sheet's body pads for the home indicator; the pinned row covers that padding and keeps it inside. */
  .us-help-foot { bottom: calc(-1 * (var(--space-4) + env(safe-area-inset-bottom))); margin-bottom: calc(-1 * (var(--space-4) + env(safe-area-inset-bottom)));
    padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom)); }
  .us-help-head .us-icon-btn { width: 44px; height: 44px; }
  .us-help-thumb { width: 96px; }
}

/* Forced colours: the circle keeps a visible edge; the dot and the panel stay distinguishable. */
@media (forced-colors: active) {
  .us-help-circle { border: 2px solid ButtonText; }
  .us-help-dot { forced-color-adjust: none; background: Highlight; box-shadow: none; }
  .us-help-tip, .us-help-panel, .us-help-send { border: 1px solid CanvasText; }
}

/* Print: the page, never the help controls. */
@media print {
  .us-help-circle-wrap, .us-help-panel, .us-help-scrim, .us-help-lightbox { display: none !important; }
}

/* ---- The circle row (ADR-US-070 spec §9; mounted by the shell, app.tsx + src/help/layer.tsx) ---------
   ONE fixed row in the bottom-right corner holds the Help & feedback circle first and the assistant's
   button group after it: [help][mic][assistant]. The ROW is what is fixed, so neither group hard-codes
   the other's width: where the assistant's group is absent (its own full page, a 403, the sign-in screens,
   the demo's sign-in, the e-mail landings) the circle alone sits in the corner; before the assistant's first
   answer an unseen box holds the group's place (.us-assist-reserve), so the circle does not move when it
   lands. Inside the row the assistant's group flows — `position: relative` keeps its tooltips anchored to
   it — and its DOM, classes and order are the assistant's own, unchanged.

   A FIXED BOX IS ALWAYS A STACKING CONTEXT, and the assistant's panel renders inside this one. So the
   row takes the tier of whatever it holds on top: 35 for the buttons, 38 while the assistant's desktop
   panel is open, 45 while its phone sheet is — exactly the tiers the panel had when the group was fixed
   on its own, so the drawer (40), the notices popover (39), the header (50) and dialogs (80) keep their
   places relative to it. The Help & feedback panel and sheet render OUTSIDE the row (38 / 44–45), and
   its lightbox at the dialogs' 80. */
.us-fab-row { position: fixed; z-index: 35; display: flex; align-items: center;
  right: max(24px, calc(env(safe-area-inset-right) + 16px)); bottom: max(24px, calc(env(safe-area-inset-bottom) + 16px)); }
html[data-assist="open"] .us-fab-row { z-index: 38; }
.us-fab-row .us-assist-fab { position: relative; right: auto; bottom: auto; z-index: auto; margin-left: 12px; }
/* THE ASSISTANT'S PLACE IS HELD WHILE ITS FIRST ANSWER IS ON ITS WAY (G3-OWNER-03). Before the availability read
   settles the assistant draws no buttons (a greyed one must never flash over a usable one), and the circle,
   alone in the row, painted in the corner and then jumped left under the pointer when the group arrived — a
   press aimed at it opened the assistant. The layer holds the group's exact footprint instead (its margin, the
   microphone, the gap, the main button; the phone sizes below), unseen and with nothing in it, so the circle
   paints once, in its final place. A 403 (the assistant hidden) drops the reserve. */
.us-assist-reserve { flex: none; width: calc(36px + 12px + 48px); height: 48px; margin-left: 12px; visibility: hidden; pointer-events: none; }
/* The assistant's panel covers the corner, in both shapes: the circle goes with it — hidden, so it also
   leaves the Tab order and the accessibility tree (the shell marks it inert as well). */
html[data-assist="open"] .us-fab-row .us-help-circle-wrap { visibility: hidden; }
@media (max-width: 720px) {
  html[data-assist="open"] .us-fab-row { z-index: 45; }
  .us-assist-reserve { width: calc(40px + 12px + 52px); height: 52px; }
}

/* ---- Your requests (RT-help-requests, RT-help-request; screens/help-requests.tsx) --------------------
   The inbox of a person's own help requests and one request's conversation with Butterpie support.
   Tokens only, in pairs the palette test checks: --ink / --ink-muted / --ink-subtle on --surface and
   --surface-2, --brand on --surface (the unread dot, the support message's accent rule — decoration),
   and the existing .us-pill tones for states. */
.us-help-inbox-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-2) var(--space-3); }
.us-help-inbox-head h1 { margin: 0; }
.us-help-tabs { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* The inbox is a LIST (a title and a line of facts per request), which a phone reads as well as a desk. */
.us-help-list { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.us-help-row { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); padding: var(--space-3) 0; border-bottom: 1px solid var(--line); }
.us-help-row:first-child { padding-top: 0; }
.us-help-row:last-child { padding-bottom: 0; border-bottom: 0; }
.us-help-row-title { display: inline-flex; align-items: flex-start; gap: var(--space-2); max-width: 100%; font-weight: 600; overflow-wrap: anywhere; }
.us-help-row[data-unread="true"] .us-help-row-title { font-weight: 700; }
.us-help-row-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); margin: 0; font-size: var(--fs-small); color: var(--ink-muted); }
/* The unread mark in a list row: a dot (decoration) beside a visually hidden "New reply". */
.us-help-unread-dot { flex: none; display: inline-block; width: 8px; height: 8px; margin-top: .45em; border-radius: 50%; background: var(--brand); }
.us-help-more { display: flex; justify-content: center; margin-top: var(--space-3); }

/* One request: the meta line, the conversation, the reply box. */
.us-help-req-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: 0; font-size: var(--fs-small); color: var(--ink-muted); }
.us-help-req-title { margin: 0; font-size: var(--fs-h3); line-height: 1.3; overflow-wrap: anywhere; }
.us-help-section-h { margin: 0 0 var(--space-3); font-size: var(--fs-body-lg); line-height: 1.3; }
.us-help-state-hint { margin: 0; font-size: var(--fs-small); color: var(--ink-muted); }
/* The notice slot above the reply box: no box while empty; focus lands on it after a request is closed
   (G2-UX-I18N-08), with the focus ring when that came from the keyboard. */
.us-help-notice:empty { display: none; }
.us-help-notice:focus:not(:focus-visible) { outline: none; }
.us-help-notice:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--radius-md); }
.us-help-log { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.us-help-msg { padding: var(--space-3) var(--space-4); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--surface); color: var(--ink); }
/* Butterpie support's messages: the second surface and a --brand rule at the start edge (decoration; the author is in words). */
.us-help-msg[data-author="SUPPORT"] { background: var(--surface-2); border-inline-start: 3px solid var(--brand); }
.us-help-msg-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-1) var(--space-3); margin: 0 0 var(--space-2); }
.us-help-msg-author { font-weight: 700; font-size: var(--fs-small); }
.us-help-msg-time { font-size: var(--fs-caption); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.us-help-msg-body { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.5; }
.us-help-sent { background: var(--surface); }
.us-help-sent-body { display: flex; flex-direction: column; gap: var(--space-3); }
.us-help-reply { display: flex; flex-direction: column; gap: var(--space-3); margin: 0; }
.us-help-reply .us-field { margin-bottom: 0; }
.us-help-privacy { font-size: var(--fs-caption); }

@media (forced-colors: active) {
  .us-help-msg[data-author="SUPPORT"] { border-inline-start-color: Highlight; }
  .us-help-unread-dot { forced-color-adjust: none; background: Highlight; }
}

/* Print: the page, never the circle row. A printed conversation is the page itself. */
@media print {
  .us-fab-row { display: none !important; }
}

/* Print: the page, never the assistant. */
@media print {
  .us-assist-layer, .us-nav-progress { display: none !important; }
}

/* Print: the controls are chrome, the book is the page. Used by RT-ryczalt-print, whose claim
   boundary deliberately sits in the BODY rather than a footer — a footer is the first thing a
   printer drops, and a printed statutory page separated from the application must still say what
   produced it and what it is not.
   A PRINTED DASHBOARD OR REPORT is the page without the app around it: no header, nav or controls,
   no shadows (they rasterise), three tiles a row, charts two a row, and a print-only head that says
   whose figures these are, for which period and when they were generated. The page script switches
   to the light theme and opens the report's disclosures on `beforeprint`, and restores both on
   `afterprint`; colours print as drawn, or a status chip loses its tone. */
@media print {
  @page { size: A4; margin: 14mm; }
  * { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  .us-no-print { display: none !important; }
  .us-table { font-size: 11pt; }
  .us-frame-header, .us-frame-nav, .us-nav-backdrop, .us-dash-controls, .us-report-toolbar { display: none; }
  .us-frame { display: block; min-height: 0; }
  .us-frame-main { padding: 0; overflow: visible; }
  .us-card, .us-report-panel, .us-kpi-card { box-shadow: none; break-inside: avoid; }
  .us-tilegrid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8pt; }
  .us-report-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .us-print-head { display: block; margin: 0 0 10pt; padding-bottom: 6pt; border-bottom: 0.75pt solid var(--line); }
  .us-print-only { display: revert; }
}

/* RT-statutory-files (0310): one card per statutory file kind, as many columns as fit. */
.stat-kind-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); gap: var(--space-3); }
.stat-kind-card { height: 100%; }

/* An optional, collapsed group of form fields (onboarding's statutory identity, 0370). The fields stack
   with the form's own rhythm; the summary is the focusable control and shows its own keyboard ring. */
.us-disclosure { display: grid; gap: var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); }
.us-disclosure > summary { cursor: pointer; font-weight: 600; }
.us-disclosure > summary:focus-visible { outline: 2px solid var(--brand-strong); outline-offset: 2px; }
