/* =========================================================
   Hectre Design System — Components stylesheet

   Version:    2.514.0      (@version 2.514.0)
   Released:   2026-10-02
   Status:     stable
   Owner:      Design

   Single-sourced versioning — the whole system ships under ONE version.
   This file mirrors DS_VERSION in "Hectre Design System.html"; keep the two
   in lockstep on every release. Pair with tokens.css and type.css.
   See CLAUDE.md for the release checklist.

   ICONS: this file styles Material Symbols glyphs (.mdi) in many components, so
   it loads the typeface itself and owns the .mdi base rule below — a consumer
   wiring "tokens.css + components.css" by hand gets working icons with no extra
   step. styles.css pulls this in for you. Never re-declare .mdi locally.
   ========================================================= */

/* Material Symbols is NOT @imported here — by decision, not omission (v2.389,
   confirmed v2.390): prod's base.css owns the family, and the compiler's
   “used but no @font-face” flag for --font-icon is accepted as permanent.
   Locked-down customer
   networks only allow hectre.com; the host app's base.css loads the family.
   The .mdi rule below keeps its literal family fallback so a host that does
   load the font renders glyphs, and one that doesn't degrades predictably. */

/* ---------- ICON ATOM ----------
   The Material Symbols glyph span. Ligature-based: <span class="mdi">search</span>.
   Components below set only size and colour; this rule makes it render as an
   icon at all. The literal family is a sanctioned fallback (see CLAUDE.md rule 14)
   for a host that loads this file without the full token layer — nowhere else in
   the system may repeat the family name.
   This rule is byte-identical to the .mdi rule in the visualisation's inline
   <style>; the two are one atom and must be edited together. */
.mdi {
  font-family: var(--font-icon, 'Material Symbols Outlined');
  font-weight: normal;
  font-style: normal;
  font-size: 24px;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  vertical-align: middle;
  white-space: nowrap;
  word-wrap: normal;
  direction: ltr;
  color: currentColor;
  -webkit-font-smoothing: antialiased;
  font-variation-settings:
    'FILL' 0,
    'wght' 350,
    'GRAD' 0,
    'opsz' 24;
}

/* ---------- BUTTONS ----------
   Three variants. The Hectre button system.

     primary    Solid orange, FLAT. THE lead CTA. One per primary action area.
                Add .standalone for a lone hero CTA (gradient only — glow dropped v2.393).
                PLACEMENT: .standalone is ONLY for a hero CTA standing alone on
                its surface (empty state, onboarding step, marketing hero) — never
                inside a composed component (date-picker footer, dialog, menu,
                toolbar, table, card with other controls): primary stays flat there.
                Same rule
                on both surfaces — see .on-petrol below.
     secondary  Orange ghost (border + text). Supporting action.
     tertiary   Neutral quiet button — no border, subtle wash on hover.
     link       Underlined orange text link. Low-stakes nav ("View details").

   Sizes:
     sm    9px / 18px  · 13/600 · radius
     md   13px / 22px  · 14/600 · radius   (default)
     lg   15px / 26px  · 15/600 · radius-md

   States (every variant):
     default · hover · active · focus-visible · disabled · loading
     Hover NEVER translates. Active translates 1px down.

   Surfaces:
     .on-petrol   swaps --primary → --lifted-orange so contrast holds on dark.
   ========================================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 14px;
  line-height: 1;
  white-space: nowrap;
  box-sizing: border-box;
  min-height: var(--control-height-md);
  padding: 0 18px;
  border-radius: var(--radius);
  border: none;
  cursor: pointer;
  transition:
    background 150ms ease,
    box-shadow 150ms ease,
    color 150ms ease,
    transform 100ms ease;
}
.btn:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
/* ---------- Size scale — one vocabulary for every variant, incl. the self-contained .btn-white
   (the .btn-white compounds beat its own later, more-specific base rule). Default = no size class. */
.btn-xs,
.btn-white.btn-xs {
  min-height: var(--control-height-xs);
  padding: 0 10px;
  font-size: 12px;
  gap: 6px;
}
.btn-sm,
.btn-white.btn-sm {
  min-height: var(--control-height-sm);
  padding: 0 18px;
  font-size: 13px;
}
.btn-md,
.btn-white.btn-md {
  min-height: var(--control-height-md);
  padding: 0 18px;
  font-size: 14px;
} /* explicit default — identical to bare .btn */
.btn-lg,
.btn-white.btn-lg {
  min-height: var(--control-height-lg);
  padding: 0 26px;
  font-size: 15px;
  border-radius: var(--radius-md);
}
/* Retired (v2.481): no button in the system uses xl. Kept one version for existing markup. */
.btn-xl,
.btn-white.btn-xl {
  min-height: var(--control-height-xl);
  padding: 0 26px;
  font-size: 16px;
  border-radius: var(--radius-md);
}

/* ---------- PRIMARY — solid orange · the lead CTA (flat; gradient only on .standalone) ---------- */
.btn-primary {
  background: var(--primary);
  color: var(--surface-elevated);
}
.btn-primary.s-hover,
.btn-primary:hover {
  background: var(--primary-press);
}
.btn-primary.s-active,
.btn-primary:active {
  background: var(--primary-active);
  transform: translateY(1px);
}
/* standalone hero CTA — keeps the gradient fill (glow dropped in v2.393) */
.btn-primary.standalone {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-press) 100%);
}
.btn-primary.standalone.s-hover,
.btn-primary.standalone:hover {
  background: linear-gradient(135deg, var(--primary-press) 0%, var(--primary-press) 100%);
}
.btn-primary.standalone.s-active,
.btn-primary.standalone:active {
  background: linear-gradient(135deg, var(--primary-active) 0%, var(--primary-active-deep) 100%);
}
.btn-primary.s-disabled,
.btn-primary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}
/* ---------- LOADING — one rule for every .btn variant (v2.466) ----------
   The fill stays at FULL strength — no fade: 40% opacity is the disabled signal, and
   a loading button is busy, not unavailable. The label is hidden with text-fill (not
   color), so the button keeps its width and currentColor stays the label colour; the
   ring draws in currentColor over --button-loading-track. That makes it correct on
   every variant and surface with no per-variant rule: white on primary / danger,
   --petrol-deep on the petrol primary, orange on secondary. Real buttons add
   .s-loading + aria-busy="true" (no native pseudo-class exists for loading). */
.btn.s-loading {
  -webkit-text-fill-color: transparent;
  pointer-events: none;
  position: relative;
  cursor: progress;
}
.btn.s-loading::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--button-loading-track);
  border-top-color: currentColor;
  animation: btn-spin 600ms linear infinite;
}
@keyframes btn-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .btn.s-loading::after {
    animation-duration: 1400ms;
  }
}

/* ---------- SECONDARY — orange ghost · supporting CTA ---------- */
.btn-secondary {
  background: transparent;
  color: var(--primary-press);
  box-shadow: inset 0 0 0 1px var(--primary);
}
.btn-secondary.s-hover,
.btn-secondary:hover {
  background: var(--tint-accent-soft);
  color: var(--accent-text-on-tint);
  box-shadow: inset 0 0 0 1px var(--accent-text-on-tint);
}
.btn-secondary.s-active,
.btn-secondary:active {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  box-shadow: inset 0 0 0 1px var(--accent-text-on-tint);
  transform: translateY(1px);
}
.btn-secondary.s-disabled,
.btn-secondary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ---------- TERTIARY — neutral quiet button (no border, subtle wash) ---------- */
.btn-tertiary {
  background: transparent;
  color: var(--ink-2);
  box-shadow: none;
}
.btn-tertiary.s-hover,
.btn-tertiary:hover {
  background: var(--line-2);
  color: var(--ink);
}
.btn-tertiary.s-active,
.btn-tertiary:active {
  background: rgba(0, 0, 0, 0.1);
  color: var(--ink);
  transform: translateY(1px);
}
.btn-tertiary.s-disabled,
.btn-tertiary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  background: transparent;
}
/* ---------- TEXT LINK — orange, never underlined in any state ---------- */
.btn-link {
  padding: 0 4px;
  background: transparent;
  color: var(--primary);
  text-decoration: none;
  box-shadow: none;
}
.btn-link.s-hover,
.btn-link:hover {
  color: var(--primary-press);
  text-decoration: none;
}
.btn-link.s-active,
.btn-link:active {
  color: var(--primary-press);
  transform: translateY(1px);
}
.btn-link.s-disabled,
.btn-link:disabled {
  color: var(--ink-3);
  cursor: not-allowed;
  opacity: 0.7;
}

/* ---------- DANGER — solid red destructive button (delete / remove confirms) ----------
   The one button that reads red. Use ONLY for the committing step of a destructive
   action (a delete confirm), never as a general CTA. Fill is --error-text (#DC2626,
   white text ≥ 4.5:1); the resting --error is too light for a white label. Same fill
   holds contrast on petrol, so no on-petrol recolour is needed. Loading: the shared
   .btn.s-loading above (white ring, fill unfaded). */
.btn-danger {
  background: var(--error-text);
  color: var(--surface-elevated);
}
.btn-danger.s-hover,
.btn-danger:hover {
  background: var(--error-press);
}
.btn-danger.s-active,
.btn-danger:active {
  transform: translateY(1px);
}
.btn-danger.s-disabled,
.btn-danger:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  transform: none;
}

/* ---------- On petrol — primary swaps to lifted-orange ----------
   Two ways in, one result: put .on-petrol on the button, OR sit it inside a
   .dark-surface ancestor (what every other component in this system uses).
   Until v2.207 only the .on-petrol form existed here, so a consumer that wrapped
   a button in .dark-surface — the documented pattern everywhere else — got the
   LIGHT button on a petrol panel. */
.btn-primary.on-petrol,
.dark-surface .btn-primary {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
  box-shadow: none;
}
.btn-primary.on-petrol.s-hover,
.dark-surface .btn-primary.s-hover,
.btn-primary.on-petrol:hover,
.dark-surface .btn-primary:hover {
  background: var(--lifted-orange-press);
}
/* The pressed step on petrol is MOTION (translate), not a third fill: a darker
   orange cannot hold 4.5:1 against the --petrol-deep label (one step below
   --lifted-orange-press already measures 4.17:1). */
.btn-primary.on-petrol.s-active,
.dark-surface .btn-primary.s-active,
.btn-primary.on-petrol:active,
.dark-surface .btn-primary:active {
  background: var(--lifted-orange-press);
  transform: translateY(1px);
}
/* standalone hero CTA on petrol — the gradient the flat button drops, plus a
   0.5px boundary ring (the glow was dropped in v2.393).
   The gradient runs lifted-orange -> --lifted-orange-press, NOT -> --primary:
   --petrol-deep on --primary is 3.81:1, so the old gradient failed AA at its
   dark end. This pair is 6.20:1 -> 4.67:1. */
.btn-primary.on-petrol.standalone,
.dark-surface .btn-primary.standalone {
  background: linear-gradient(135deg, var(--lifted-orange) 0%, var(--lifted-orange-press) 100%);

  box-shadow: 0 0 0 0.5px var(--cta-ring-on-dark);
}
.btn-primary.on-petrol.standalone.s-hover,
.dark-surface .btn-primary.standalone.s-hover,
.btn-primary.on-petrol.standalone:hover,
.dark-surface .btn-primary.standalone:hover {
  background: var(--lifted-orange-press);
}
.btn-primary.on-petrol.standalone.s-active,
.dark-surface .btn-primary.standalone.s-active,
.btn-primary.on-petrol.standalone:active,
.dark-surface .btn-primary.standalone:active {
  background: var(--lifted-orange-press);
  transform: translateY(1px);
}
.btn-primary.on-petrol.s-disabled,
.dark-surface .btn-primary.s-disabled {
  opacity: 0.4;
  box-shadow: none;
  transform: none;
}
.btn-secondary.on-petrol,
.dark-surface .btn-secondary {
  background: transparent;
  color: var(--lifted-orange);
  box-shadow: inset 0 0 0 1px var(--lifted-orange);
}
.btn-secondary.on-petrol.s-hover,
.dark-surface .btn-secondary.s-hover,
.btn-secondary.on-petrol:hover,
.dark-surface .btn-secondary:hover {
  background: var(--tint-lifted-soft);
  color: var(--lifted-orange);
  box-shadow: inset 0 0 0 1px var(--lifted-orange);
}
.btn-secondary.on-petrol.s-active,
.dark-surface .btn-secondary.s-active,
.btn-secondary.on-petrol:active,
.dark-surface .btn-secondary:active {
  background: var(--tint-lifted-strong);
  color: var(--lifted-orange);
  box-shadow: inset 0 0 0 1px var(--lifted-orange);
  transform: translateY(1px);
}
.btn-secondary.on-petrol.s-disabled,
.dark-surface .btn-secondary.s-disabled {
  opacity: 0.4;
}
.btn-tertiary.on-petrol,
.dark-surface .btn-tertiary {
  color: var(--text-on-dark-secondary);
}
.btn-tertiary.on-petrol.s-hover,
.dark-surface .btn-tertiary.s-hover,
.btn-tertiary.on-petrol:hover,
.dark-surface .btn-tertiary:hover {
  background: var(--tint-on-dark-soft);
  color: var(--surface-elevated);
}
.btn-tertiary.on-petrol.s-active,
.dark-surface .btn-tertiary.s-active,
.btn-tertiary.on-petrol:active,
.dark-surface .btn-tertiary:active {
  background: var(--tint-on-dark-strong);
}
.btn-link.on-petrol,
.dark-surface .btn-link {
  color: var(--lifted-orange);
  text-decoration-color: var(--lifted-orange);
}
/* Was malformed until v2.207: this rule had swallowed a stray duplicate of the
   .btn base declarations, so a disabled tertiary button on petrol was also
   re-setting its own display, padding, radius and background. */
.btn-tertiary.on-petrol.s-disabled,
.dark-surface .btn-tertiary.s-disabled {
  color: var(--text-on-dark-disabled);
}
/* ---------- BUTTON COLOUR (v2.473, revised v2.474 / v2.483) ----------
   Emphasis (.btn-primary / .btn-secondary / .btn-tertiary) and colour combine.
   Allowed: primary: brand, neutral (white fill), danger, warning (ink label),
   success (filled, the Done button). Secondary: brand, danger, warning,
   success (outline). Tertiary: neutral (default), accent (.is-accent, orange
   text), danger, warning, success. No info colour.
   Deprecated aliases, kept one version: .btn-danger = .btn-primary.is-danger,
   .btn-white = .btn-primary.is-neutral, .btn-link = .btn-tertiary (an action)
   or .text-link (navigation). */
.btn-primary.is-danger {
  background: var(--error-text);
  color: var(--surface-elevated);
}
.btn-primary.is-danger.s-hover,
.btn-primary.is-danger:hover,
.btn-primary.is-danger.s-active,
.btn-primary.is-danger:active {
  background: var(--error-press);
}
.btn-primary.is-warning {
  background: var(--warning);
  color: var(--ink);
}
.btn-primary.is-warning.s-hover,
.btn-primary.is-warning:hover,
.btn-primary.is-warning.s-active,
.btn-primary.is-warning:active {
  background: var(--warning-press);
  color: var(--ink);
}
.btn-secondary.is-danger {
  color: var(--error-text);
  box-shadow: inset 0 0 0 1px var(--error-text);
}
.btn-secondary.is-danger.s-hover,
.btn-secondary.is-danger:hover {
  background: var(--tint-error-bg);
  color: var(--error-press);
  box-shadow: inset 0 0 0 1px var(--error-press);
}
.btn-secondary.is-danger.s-active,
.btn-secondary.is-danger:active {
  background: var(--tint-error-medium);
  color: var(--error-press);
}
.btn-tertiary.is-danger {
  color: var(--error-text);
}
.btn-tertiary.is-danger.s-hover,
.btn-tertiary.is-danger:hover {
  background: var(--tint-error-bg);
  color: var(--error-press);
}
.btn-tertiary.is-danger.s-active,
.btn-tertiary.is-danger:active {
  background: var(--tint-error-medium);
  color: var(--error-press);
}
.btn-secondary.is-warning {
  color: var(--warning-text);
  box-shadow: inset 0 0 0 1px var(--warning-text);
}
.btn-secondary.is-warning.s-hover,
.btn-secondary.is-warning:hover {
  background: var(--tint-warning-bg);
  color: var(--warning-text);
  box-shadow: inset 0 0 0 1px var(--warning-text);
}
.btn-secondary.is-warning.s-active,
.btn-secondary.is-warning:active {
  background: var(--tint-warning-medium);
  color: var(--warning-text);
}
.btn-tertiary.is-warning {
  color: var(--warning-text);
}
.btn-tertiary.is-warning.s-hover,
.btn-tertiary.is-warning:hover {
  background: var(--tint-warning-bg);
  color: var(--warning-text);
}
.btn-tertiary.is-warning.s-active,
.btn-tertiary.is-warning:active {
  background: var(--tint-warning-medium);
  color: var(--warning-text);
}
.btn-secondary.is-success {
  color: var(--success-text);
  box-shadow: inset 0 0 0 1px var(--success-text);
}
.btn-secondary.is-success.s-hover,
.btn-secondary.is-success:hover {
  background: var(--tint-success-bg);
  color: var(--success-press);
  box-shadow: inset 0 0 0 1px var(--success-press);
}
.btn-secondary.is-success.s-active,
.btn-secondary.is-success:active {
  background: var(--tint-success-medium);
  color: var(--success-press);
}
.btn-tertiary.is-success {
  color: var(--success-text);
}
.btn-tertiary.is-success.s-hover,
.btn-tertiary.is-success:hover {
  background: var(--tint-success-bg);
  color: var(--success-press);
}
.btn-tertiary.is-success.s-active,
.btn-tertiary.is-success:active {
  background: var(--tint-success-medium);
  color: var(--success-press);
}
.btn-tertiary.is-accent {
  color: var(--primary-press);
}
.btn-tertiary.is-accent.s-hover,
.btn-tertiary.is-accent:hover {
  background: var(--tint-accent-soft);
  color: var(--accent-text-on-tint);
}
.btn-tertiary.is-accent.s-active,
.btn-tertiary.is-accent:active {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
}
.btn-primary.is-neutral {
  background: var(--surface-elevated);
  color: var(--ink);
  box-shadow: var(--dp-shadow);
}
.btn-primary.is-neutral.s-hover,
.btn-primary.is-neutral:hover {
  background: var(--surface-elevated);
  color: var(--ink);
  box-shadow: var(--dp-shadow-hi);
}
.btn-primary.is-neutral.s-active,
.btn-primary.is-neutral:active {
  background: var(--paper);
  color: var(--ink);
  box-shadow: var(--dp-shadow);
}
.btn-primary.is-success {
  background: var(--success-text);
  color: var(--surface-elevated);
}
.btn-primary.is-success.s-hover,
.btn-primary.is-success:hover,
.btn-primary.is-success.s-active,
.btn-primary.is-success:active {
  background: var(--success-press);
  color: var(--surface-elevated);
}
.dark-surface .btn-primary.is-danger,
.btn-primary.on-petrol.is-danger {
  background: var(--error-on-dark);
  color: var(--petrol-deep);
}
.dark-surface .btn-primary.is-danger.s-hover,
.btn-primary.on-petrol.is-danger.s-hover,
.dark-surface .btn-primary.is-danger:hover,
.btn-primary.on-petrol.is-danger:hover,
.dark-surface .btn-primary.is-danger.s-active,
.btn-primary.on-petrol.is-danger.s-active,
.dark-surface .btn-primary.is-danger:active,
.btn-primary.on-petrol.is-danger:active {
  background: var(--error-on-dark-press);
  color: var(--petrol-deep);
}
.dark-surface .btn-primary.is-warning,
.btn-primary.on-petrol.is-warning {
  background: var(--warning-on-dark);
  color: var(--petrol-deep);
}
.dark-surface .btn-primary.is-warning.s-hover,
.btn-primary.on-petrol.is-warning.s-hover,
.dark-surface .btn-primary.is-warning:hover,
.btn-primary.on-petrol.is-warning:hover,
.dark-surface .btn-primary.is-warning.s-active,
.btn-primary.on-petrol.is-warning.s-active,
.dark-surface .btn-primary.is-warning:active,
.btn-primary.on-petrol.is-warning:active {
  background: var(--warning);
  color: var(--petrol-deep);
}
.dark-surface .btn-secondary.is-danger,
.btn-secondary.on-petrol.is-danger {
  color: var(--error-on-dark);
  box-shadow: inset 0 0 0 1px var(--error-on-dark);
}
.dark-surface .btn-secondary.is-danger.s-hover,
.btn-secondary.on-petrol.is-danger.s-hover,
.dark-surface .btn-secondary.is-danger:hover,
.btn-secondary.on-petrol.is-danger:hover,
.dark-surface .btn-secondary.is-danger.s-active,
.btn-secondary.on-petrol.is-danger.s-active,
.dark-surface .btn-secondary.is-danger:active,
.btn-secondary.on-petrol.is-danger:active {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
  box-shadow: inset 0 0 0 1px var(--error-on-dark);
}
.dark-surface .btn-tertiary.is-danger,
.btn-tertiary.on-petrol.is-danger {
  color: var(--error-on-dark);
}
.dark-surface .btn-tertiary.is-danger.s-hover,
.btn-tertiary.on-petrol.is-danger.s-hover,
.dark-surface .btn-tertiary.is-danger:hover,
.btn-tertiary.on-petrol.is-danger:hover,
.dark-surface .btn-tertiary.is-danger.s-active,
.btn-tertiary.on-petrol.is-danger.s-active,
.dark-surface .btn-tertiary.is-danger:active,
.btn-tertiary.on-petrol.is-danger:active {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .btn-secondary.is-warning,
.btn-secondary.on-petrol.is-warning {
  color: var(--warning-on-dark);
  box-shadow: inset 0 0 0 1px var(--warning-on-dark);
}
.dark-surface .btn-secondary.is-warning.s-hover,
.btn-secondary.on-petrol.is-warning.s-hover,
.dark-surface .btn-secondary.is-warning:hover,
.btn-secondary.on-petrol.is-warning:hover,
.dark-surface .btn-secondary.is-warning.s-active,
.btn-secondary.on-petrol.is-warning.s-active,
.dark-surface .btn-secondary.is-warning:active,
.btn-secondary.on-petrol.is-warning:active {
  background: var(--tint-warning-on-dark);
  color: var(--warning-on-dark);
  box-shadow: inset 0 0 0 1px var(--warning-on-dark);
}
.dark-surface .btn-tertiary.is-warning,
.btn-tertiary.on-petrol.is-warning {
  color: var(--warning-on-dark);
}
.dark-surface .btn-tertiary.is-warning.s-hover,
.btn-tertiary.on-petrol.is-warning.s-hover,
.dark-surface .btn-tertiary.is-warning:hover,
.btn-tertiary.on-petrol.is-warning:hover,
.dark-surface .btn-tertiary.is-warning.s-active,
.btn-tertiary.on-petrol.is-warning.s-active,
.dark-surface .btn-tertiary.is-warning:active,
.btn-tertiary.on-petrol.is-warning:active {
  background: var(--tint-warning-on-dark);
  color: var(--warning-on-dark);
}
.dark-surface .btn-secondary.is-success,
.btn-secondary.on-petrol.is-success {
  color: var(--success-on-dark);
  box-shadow: inset 0 0 0 1px var(--success-on-dark);
}
.dark-surface .btn-secondary.is-success.s-hover,
.btn-secondary.on-petrol.is-success.s-hover,
.dark-surface .btn-secondary.is-success:hover,
.btn-secondary.on-petrol.is-success:hover,
.dark-surface .btn-secondary.is-success.s-active,
.btn-secondary.on-petrol.is-success.s-active,
.dark-surface .btn-secondary.is-success:active,
.btn-secondary.on-petrol.is-success:active {
  background: var(--tint-success-on-dark);
  color: var(--success-on-dark);
  box-shadow: inset 0 0 0 1px var(--success-on-dark);
}
.dark-surface .btn-tertiary.is-success,
.btn-tertiary.on-petrol.is-success {
  color: var(--success-on-dark);
}
.dark-surface .btn-tertiary.is-success.s-hover,
.btn-tertiary.on-petrol.is-success.s-hover,
.dark-surface .btn-tertiary.is-success:hover,
.btn-tertiary.on-petrol.is-success:hover,
.dark-surface .btn-tertiary.is-success.s-active,
.btn-tertiary.on-petrol.is-success.s-active,
.dark-surface .btn-tertiary.is-success:active,
.btn-tertiary.on-petrol.is-success:active {
  background: var(--tint-success-on-dark);
  color: var(--success-on-dark);
}
.dark-surface .btn-tertiary.is-accent,
.btn-tertiary.on-petrol.is-accent {
  color: var(--lifted-orange);
}
.dark-surface .btn-tertiary.is-accent.s-hover,
.btn-tertiary.on-petrol.is-accent.s-hover,
.dark-surface .btn-tertiary.is-accent:hover,
.btn-tertiary.on-petrol.is-accent:hover {
  background: var(--tint-lifted-soft);
  color: var(--lifted-orange);
}
.dark-surface .btn-tertiary.is-accent.s-active,
.btn-tertiary.on-petrol.is-accent.s-active,
.dark-surface .btn-tertiary.is-accent:active,
.btn-tertiary.on-petrol.is-accent:active {
  background: var(--tint-lifted-strong);
  color: var(--lifted-orange);
}
.dark-surface .btn-primary.is-neutral,
.btn-primary.on-petrol.is-neutral {
  background: var(--surface-elevated);
  color: var(--ink);
  box-shadow: none;
}
.dark-surface .btn-primary.is-neutral.s-hover,
.btn-primary.on-petrol.is-neutral.s-hover,
.dark-surface .btn-primary.is-neutral:hover,
.btn-primary.on-petrol.is-neutral:hover,
.dark-surface .btn-primary.is-neutral.s-active,
.btn-primary.on-petrol.is-neutral.s-active,
.dark-surface .btn-primary.is-neutral:active,
.btn-primary.on-petrol.is-neutral:active {
  background: var(--paper);
  color: var(--ink);
  box-shadow: none;
}
.dark-surface .btn-primary.is-success,
.btn-primary.on-petrol.is-success {
  background: var(--success-on-dark);
  color: var(--petrol-deep);
  box-shadow: none;
}
.dark-surface .btn-primary.is-success.s-hover,
.btn-primary.on-petrol.is-success.s-hover,
.dark-surface .btn-primary.is-success:hover,
.btn-primary.on-petrol.is-success:hover,
.dark-surface .btn-primary.is-success.s-active,
.btn-primary.on-petrol.is-success.s-active,
.dark-surface .btn-primary.is-success:active,
.btn-primary.on-petrol.is-success:active {
  background: var(--success);
  color: var(--petrol-deep);
}
/* Pressed state (v2.477): every filled button has a pressed step distinct
   from hover. Light: one shade deeper. Petrol: a deeper fill would drop the
   petrol-deep label below 4.5:1, so the press is an inset shadow on the hover
   fill, plus the 1px move. */
.btn-primary.is-danger.s-active,
.btn-primary.is-danger:active,
.btn-danger.s-active,
.btn-danger:active {
  background: var(--error-active);
}
.btn-primary.is-warning.s-active,
.btn-primary.is-warning:active {
  background: var(--warning-active);
  color: var(--ink);
}
.btn-primary.is-success.s-active,
.btn-primary.is-success:active {
  background: var(--success-active);
  color: var(--surface-elevated);
}
.dark-surface .btn-primary.s-active,
.dark-surface .btn-primary:active,
.btn-primary.on-petrol.s-active,
.btn-primary.on-petrol:active {
  box-shadow: var(--shadow-button-pressed);
}
.dark-surface .btn-primary.is-neutral.s-active,
.dark-surface .btn-primary.is-neutral:active,
.btn-primary.on-petrol.is-neutral.s-active,
.btn-primary.on-petrol.is-neutral:active {
  box-shadow: var(--shadow-button-pressed);
}

/* ---------- TEXT LINK ----------
   The link colour had no owner: there was no `a` rule anywhere in this system,
   so every prose link inherited --primary at 3.29:1 on paper — a 1.4.3 failure
   across 212 cross-references and the pullable .text-link atom itself. It takes
   --primary-press on light (the accent step already used wherever accent text
   must pass) and --lifted-orange on petrol, where --primary would be wrong.
   NEVER underlined, in any state (v2.453): every anchor and .btn-link resets
   text-decoration, so a host or browser default cannot add one. */
a,
.text-link {
  color: var(--primary-press);
  text-decoration: none;
}
a:hover,
a:focus,
a:active,
.text-link:hover,
.text-link:focus,
.text-link:active {
  text-decoration: none;
}
a:hover,
.text-link:hover {
  color: var(--accent-text-on-tint);
}
a:focus-visible,
.text-link:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
  border-radius: var(--radius-sm);
}
.dark-surface a,
.dark-surface .text-link,
.photo-viewer:not(.is-light) a {
  color: var(--lifted-orange);
}
.dark-surface a:hover,
.dark-surface .text-link:hover {
  color: var(--lifted-orange-press);
}
.dark-surface a:focus-visible,
.dark-surface .text-link:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}

/* ---------- INPUT ---------- */
.input {
  font-family: var(--font-body);
  font-size: 14.5px;
  padding: 10px 14px;
  border-radius: var(--radius);
  background: var(--surface-elevated);
  color: var(--ink);
  border: var(--input-border);
  transition:
    border-color 150ms,
    box-shadow 150ms,
    background 150ms;
  width: 100%;
  line-height: 1.3;
  outline: none;
}
.input::placeholder {
  color: var(--ink-3);
}
/* Disabled, written the way .select is: class AND native attribute in one rule.
   Until v2.217 .input had NO disabled styling — the only differing property
   between an editable and a disabled field was `cursor`, which needs a pointer
   already hovering and does not exist on touch at all. Four disabled fields in
   the docs were pixel-identical to editable ones. */
.input.s-hover {
  border-color: var(--border-stronger);
}
.input.s-focus,
.input.s-typing,
.input:focus {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.input.s-error {
  border-color: var(--error);
  box-shadow: var(--focus-ring-error);
}
.input.s-success {
  border-color: var(--success);
  box-shadow: var(--focus-ring-success);
}
.input.s-disabled,
.input:disabled {
  background: var(--paper);
}

.input.s-typing {
  display: flex;
  align-items: center;
  cursor: text;
  min-height: 40px;
}
.input .caret {
  display: inline-block;
  width: 1.5px;
  height: 16px;
  background: var(--primary);
  margin-left: 1px;
  animation: blink 1s steps(2, start) infinite;
}
@keyframes blink {
  to {
    opacity: 0;
  }
}

/* ---------- SELECT ----------
   ALWAYS a non-native control: a <button class="select"> trigger plus a
   .select-panel listbox (add .is-searchable past 7 options). Never a native
   <select>/<option> — the native popup is drawn by the operating system, so it
   cannot take our tokens, surface, tick, search header, or the petrol theme,
   and its closed state ignores every state class below. */
.select {
  font-family: var(--font-body);
  font-size: 14.5px;
  padding: 10px 38px 10px 14px;
  border-radius: var(--radius);
  background: var(--surface-elevated);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  transition:
    border-color 150ms,
    box-shadow 150ms;
  width: 100%;
  line-height: 1.3;
  cursor: pointer;
  outline: none;
  display: flex;
  align-items: center;
  gap: 8px;
  appearance: none;
  -webkit-appearance: none;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M3 4.5L6 7.5L9 4.5" stroke="%236A6A65" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>');
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px;
  text-align: left;
}
/* .select paints its own chevron as a background-image. A trigger that supplies
   its OWN icon element (so the caret can rotate on open, or take a token colour
   the data-URI cannot) must add .has-caret, or the field shows two arrows. */
.select.has-caret {
  background-image: none;
  padding-right: 14px;
}
/* The trigger's value label — one line, ellipsised, so a long option can never
   push the caret out of the field. */
.select > span:first-child {
  flex: 1;
  min-width: 0;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.select:focus-visible {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.dark-surface .select:focus-visible {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* The caret glyph belongs to the component, not to each instance's inline style.
   On petrol --ink-3 measures 1.85:1 against the trigger and fails WCAG 1.4.11 —
   it is the only affordance signalling the field opens, so it takes
   --text-on-dark-muted (4.3:1), matching the .select-mini sibling. */
.select .mdi {
  color: var(--ink-3);
}
.dark-surface .select .mdi {
  color: var(--text-on-dark-muted);
}
.select.placeholder {
  color: var(--ink-3);
}
.select.s-hover {
  border-color: var(--border-stronger);
}
.select.s-focus,
.select:focus,
.select.s-open {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.select.s-open .mdi {
  color: var(--primary);
}
.dark-surface .select.s-open .mdi {
  color: var(--lifted-orange);
}
.select.s-error {
  border-color: var(--error);
  box-shadow: var(--focus-ring-error);
}

/* Positioning context for a control that anchors a panel/affordance to itself.
   .input-wrap is an accepted alias of .select-wrap — same rule, one declaration —
   kept because product code addresses inputs by that name. Do not give it its own
   declaration or the two will drift. */
.form-select,
.form-input,
.filter-select,
.select-wrap,
.input-wrap {
  position: relative;
}
/* Resting boundary on petrol: 0.45 white (--border-on-dark-hover) is the minimum
   that clears 3:1 against a petrol field — the border is the only affordance for a
   closed trigger — so hover steps up to --border-on-dark-emphasis (0.62). */
.dark-surface .select {
  background-color: var(--tint-on-dark-faint);
  color: var(--surface-elevated);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .select:hover {
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .select:focus,
.dark-surface .select.s-focus,
.dark-surface .select.s-open {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .select.placeholder {
  color: var(--text-on-dark-secondary);
}
/* Was `.select.s-disabled, .dark-surface .select:disabled` until v2.217 — the first
   selector had no .dark-surface prefix, so a LIGHT .select.s-disabled picked up
   the petrol fill from this later rule at equal specificity. */

/* ---------- DISABLED FIELDS ----------
   One rule for the whole family. Written together because v2.217 found the set
   had drifted three ways at once: .input had NO disabled styling at all (only
   the UA cursor differed), .select.s-disabled rendered the LIGHT treatment on
   petrol (paper fill, dark ink, on a petrol surface), and every petrol disabled
   fill resolved to --tint-on-dark-faint, which IS the enabled fill.
   Both the class and the native attribute, the way every .btn-* is written. */
.input.is-disabled,
.select.is-disabled,
.input.s-disabled,
.input:disabled,
.select.s-disabled,
.select:disabled,
.select-mini.s-disabled,
.select-mini:disabled {
  background-color: var(--input-bg-disabled);
  color: var(--ink-3);
  border-color: var(--line);
  box-shadow: none;
  cursor: not-allowed;
}
.input.s-disabled::placeholder,
.input:disabled::placeholder {
  color: var(--ink-3);
}
/* On dark (petrol): the base field. Declared before the disabled / error rules so they still win. */
.dark-surface .input {
  background: var(--tint-on-dark-faint);
  color: var(--surface-elevated);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .input:hover {
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .input::placeholder,
.dark-surface input::placeholder,
.dark-surface textarea::placeholder {
  color: var(--text-on-dark-secondary);
}
.dark-surface .input:focus {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .input.is-disabled,
.dark-surface .select.is-disabled,
.dark-surface .input.s-disabled,
.dark-surface .input:disabled,
.dark-surface .select.s-disabled,
.dark-surface .select:disabled,
.dark-surface .select-mini.s-disabled,
.dark-surface .select-mini:disabled {
  background-color: var(--input-bg-disabled-on-dark);
  color: var(--text-on-dark-disabled);
  border-color: var(--border-on-dark);
}
.dark-surface .input.s-disabled::placeholder,
.dark-surface .input:disabled::placeholder {
  color: var(--text-on-dark-disabled);
}

/* ---------- LOCKED (DEPENDENT) FIELDS ----------
   A pick control that cannot be used YET: it unlocks when a prerequisite field
   is chosen (Size band unlocks after Produce type; Colour grade after Variety
   — Dashboard / Assessments, More filters). Not disabled: disabled means "not
   available here"; locked means "pick something else first", so the trigger
   text carries the instruction in italic ("Pick produce type first") and
   the trailing glyph is a LOCK, never a chevron — wording + icon carry the
   state, not colour alone. Fill reuses the disabled family above so inertness
   reads instantly; on petrol the text stays --text-on-dark-muted (not the
   disabled tone) because the instruction must remain readable. Defined for
   pick controls only (.select and its compact sibling .select-mini) — a locked
   free-text input is a disabled input with help text. Markup:
   aria-disabled="true" (plus tabindex="-1" on a <button class="select">), the
   instruction as the trigger text — nothing else: the state paints its own
   lock (below) and drops the .select chevron.
   .is-locked, not .s-locked — no native pseudo backs this state.
*/
/* v2.382 — the state paints its OWN lock via ::after (codepoint \e897, so it
   renders identically under the ligature font here and the class-based icon
   font the product repos ship — no glyph text in markup, nothing to clip).
   The trigger becomes display:block so the instruction ellipsises as plain
   text while the lock sits in reserved right padding; any legacy inline
   <span class="mdi">lock</span> is hidden rather than doubled. */
.select.is-locked,
.select-mini.is-locked {
  display: block;
  position: relative;
  text-align: left;
  background-color: var(--input-bg-disabled);
  color: var(--ink-3);
  font-style: italic;
  border-color: var(--line);
  box-shadow: none;
  cursor: not-allowed;
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding-right: 34px;
}
.select.is-locked {
  background-image: none;
}
.select.is-locked::after,
.select-mini.is-locked::after {
  content: '';
  width: 16px;
  height: 16px;
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  background-color: currentColor;
  -webkit-mask: var(--icon-mask-lock) center / contain no-repeat;
  mask: var(--icon-mask-lock) center / contain no-repeat;
  color: var(--ink-3);
}
.select.is-locked .mdi,
.select-mini.is-locked .mdi {
  display: none;
}
.dark-surface .select.is-locked,
.dark-surface .select-mini.is-locked {
  background-color: var(--input-bg-disabled-on-dark);
  color: var(--text-on-dark-muted);
  border-color: var(--border-on-dark);
}
.dark-surface .select.is-locked::after,
.dark-surface .select-mini.is-locked::after {
  color: var(--text-on-dark-muted);
}

/* ---------- SELECT PANEL ---------- (the dropdown surface of the Select field)
   .select-panel is the panel, .select-option the row. Add .is-searchable when the
   list has MORE THAN 7 options: the panel then carries a .menu-search header and
   its rows move inside a scrolling .select-options region. Below 8 options the
   whole list is visible at a glance and a search field is noise. */
.select-panel {
  margin-top: 6px;
  background: var(--surface-elevated);
  border-radius: var(--radius-md);
  border: 0.5px solid var(--line);
  box-shadow: var(--shadow-card);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.select-option {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  min-height: 36px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--ink);
  cursor: pointer;
}
/* Option rows are often <button>s (keyboard + click for free). Reset the
   browser button chrome so a button row renders exactly like a div row:
   no outset border, no grey fill, left-aligned, full width (v2.477). */
button.select-option {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  border: 0;
  background-color: transparent;
  text-align: left;
  line-height: 1.3;
}
/* Pointer hover and keyboard highlight are ONE visual state — the arrow keys must
   not invent a second, competing appearance. */
.select-option:hover,
.select-option.is-highlighted {
  background: var(--tint-neutral-soft);
}
/* Selected pairs the accent tint with a tick, so selection never rests on colour
   alone. --primary-press was chosen over --primary (3.53:1) but MEASURED ON THE
   LIVE PANEL it is 4.48:1, not the 5.02:1 this comment used to claim — the tint
   under the label costs more than token-on-white arithmetic suggests. Logged in
   Accessibility → Open items ("Accent labels on accent tints"); the fix is
   --accent-text-on-tint, pending the sweep decision. */
.select-option.is-selected {
  background: var(--tint-accent-soft);
  color: var(--accent-text-on-tint);
  font-weight: 500;
}
/* v2.448: pseudo-element icons are SVG masks painted in currentColor, never icon-font
   ligatures or codepoints — hosts using a different icon font (@mdi/font) render them too. */
.select-option.is-selected::after {
  content: '';
  display: block;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: var(--icon-mask-check) center / contain no-repeat;
  mask: var(--icon-mask-check) center / contain no-repeat;
  color: var(--accent-text-on-tint);
}
.select-option .so-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.select-option.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
/* Searchable: the search header is pinned, only the rows scroll. The cap sits at
   7.5 rows so a partial row shows there is more below. */
.select-panel.is-searchable {
  padding-top: 2px;
}
.select-options {
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: 280px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
/* Clear-selection footer — an optional pinned row under the options (it never
   scrolls away and never filters out): a full-width quiet button that empties
   the selection and returns the trigger to its placeholder. Render it only
   while something is selected. */
.select-clear {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 38px;
  margin-top: 5px;
  padding: 9px 10px;
  border: none;
  border-top: 0.5px solid var(--line);
  background: transparent;
  border-radius: 0 0 var(--radius-sm) var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--ink-2);
}
.select-clear:hover {
  background: var(--tint-neutral-soft);
  color: var(--ink);
}
.select-clear:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
/* Shared no-results row for any dropdown panel (.select-panel or .menu). */
.panel-empty {
  padding: 16px 10px;
  text-align: center;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink-2);
}
.panel-empty strong {
  color: var(--ink);
  font-weight: 600;
}
.dark-surface .select-panel {
  background: var(--petrol);
  border-color: var(--border-on-dark-medium);
  box-shadow: var(--dp-shadow-hi);
}
.dark-surface .select-option {
  color: var(--text-on-dark-secondary);
}
.dark-surface .select-option:hover,
.dark-surface .select-option.is-highlighted {
  background: var(--tint-on-dark-medium);
  color: var(--text-on-dark);
}
.dark-surface .select-option.is-selected {
  background: var(--tint-lifted-medium);
  color: var(--surface-elevated);
}
.dark-surface .select-option.is-selected::after {
  color: var(--surface-elevated);
}
.dark-surface .select-option .so-meta {
  color: var(--text-on-dark-muted);
}
.dark-surface .select-clear {
  border-top-color: var(--tint-on-dark-soft);
  color: var(--text-on-dark-secondary);
}
.dark-surface .select-clear:hover {
  background: var(--tint-on-dark-medium);
  color: var(--text-on-dark);
}
.dark-surface .select-clear:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .panel-empty {
  color: var(--text-on-dark-secondary);
}
.dark-surface .panel-empty strong {
  color: var(--text-on-dark);
}

/* ---------- CHOICE (radio + checkbox) ---------- */
.choice {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  font-size: 14px;
  color: var(--ink);
  user-select: none;
  line-height: 1.2;
}
/* The fake .vis box is the visible control; hide the native input accessibly so it
   doesn't render alongside the styled box while remaining focusable/operable. */
.choice input[type='checkbox'],
.choice input[type='radio'] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
  clip-path: inset(50%);
  overflow: hidden;
}
.choice .vis {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  background: var(--surface-elevated);
  border: 1px solid rgba(0, 0, 0, 0.3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition:
    border-color 150ms,
    background 150ms,
    box-shadow 150ms;
  color: var(--surface-elevated);
}
.choice.radio .vis {
  border-radius: 50%;
}
.choice.check .vis {
  box-sizing: border-box;
  width: var(--checkbox-size);
  height: var(--checkbox-size);
  border-radius: var(--checkbox-radius);
}

.choice.s-hover .vis {
  border-color: var(--ink-2);
}
.choice.s-focus .vis {
  box-shadow: var(--focus-ring-accent-strong);
  border-color: var(--primary);
}

.choice.radio.checked .vis {
  border-color: var(--primary);
}
.choice.radio.checked .vis::after {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--primary);
}

.choice.check.checked .vis,
.choice.check.indeterminate .vis {
  background: var(--primary);
  border-color: var(--primary);
}
.choice.check.checked .vis::after {
  content: '';
  width: 12px;
  height: 12px;
  background-color: currentColor;
  -webkit-mask: var(--icon-mask-check-thin) center / 12px no-repeat;
  mask: var(--icon-mask-check-thin) center / 12px no-repeat;
}
.choice.check.indeterminate .vis::after {
  content: '';
  width: 12px;
  height: 12px;
  background-color: currentColor;
  -webkit-mask: var(--icon-mask-dash-thin) center / 12px no-repeat;
  mask: var(--icon-mask-dash-thin) center / 12px no-repeat;
}

.choice.s-disabled {
  opacity: 0.42;
  cursor: not-allowed;
  color: var(--ink-2);
}
.choice.s-disabled .vis {
  background: var(--paper);
  border-color: var(--line);
}
.choice.s-disabled.radio.checked .vis::after {
  background: var(--ink-3);
}
.choice.s-disabled.check.checked .vis,
.choice.s-disabled.check.indeterminate .vis {
  background: var(--ink-3);
  border-color: var(--ink-3);
}

.choice.s-error .vis {
  border-color: var(--error);
}
.choice.s-error.radio.checked .vis::after {
  background: var(--error);
}
.choice.s-error.check.checked .vis {
  background: var(--error);
  border-color: var(--error);
}

/* ---------- FIELD STACK / LABEL / HELP / MSG ---------- */
.field-stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-height: 86px;
}
/* Compact caption-above-a-control row for dense panels (More filters, sheets):
   the sibling of .field-stack with NO reserved help/error slot — .field-stack's
   86px min-height exists so validation messages don't reflow a form, which a
   filter panel never shows. Formalised v2.382; the markup convention
   (<div class="field"><label>…</label><control>) long predates the rule. */
.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.field > label {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.003em;
}
.dark-surface .field > label {
  color: var(--text-on-dark-secondary);
}
/* One class for the uppercase-mono caption with a leading icon (v2.382) —
   previously composed by hand from the mono ladder + an .mdi each time. */
.icon-caption {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.icon-caption .mdi {
  font-size: 13px;
  flex-shrink: 0;
}
.dark-surface .icon-caption {
  color: var(--text-on-dark-muted);
}

.field-label {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -0.003em;
}
/* Icon-prefixed field label — the glyph names the *kind* of value (variety, lot,
   room). Decorative: it repeats what the label already says, so it is
   aria-hidden and the label text is never dropped in favour of the icon. */
.field-label.has-icon {
  display: flex;
  align-items: center;
  gap: 7px;
}
.field-label.has-icon > .mdi {
  font-size: 16px;
  color: var(--ink-3);
  flex-shrink: 0;
}
.dark-surface .field-label.has-icon > .mdi {
  color: var(--text-on-dark-muted);
}
.field-help {
  font-size: 11.5px;
  color: var(--ink-3);
  line-height: 1.4;
}
/* Hint with a leading glyph — the icon stays top-aligned and never re-flows
   under the text, so a wrapping hint keeps a clean text column. */
.field-help.has-icon,
.help.has-icon {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  line-height: 1.45;
}
.field-help.has-icon > .mdi,
.help.has-icon > .mdi {
  font-size: 14px;
  flex-shrink: 0;
  margin-top: 1px;
}
.range-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.field-msg {
  font-size: 11.5px;
  line-height: 1.4;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.field-msg.is-error {
  color: var(--error-text);
}
.field-msg.is-success {
  color: var(--success-text);
}
.field-msg svg {
  flex-shrink: 0;
}

/* ---------- BACK BUTTON ----------
   Leaves a detail page. First in the .dashboard-header's reading order, before the
   page title, because the reader asks "where am I" and "how do I get out" in that
   order.

   NO LABEL — ICON ONLY (v2.399). v2.250 had it name its destination ("Load
   assessments"), v2.260 cut that to a bare "Back", and v2.399 removes the label
   entirely: the BREADCRUMB directly below the header already spells out the trail
   (LOAD ASSESSMENTS / ASSESSMENT DETAIL VIEW), so any label on the button is
   repetition. The accessible name lives in aria-label="Back", and the control is a
   44x44 target at every width — touched on small screens, pointed at on large
   ones, the same target either way.

   NO FILL, EVER. Not at rest, not on hover, not on press. The whole affordance is
   the colour change plus the arrow's step, which is why it works identically on
   paper and on petrol and why it needs no tint to sit on. It also removes the AA
   problem v2.252 had to solve: an orange label on a tint measured 3.81:1 on petrol,
   and there is now no tint under it.

   VISIBILITY IS THE RULE: present on a detail page, absent on a top-level one,
   added and removed with the route — never disabled. There is no such thing as a
   back button with nowhere to go. */
.back-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  margin-left: -11px;
  padding: 0;
  border: none;
  background: none;
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
  transition: color 150ms;
}
.back-button .mdi {
  font-size: 20px;
  flex-shrink: 0;
  transition:
    transform 150ms,
    color 150ms;
}
/* Hover and press — the arrow goes accent and steps left. --primary-press, not
   --primary: the one accent-as-foreground step the system uses on paper. */
.back-button:hover,
.back-button:focus-visible {
  color: var(--primary-press);
}
.back-button:hover .mdi {
  transform: translateX(-2px);
}
.back-button:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
  border-radius: var(--radius-sm);
}
.back-button:active {
  color: var(--accent-text-on-tint);
}
.back-button:active .mdi {
  transform: translateX(-3px);
}
/* Hidden — the animated transition between routes only, never a resting condition.
   A detail page with no parent removes the button from the DOM. */
.back-button.is-hidden {
  opacity: 0;
  pointer-events: none;
  transform: translateX(-6px);
  transition:
    color 150ms,
    opacity 150ms,
    transform 150ms;
}
/* On petrol the accent is --lifted-orange: --primary-press there is 2.31:1. With no
   fill under it the glyph carries the accent directly, which is what the product
   does on both surfaces.
   Petrol PRESS keeps --lifted-orange rather than stepping to --lifted-orange-press:
   that step is a press FILL value, not a foreground one. The arrow's extra 3px of
   travel carries the press instead, and it costs no contrast. */
.dark-surface .back-button {
  color: var(--text-on-dark);
}
.dark-surface .back-button:hover,
.dark-surface .back-button:focus-visible {
  color: var(--lifted-orange);
}
.dark-surface .back-button:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .back-button:active {
  color: var(--lifted-orange);
}

/* ---------- ACCENT AS TEXT ON PAPER ----------
   The inverse of the v2.214 exemption, and it had no register row. That row covers
   WHITE labels ON the raw accent fill; this is accent text ON paper — --primary
   (#EA5D2E) measures 3.29–3.44:1 there, while --primary-press is 4.91:1.
   An enumeration finds 74 rules setting color:var(--primary) without a .dark-surface
   prefix; the large majority are borders, fills or icon glyphs (graphics, 3:1). The
   seven below render TEXT, and are repointed. Two of them — .ci-arrow and the table
   sort arrow — are arguably graphics, but they sit beside repointed text and a split
   would read as an inconsistency rather than a rule.
   .tab.active is the one worth remembering: the v2.273 sweep repointed its CHILD
   .tab-count to 4.99 and left the parent label at 3.29, so the active tab's label was
   less legible than the badge inside it. A sweep scoped to "accent on a tint" could
   not see a parent whose own ground is paper. */

/* ---------- ON-DARK INK FALLBACKS ----------
   --ink-3 is a light-surface token. Any rule that sets it and has no .dark-surface
   counterpart renders it on petrol, where it measures 1.85–2.78:1. An enumeration of
   components.css finds 54 rules setting color:var(--ink-3) with no on-dark pair; the
   seven below are the ones with a petrol specimen proving the failure. The remaining
   47 have no petrol specimen at all, so they are either light-only by nature (form
   placeholders, disabled fields) or missing parity coverage — recorded in the
   register rather than fixed blind, because an override on a rule that never renders
   on petrol is dead weight and the measurement is what decides which. */
.dark-surface .sheet-eyebrow {
  color: var(--text-on-dark-muted);
}
.dark-surface .kicker.muted {
  color: var(--text-on-dark-secondary);
}
.dark-surface .chip .chip-count {
  color: var(--text-on-dark-secondary);
}
.dark-surface .metric-tile .mt-val .u {
  color: var(--text-on-dark-secondary);
}
.dark-surface .wheel-colon {
  color: var(--text-on-dark-secondary);
}
.dark-surface .demo-label {
  color: var(--text-on-dark-muted);
}
.dark-surface .tile-handle {
  color: var(--text-on-dark-secondary);
}

/* ---------- BADGES / TAGS ----------
   Compact inline tag (v2.20); icon optional, dot is default. Tone variants are
   decorative, never semantic — see TAG VARIANTS below. */
.tag-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  max-width: 160px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
}
.tag-pill .mdi {
  font-size: 11px;
  margin-left: -1px;
  flex-shrink: 0;
}
/* TRUNCATION (v2.368). A tag is ONE LINE, capped at 160px. Curated labels
   (variety, line, bay) are written to fit; an unbounded value (grower names,
   custom tags) goes in a .tag-label span — whole-tail ellipsis — with the full
   value on the pill's title attribute and reachable elsewhere on the surface.
   Icons and dots never shrink or truncate; never a second line, a smaller
   font, or a mid-string ellipsis. */
.tag-pill .live-dot {
  flex-shrink: 0;
}
.tag-pill .tag-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Surface-relative cap (v2.430). Inside a card whose tag row owns the full card
   width (dashboard scan cards: load number, grower name), .is-fluid lifts the
   160px cap to the container — the pill may grow to 100% of the row, and only
   when even that overflows does the whole-tail ellipsis (and the title rule)
   apply. 160px stays the default everywhere else; never use .is-fluid on inline
   tag rows where neighbours compete for the same line. */
.tag-pill.is-fluid {
  max-width: 100%;
}

/* ---------- TAG VARIANTS (colour decorative, never semantic) ---------- */
.tag-pill.is-orange {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
}
.tag-pill.is-petrol {
  background: var(--petrol-soft);
  color: var(--petrol);
}
.tag-pill.is-neutral {
  background: var(--tint-neutral);
  color: var(--ink-2);
}
.tag-pill.is-live {
  background: var(--tint-success-medium);
  color: var(--success-text);
}
.tag-pill.is-live .live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--success);
  box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18);
  animation: livepulse 2s ease-in-out infinite;
}
@keyframes livepulse {
  0%,
  100% {
    box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18);
  }
  50% {
    box-shadow: 0 0 0 5px rgba(16, 185, 129, 0.06);
  }
}
.dark-surface .tag-pill {
  background: var(--tint-on-dark-soft);
  color: rgba(255, 255, 255, 0.85);
}
.dark-surface .tag-pill.is-orange {
  background: var(--tint-lifted-strong);
  color: var(--surface-elevated);
}
.dark-surface .tag-pill.is-petrol {
  background: var(--tint-on-dark-soft);
  color: var(--surface-elevated);
}
.dark-surface .tag-pill.is-neutral {
  background: var(--tint-on-dark-faint);
  color: var(--text-on-dark-secondary);
}
.dark-surface .tag-pill.is-live {
  background: var(--tint-success-on-dark);
  color: var(--success-on-dark);
}
.dark-surface .tag-pill.is-live .live-dot {
  background: #34d399;
  box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.22);
}

/* ---------- BADGE SELECT (a badge that is a status select) ----------
   The room status badge: a .badge rendered as a button with a trailing caret;
   clicking opens a menu to change the status. Tone comes from the badge tone
   classes (.is-success / .is-info / .is-neutral …); read-only falls back to a
   plain .badge. Token-only, light + petrol. */
.badge-select-wrap {
  position: relative;
  display: inline-block;
}
.badge-select {
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
.badge-select .mdi {
  margin-left: 1px;
  margin-right: -2px;
}
.badge-select:hover,
.badge-select.is-open {
  box-shadow: inset 0 0 0 1px currentColor;
}
.badge-select:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.badge-select-menu {
  position: absolute;
  z-index: 70;
  top: calc(100% + 6px);
  right: 0;
  min-width: 184px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.badge-select-menu[hidden] {
  display: none;
}
.badge-select-option {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 36px;
  padding: 9px 10px;
  border: none;
  background: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 400;
  color: var(--ink);
  text-align: left;
}
.badge-select-option:hover {
  background: var(--tint-neutral-soft);
}
.badge-select-option.is-selected {
  background: var(--tint-accent-soft);
  color: var(--accent-text-on-tint);
  font-weight: 500;
}
.badge-select-option.is-selected::after {
  content: '';
  display: block;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: var(--icon-mask-check) center / contain no-repeat;
  mask: var(--icon-mask-check) center / contain no-repeat;
  margin-left: auto;
  color: var(--accent-text-on-tint);
}

.badge-select-option .bso-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.badge-select-option .bso-check {
  display: none;
}
.badge-select-option:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.dark-surface .badge-select-menu {
  background: var(--petrol);
  border-color: var(--border-on-dark-medium);
  box-shadow: var(--dp-shadow-hi);
}
.dark-surface .badge-select-option {
  color: var(--text-on-dark-secondary);
}
.dark-surface .badge-select-option.is-selected {
  background: var(--tint-lifted-medium);
  color: var(--surface-elevated);
}
.dark-surface .badge-select-option.is-selected::after {
  color: var(--surface-elevated);
}

.dark-surface .badge-select-option:hover {
  background: var(--tint-on-dark-medium);
  color: var(--text-on-dark);
}
.dark-surface .badge-select-option .bso-check {
  color: var(--lifted-orange);
}
.dark-surface .badge-select-option:focus-visible {
  outline-color: var(--lifted-orange);
}

/* ---------- CAPACITY BAR (fill / progress meter) ----------
   A thin track showing how full something is (room capacity, progress). The
   fill width is a live value (inline style); its tone is a modifier class.
   Token-only, light + petrol. */
.capacity-bar {
  height: 6px;
  background: var(--paper);
  border: 0.5px solid var(--line-2);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.capacity-bar-fill {
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--primary);
}
.capacity-bar-fill.is-accent {
  background: var(--primary);
}
.capacity-bar-fill.is-success {
  background: var(--success);
}
.capacity-bar-fill.is-info {
  background: var(--info);
}
.capacity-bar-fill.is-warning {
  background: var(--warning);
}
.capacity-bar-fill.is-neutral {
  background: var(--petrol-mid);
}
.dark-surface .capacity-bar {
  background: var(--tint-on-dark-soft);
  border-color: var(--tint-on-dark-strong);
}
.dark-surface .capacity-bar-fill,
.dark-surface .capacity-bar-fill.is-accent {
  background: var(--lifted-orange);
}
/* Each tone needs its own on-dark fill, and they must come AFTER the .dark-surface
   default: that default is (0,2,0), the same specificity as .capacity-bar-fill.is-info,
   so before v2.349 source order alone decided it and EVERY tone rendered accent on
   petrol. The bar looked like a deliberate monochrome choice while the spec table said
   the bar carries the state. */
.dark-surface .capacity-bar-fill.is-success {
  background: var(--success-on-dark);
}
.dark-surface .capacity-bar-fill.is-info {
  background: var(--info-on-dark);
}
.dark-surface .capacity-bar-fill.is-warning {
  background: var(--warning-on-dark);
}
.dark-surface .capacity-bar-fill.is-neutral {
  background: var(--neutral-on-dark);
}

/* ---------- KICKER ----------
   Mono uppercase pill that sits above a heading — the most-used label in the
   system. Default = primary on light; .muted neutral; .petrol on light;
   .lifted on dark (petrol) surfaces. */
.kicker {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-text-on-tint);
  background: var(--tint-accent-medium);
  border-radius: var(--radius-pill);
  padding: 5px 12px;
  margin-bottom: 14px;
  white-space: nowrap;
}
.kicker.muted {
  color: var(--ink-3);
  background: var(--line-2);
}
.kicker.petrol {
  color: var(--petrol);
  background: rgba(23, 58, 76, 0.1);
}
.kicker.lifted {
  color: var(--surface-elevated);
  background: var(--tint-lifted-soft);
}

/* ---------- CARD VARIANTS ---------- */
.card-demo {
  border-radius: 14px;
  padding: 26px 22px;
  min-height: 200px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  position: relative;
  overflow: hidden;
}
.card-demo.default {
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
}
.card-demo.marker {
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-top: 3px solid var(--primary);
}
.card-demo.anchor {
  background: linear-gradient(135deg, var(--petrol) 0%, var(--petrol-deep) 100%);
  color: var(--surface-elevated);
}

.card-demo h5 {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.018em;
  margin: 0;
}
.card-demo p {
  font-size: 13px;
  line-height: 1.5;
  margin: 0;
}
.card-demo.anchor p {
  color: var(--text-on-dark-secondary);
}

/* ---------- SHADOW TILES ----------
   The elevation specimen: one tile per shadow token, each labelled with the token
   name and the role it carries. Light surfaces only — on petrol elevation is a
   surface step, not a shadow. */
.shadow-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
  margin-top: 14px;
}
.shadow-grid .sh {
  background: var(--surface-elevated);
  border-radius: var(--radius-lg);
  padding: 24px;
  text-align: center;
}
.shadow-grid .sh-card {
  box-shadow: var(--shadow-card);
}
.shadow-grid .sh-frame {
  box-shadow: var(--shadow-frame);
}
.shadow-grid .sh-cta {
  box-shadow: var(--shadow-cta-glow);
}
.shadow-grid .nm {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-3);
  letter-spacing: 0.04em;
  margin-bottom: 6px;
  text-transform: uppercase;
}
.shadow-grid .role {
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--ink-2);
}
@media (max-width: 880px) {
  .shadow-grid {
    grid-template-columns: 1fr;
  }
}

/* =========================================================================
   v2.0 — DATA-DENSE COMPONENTS
   The following mirror the live design-system page 1:1 (Tables, Tab bar,
   Filter chips, Select cards, Graphs & data viz, Dashboard navigation).
   Wrap any block in .dark-surface to get the petrol-surface treatment.
   ========================================================================= */

/* ---------- DATA TABLE ----------
   Paper header row, hover, right-aligned numerics (.num), status badges.
   Search (.table-search) lives ABOVE the table, never inside it.
   Rows: 78px default (--table-row-height) · 36px on .compact · .roomy = deprecated
   no-op alias of the default. The header row is sticky. A long list scrolls with the page
   (.data-table-page, v2.482); a table inside a card or modal uses
   .data-table-frame > .data-table-scroll. */
.data-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface-elevated);
  border: none;
  border-radius: var(--table-radius);
  overflow: clip;
  box-shadow: var(--table-shadow);
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
}
.data-table th {
  position: sticky;
  top: var(--table-header-offset);
  z-index: 2;
  text-align: left;
  padding: 12px 14px;
  background: var(--paper);
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: var(--tr-meta);
  text-transform: uppercase;
  color: var(--ink-3);
  box-shadow: inset 0 -0.5px 0 var(--line);
  white-space: nowrap;
}
.data-table th.sorted {
  color: var(--primary);
}
.data-table td {
  padding: 0 14px;
  height: var(--table-row-height);
  border-bottom: 0.5px solid var(--line-2);
  vertical-align: middle;
}
.data-table.compact td {
  height: var(--table-row-height-compact);
}
/* v2.362 — 56px is the DEFAULT row height (was 44px). The in-cell column-group chart measures
   ~46px (30px bar area + 3px gap + 13px label); a td height is a MINIMUM, not a cap, so under
   the 44px default chart rows silently grew while chartless rows stayed short — data-dependent
   row heights. v2.360 fixed that with an opt-in .roomy band; v2.362 promotes 56px to the default
   so every table levels with no class to remember. .roomy stays as a no-op alias for markup that
   already carries it — do not add it to new tables. The 44px band is retired. One density per
   table, per screen — the same rule .compact carries. */
.data-table.roomy td {
  height: var(--table-row-height);
}
.data-table .num {
  text-align: right;
  font-feature-settings: 'tnum';
}
.data-table tbody tr {
  transition: background 120ms ease;
}
.data-table tbody tr:hover {
  background: var(--table-row-hover);
  cursor: pointer;
}
.data-table tbody tr:last-child td {
  border-bottom: none;
}
.dark-surface .data-table {
  background: var(--petrol-deep);
  border: 0.5px solid var(--tint-on-dark-soft);
  box-shadow: none;
  color: var(--surface-elevated);
}
.dark-surface .data-table th {
  background: linear-gradient(var(--tint-on-dark-faint), var(--tint-on-dark-faint)), var(--petrol-deep);
  color: var(--text-on-dark-secondary);
  box-shadow: inset 0 -0.5px 0 var(--tint-on-dark-soft);
}
.dark-surface .data-table th.sorted {
  color: var(--lifted-orange);
}
.dark-surface .data-table td {
  border-bottom-color: var(--border-on-dark);
}
.dark-surface .data-table tbody tr:hover {
  background: var(--table-row-hover-dark);
}

/* search field above a table */
.table-search {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  padding: 8px 14px;
  max-width: 360px;
}
.table-search .mdi {
  font-size: 18px;
  color: var(--ink-3);
}
.table-search input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
}
.table-search input::placeholder {
  color: var(--ink-3);
}
.table-search kbd {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
  background: var(--line-2);
  border: 0.5px solid var(--line);
  border-radius: 4px;
  padding: 2px 6px;
}
.dark-surface .table-search {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.14);
}
.dark-surface .table-search .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .table-search input {
  color: var(--surface-elevated);
}
.dark-surface .table-search input::placeholder {
  color: var(--text-on-dark-secondary);
}
.dark-surface .table-search kbd {
  background: rgba(0, 0, 0, 0.25);
  color: rgba(255, 255, 255, 0.65);
  border-color: var(--tint-on-dark-soft);
}

/* ---------- STATUS BADGE ----------
   Pill badge for semantic state (success / warning / error / info), in table cells
   and inline. Pair with a leading colored dot (.dot) and an uppercase mono label —
   colour is never the only signal. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: var(--radius-sm);
}
.badge .dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
}
.badge .mdi {
  font-size: 12px;
  margin-left: -1px;
}
.badge .badge-icon {
  font-size: 12px;
  line-height: 1;
  margin-left: -1px;
}
.badge .badge-icon svg,
.badge .badge-icon img {
  width: 12px;
  height: 12px;
  display: block;
}
.badge.is-success {
  background: var(--tint-success-medium);
  color: var(--success-text);
}
.badge.is-success .dot {
  background: var(--success);
}
.badge.is-warning {
  background: var(--tint-warning-medium);
  color: var(--warning-text);
}
.badge.is-warning .dot {
  background: var(--warning);
}
.badge.is-error {
  background: var(--tint-error-medium);
  color: var(--error-press);
}
.badge.is-error .dot {
  background: var(--error);
}
.badge.is-info {
  background: var(--tint-info-medium);
  color: var(--info-press);
}
.badge.is-info .dot {
  background: var(--info);
}
.badge.is-neutral {
  background: var(--tint-neutral);
  color: var(--ink-2);
}
.badge.is-neutral .dot {
  background: var(--ink-3);
}
.dark-surface .badge.is-success {
  background: var(--tint-success-on-dark);
  color: var(--success-on-dark);
}
.dark-surface .badge.is-warning {
  background: var(--tint-warning-on-dark);
  color: var(--warning-on-dark);
}
.dark-surface .badge.is-error {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .badge.is-info {
  background: var(--tint-info-on-dark);
  color: var(--info-on-dark);
}
.dark-surface .badge.is-neutral {
  background: var(--tint-on-dark-soft);
  color: rgba(255, 255, 255, 0.8);
}

/* ---------- TABLE PAGINATION · rows per page ----------
   Footer strip below a data table: a "showing" count (left), then a
   rows-per-page selector, a 1–N range readout, and prev/next nav (right).
   Lives on a .table-pagination bar with a top hairline; wrap in
   .dark-surface for the petrol treatment. */
.table-pagination {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 12px 18px;
  border-top: 0.5px solid var(--line);
  background: var(--paper);
}
.table-pagination .tp-count {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--ink-3);
}
.table-pagination .tp-spacer {
  flex: 1;
}

/* rows-per-page selector — "Show [10] rows" button + pop-up menu.
   One body voice (12.5px, matching the menu options): muted lead/unit, 600 value (v2.418). */
.page-size {
  position: relative;
}
.page-size-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 12.5px;
  color: var(--ink);
}
.page-size-btn .ps-lead,
.page-size-btn .ps-unit {
  color: var(--ink-3);
}
.page-size-btn .ps-val {
  font-weight: 600;
  font-feature-settings: 'tnum';
}
.page-size-btn .mdi {
  font-size: 14px;
  color: var(--ink-3);
}
.page-size-menu {
  position: absolute;
  bottom: calc(100% + 6px);
  right: 0;
  min-width: 130px;
  z-index: 50;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
  padding: 4px;
}
.page-size-opt {
  width: 100%;
  text-align: left;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border: none;
  background: transparent;
  color: var(--ink);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 12.5px;
}
.page-size-opt .ps-label {
  flex: 1;
  font-feature-settings: 'tnum';
}
/* The tick is reserved for alignment but shown ONLY for the current active selection. */
.page-size-opt .mdi {
  font-size: 12px;
  color: transparent;
}
.page-size-opt:not(.is-selected) .mdi {
  visibility: hidden;
}
.page-size-opt:hover {
  background: var(--tint-neutral-soft);
}
.page-size-opt.is-selected {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  font-weight: 600;
}
.page-size-opt.is-selected .mdi {
  color: var(--primary);
}

/* range readout — "1–10 of 142" */
.page-range {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--ink-3);
}
.page-range b {
  color: var(--ink);
  font-weight: 500;
}

/* prev / next */
.page-nav {
  width: 30px;
  height: 30px;
  border: 0.5px solid var(--line);
  background: var(--surface-elevated);
  border-radius: var(--radius);
  cursor: pointer;
  display: grid;
  place-items: center;
}
.page-nav .mdi {
  font-size: 16px;
  color: var(--ink-3);
}
.page-nav:hover:not(:disabled) {
  border-color: var(--border-stronger);
}
.page-nav:disabled {
  opacity: 0.4;
  cursor: default;
}

.dark-surface .table-pagination {
  background: rgba(0, 0, 0, 0.18);
  border-top-color: var(--tint-on-dark-soft);
}
.dark-surface .table-pagination .tp-count,
.dark-surface .page-range {
  color: var(--text-on-dark-muted);
}
.dark-surface .page-range b {
  color: var(--surface-elevated);
}
.dark-surface .page-size-btn {
  background: var(--tint-on-dark-faint);
  border-color: rgba(255, 255, 255, 0.14);
  color: var(--surface-elevated);
}
.dark-surface .page-size-btn .ps-lead,
.dark-surface .page-size-btn .ps-unit,
.dark-surface .page-size-btn .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .page-size-menu {
  background: var(--petrol);
  border-color: rgba(255, 255, 255, 0.14);
}
.dark-surface .page-size-opt {
  color: var(--surface-elevated);
}
.dark-surface .page-size-opt:hover {
  background: rgba(255, 255, 255, 0.08);
}
.dark-surface .page-size-opt.is-selected {
  background: var(--tint-lifted-strong);
  color: var(--lifted-orange);
}
.dark-surface .page-size-opt.is-selected .mdi {
  color: var(--lifted-orange);
}
.dark-surface .page-nav {
  background: var(--tint-on-dark-faint);
  border-color: rgba(255, 255, 255, 0.14);
}
.dark-surface .page-nav .mdi {
  color: rgba(255, 255, 255, 0.7);
}

/* ---------- TAB BAR ----------
   Inline nav for equal-weight siblings. One active, 2px underline + 600.
   Counts in mono pills. Same height as a 42px input row. */
.tabbar {
  display: inline-flex;
  align-items: center;
  gap: 0;
  border-bottom: 0.5px solid var(--line);
  padding: 0;
}
.tab {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 14px;
  color: var(--ink-3);
  background: transparent;
  border: none;
  padding: 10px 16px;
  cursor: pointer;
  position: relative;
  transition: color 150ms;
}
.tab:hover {
  color: var(--ink);
}
.tab.active {
  color: var(--primary-press);
  font-weight: 600;
}
.tab.active::after {
  content: '';
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: -0.5px;
  height: 2px;
  background: var(--primary);
  border-radius: 2px 2px 0 0;
}
.tab-count {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--ink-3);
  background: var(--line-2);
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  margin-left: 6px;
}
.tab.active .tab-count {
  color: var(--accent-text-on-tint);
  background: var(--tint-accent-medium);
}
.dark-surface .tabbar {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .tab {
  color: var(--text-on-dark-muted);
}
.dark-surface .tab:hover {
  color: var(--surface-elevated);
}
.dark-surface .tab.active {
  color: var(--lifted-orange);
}
.dark-surface .tab.active::after {
  background: var(--lifted-orange);
}
.dark-surface .tab-count {
  color: var(--text-on-dark-secondary);
  background: rgba(255, 255, 255, 0.08);
}
.dark-surface .tab.active .tab-count {
  color: var(--surface-elevated);
  background: var(--tint-lifted-strong);
}

/* ---------- BREADCRUMB NAVIGATION ----------
   The location trail shown above a page title (dashboard content header).
   Mono uppercase, slash-separated; earlier crumbs are links, the current
   crumb is a non-interactive accent label. On dark, accent → lifted-orange. */
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.breadcrumb-crumb {
  color: var(--ink-3);
  text-decoration: none;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.breadcrumb-crumb:hover {
  color: var(--ink);
}
.breadcrumb-crumb:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 2px;
}
.breadcrumb-crumb.is-current {
  color: var(--primary);
  cursor: default;
}
.breadcrumb-sep {
  color: var(--ink-3);
  opacity: 0.6;
}
.dark-surface .breadcrumb-crumb {
  color: var(--text-on-dark-muted);
}
.dark-surface .breadcrumb-crumb:hover {
  color: var(--surface-elevated);
}
.dark-surface .breadcrumb-crumb:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .breadcrumb-crumb.is-current {
  color: var(--lifted-orange);
}
.dark-surface .breadcrumb-sep {
  color: var(--text-on-dark-muted);
}

/* ---------- FILTER CHIPS ----------
   Multi-select filters. Unselected = neutral pill; selected = primary tint
   + border + leading check arrow. On dark, primary → lifted-orange. */
.chip-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 12.5px;
  height: 30px;
  box-sizing: border-box;
  color: var(--ink);
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-pill);
  padding: 6px 11px;
  cursor: pointer;
  transition:
    background 150ms,
    border-color 150ms,
    color 150ms,
    box-shadow 150ms;
  white-space: nowrap;
}
.chip .chip-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}
.chip .chip-count {
  font-family: var(--font-display);
  font-size: 10.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
.chip:hover {
  border-color: var(--border-stronger);
  color: var(--ink);
}
.chip.selected {
  background: var(--primary);
  border-color: transparent;
  color: var(--surface-elevated);
  font-weight: 600;
}
/* Full white, matching the chip's own label. The count previously took
   --text-on-dark-secondary (0.78 white) — an on-dark token with no surface scope, so
   it landed on the LIGHT accent chip at 3.44:1. That equals the v2.214 white-on-raw-
   accent figure, but that exemption was written for FULL-white labels; 0.78 white is a
   weaker case than the one decided. Matching the label puts it inside the existing
   exemption instead of beside it. */
.chip.selected .chip-count {
  color: var(--surface-elevated);
}
.chip .chip-arrow {
  display: none;
}
.chip.selected .chip-arrow {
  display: inline-flex;
  align-items: center;
  color: var(--surface-elevated);
}
.chip.selected .chip-arrow svg {
  width: 13px;
  height: 13px;
}
.dark-surface .chip {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.14);
  color: var(--text-on-dark-secondary);
}
.dark-surface .chip:hover {
  border-color: rgba(255, 255, 255, 0.28);
  color: var(--surface-elevated);
}
.dark-surface .chip.selected {
  background: var(--lifted-orange);
  border-color: transparent;
  color: var(--petrol-deep);
  box-shadow: none;
}
/* Opaque --petrol-deep, not a softened alpha: at 0.7 over --lifted-orange this
   composites to 3.84:1 and fails 1.4.3 for 10.5px/700 text (large starts at 18.66px
   bold). Opaque measures 6.48:1 and agrees with the .chip-arrow beside it (v2.357). */
.dark-surface .chip.selected .chip-count {
  color: var(--petrol-deep);
}
.dark-surface .chip.selected .chip-arrow {
  color: var(--petrol-deep);
}

/* Sortable — chips the user can reorder by priority. The grip is part of the
   chip, not a separate control, and drag is never the ONLY way to reorder:
   the chip stays focusable and moves with the arrow keys. */
.chip .chip-grip {
  display: none;
}
.chip-row.is-sortable .chip {
  padding-left: 6px;
}
.chip-row.is-sortable .chip .chip-grip {
  display: inline-flex;
  align-items: center;
  color: var(--ink-3);
  cursor: grab;
}
.chip-row.is-sortable .chip .chip-grip .mdi {
  font-size: 15px;
}
.chip-row.is-sortable .chip:hover .chip-grip {
  color: var(--ink-2);
}
.chip.is-dragging {
  opacity: 0.5;
  cursor: grabbing;
  box-shadow: var(--shadow-card);
}
.chip.is-drop-target {
  border-color: var(--primary);
  border-style: dashed;
}
.dark-surface .chip-row.is-sortable .chip .chip-grip {
  color: var(--text-on-dark-muted);
}
.dark-surface .chip-row.is-sortable .chip:hover .chip-grip {
  color: var(--text-on-dark-secondary);
}
.dark-surface .chip.selected .chip-grip {
  color: var(--petrol-deep);
}
.dark-surface .chip.is-drop-target {
  border-color: var(--lifted-orange);
}
/* Overflow — past N chips the rest collapse behind a count. The button is a
   real disclosure (aria-expanded), so the hidden chips stay reachable. */
.chip-more {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 30px;
  box-sizing: border-box;
  padding: 6px 11px;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
  background: transparent;
  border: 0.5px dashed var(--border-strong);
  border-radius: var(--radius-pill);
  cursor: pointer;
  white-space: nowrap;
}
.chip-more:hover {
  border-color: var(--primary);
  color: var(--primary-press);
  border-style: solid;
}
.chip-more:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.dark-surface .chip-more {
  color: var(--text-on-dark-secondary);
  border-color: var(--border-on-dark);
}
.dark-surface .chip-more:hover {
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}

/* applied-filter bar — removable key·value tags */
.filter-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 12px 14px;
  background: var(--paper);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
}
.filter-bar .fb-label {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-right: 2px;
}
.filter-tag {
  display: inline-flex;
  align-items: center;
  gap: 0;
  font-family: var(--font-body);
  font-size: 13px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.filter-tag .ft-key {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 6px 8px;
  background: rgba(0, 0, 0, 0.03);
  border-right: 0.5px solid var(--line);
}
.filter-tag .ft-val {
  padding: 6px 6px 6px 10px;
  color: var(--ink);
  font-weight: 500;
}
.filter-tag .ft-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  align-self: stretch;
  color: var(--ink-3);
  cursor: pointer;
  border-left: 0.5px solid transparent;
  transition:
    background 150ms,
    color 150ms;
}
.filter-tag .ft-x:hover {
  background: var(--tint-accent-medium);
  color: var(--primary-press);
}
.filter-tag .ft-x svg {
  width: 11px;
  height: 11px;
}
.filter-tag.accent {
  border-color: var(--primary);
}
.filter-tag.accent .ft-key {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  border-right-color: rgba(234, 93, 46, 0.25);
}
.filter-tag.accent .ft-val {
  color: var(--primary-press);
  font-weight: 600;
}
.filter-add {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  background: transparent;
  border: 0.5px dashed var(--line);
  border-radius: var(--radius);
  padding: 6px 10px;
  cursor: pointer;
}
.filter-add:hover {
  border-color: var(--primary);
  color: var(--primary);
}
.filter-add .mdi {
  font-size: 16px;
}
.filter-clear {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: transparent;
  border: none;
  cursor: pointer;
  margin-left: auto;
}
.filter-clear:hover {
  color: var(--error-text);
}
.dark-surface .filter-bar {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .filter-bar .fb-label {
  color: var(--text-on-dark-secondary);
}
.dark-surface .filter-tag {
  background: var(--tint-on-dark-faint);
  border-color: rgba(255, 255, 255, 0.14);
}
.dark-surface .filter-tag .ft-key {
  background: rgba(0, 0, 0, 0.25);
  color: var(--text-on-dark-muted);
  border-right-color: var(--tint-on-dark-medium);
}
.dark-surface .filter-tag .ft-val {
  color: var(--surface-elevated);
}
.dark-surface .filter-tag .ft-x {
  color: var(--text-on-dark-muted);
}
.dark-surface .filter-tag .ft-x:hover {
  background: var(--tint-lifted-strong);
  color: var(--lifted-orange);
}
.dark-surface .filter-tag.accent {
  border-color: var(--lifted-orange);
}
.dark-surface .filter-tag.accent .ft-key {
  background: var(--tint-lifted-strong);
  color: var(--surface-elevated);
  border-right-color: var(--border-lifted-strong);
}
.dark-surface .filter-tag.accent .ft-val {
  color: var(--surface-elevated);
}
.dark-surface .filter-add {
  border-color: rgba(255, 255, 255, 0.2);
  color: var(--text-on-dark-secondary);
}
.dark-surface .filter-add:hover {
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}
.dark-surface .filter-clear {
  color: var(--text-on-dark-secondary);
}

/* ---------- SELECT CARDS ----------
   Big single-select tiles — icon, eyebrow, title, one line of body. The whole
   tile is the control; selection = 1px primary border + faint wash (no shift). */
.select-card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}
@media (max-width: 640px) {
  .select-card-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- SELECT CARD · surface + states (v2.480, one definition) ----------
   Borderless on paper: the soft card shadow lifts it, deepening on hover;
   selected adds an accent tint and a 1.5px inset ring. On petrol there is no
   shadow, so a 0.5px hairline carries the edge and the ring goes lifted orange. */
.select-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px;
  background: var(--surface-elevated);
  border: none;
  border-radius: var(--radius-2xl);
  box-shadow: var(--dp-shadow);
  cursor: pointer;
  transition:
    background 150ms,
    box-shadow 150ms,
    border-color 150ms;
}
.select-card:hover {
  box-shadow: var(--dp-shadow-hi);
}
.select-card.selected {
  background: linear-gradient(var(--tint-accent-faint), var(--tint-accent-faint)), var(--surface-elevated);
  box-shadow:
    var(--dp-shadow-hi),
    inset 0 0 0 1.5px var(--primary);
}
.select-card:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.dark-surface .select-card {
  background: var(--tint-on-dark-faint);
  border: 0.5px solid var(--tint-on-dark-soft);
  box-shadow: none;
  color: var(--surface-elevated);
}
.dark-surface .select-card:hover {
  border-color: var(--border-on-dark-strong);
  box-shadow: none;
}
.dark-surface .select-card.selected {
  background: var(--tint-lifted-faint);
  border-color: var(--lifted-orange);
  box-shadow: inset 0 0 0 1.5px var(--lifted-orange);
}
.dark-surface .select-card:focus-visible {
  outline-color: var(--lifted-orange);
}
.select-card .sc-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.select-card .sc-icon {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: var(--tint-accent-medium);
  color: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.select-card.selected .sc-icon {
  background: var(--primary);
  color: var(--surface-elevated);
}
.select-card .sc-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  color: var(--ink);
  letter-spacing: -0.014em;
  margin: 0;
}
.select-card .sc-meta {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.select-card.selected .sc-meta {
  color: var(--accent-text-on-tint);
}
.select-card .sc-body {
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.5;
  margin: 0;
}

.dark-surface .select-card .sc-meta {
  color: var(--lifted-orange);
}
.dark-surface .select-card .sc-title {
  color: var(--surface-elevated);
}
.dark-surface .select-card .sc-body {
  color: var(--text-on-dark-secondary);
}
.dark-surface .select-card .sc-icon {
  background: var(--tint-lifted-strong);
  color: var(--lifted-orange);
}
.dark-surface .select-card.selected .sc-icon {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}

/* ---------- GRAPHS & DATA VIZ (beta) ----------
   Horizontal bar (.graph-h): mono label · gradient bar · mono annotation.
   Vertical multi-colour chart (.data-demo) sits on a petrol surface. */
.graph-h {
  display: grid;
  grid-template-columns: 160px 1fr 200px;
  column-gap: 24px;
  row-gap: 14px;
  align-items: center;
  padding: 8px 4px;
}
.graph-h .gh-label {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--ink-3);
}
.graph-h .gh-label strong {
  color: var(--ink);
  font-weight: 600;
  margin-right: 8px;
}
.graph-h .gh-bar {
  height: 20px;
  border-radius: 3px;
  background: var(--line-2);
  position: relative;
  overflow: hidden;
}
.graph-h .gh-bar .gh-fill {
  height: 100%;
  border-radius: 3px;
  background: linear-gradient(90deg, var(--primary) 0%, var(--lifted-orange) 100%);
  transition: width 400ms ease;
}
.graph-h .gh-annot {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.04em;
  font-weight: 500;
  color: var(--primary-press);
}
.graph-h .gh-annot .plus {
  font-weight: 700;
  margin-right: 6px;
}
.dark-surface .graph-h .gh-label {
  color: var(--text-on-dark-muted);
}
.dark-surface .graph-h .gh-label strong {
  color: rgba(255, 255, 255, 0.85);
}
.dark-surface .graph-h .gh-bar {
  background: var(--tint-on-dark-faint);
}
.dark-surface .graph-h .gh-annot {
  color: var(--lifted-orange);
}
.data-demo {
  background: var(--petrol);
  border-radius: var(--radius-2xl);
  padding: 28px 32px 22px;
  color: var(--surface-elevated);
}
.dd-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 18px;
}
.dd-title {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.014em;
  color: var(--surface-elevated);
}
.dd-meta {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-on-dark-muted);
  font-weight: 500;
}
.dd-chart {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 10px;
  align-items: end;
  height: 160px;
  padding-bottom: 6px;
}
.dd-bar {
  height: 100%;
  border-radius: 6px 6px 2px 2px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 8px 6px;
  color: rgba(255, 255, 255, 0.92);
  font-family: var(--font-display);
  font-weight: 600;
}
.dd-bar.dark-text {
  color: rgba(0, 0, 0, 0.78);
}
.dd-bar .dd-name {
  font-size: 10px;
  letter-spacing: 0.04em;
  margin-bottom: 4px;
  text-transform: uppercase;
  font-family: var(--font-mono);
  opacity: 0.85;
}
.dd-bar .dd-val {
  font-size: 18px;
  letter-spacing: -0.024em;
  font-feature-settings: 'tnum';
  line-height: 1;
}
.dd-caption {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 0.5px solid var(--tint-on-dark-soft);
  font-size: 12.5px;
  color: rgba(255, 255, 255, 0.7);
  line-height: 1.5;
}

/* ---------- DASHBOARD NAVIGATION (all breakpoints) ----------
   The product application frame: petrol sidebar + paper content + a paper
   header bar (profile top-right). One component, three sidebar modes:
     .dashboard-sidebar         docked, 240px, full labels         (desktop)
     .dashboard-sidebar.rail    collapsed, 72px, icons only         (desktop)
     .dashboard-sidebar.drawer  overlay, 300px, revealed by burger  (tablet/phone)
   Responsive contract: ≥900px the sidebar is docked (add .rail to collapse to
   the icon rail); <900px the docked sidebar is removed from flow and the
   burger reveals a .drawer over a .dashboard-scrim when .dashboard-shell.is-drawer-open.
   The live React component measures its own width to switch automatically —
   these classes are the styling contract for that behaviour. */
.dashboard-shell {
  display: flex;
  height: 100%;
  width: 100%;
  background: var(--paper);
  position: relative;
  font-family: var(--font-body);
  color: var(--ink);
}
.dashboard-sidebar {
  flex-shrink: 0;
  width: var(--nav-width-expanded);
  background: var(--petrol-deep);
  color: var(--surface-elevated);
  border-right: 0.5px solid var(--petrol-mid);
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}
/* v2.387 — brand height matches .dashboard-header (66px) so the sidebar and
   header bottom hairlines sit on one line across the shell. */
.dashboard-brand {
  height: 66px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  padding: 0 12px 0 18px;
  border-bottom: 0.5px solid var(--petrol-mid);
}
.dashboard-brand img {
  height: 22px;
  display: block;
}
.dashboard-nav {
  flex: 1;
  min-height: 0;
  padding: 12px 10px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  overflow-y: auto;
}
/* Column flex children default to flex-shrink:1 — with every group expanded the
   rows squeeze instead of scrolling, which reads as uneven gaps. Pin them. */
.dashboard-nav > *,
.dashboard-nav-group > *,
.dashboard-nav-group-items > * {
  flex: 0 0 auto;
}
/* ---------- SCROLL AREA · themed overflow scrollbar (atom) ----------
   Give any overflowing region a slim tokened scrollbar instead of the heavy OS
   default. Light thumb on light surfaces; on petrol add .is-on-dark (or place
   inside .dark-surface). overflow pins the horizontal axis — setting
   overflow-y alone forces overflow-x:auto and grows a stray horizontal bar.
   The nav's .dashboard-nav.is-scrollable is this atom's on-dark form. */
.scroll-area {
  overflow: hidden auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.scroll-area::-webkit-scrollbar {
  width: 8px;
}
.scroll-area::-webkit-scrollbar-track {
  background: transparent;
}
.scroll-area::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background-clip: content-box;
}
.scroll-area::-webkit-scrollbar-thumb:hover {
  background: var(--border-stronger);
  border: 2px solid transparent;
  background-clip: content-box;
}
.scroll-area.is-on-dark,
.dark-surface .scroll-area,
.dashboard-nav.is-scrollable {
  overflow: hidden auto;
  scrollbar-width: thin;
  scrollbar-color: var(--tint-on-dark-strong) transparent;
}
.scroll-area.is-on-dark::-webkit-scrollbar,
.dark-surface .scroll-area::-webkit-scrollbar,
.dashboard-nav.is-scrollable::-webkit-scrollbar {
  width: 8px;
}
.scroll-area.is-on-dark::-webkit-scrollbar-track,
.dark-surface .scroll-area::-webkit-scrollbar-track,
.dashboard-nav.is-scrollable::-webkit-scrollbar-track {
  background: transparent;
}
.scroll-area.is-on-dark::-webkit-scrollbar-thumb,
.dark-surface .scroll-area::-webkit-scrollbar-thumb,
.dashboard-nav.is-scrollable::-webkit-scrollbar-thumb {
  background: var(--tint-on-dark-strong);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background-clip: content-box;
}
.scroll-area.is-on-dark::-webkit-scrollbar-thumb:hover,
.dark-surface .scroll-area::-webkit-scrollbar-thumb:hover,
.dashboard-nav.is-scrollable::-webkit-scrollbar-thumb:hover {
  background: var(--border-on-dark-strong);
  border: 2px solid transparent;
  background-clip: content-box;
}
.dashboard-section {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--nav-section-caption);
  padding: 10px 12px 6px;
}
.dashboard-nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 12px;
  font-family: var(--font-body, 'Noto Sans', -apple-system, sans-serif);
  border-radius: var(--radius);
  border-left: 3px solid transparent;
  color: var(--nav-label);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  background: transparent;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.dashboard-nav-item .mdi {
  font-size: 20px;
  line-height: 1;
  flex-shrink: 0;
  color: var(--nav-icon);
  font-variation-settings:
    'FILL' 0,
    'wght' 350,
    'GRAD' 0,
    'opsz' 24;
  white-space: nowrap;
  -webkit-font-smoothing: antialiased;
}
.dashboard-nav-item:hover {
  background: var(--nav-row-hover-fill);
  color: var(--nav-label-active);
}
.dashboard-nav-item:hover .mdi {
  color: var(--nav-icon-active);
}
.dashboard-nav-item.is-active {
  background: var(--nav-row-active-fill);
  color: var(--nav-label-active);
  font-weight: 600;
  border-left-color: var(--lifted-orange);
  padding-left: 9px;
}
.dashboard-nav-item.is-active .mdi {
  color: var(--nav-icon-active);
  font-variation-settings:
    'FILL' 1,
    'wght' 400,
    'GRAD' 0,
    'opsz' 24;
}
/* ---- Collapsible nav groups ---------------------------------------------
   A captioned nav section that folds. The caption becomes a button carrying
   aria-expanded and a chevron; its children are .dashboard-nav-subitem rows
   (no icon, indented behind a hairline guide). A closed group drops its items
   from the flow, so nothing hidden stays focusable. In the icon rail the
   toggle and the sub-links are hidden — only top-level items show there. */
.dashboard-nav-group {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
}
/* The group header reads as a NAV ROW, not a section heading: same family, size,
   weight, height, gap and leading icon as .dashboard-nav-item — a nav row that
   happens to carry a trailing expand/collapse chevron. The mono uppercase
   caption style belongs to .dashboard-section (a static label), not here. */
.dashboard-nav-group-toggle {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 12px;
  border: 0;
  border-left: 3px solid transparent;
  border-radius: var(--radius);
  background: transparent;
  font-family: var(--font-body, 'Noto Sans', -apple-system, sans-serif);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0;
  line-height: 1;
  text-transform: none;
  color: var(--nav-label);
  text-align: left;
  cursor: pointer;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.dashboard-nav-group-toggle .mdi {
  font-size: 20px;
  line-height: 1;
  flex-shrink: 0;
  color: var(--nav-icon);
  font-variation-settings:
    'FILL' 0,
    'wght' 350,
    'GRAD' 0,
    'opsz' 24;
  white-space: nowrap;
  -webkit-font-smoothing: antialiased;
}
.dashboard-nav-group-toggle:hover .mdi {
  color: var(--nav-icon-active);
}
.dashboard-nav-group-toggle:active .mdi {
  color: var(--nav-icon-active);
}
.dashboard-nav-group-toggle:hover {
  background: var(--nav-row-hover-fill);
  color: var(--nav-label-active);
}
.dashboard-nav-group-toggle:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* Pushed to the right edge of the row; smaller and quieter than the leading icon. */
.dashboard-nav-group-toggle .dashboard-nav-group-chevron {
  margin-left: auto;
  font-size: 18px;
  flex-shrink: 0;
  color: inherit;
  transition: transform var(--dur-base) var(--ease-out);
}
.dashboard-nav-group-chevron {
  font-size: 18px;
  flex-shrink: 0;
  transition: transform var(--dur-base) var(--ease-out);
}
.dashboard-nav-group-items {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  gap: 2px;
  margin: 2px 0 4px 22px;
  padding-left: 6px;
  border-left: 0.5px solid var(--border-on-dark);
}
.dashboard-nav-group:not(.is-open) > .dashboard-nav-group-items {
  display: none;
}
.dashboard-nav-group:not(.is-open) .dashboard-nav-group-chevron {
  transform: rotate(-90deg);
}
.dashboard-nav-subitem {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  min-height: 36px;
  padding: 0 10px;
  border-radius: var(--radius);
  color: var(--nav-label);
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  text-decoration: none;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.dashboard-nav-subitem:hover {
  background: var(--nav-row-hover-fill);
  color: var(--nav-label-active);
}
/* Nav rows are links: never inherit the prose-link underline, whatever the host app
   styles its anchors with. */
.dashboard-nav-item:hover,
.dashboard-nav-item:focus,
.dashboard-nav-subitem:hover,
.dashboard-nav-subitem:focus {
  text-decoration: none;
}
.dashboard-nav-subitem:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dashboard-nav-subitem.is-active {
  background: var(--nav-row-active-fill);
  color: var(--nav-label-active);
  font-weight: 600;
}
.dashboard-sidebar.rail .dashboard-nav-group-toggle,
.dashboard-sidebar.rail .dashboard-nav-group-items {
  display: none;
}
.dashboard-sidebar.is-light-rail .dashboard-nav-group-toggle {
  color: var(--nav-label-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-group-toggle .mdi {
  color: var(--nav-icon-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-group-toggle:hover .mdi,
.dashboard-sidebar.is-light-rail .dashboard-nav-group-toggle:active .mdi {
  color: var(--nav-icon-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-group-toggle:hover {
  background: var(--nav-row-hover-fill-on-light);
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-group-toggle:focus-visible,
.dashboard-sidebar.is-light-rail .dashboard-nav-subitem:focus-visible {
  box-shadow: var(--focus-ring-accent-strong);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-group-items {
  border-left-color: var(--line);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-subitem {
  color: var(--nav-label-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-subitem:hover {
  background: var(--nav-row-hover-fill-on-light);
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-subitem.is-active {
  background: var(--nav-row-active-fill-on-light);
  color: var(--nav-label-active-on-light);
}
@media (prefers-reduced-motion: reduce) {
  .dashboard-nav-group-chevron {
    transition: none;
  }
}
/* ---- Rail tooltips ------------------------------------------------------
   In the icon rail the label is hidden, so every item carries its name in a
   tooltip instead: wrap the item in .tooltip-wrap.is-rail-tooltip and add a
   .tooltip.is-right.is-terse sibling. The bubble only appears in the rail —
   the docked sidebar already shows the label. Give the item an aria-label
   (the visual label is display:none) and aria-hidden on the bubble, so the
   name is announced once. The nav must not clip: a rail carrying tooltips
   uses overflow:visible (pair .is-scrollable with a wider hover target
   instead of clipping the bubble). */
.dashboard-sidebar.has-rail-tooltips,
.dashboard-sidebar.has-rail-tooltips .dashboard-nav {
  overflow: visible;
}
.tooltip-wrap.is-rail-tooltip {
  display: block;
}
.tooltip-wrap.is-rail-tooltip > .tooltip {
  display: none;
}
.dashboard-sidebar.rail .tooltip-wrap.is-rail-tooltip > .tooltip {
  display: block;
}
.dashboard-sidebar .tooltip {
  background: var(--tooltip-bg-on-dark);
  color: var(--tooltip-text-on-dark);
}
.dashboard-sidebar.is-light-rail .tooltip {
  background: var(--tooltip-bg);
  color: var(--tooltip-text);
}
/* ---- Collapse navigation ------------------------------------------------------
   The rail toggle pinned below the dashboard-nav list. Same slot in both states:
   unfolded it is a full-width row reading 'Collapse menu'; collapsed it is the
   46px centred square of the rail. ONE chevron glyph rotates 180deg between the
   two (never a glyph swap), the label fades + slides out, and the parent sidebar
   tweens its width — all on --dur-base / --ease-out so it reads as one motion.
   Inside a .dashboard-sidebar.rail it follows the parent automatically; the
   .is-rail modifier is for standalone specimens. .is-on-light for the light rail. */
.nav-collapse-slot {
  flex-shrink: 0;
  padding: 6px 10px;
  border-top: 0.5px solid var(--border-on-dark);
}
.nav-collapse {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  height: 40px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--nav-label);
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    width var(--dur-base) var(--ease-out);
}
.nav-collapse .mdi {
  font-size: 18px;
  flex-shrink: 0;
  color: var(--nav-icon);
  transition:
    transform var(--dur-base) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.nav-collapse-label {
  white-space: nowrap;
  transition:
    opacity var(--dur-base) var(--ease-out),
    transform var(--dur-base) var(--ease-out);
}
.nav-collapse:hover {
  background: var(--nav-row-hover-fill);
  color: var(--nav-label-active);
}
.nav-collapse:hover .mdi {
  color: var(--nav-icon-active);
}
.nav-collapse:active {
  background: var(--tint-on-dark-medium);
}
.nav-collapse:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.nav-collapse[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
.nav-collapse[disabled]:hover {
  background: transparent;
  color: var(--nav-label);
}
.nav-collapse.is-rail,
.dashboard-sidebar.rail .nav-collapse {
  width: 46px;
  min-width: 46px;
  height: 46px;
  justify-content: center;
  gap: 0;
  padding: 0;
  margin: 0 auto;
}
.nav-collapse.is-rail .mdi,
.dashboard-sidebar.rail .nav-collapse .mdi {
  transform: rotate(180deg);
}
.nav-collapse.is-rail .nav-collapse-label,
.dashboard-sidebar.rail .nav-collapse-label {
  opacity: 0;
  transform: translateX(-8px);
  width: 0;
}
.dashboard-sidebar.rail .nav-collapse-slot {
  padding: 6px 14px;
}
.nav-collapse.is-on-light {
  color: var(--nav-label-on-light);
}
.nav-collapse.is-on-light .mdi {
  color: var(--nav-icon-on-light);
}
.nav-collapse.is-on-light:hover {
  background: var(--tint-neutral-soft);
  color: var(--nav-label-active-on-light);
}
.nav-collapse.is-on-light:hover .mdi {
  color: var(--nav-label-active-on-light);
}
.nav-collapse.is-on-light:active {
  background: var(--tint-neutral);
}
.nav-collapse.is-on-light:focus-visible {
  box-shadow: var(--focus-ring-accent-strong);
}
.nav-collapse.is-on-light[disabled]:hover {
  background: transparent;
  color: var(--nav-label-on-light);
}
.nav-collapse-slot.is-on-light {
  border-top: 0.5px solid var(--line);
}
.dashboard-sidebar.is-light-rail {
  background: var(--surface-elevated);
  border-right-color: var(--line);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item {
  color: var(--nav-label-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item .mdi {
  color: var(--nav-icon-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item:hover {
  background: var(--nav-row-hover-fill-on-light);
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item:hover .mdi {
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item.is-active {
  background: var(--nav-row-active-fill-on-light);
  color: var(--nav-label-active-on-light);
  border-left-color: var(--primary);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item.is-active .mdi {
  color: var(--nav-icon-active-on-light);
}
/* ---- Row states · explicit ------------------------------------------------
   Every state of every navigation row, spelled out: family, size, weight,
   letter-spacing, colour, fill. Three row types (item / sub-item / group
   caption) x two surfaces (petrol, light rail). The top-level item has NO focus
   state by decision; the sub-item and the group toggle keep a :focus-visible
   ring, so their state sets are one longer. Type NEVER changes
   family or size between states — only weight (500 -> 600 when current),
   colour and fill move, so rows never reflow as you move through them. */
.dashboard-nav-item {
  letter-spacing: 0;
  line-height: 1;
}
.dashboard-nav-item:active {
  background: var(--nav-row-press-fill);
  color: var(--nav-label-active);
}
.dashboard-nav-item:active .mdi {
  color: var(--nav-icon-active);
}
.dashboard-nav-item.is-active:hover,
.dashboard-nav-item.is-active:active {
  background: var(--nav-row-active-fill);
  color: var(--nav-label-active);
}
.dashboard-nav-item.is-active:hover .mdi,
.dashboard-nav-item.is-active:active .mdi {
  color: var(--nav-icon-active);
}
.dashboard-nav-subitem {
  letter-spacing: 0;
  line-height: 1;
}
.dashboard-nav-subitem:active {
  background: var(--nav-row-press-fill);
  color: var(--nav-label-active);
}
.dashboard-nav-subitem.is-active:hover,
.dashboard-nav-subitem.is-active:active {
  background: var(--nav-row-active-fill);
  color: var(--nav-label-active);
}
.dashboard-nav-group-toggle:active {
  background: var(--nav-row-press-fill);
  color: var(--nav-label-active);
}
/* light rail — same six states, resolved through the on-light role tokens */
.dashboard-sidebar.is-light-rail .dashboard-nav-item:active {
  background: var(--nav-row-press-fill-on-light);
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item:active .mdi {
  color: var(--nav-icon-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item.is-active:hover,
.dashboard-sidebar.is-light-rail .dashboard-nav-item.is-active:active {
  background: var(--nav-row-active-fill-on-light);
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-item.is-active:hover .mdi,
.dashboard-sidebar.is-light-rail .dashboard-nav-item.is-active:active .mdi {
  color: var(--nav-icon-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-subitem:active {
  background: var(--nav-row-press-fill-on-light);
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-subitem.is-active:hover,
.dashboard-sidebar.is-light-rail .dashboard-nav-subitem.is-active:active {
  background: var(--nav-row-active-fill-on-light);
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar.is-light-rail .dashboard-nav-group-toggle:active {
  background: var(--nav-row-press-fill-on-light);
  color: var(--nav-label-active-on-light);
}
.dashboard-sidebar {
  transition: width var(--dur-base) var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .dashboard-sidebar,
  .nav-collapse,
  .nav-collapse .mdi,
  .nav-collapse-label {
    transition: none;
  }
}
.dashboard-sidebar.rail {
  width: var(--nav-width-rail);
}
.dashboard-sidebar.rail .dashboard-brand {
  justify-content: center;
  padding: 0;
}
.dashboard-sidebar.rail .dashboard-nav {
  padding: 10px 14px;
}
.dashboard-sidebar.rail .dashboard-section {
  display: none;
}
.dashboard-sidebar.rail .dashboard-nav-item {
  width: 46px;
  height: 46px;
  justify-content: center;
  gap: 0;
  padding: 0;
  margin: 0 auto;
  border-left: none;
  border-radius: 11px;
}
.dashboard-sidebar.rail .dashboard-nav-item .dashboard-label {
  display: none;
}
.dashboard-sidebar.rail .dashboard-nav-item.is-active {
  padding: 0;
}
.dashboard-sidebar.drawer {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 40;
  width: var(--nav-width-drawer);
  border-right: none;
  box-shadow: 0 12px 48px rgba(14, 41, 53, 0.4);
  transform: translateX(-100%);
  transition: transform var(--dur-base) var(--ease-out);
}
.dashboard-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--paper);
  position: relative;
  z-index: 2;
}
.dashboard-header {
  height: 66px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 var(--page-padding);
  border-bottom: 0.5px solid var(--line);
  background: var(--paper);
}
.dashboard-burger {
  display: none;
  width: 38px;
  height: 38px;
  border: none;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}
.dashboard-burger span {
  display: block;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  margin: 2px 0;
}
.dashboard-burger span:last-child {
  width: 12px;
}
.dashboard-spacer {
  flex: 1;
}
.dashboard-profile {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 6px 0 8px;
  border: none;
  background: transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.dashboard-avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--petrol-deep);
  color: var(--surface-elevated);
  display: grid;
  place-items: center;
  font-weight: 600;
  font-size: 13px;
  flex-shrink: 0;
}
.dashboard-profile .mdi {
  font-size: 18px;
  color: var(--ink-3);
}
.dark-surface .dashboard-profile .mdi {
  color: var(--text-on-dark-muted);
}
/* profile dropdown — opens from the header avatar, top-right */
.dashboard-profile-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 80;
  min-width: 224px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-frame);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.dashboard-profile-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px 6px;
  border-bottom: 0.5px solid var(--line-2);
  margin-bottom: 4px;
}
.dashboard-profile-head .dashboard-avatar {
  width: 34px;
  height: 34px;
  font-size: 12px;
}
.dashboard-profile-name {
  font-weight: 600;
  font-size: 13px;
  color: var(--ink);
  line-height: 1.25;
}
.dashboard-profile-email {
  font-size: 11px;
  color: var(--ink-2);
  line-height: 1.3;
  word-break: break-all;
}
.dashboard-profile-role {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.04em;
  color: var(--ink-3);
}
.dashboard-profile-item {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 34px;
  padding: 0 10px;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  cursor: pointer;
  text-decoration: none;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  width: 100%;
  text-align: left;
}
.dashboard-profile-item .mdi {
  font-size: 17px;
  color: var(--ink-3);
  width: 34px;
  text-align: center;
  flex-shrink: 0;
}
.dashboard-profile-item:hover {
  background: var(--tint-neutral);
}
/* --text-accent on --tint-accent-soft is 4.28:1 light / 4.10:1 petrol. The label
   takes --accent-text-on-tint on light; on petrol it goes white and the accent
   stays in the icon and the fill — the v2.197 answer for the same problem. */
.dashboard-profile-item.is-active {
  background: var(--tint-accent-soft);
  color: var(--accent-text-on-tint);
  font-weight: 600;
}
.dark-surface .dashboard-profile-item.is-active .mdi {
  color: var(--lifted-orange);
}
.dashboard-profile-item.is-active .mdi {
  color: var(--primary);
}
.dashboard-profile-item.is-danger:hover {
  background: var(--tint-error-bg);
  color: var(--error-text);
}
.dashboard-profile-item.is-danger:hover .mdi {
  color: var(--error-text);
}
.dashboard-profile:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.dashboard-profile-item:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}
.dark-surface .dashboard-profile-menu {
  background: var(--petrol);
  border-color: var(--border-on-dark);
}
.dark-surface .dashboard-profile-name {
  color: var(--text-on-dark);
}
.dark-surface .dashboard-profile-email {
  color: var(--text-on-dark-secondary);
}
.dark-surface .dashboard-profile-role {
  color: var(--text-on-dark-secondary);
}
.dark-surface .dashboard-profile-item {
  color: var(--text-on-dark-secondary);
}
.dark-surface .dashboard-profile-item:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .dashboard-profile-item .mdi {
  color: var(--text-on-dark-secondary);
}
.dark-surface .dashboard-profile-item.is-danger:hover {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .dashboard-profile-item.is-danger:hover .mdi {
  color: var(--error-on-dark);
}
.dark-surface .dashboard-profile-item.is-active {
  background: var(--tint-lifted-medium);
  color: var(--surface-elevated);
}
.dark-surface .dashboard-profile:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .dashboard-profile-item:focus-visible {
  outline-color: var(--lifted-orange);
}

/* profile menu — trigger open state (avatar pill + accent chevron) */
.dashboard-profile.is-open {
  background: var(--tint-accent-soft);
}
.dashboard-profile.is-open .mdi {
  color: var(--primary);
}
.dark-surface .dashboard-profile.is-open {
  background: var(--tint-lifted-soft);
}
.dark-surface .dashboard-profile.is-open .mdi {
  color: var(--lifted-orange);
}
/* profile menu — super-admin tenant-switcher variant */
.dashboard-profile-menu.is-superadmin {
  min-width: 288px;
}
.dashboard-profile-tenant {
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.3;
  margin-top: 3px;
}
.dashboard-profile-switch {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 38px;
  padding: 0 10px;
  border: none;
  background: transparent;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  border-radius: var(--radius-sm);
  text-align: left;
}
.dashboard-profile-switch .mdi {
  font-size: 17px;
  color: var(--ink-3);
  width: 34px;
  text-align: center;
  flex-shrink: 0;
}
.dashboard-profile-switch .switch-caret {
  margin-left: auto;
  width: auto;
}
.dashboard-profile-switch:hover {
  background: var(--tint-neutral);
}
.dashboard-profile-switch:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}
.dashboard-profile-switchbody {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: 4px;
  border-bottom: 0.5px solid var(--line-2);
  margin-bottom: 4px;
}
.dashboard-profile-menu .search-field {
  border-color: transparent;
  background: transparent;
  padding: 8px 10px;
}
.dashboard-profile-menu .search-field.is-focus,
.dashboard-profile-menu .search-field:focus-within {
  border-color: transparent;
  background: var(--tint-neutral);
  box-shadow: var(--focus-ring-accent-strong);
}
.dashboard-profile-tenants,
.dashboard-profile-languages {
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: 216px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.dashboard-profile-tenants .dashboard-profile-item,
.dashboard-profile-languages .dashboard-profile-item {
  height: auto;
  min-height: 34px;
  padding-top: 6px;
  padding-bottom: 6px;
}
.dark-surface .dashboard-profile-tenant {
  color: var(--text-on-dark-secondary);
}
.dark-surface .dashboard-profile-switch {
  color: var(--text-on-dark);
}
.dark-surface .dashboard-profile-switch .mdi {
  color: var(--text-on-dark-secondary);
}
.dark-surface .dashboard-profile-switch:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .dashboard-profile-switch:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .dashboard-profile-switchbody {
  border-color: var(--border-on-dark);
}
.dark-surface .dashboard-profile-menu .search-field {
  border-color: transparent;
  background: transparent;
}
.dark-surface .dashboard-profile-menu .search-field:focus-within {
  background: var(--tint-on-dark-soft);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* profile menu — language list (v2.508; one name per language v2.510). 20–30
   languages: a filter + a scrolling list (same box as the tenant list); the current
   language is pinned first. Names are written in the current UI language only. */
/* profile menu — flags + aligned filter (v2.509). The flag sits in the 34px icon
   slot (20×15 + 7px each side) so every label starts at the same x; the current
   language's tick moves to the trailing edge. The filter's icon takes the same
   34px slot and 13px text, so "Search languages" lines up with the item labels. */
.dashboard-profile-flag {
  width: 20px;
  height: 15px;
  margin: 0 7px;
  flex-shrink: 0;
  object-fit: cover;
  border-radius: 2px;
  box-shadow: 0 0 0 0.5px var(--line);
}
.dark-surface .dashboard-profile-flag {
  box-shadow: 0 0 0 0.5px var(--border-on-dark);
}
.dashboard-profile-item .dashboard-profile-check {
  width: auto;
  margin-left: auto;
}
.dashboard-profile-menu .search-field .mdi {
  width: 34px;
  text-align: center;
  font-size: 17px;
  flex-shrink: 0;
}
.dashboard-profile-menu .search-field input {
  font-size: 13px;
}
/* Language select is collapsed until the row is clicked (v2.511). */
.dashboard-profile-switchbody[hidden] {
  display: none;
}
.dashboard-content {
  flex: 1;
  min-height: 0;
  background: var(--surface-elevated);
  padding: var(--page-padding) var(--page-padding) calc(var(--page-padding) * 1.5);
  overflow: auto;
}
/* Page padding — the two page margins (v2.460, rule 11k). Exactly two, nothing else:
   .page-padding-dashboard for dashboard page content, .page-padding-app for App screens.
   Both .dashboard-content and .dashboard-header carry the dashboard margin built in
   (v2.463: one token per margin — --page-padding, --app-screen-padding-inline). */
.page-padding-dashboard {
  padding: var(--page-padding) var(--page-padding) calc(var(--page-padding) * 1.5);
}
.page-padding-app {
  padding-left: calc(var(--app-screen-padding-inline) + var(--safe-left));
  padding-right: calc(var(--app-screen-padding-inline) + var(--safe-right));
}
.dashboard-scrim {
  position: absolute;
  inset: 0;
  z-index: 30;
  background: rgba(14, 20, 17, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
}
.dashboard-shell.is-drawer-open .dashboard-scrim {
  opacity: 1;
  pointer-events: auto;
}
.dashboard-shell.is-drawer-open .dashboard-sidebar.drawer {
  transform: translateX(0);
}
@media (max-width: 900px) {
  .dashboard-sidebar:not(.drawer) {
    display: none;
  }
  .dashboard-burger {
    display: inline-flex;
  }
  .dashboard-header {
    height: 56px;
  }
}
@media (max-width: 540px) {
  .dashboard-sidebar.drawer {
    width: calc(100% - 46px);
  }
}

/* =========================================================================
   v2.1 — DASHBOARD / DATA-APP COMPONENTS
   Ported from the Arc dashboard redesign. Toast, empty state, menu/popover,
   filter toolbar, date-range picker, selection bar, bento grid, and the
   table row-select / sortable-header / column-menu additions.
   Wrap any block in .dark-surface for the petrol treatment.
   ========================================================================= */

/* ---------- TOAST / SNACKBAR ----------
   Transient confirmation that flies in from the top, holds, flies out.
   Drive visibility from JS (add .is-in to animate). One line title + one
   line of detail; a coloured status disc on the left. */
@keyframes toastInOut {
  0% {
    opacity: 0;
    transform: translate(-50%, -22px);
  }
  9% {
    opacity: 1;
    transform: translate(-50%, 0);
  }
  86% {
    opacity: 1;
    transform: translate(-50%, 0);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -22px);
  }
}
.toast {
  position: fixed;
  top: 18px;
  left: 50%;
  transform: translate(-50%, -22px);
  z-index: 4000;
  display: inline-flex;
  align-items: center;
  gap: 11px;
  padding: 11px 17px 11px 12px;
  background: var(--surface-elevated);
  border-radius: var(--radius-pill);
  box-shadow:
    0 16px 40px rgba(0, 0, 0, 0.24),
    0 0 0 0.5px var(--line);
  opacity: 0;
  pointer-events: none;
}
.toast.is-in {
  animation: toastInOut 2.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.toast.is-static {
  position: static;
  transform: none;
  opacity: 1;
  animation: none;
}
.toast-icon {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  background: var(--success);
  color: var(--surface-elevated);
}
.toast-icon .mdi {
  font-size: 17px;
  font-variation-settings:
    'FILL' 1,
    'wght' 500,
    'GRAD' 0,
    'opsz' 20;
}
.toast.is-error .toast-icon {
  background: var(--error);
}
.toast.is-warning .toast-icon {
  background: var(--warning);
}
.toast.is-info .toast-icon {
  background: var(--info);
}
.toast-title {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 13.5px;
  color: var(--ink);
  line-height: 1.2;
}
.toast-sub {
  font-family: var(--font-body);
  font-size: 11.5px;
  color: var(--ink-3);
  line-height: 1.3;
  margin-top: 1px;
}

/* ---------- HISTORY: the FIRST Result state (v2.255–v2.321) ----------
   Not the component below. A separate v2.255 component with .result-state-disc /
   -title / -body / -count and the tones .is-clear / .is-error / .is-empty reported
   the same situations as the then-named Process status, under a second vocabulary
   and at a second size, and disagreed with it on one: "no scans available" was RED
   there and AMBER here. It was merged into Process status in v2.321 and its classes
   deleted; v2.327 settled the tone as RED on both; and in v2.333 Process status was
   itself renamed Result state, so the .result-state* names are live again — with
   DIFFERENT parts (-icon, -measure, -head, -note, -actions, -halo). The live
   implementation is the RESULT STATE section further down; nothing here is a rule. */

/* ---------- EMPTY STATE ----------
   Friendly, differentiated. .empty-state centred in the content area;
   a tinted circular .empty-art holds a glyph; title + up to two body lines;
   an optional primary action (use .btn / .btn-primary). */
.empty-state {
  padding: 60px 24px;
  text-align: center;
}
.empty-art {
  position: relative;
  width: 132px;
  height: 132px;
  margin: 0 auto;
}
.empty-art .empty-disc {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--tint-accent-soft);
}
.empty-art .empty-glyph {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--primary);
}
.empty-art .empty-glyph .mdi {
  font-size: 56px;
}
.empty-art .empty-spark {
  position: absolute;
  top: 10px;
  right: 20px;
  color: var(--primary);
}
.empty-art .empty-spark .mdi {
  font-size: 26px;
  font-variation-settings:
    'FILL' 1,
    'wght' 400,
    'GRAD' 0,
    'opsz' 24;
}
.empty-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 24px;
  letter-spacing: -0.02em;
  color: var(--ink);
  margin-top: 20px;
}
.empty-body {
  font-family: var(--font-body);
  font-size: 14.5px;
  color: var(--ink-2);
  line-height: 1.5;
  max-width: 440px;
  margin: 12px auto 0;
}
.empty-body + .empty-body {
  margin-top: 4px;
}
.empty-state .empty-action {
  margin-top: 20px;
}
.empty-actions {
  display: flex;
  gap: 10px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 20px;
}
.empty-actions .empty-action {
  margin-top: 0;
}
.dark-surface .empty-art .empty-disc {
  background: var(--tint-lifted-selected);
}
.dark-surface .empty-art .empty-glyph {
  color: var(--lifted-orange);
}
.dark-surface .empty-art .empty-spark {
  color: var(--lifted-orange);
}
.dark-surface .empty-title {
  color: var(--surface-elevated);
}
.dark-surface .empty-body {
  color: rgba(255, 255, 255, 0.7);
}

/* Filtered — "no results for THESE filters", not "nothing here yet". Neutral
   art instead of accent (there is nothing to celebrate), smaller, and the
   action clears the filters rather than creating something. */
.empty-state.is-filtered {
  padding: 40px 24px;
}
.empty-state.is-filtered .empty-art {
  width: 88px;
  height: 88px;
}
.empty-state.is-filtered .empty-art .empty-disc {
  background: var(--tint-neutral-soft);
}
.empty-state.is-filtered .empty-art .empty-glyph {
  color: var(--ink-3);
}
.empty-state.is-filtered .empty-art .empty-glyph .mdi {
  font-size: 38px;
}
.empty-state.is-filtered .empty-title {
  font-size: 18px;
  margin-top: 14px;
}
.empty-state.is-filtered .empty-body {
  font-size: 13.5px;
  margin-top: 8px;
}
.dark-surface .empty-state.is-filtered .empty-art .empty-disc {
  background: var(--tint-on-dark-soft);
}
.dark-surface .empty-state.is-filtered .empty-art .empty-glyph {
  color: var(--text-on-dark-muted);
}
/* Inline — an empty slot INSIDE a container (a panel, a drop zone, one column
   of a two-list dialog), not a whole page. Dashed boundary, one line, no art. */
.empty-inline {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 18px 16px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink-3);
  text-align: center;
}
.empty-inline .mdi {
  font-size: 17px;
  flex-shrink: 0;
}
.empty-inline.is-drop-target {
  border-color: var(--primary);
  background: var(--tint-accent-faint);
  color: var(--accent-text-on-tint);
}
.dark-surface .empty-inline {
  border-color: var(--border-on-dark);
  color: var(--text-on-dark-muted);
}
/* On petrol the label goes WHITE, not lifted-orange: --lifted-orange on
   --tint-lifted-soft over petrol is 4.36:1. The fill and border carry the accent
   — the same answer the selected+open filter trigger reached in v2.197. */
.dark-surface .empty-inline.is-drop-target {
  border-color: var(--lifted-orange);
  background: var(--tint-lifted-soft);
  color: var(--surface-elevated);
}
.dark-surface .empty-inline.is-drop-target .mdi {
  color: var(--lifted-orange);
}

/* ---------- DRAG HANDLE ----------
   The grip that says "this moves". A real button, because drag can never be the
   only way to reorder — it carries aria-label and moves its parent with the
   arrow keys, so reordering does not depend on a pointer or on fine motor
   control. Appears on hover and focus of the thing it moves; a permanently
   visible grip on every row turns a list into a control panel. */
.drag-handle {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-3);
  cursor: grab;
  opacity: 0;
  transition:
    opacity 150ms,
    background 150ms,
    color 150ms;
}
.drag-handle .mdi {
  font-size: 17px;
}
.drag-handle:hover {
  background: var(--tint-neutral-soft);
  color: var(--ink-2);
}
.drag-handle:active {
  cursor: grabbing;
  background: var(--tint-neutral);
}
.drag-handle:focus-visible {
  outline: none;
  opacity: 1;
  box-shadow: var(--focus-ring-accent-strong);
}
.drag-handle.is-always {
  opacity: 1;
}
[data-draggable]:hover .drag-handle,
[data-draggable]:focus-within .drag-handle {
  opacity: 1;
}
.dark-surface .drag-handle {
  color: var(--text-on-dark-muted);
}
.dark-surface .drag-handle:hover {
  background: var(--tint-on-dark-soft);
  color: var(--text-on-dark-secondary);
}
.dark-surface .drag-handle:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* Tile chrome — the handle row on a resizable tile: grip on the left, size cycle
   on the right. The size button CYCLES rather than opening a menu, because there
   are only two or three widths and a menu costs more than the change is worth. */
.tile-chrome {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 24px;
}
.tile-chrome .tile-chrome-spacer {
  margin-left: auto;
}
/* margin-left:auto — the size control is a trailing control, so it sits at the
   right edge of whatever row it is in without the row needing a spacer. */
.tile-size-cycle {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 var(--space-3);
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
  cursor: pointer;
  opacity: 0;
  transition:
    opacity 150ms,
    background 150ms,
    color 150ms;
}
.tile-size-cycle .mdi {
  font-size: 15px;
}
.tile-size-cycle:hover {
  background: var(--tint-neutral-soft);
  color: var(--ink-2);
}
.tile-size-cycle:focus-visible {
  outline: none;
  opacity: 1;
  box-shadow: var(--focus-ring-accent-strong);
}
[data-draggable]:hover .tile-size-cycle,
[data-draggable]:focus-within .tile-size-cycle {
  opacity: 1;
}
.dark-surface .tile-size-cycle {
  color: var(--text-on-dark-muted);
}
.dark-surface .tile-size-cycle:hover {
  background: var(--tint-on-dark-soft);
  color: var(--text-on-dark-secondary);
}
.dark-surface .tile-size-cycle:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}

/* ---------- INLINE EDIT FIELD ----------
   A value that becomes editable where it sits — a cell, a detail row, a card
   header. Correcting one field should not open a form.
   The resting state is TEXT, not a field: a table of 200 rows of input boxes is
   unreadable, and a value that looks editable when it is not is worse than one
   that does not advertise. The affordance appears on hover and focus only, and
   the element is a real <button> until it becomes a real <input>, so keyboard
   and screen-reader users reach it the same way.
   Committing is explicit — Enter or blur — and .is-saving holds the old value
   visible until the write lands. Never optimistic: a packhouse correction that
   silently failed is worse than one that visibly waited. */
/* No max-width here. The button already shrink-wraps, and max-width:100% against
   a shrink-to-fit wrapper is circular: the wrapper takes the button's width, the
   button is then clamped to the wrapper, and because .ie-value sets
   overflow:hidden its automatic minimum size is 0 — so the value absorbs the
   shortfall and truncates instead of the button growing. To cap it, give the
   WRAPPER a width; the ellipsis then does what it is for. */
.inline-edit {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 3px 6px;
  margin: -3px -6px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  text-align: left;
  cursor: text;
  transition:
    background 150ms,
    border-color 150ms;
}
.inline-edit .ie-value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.inline-edit .ie-pencil {
  flex-shrink: 0;
  opacity: 0;
  color: var(--ink-3);
  transition: opacity 150ms;
}
.inline-edit .ie-pencil .mdi {
  font-size: 14px;
}
.inline-edit:hover {
  background: var(--tint-neutral-soft);
  border-color: var(--line);
}
.inline-edit:hover .ie-pencil {
  opacity: 1;
}
.inline-edit:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
  border-color: var(--primary);
}
.inline-edit:focus-visible .ie-pencil {
  opacity: 1;
}
.inline-edit.is-empty .ie-value {
  color: var(--ink-3);
  font-style: italic;
}
/* Editing — now a real input. Keeps the resting box so the row does not jump. */
.inline-edit.is-editing {
  cursor: text;
  background: var(--surface-elevated);
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.inline-edit.is-editing .ie-pencil {
  display: none;
}
.inline-edit-input {
  width: 100%;
  min-width: 60px;
  padding: 0;
  border: none;
  background: transparent;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  color: inherit;
  outline: none;
}
/* Saving — the old value stays legible; the spinner replaces the pencil. */
.inline-edit.is-saving {
  background: var(--tint-neutral-soft);
  border-color: var(--line);
  color: var(--ink-2);
  cursor: progress;
}
.inline-edit.is-saving .ie-pencil {
  opacity: 1;
  display: inline-flex;
}
.inline-edit.is-saving .spinner {
  width: 13px;
  height: 13px;
  border-width: 1.5px;
}
/* Error — the write failed. The field STAYS open with the attempted value. */
.inline-edit.is-error {
  background: var(--tint-error-bg);
  border-color: var(--error);
  color: var(--error-press);
}
.inline-edit.is-error .ie-pencil {
  opacity: 1;
  color: var(--error-press);
}
.inline-edit-error {
  display: block;
  margin-top: var(--space-1);
  font-family: var(--font-body);
  font-size: 11.5px;
  color: var(--error-press);
}
.inline-edit:disabled,
.inline-edit.s-disabled {
  cursor: not-allowed;
  color: var(--ink-3);
  background: transparent;
  border-color: transparent;
}
.inline-edit:disabled .ie-pencil,
.inline-edit.s-disabled .ie-pencil {
  display: none;
}
/* ---------- INLINE EDIT · DROPDOWN (v2.484) ----------
   An inline edit whose value is picked, not typed (product: InlineField with
   options). Resting = plain text, caret on hover. Picking does not close the
   list: the panel goes .is-saving and every option is disabled (45%, no hover,
   no click) until the backend answers; the trigger shows the spinner. Success
   closes the list on the new value; failure re-enables the options and shows
   the reason at the top of the list. Never optimistic. */
.inline-edit-select {
  position: relative;
  display: inline-block;
}
.inline-edit.is-select {
  cursor: pointer;
}
.inline-edit.is-select .ie-pencil .mdi {
  font-size: 16px;
}
.inline-edit.is-select.is-open {
  background: var(--surface-elevated);
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.inline-edit.is-select.is-open .ie-pencil,
.inline-edit.is-select.is-saving .ie-pencil {
  opacity: 1;
}
.inline-edit-select > .select-panel:not(.is-inline) {
  position: absolute;
  top: calc(100% + 3px);
  left: -6px;
  z-index: 40;
  width: max-content;
  min-width: calc(100% + 12px);
  max-width: 280px;
}
.select-panel.is-saving {
  cursor: progress;
}
.select-panel.is-saving .select-option {
  opacity: 0.45;
  pointer-events: none;
}
.select-panel-error {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-bottom: 4px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  background: var(--tint-error-bg);
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.4;
  color: var(--error-press);
}
.select-panel-error .mdi {
  flex-shrink: 0;
  font-size: 15px;
}
.dark-surface .inline-edit.is-select.is-open {
  background: var(--tint-on-dark-faint);
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .select-panel-error {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .inline-edit {
  color: var(--text-on-dark);
}
.dark-surface .inline-edit .ie-pencil {
  color: var(--text-on-dark-muted);
}
.dark-surface .inline-edit:hover {
  background: var(--tint-on-dark-soft);
  border-color: var(--border-on-dark);
}
.dark-surface .inline-edit:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
  border-color: var(--lifted-orange);
}
.dark-surface .inline-edit.is-empty .ie-value {
  color: var(--text-on-dark-muted);
}
.dark-surface .inline-edit.is-editing {
  background: var(--tint-on-dark-faint);
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .inline-edit.is-saving {
  background: var(--tint-on-dark-soft);
  border-color: var(--border-on-dark);
  color: var(--text-on-dark-secondary);
}
.dark-surface .inline-edit.is-error {
  background: var(--tint-error-on-dark);
  border-color: var(--error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .inline-edit.is-error .ie-pencil {
  color: var(--error-on-dark);
}
.dark-surface .inline-edit-error {
  color: var(--error-on-dark);
}
.dark-surface .inline-edit:disabled,
.dark-surface .inline-edit.s-disabled {
  color: var(--text-on-dark-disabled);
}

/* ---------- RANGE SLIDER ----------
   A native <input type="range">, restyled. Native because it is the only control
   that already answers to arrow keys, Home/End and Page Up/Down, and announces
   its value — a div-based slider re-implements all of that and usually gets it
   wrong.
   NEVER the only way to set the value: pair it with a number field so a precise
   figure can be typed. A slider alone cannot express "exactly 12.5%".
   The thumb is 20px so it clears a touch target at the 6px track height; the
   fill left of the thumb is drawn with a gradient on the track, because ::before
   on an input is not rendered. */
.range-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 20px;
  background: transparent;
  cursor: pointer;
  display: block;
}
.range-slider:focus {
  outline: none;
}
/* v2.455 — orange fill left of the handle. WebKit/Blink: the track paints it from
   --range-slider-fill (0–100, unitless = the value as a percentage of min→max; the host sets it
   inline and on every input). The stop is offset by half the 20px thumb so the fill ends under
   the handle's centre at every value. Firefox: native ::-moz-range-progress, no binding needed. */
.range-slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--radius-pill);
  background: linear-gradient(
    to right,
    var(--primary) 0,
    var(--primary) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-neutral) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-neutral) 100%
  );
}
.range-slider::-moz-range-track {
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--tint-neutral);
}
.range-slider::-moz-range-progress {
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--primary);
}
.range-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  margin-top: -7px;
  border-radius: var(--radius-circle);
  background: var(--surface-elevated);
  border: 2px solid var(--primary);
  box-shadow: var(--shadow-card);
  transition:
    border-color 150ms,
    box-shadow 150ms;
}
.range-slider::-moz-range-thumb {
  width: 20px;
  height: 20px;
  border: 2px solid var(--primary);
  border-radius: var(--radius-circle);
  background: var(--surface-elevated);
  box-shadow: var(--shadow-card);
  transition:
    border-color 150ms,
    box-shadow 150ms;
}
.range-slider:hover::-webkit-slider-thumb {
  border-color: var(--primary-press);
}
.range-slider:hover::-moz-range-thumb {
  border-color: var(--primary-press);
}
.range-slider:focus-visible::-webkit-slider-thumb {
  box-shadow: var(--focus-ring-accent-strong);
}
.range-slider:focus-visible::-moz-range-thumb {
  box-shadow: var(--focus-ring-accent-strong);
}
/* Disabled takes BOTH forms, the way .select and every .btn-* do: the native
   :disabled and a forceable class. Shipped in v2.215 with only :disabled, so a
   consumer writing class="range-slider is-disabled" — copying the specimen, or
   following the .switch / .menu-item pattern documented on the same page —
   got a fully live, draggable slider in a read-only view.
   The class is .s-disabled, not .is-disabled: this system uses .s-disabled for
   controls that have a native :disabled and .is-disabled for constructs that
   do not. A range input has one. */
.range-slider.s-disabled,
.range-slider:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}
.range-slider.s-disabled::-webkit-slider-thumb,
.range-slider:disabled::-webkit-slider-thumb {
  border-color: var(--border-strong);
  box-shadow: none;
}
.range-slider.s-disabled::-moz-range-thumb,
.range-slider:disabled::-moz-range-thumb {
  border-color: var(--border-strong);
  box-shadow: none;
}
.dark-surface .range-slider.s-disabled::-webkit-slider-thumb,
.dark-surface .range-slider:disabled::-webkit-slider-thumb {
  border-color: var(--border-on-dark-hover);
}
.dark-surface .range-slider.s-disabled::-moz-range-thumb,
.dark-surface .range-slider:disabled::-moz-range-thumb {
  border-color: var(--border-on-dark-hover);
}
.range-slider.is-error::-webkit-slider-thumb {
  border-color: var(--error);
}
.range-slider.is-error::-moz-range-thumb {
  border-color: var(--error);
}
.range-slider.s-disabled::-webkit-slider-runnable-track,
.range-slider:disabled::-webkit-slider-runnable-track {
  background: linear-gradient(
    to right,
    var(--border-strong) 0,
    var(--border-strong) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-neutral) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-neutral) 100%
  );
}
.range-slider.s-disabled::-moz-range-progress,
.range-slider:disabled::-moz-range-progress {
  background: var(--border-strong);
}
.range-slider.is-error::-webkit-slider-runnable-track {
  background: linear-gradient(
    to right,
    var(--error) 0,
    var(--error) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-neutral) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-neutral) 100%
  );
}
.range-slider.is-error::-moz-range-progress {
  background: var(--error);
}
.dark-surface .range-slider::-webkit-slider-runnable-track {
  background: linear-gradient(
    to right,
    var(--lifted-orange) 0,
    var(--lifted-orange) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-on-dark-soft) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-on-dark-soft) 100%
  );
}
.dark-surface .range-slider.s-disabled::-webkit-slider-runnable-track,
.dark-surface .range-slider:disabled::-webkit-slider-runnable-track {
  background: linear-gradient(
    to right,
    var(--border-on-dark-hover) 0,
    var(--border-on-dark-hover) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-on-dark-soft) calc(10px + (100% - 20px) * var(--range-slider-fill) / 100),
    var(--tint-on-dark-soft) 100%
  );
}
.dark-surface .range-slider::-moz-range-track {
  background: var(--tint-on-dark-soft);
}
.dark-surface .range-slider::-moz-range-progress {
  background: var(--lifted-orange);
}
.dark-surface .range-slider.s-disabled::-moz-range-progress,
.dark-surface .range-slider:disabled::-moz-range-progress {
  background: var(--border-on-dark-hover);
}
.dark-surface .range-slider::-webkit-slider-thumb {
  background: var(--petrol-deep);
  border-color: var(--lifted-orange);
}
.dark-surface .range-slider::-moz-range-thumb {
  background: var(--petrol-deep);
  border-color: var(--lifted-orange);
}
.dark-surface .range-slider:hover::-webkit-slider-thumb {
  border-color: var(--lifted-orange-press);
}
.dark-surface .range-slider:hover::-moz-range-thumb {
  border-color: var(--lifted-orange-press);
}
.dark-surface .range-slider:focus-visible::-webkit-slider-thumb {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* The row: a label, the slider, and a number field that is the same value. */
.range-slider-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.range-slider-row .rs-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.range-slider-row .rs-label {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.range-slider-row .rs-field {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 5px;
}
.range-slider-row .rs-field .input {
  width: 66px;
  text-align: right;
  padding: 6px 8px;
  font-size: 13px;
}
.range-slider-row .rs-unit {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-3);
}
.range-slider-row .rs-scale {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  color: var(--ink-3);
}
.dark-surface .range-slider-row .rs-label {
  color: var(--text-on-dark);
}
.dark-surface .range-slider-row .rs-unit,
.dark-surface .range-slider-row .rs-scale {
  color: var(--text-on-dark-muted);
}

/* ---------- PROGRESS BAR ----------
   Determinate progress. A track and a fill; the number goes BESIDE the bar,
   never inside it — a percentage inside a 6px rule is unreadable at every width,
   and widening the bar to fit it turns a progress indicator into a chart.
   Always role="progressbar" with aria-valuenow: the fill length is the only
   visual signal and nothing announces it otherwise.
   .is-indeterminate does NOT fake a percentage — it sweeps, because a bar that
   sits at 90% for a minute is a lie the reader remembers. */
.progress-bar {
  position: relative;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--tint-neutral);
  overflow: hidden;
}
.progress-bar-fill {
  height: 100%;
  border-radius: inherit;
  background: var(--primary);
  transition: width 300ms ease;
}
.progress-bar.is-small {
  height: 4px;
}
.progress-bar.is-large {
  height: 10px;
}
.progress-bar.is-success .progress-bar-fill {
  background: var(--success);
}
.progress-bar.is-warning .progress-bar-fill {
  background: var(--warning);
}
.progress-bar.is-error .progress-bar-fill {
  background: var(--error);
}
.progress-bar.is-indeterminate .progress-bar-fill {
  width: 34%;
  animation: progress-sweep 1.5s ease-in-out infinite;
}
@keyframes progress-sweep {
  0% {
    transform: translateX(-105%);
  }
  100% {
    transform: translateX(400%);
  }
}
.dark-surface .progress-bar,
.photo-viewer:not(.is-light) .progress-bar {
  background: var(--tint-on-dark-soft);
}
.dark-surface .progress-bar-fill,
.photo-viewer:not(.is-light) .progress-bar-fill {
  background: var(--lifted-orange);
}
.dark-surface .progress-bar.is-success .progress-bar-fill {
  background: var(--success-on-dark);
}
.dark-surface .progress-bar.is-error .progress-bar-fill {
  background: var(--error-on-dark);
}
@media (prefers-reduced-motion: reduce) {
  .progress-bar-fill {
    transition: none;
  }
  .progress-bar.is-indeterminate .progress-bar-fill {
    animation-duration: 3.5s;
  }
}

/* ---------- PHOTO CARD ----------
   A photograph as a record: frame, then a title and one line of meta. The whole
   card is the control — it opens the Photo viewer — so it is a <button>, not a
   div with a click handler. Status belongs in a corner slot on the frame, never
   in the body, or a grid of cards loses its scan line. */
.photo-card {
  display: flex;
  flex-direction: column;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-align: left;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-body);
  transition:
    border-color 150ms,
    box-shadow 150ms;
}
.photo-card:hover {
  border-color: var(--border-stronger);
  box-shadow: var(--shadow-card);
}
.photo-card:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.photo-card.is-selected {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
}
.photo-card .photo-frame {
  border: none;
  border-radius: 0;
}
.photo-card-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 10px 12px 12px;
}
.photo-card-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.photo-card-meta {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.dark-surface .photo-card,
.photo-viewer:not(.is-light) .photo-card {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
.dark-surface .photo-card:hover,
.photo-viewer:not(.is-light) .photo-card:hover {
  border-color: var(--border-on-dark-hover);
}
.dark-surface .photo-card.is-selected,
.photo-viewer:not(.is-light) .photo-card.is-selected {
  border-color: var(--lifted-orange);
  box-shadow: 0 0 0 1px var(--lifted-orange);
}
.dark-surface .photo-card-title,
.photo-viewer:not(.is-light) .photo-card-title {
  color: var(--text-on-dark);
}
.dark-surface .photo-card-meta,
.photo-viewer:not(.is-light) .photo-card-meta {
  color: var(--text-on-dark-secondary);
}

/* ---------- PHOTO STRIP ----------
   The horizontal rail of every capture in a set, with one current. Scroll-snaps
   so a thumbnail never sits half-cut, and the current item is marked by a border
   AND its index — position in a strip is not a colour cue. */
.photo-strip {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 8px;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}
.photo-strip-item {
  position: relative;
  flex: 0 0 auto;
  width: 72px;
  border: 2px solid transparent;
  border-radius: var(--radius);
  overflow: hidden;
  background: none;
  padding: 0;
  cursor: pointer;
  scroll-snap-align: start;
}
.photo-strip-item .photo-frame {
  aspect-ratio: 1;
  border: none;
  border-radius: 0;
}
/* The index sits on its own scrim chip rather than relying on a text-shadow
   over the photograph. A shadow works on an image and vanishes on the .is-empty
   placeholder — which is every frame in these docs, and every capture slot
   before its photo arrives. The chip reads on both: white on --scrim-photo is
   ~6.9:1 composited over the light placeholder and higher over petrol. */
.photo-strip-item .ps-index {
  position: absolute;
  right: 3px;
  bottom: 3px;
  z-index: 2;
  padding: 1px 4px;
  border-radius: var(--radius-sm);
  background: var(--scrim-photo);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--surface-elevated);
}
.photo-strip-item.is-current {
  border-color: var(--primary);
}
.photo-strip-item:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.photo-strip-item.has-findings::after {
  content: '';
  position: absolute;
  top: 4px;
  left: 4px;
  width: 7px;
  height: 7px;
  border-radius: var(--radius-circle);
  background: var(--error-press);
  border: 1.5px solid var(--surface-elevated);
  z-index: 2;
}
.dark-surface .photo-strip-item.is-current,
.photo-viewer:not(.is-light) .photo-strip-item.is-current {
  border-color: var(--lifted-orange);
}
/* ---------- PHOTO PANE + SPLIT CARD ----------
   The photo half of a full-width record card: a header (icon, title, count,
   pager), a 3-up grid of captures, and an empty state — plus the .split-card
   layout that docks it beside the record's content. The pager is the Record
   pager; each capture is a Photo frame in a button; clicking one opens the
   Photo viewer. Below 640px the split stacks and the side divider rotates
   from a left hairline to a top one; inside a bento tile the same stack is
   driven by TILE width (container query, under 560px) so a half tile always
   gets the stacked form. */
.split-card {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: stretch;
}
.split-card-main {
  flex: 1 1 auto;
  min-width: min(280px, 100%);
}
.split-card-side {
  flex: 1 1 260px;
  min-width: min(220px, 100%);
  border-left: 0.5px solid var(--line);
  padding-left: 16px;
  display: flex;
  flex-direction: column;
}
@media (max-width: 640px) {
  .split-card-main,
  .split-card-side {
    flex: 1 1 100%;
  }
  .split-card-side {
    border-left: none;
    padding-left: 0;
    border-top: 0.5px solid var(--line);
    padding-top: 16px;
  }
}
/* Width-driven stack (v2.403 in bento, generalized v2.419) — the stacked form is
   driven by CARD width: .split-card-frame and .bento-tile are size containers, so
   a half tile or a half-width card stacks at any viewport. 560px mirrors the
   geometry the 640px viewport rule protects. */
.split-card-frame {
  container-type: inline-size;
  padding: 16px 18px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-frame);
}
.dark-surface .split-card-frame {
  background: var(--petrol-deep);
  border-color: var(--tint-on-dark-soft);
  box-shadow: none;
}
@container (max-width: 560px) {
  .split-card-main,
  .split-card-side {
    flex: 1 1 100%;
  }
  .split-card-side {
    border-left: none;
    padding-left: 0;
    border-top: 0.5px solid var(--line);
    padding-top: 16px;
  }
}
.dark-surface .bento-tile .split-card-side {
  border-top-color: var(--tint-on-dark-soft);
}
.photo-pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}
.photo-pane-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin-bottom: 10px;
  min-height: 34px;
}
.photo-pane-head > .mdi {
  font-size: 15px;
  color: var(--ink-3);
}
.photo-pane-title {
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 13px;
  color: var(--ink);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.photo-pane-meta {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.photo-pane-head .record-pager {
  margin-left: auto;
  flex: 0 0 auto;
}
.photo-pane-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.photo-pane-item {
  position: relative;
  padding: 0;
  border: none;
  background: none;
  cursor: zoom-in;
  border-radius: var(--radius-md);
  overflow: hidden;
  text-align: left;
}
.photo-pane-item:hover .photo-frame {
  border-color: var(--border-stronger);
}
.photo-pane-item:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.photo-pane-item-label {
  position: absolute;
  left: 6px;
  bottom: 6px;
  z-index: 2;
  padding: 2px 7px;
  background: var(--scrim-photo);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 600;
  color: var(--surface-elevated);
}
.photo-pane-empty {
  flex: 1;
  display: grid;
  place-items: center;
  min-height: 96px;
  border: 1px dashed var(--line);
  border-radius: var(--radius-md);
  font-family: var(--font-body);
  font-size: 12px;
  color: var(--ink-3);
}
.dark-surface .split-card-side {
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .photo-pane-head > .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .photo-pane-title {
  color: var(--text-on-dark);
}
.dark-surface .photo-pane-meta {
  color: var(--text-on-dark-muted);
}
.dark-surface .photo-pane-item:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .photo-pane-empty {
  border-color: var(--border-on-dark);
  color: var(--text-on-dark-secondary);
}

/* ---------- MENU / POPOVER ----------
   The shared dropdown surface used by row actions, filter flyouts, and the
   page-size / column menus. .menu is the panel; .menu-item the row.
   Add .is-selected (accent tint) or .is-danger (destructive). Anchor with
   inline top/left/right; the panel itself is position-agnostic. */
.menu {
  position: absolute;
  z-index: 80;
  min-width: 180px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-frame);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.menu-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 6px 10px 4px;
}
.menu-divider {
  height: 0.5px;
  background: var(--line-2);
  margin: 4px 0;
}
.menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 36px;
  padding: 0 10px;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  cursor: pointer;
  text-decoration: none;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
  width: 100%;
  text-align: left;
}
.menu-item .mdi {
  font-size: 17px;
  color: var(--ink-3);
  flex-shrink: 0;
}
.menu-item .mi-label {
  flex: 1;
}
.menu-item:hover {
  background: var(--tint-accent-soft, rgba(234, 93, 46, 0.09));
  color: var(--accent-text-on-tint);
}
.menu-item:hover .mdi {
  color: var(--primary);
}
.menu-item.is-selected {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  font-weight: 600;
}
.menu-item.is-selected .mdi {
  color: var(--primary);
}
.menu-item.is-danger {
  color: var(--ink-2);
}
.menu-item.is-danger:hover {
  background: rgba(239, 68, 68, 0.08);
  color: var(--error-text);
}
.menu-item.is-danger:hover .mdi {
  color: var(--error-text);
}
.menu-item.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
/* checkbox menu row (multi-select filter / column visibility) */
.menu-check {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 34px;
  padding: 0 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink);
  width: 100%;
}
.menu-check:hover {
  background: var(--tint-accent-soft);
}
.menu-check .box {
  box-sizing: border-box;
  width: var(--checkbox-size);
  height: var(--checkbox-size);
  border-radius: var(--checkbox-radius);
  border: 1px solid rgba(0, 0, 0, 0.3);
  background: var(--surface-elevated);
  display: grid;
  place-items: center;
  color: transparent;
  flex-shrink: 0;
}
.menu-check .box .mdi {
  display: none;
}
.menu-check.is-checked .box {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--surface-elevated);
}
.menu-check.is-checked .box::after {
  content: '';
  width: 12px;
  height: 12px;
  background-color: currentColor;
  -webkit-mask: var(--icon-mask-check-thin) center / 12px no-repeat;
  mask: var(--icon-mask-check-thin) center / 12px no-repeat;
}

.menu-check .mc-label {
  flex: 1;
}
.menu-check.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
/* v2.391 — two column-menu row states from Dashboard / Assessments' Table settings:
   .is-required = locked ON (the column the table cannot lose — pair with a
   .menu-check-meta "required" tag so the lock reads as policy, not breakage);
   .is-width-locked = cannot turn ON at this width (dimmer than disabled; carry a
   title="Not enough width — turn off another column first" so the lock explains itself). */
.menu-check.is-required {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}
.menu-check.is-width-locked {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}
.menu-check-meta {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  flex-shrink: 0;
}
/* radio menu row — one exclusive setting inside a flyout; two-line label
   (title + description) because a setting needs its consequence stated. */
.menu-radio {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 10px;
  border: none;
  background: transparent;
  text-align: left;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink);
  width: 100%;
}
.menu-radio:hover {
  background: var(--tint-accent-soft);
}
.menu-radio:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}
.menu-radio .dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--surface-elevated);
  flex-shrink: 0;
  margin-top: 1px;
  display: grid;
  place-items: center;
}
.menu-radio.is-checked .dot {
  border-color: var(--primary);
}
.menu-radio.is-checked .dot::after {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--primary);
}
.menu-radio.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}
.menu-radio-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.menu-radio-title {
  font-weight: 600;
  line-height: 1.25;
}
.menu-radio-description {
  font-size: 11.5px;
  color: var(--ink-3);
  line-height: 1.4;
}
/* optional search header inside a flyout (FilterSelects with many options) */
.menu-search {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  margin: -2px -2px 4px;
  border-bottom: 0.5px solid var(--line-2);
}
.menu-search .mdi {
  font-size: 16px;
  color: var(--ink-3);
}
.menu-search input {
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: 13px;
  flex: 1;
  min-width: 0;
  color: var(--ink);
}
.menu-search input::placeholder {
  color: var(--ink-3);
}
.dark-surface .menu-search {
  border-bottom-color: var(--border-on-dark-medium);
}
.dark-surface .menu-search .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .menu-search input {
  color: var(--surface-elevated);
}
.dark-surface .menu {
  background: var(--petrol-deep);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .menu-item {
  color: var(--text-on-dark-secondary);
}
.dark-surface .menu-item:hover {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .menu-item:hover .mdi {
  color: var(--lifted-orange);
}
.dark-surface .menu-item.is-selected {
  background: var(--tint-lifted-selected);
  color: var(--lifted-orange);
}
.dark-surface .menu-item.is-selected .mdi {
  color: var(--lifted-orange);
}
.dark-surface .menu-check {
  color: var(--surface-elevated);
}
.dark-surface .menu-check:hover {
  background: var(--tint-lifted-medium);
}
.dark-surface .menu-label {
  color: var(--text-on-dark-muted);
}
.dark-surface .menu-divider {
  background: var(--border-on-dark-medium);
}
.dark-surface .menu-check .box {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .menu-check.is-checked .box {
  background: var(--lifted-orange);
  border-color: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .menu-check-meta {
  color: var(--text-on-dark-secondary);
}
.dark-surface .menu-radio {
  color: var(--surface-elevated);
}
.dark-surface .menu-radio:hover {
  background: var(--tint-lifted-medium);
}
.dark-surface .menu-radio:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .menu-radio .dot {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .menu-radio.is-checked .dot {
  border-color: var(--lifted-orange);
}
.dark-surface .menu-radio.is-checked .dot::after {
  background: var(--lifted-orange);
}
.dark-surface .menu-radio-description {
  color: var(--text-on-dark-secondary);
}

/* ---------- VIEW SETTINGS ----------
   v2.392 — the assessments views' “View settings” flyout (Dashboard / Assessments).
   ITS OWN flyout, opened from the toolbar — NOT part of Table settings (v2.391
   documented it combined; corrected). Rides the .menu surface; each section is a
   titled setting (an optional one-line consequence) and a full-width .segmented
   control (.full.is-medium) that applies instantly. v2.408 — resized to the
   More-filters panel scale (320px, .mf-title-sized header, 12.5px labels) to
   match the shipped flyout exactly. v2.405 — the v2.404 switch rows are
   retired (the settings pick between two named modes — Piece/Weight,
   Device/Camera — not on/off); the flyout reuses Segmented control as-is. */
.view-settings {
  width: 320px;
  max-width: 100%;
  padding: 16px 18px;
}
.view-settings-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}
.view-settings-head .mdi {
  color: var(--primary);
  font-size: 16px;
}
.view-settings-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  color: var(--ink);
  flex: 1;
}
.view-settings-close {
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  border-radius: var(--radius);
  cursor: pointer;
  display: grid;
  place-items: center;
  color: var(--ink-3);
}
.view-settings-close:hover {
  background: var(--tint-neutral);
}
.view-settings-close:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}
.view-settings-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 0;
}
.view-settings-section-title {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 12.5px;
  color: var(--ink);
}
.view-settings-description {
  font-size: 11px;
  color: var(--ink-3);
  line-height: 1.4;
  margin: 0;
}
.dark-surface .view-settings-head .mdi {
  color: var(--lifted-orange);
}
.dark-surface .view-settings-title {
  color: var(--surface-elevated);
}
.dark-surface .view-settings-close {
  color: var(--text-on-dark-secondary);
}
.dark-surface .view-settings-close:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .view-settings-close:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .view-settings-section-title {
  color: var(--surface-elevated);
}
.dark-surface .view-settings-description {
  color: var(--text-on-dark-secondary);
}

/* ---------- FILTER TOOLBAR ----------
   The list-view control strip: a search field, a row of filter-pill
   dropdowns (single & multi-select), then Clear all and More pushed
   right. Richer than the toggle .chip filters. Pills open a .menu. */
/* ONE LINE, ALWAYS. The bar never wraps and never grows a second row: the filter
   zone gives way instead, pushing whatever no longer fits into More filters.
   Two zones, and consumers must use both:
     .toolbar-filters — first-level filters. Shrinks; its overflow is hidden and
                        the hidden pills are surfaced in the More panel.
     .toolbar-tail    — pinned right. Clear all, More and Refresh live here
                        and NEVER shrink or overflow, so the escape hatch and the
                        reset are always reachable at any width. */
.filter-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;
  overflow-x: clip;
  overflow-y: visible;
  overflow-clip-margin: 6px;
  padding: 8px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-frame);
}
.filter-toolbar.is-plain {
  padding: 10px 0;
  background: transparent;
  border: none;
  box-shadow: none;
  gap: 8px;
}
/* REQUIRED WRAPPER. .filter-toolbar clips horizontally (overflow-x:clip — that
   is how a pill that does not fit disappears from the bar) while leaving the
   vertical axis visible, so a pill's own dropdown can drop below the bar instead
   of being cut off. overflow-x:clip is deliberate: unlike overflow:hidden it lets
   the other axis stay visible, and it creates no scroll container. The wide More
   panel still needs a horizontally-unclipped anchor, so the bar is always
   wrapped: .filter-toolbar-wrap is the overflow-visible positioning context the
   .more-filters panel anchors to, and it owns the bar's outer width.
   Never anchor the More panel inside .filter-toolbar itself. */
.filter-toolbar-wrap {
  position: relative;
  overflow: visible;
  width: 100%;
}
/* The More panel anchors to .filter-toolbar-wrap, NOT to a relatively positioned
   div inside the bar: the wrap is the only ancestor wide enough to right-align
   against and to cap the panel's width, and because .filter-toolbar is
   position:static an absolutely positioned panel escapes its clip anyway. The
   [data-menu] wrapper around .toolbar-more is therefore forced static — a relative
   one makes the ~100px button the containing block, so right:0 and max-width both
   resolve against the button instead of the bar and the panel overhangs. */
.filter-toolbar .toolbar-tail > [data-menu]:has(> .toolbar-more) {
  position: static;
}
.filter-toolbar-wrap .more-filters {
  top: calc(100% + 6px);
  right: 0;
  max-width: 100%;
}
/* REQUIRED MEASURING ESCAPE. The overflow logic decides which filters fit by
   reading their natural widths — impossible while the bar clips and the
   overflowed filters are display:none. Add .is-measuring for that one frame: the
   bar stops clipping, every filter is laid out and visible, and transitions are
   off so nothing animates mid-measure. Without it a filter pushed into More can
   never come back when the bar widens again. Always removed in the same frame.
   The escape must match the SAME selectors as the hide rule below — [data-filter]
   as well as .filter-pill — or a non-pill filter (button, select) measures as
   zero-width and is stranded in More for good. */
.filter-toolbar.is-measuring {
  overflow: visible;
}
.filter-toolbar.is-measuring [data-filter].is-overflowed,
.filter-toolbar.is-measuring .filter-pill.is-overflowed {
  display: inline-flex;
  visibility: hidden;
  position: absolute;
  pointer-events: none;
}
.filter-toolbar.is-measuring .toolbar-filters {
  flex: 0 0 auto;
  overflow: visible;
}
.filter-toolbar.is-measuring * {
  transition: none !important;
  animation: none !important;
}
/* White, not --paper: the field must read as an input sitting ON the bar, and it
   matches the .search-field atom it is a sized copy of. --paper is the bar's own
   tone, so the field disappeared into it. */
.toolbar-search {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 12px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  min-width: 200px;
  flex: 0 0 220px;
}
.filter-toolbar.is-plain .toolbar-search {
  border-width: 1px;
  border-color: var(--border-strong);
  flex: 0 0 auto;
}
.toolbar-search .mdi {
  font-size: 16px;
  color: var(--ink-3);
}
.toolbar-search input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink);
}
.toolbar-search input::placeholder {
  color: var(--ink-3);
}
.toolbar-divider {
  width: 1px;
  height: 22px;
  background: var(--line-2);
  margin: 0 4px;
  flex-shrink: 0;
}
.toolbar-spacer {
  flex: 1;
}
.toolbar-filters {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: clip;
  overflow-y: visible;
  overflow-clip-margin: 6px;
}
/* Tail order is fixed: Clear all, then More, then Refresh, then VIEW SETTINGS
   LAST (amended v2.367 and again v2.396 — "More last" until Refresh joined,
   "Refresh last" until View settings joined). More stays the right-most FILTER
   control — a constant target the eye and the mouse can always find — Refresh
   sits past it because it acts on the data, not the filters, and View settings
   sits past Refresh because it acts on how the data is SHOWN. Clear all sits
   immediately before More, so the controls that always survive overflow sit
   together at the right edge. Slot content a consumer adds to the tail carries
   no order (order:0), so it lands before the system controls. */
.toolbar-tail {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  margin-left: auto;
  padding-left: 6px;
}
.toolbar-tail > .toolbar-more,
.toolbar-tail > [data-menu]:has(> .toolbar-more) {
  order: 2;
}
.toolbar-tail > .toolbar-clear {
  order: 1;
}
.toolbar-tail > .toolbar-refresh {
  order: 3;
}
.toolbar-tail > .toolbar-view-settings {
  order: 4;
}
/* A first-level filter the bar could not fit. Hidden here, listed in the More
   panel under .mf-overflow — moved, never lost. */
.filter-toolbar [data-filter].is-overflowed,
.filter-pill.is-overflowed {
  display: none;
}
/* Overflowed filters, surfaced at the top of the More panel. */
.mf-overflow {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-bottom: 12px;
  margin-bottom: 12px;
  border-bottom: 0.5px solid var(--line);
}
.mf-overflow:empty,
.mf-overflow[hidden] {
  display: none;
}
.mf-overflow-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.dark-surface .mf-overflow {
  border-bottom-color: var(--border-on-dark);
}
.dark-surface .mf-overflow-label {
  color: var(--text-on-dark-muted);
}

.filter-pill:focus-visible,
.toolbar-btn:focus-visible,
.toolbar-more:focus-visible,
.toolbar-clear:focus-visible,
.toolbar-refresh:focus-visible,
.toolbar-view-settings:focus-visible,
.select-mini:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.dark-surface .filter-pill:focus-visible,
.dark-surface .toolbar-btn:focus-visible,
.dark-surface .toolbar-more:focus-visible,
.dark-surface .toolbar-clear:focus-visible,
.dark-surface .toolbar-refresh:focus-visible,
.dark-surface .toolbar-view-settings:focus-visible,
.dark-surface .select-mini:focus-visible {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}

.filter-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 10px 0 12px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 13px;
  color: var(--ink-2);
  white-space: nowrap;
}
.filter-pill .fp-icon {
  font-size: 14px;
  color: var(--ink-3);
}
.filter-pill .fp-caret {
  font-size: 14px;
  color: var(--ink-3);
}
.filter-pill:hover {
  border-color: var(--border-stronger);
}
.filter-pill.is-active {
  background: var(--tint-accent-medium);
  border-color: var(--accent-border-soft);
  color: var(--accent-text-on-tint);
  font-weight: 600;
}
.filter-pill.is-active .fp-icon,
.filter-pill.is-active .fp-caret {
  color: var(--primary);
}
.filter-pill .fp-count {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  background: var(--primary);
  color: var(--surface-elevated);
  border-radius: var(--radius-pill);
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* More-filters overflow button — a filter-pill sibling with a tune icon + optional count. */
/* Always visible — it is the escape hatch for everything the bar could not fit,
   so it is never hidden, never shrunk, and never overflowed. */
.toolbar-more {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  white-space: nowrap;
  height: 36px;
  padding: 0 12px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  color: var(--ink-2);
}
.toolbar-more .mdi {
  font-size: 14px;
  color: var(--ink-3);
}
.toolbar-more:hover {
  border-color: var(--border-stronger);
}
.toolbar-more.is-active {
  background: var(--tint-accent-medium);
  border-color: var(--accent-border-soft);
  color: var(--accent-text-on-tint);
}
.toolbar-more.is-active .mdi {
  color: var(--primary);
}
.toolbar-more.is-open {
  background: var(--surface-elevated);
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.toolbar-more.is-open .mdi {
  color: var(--primary);
}
.toolbar-more.is-active.is-open {
  color: var(--accent-text-on-tint);
}
/* Petrol had NO open rule, so the light one applied and the button turned white
   on the petrol bar — the same click produced a different effect on each surface.
   Same tint + lifted-orange treatment as the pill. */
.dark-surface .toolbar-more.is-open {
  background: var(--tint-on-dark-medium);
  border-color: var(--lifted-orange);
  color: var(--text-on-dark);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .toolbar-more.is-open .mdi {
  color: var(--lifted-orange);
}
.toolbar-more .fp-count {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  background: var(--primary);
  color: var(--surface-elevated);
  border-radius: var(--radius-pill);
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Clear-all — right-aligned quiet reset; accent-bordered, appears only when a filter is set. */
.toolbar-clear {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  height: 36px;
  padding: 0 12px;
  background: transparent;
  border: 0.5px solid var(--accent-border-soft);
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--accent-text-on-tint);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  white-space: nowrap;
}
.toolbar-clear .mdi {
  font-size: 14px;
  color: var(--primary);
}
.toolbar-clear:hover {
  border-color: var(--primary);
}
/* Refresh button — icon-only data action that reloads what the bar controls.
   Same 36px control recipe; pinned order:3 in the tail (past More;
   View settings, order:4, sits past it since v2.396) because it acts on the
   data, not the filters. Icon-only, so it must carry
   aria-label="Refresh"; add .is-loading + aria-busy="true" while a fetch is in
   flight — continuous spin plus the accent icon step, so the state survives
   prefers-reduced-motion (slowed, never removed, matching .spinner). */
.toolbar-refresh {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  padding: 0;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
}
.toolbar-refresh .mdi {
  font-size: 16px;
  color: var(--ink-3);
}
.toolbar-refresh:hover,
.toolbar-refresh.s-hover {
  border-color: var(--border-stronger);
}
.toolbar-refresh.s-focus {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.toolbar-refresh.is-loading .mdi {
  color: var(--primary);
  animation: btn-spin 600ms linear infinite;
}
.toolbar-refresh:disabled,
.toolbar-refresh.is-disabled {
  background: var(--paper);
  border-color: var(--line);
  box-shadow: none;
  cursor: not-allowed;
}
.toolbar-refresh:disabled .mdi,
.toolbar-refresh.is-disabled .mdi {
  color: var(--ink-3);
}
.dark-surface .toolbar-refresh {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .toolbar-refresh .mdi {
  color: var(--lifted-orange);
}
.dark-surface .toolbar-refresh:hover,
.dark-surface .toolbar-refresh.s-hover {
  background: var(--tint-on-dark-strong);
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .toolbar-refresh.s-focus {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .toolbar-refresh.is-loading .mdi {
  color: var(--lifted-orange);
}
.dark-surface .toolbar-refresh:disabled,
.dark-surface .toolbar-refresh.is-disabled {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
.dark-surface .toolbar-refresh:disabled .mdi,
.dark-surface .toolbar-refresh.is-disabled .mdi {
  color: var(--text-on-dark-muted);
}
@media (prefers-reduced-motion: reduce) {
  .toolbar-refresh.is-loading .mdi {
    animation-duration: 1400ms;
  }
}
/* View settings button — icon-only VIEW action that opens the display-options
   panel (columns, density, grouping) for the list the bar controls. Same 36×36px
   outlined control recipe as .toolbar-refresh — NOT .icon-button, which is
   borderless; every control in this bar carries the outlined edge. Pinned
   order:4, LAST in the tail (tail-order rule amended v2.396): Refresh acts on
   the data, View settings on how the data is shown. Icon-only, so it always
   carries aria-label="View settings"; add .is-open + aria-expanded="true" while
   its panel is open — the same open treatment as .toolbar-more. */
.toolbar-view-settings {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  padding: 0;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
}
.toolbar-view-settings .mdi {
  font-size: 16px;
  color: var(--ink-3);
}
.toolbar-view-settings:hover,
.toolbar-view-settings.s-hover {
  border-color: var(--border-stronger);
}
.toolbar-view-settings.s-focus {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.toolbar-view-settings.is-open {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.toolbar-view-settings.is-open .mdi {
  color: var(--primary);
}
.toolbar-view-settings:disabled,
.toolbar-view-settings.is-disabled {
  background: var(--paper);
  border-color: var(--line);
  box-shadow: none;
  cursor: not-allowed;
}
.toolbar-view-settings:disabled .mdi,
.toolbar-view-settings.is-disabled .mdi {
  color: var(--ink-3);
}
.dark-surface .toolbar-view-settings {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .toolbar-view-settings .mdi {
  color: var(--lifted-orange);
}
.dark-surface .toolbar-view-settings:hover,
.dark-surface .toolbar-view-settings.s-hover {
  background: var(--tint-on-dark-strong);
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .toolbar-view-settings.s-focus {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .toolbar-view-settings.is-open {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .toolbar-view-settings.is-open .mdi {
  color: var(--lifted-orange);
}
.dark-surface .toolbar-view-settings:disabled,
.dark-surface .toolbar-view-settings.is-disabled {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
.dark-surface .toolbar-view-settings:disabled .mdi,
.dark-surface .toolbar-view-settings.is-disabled .mdi {
  color: var(--text-on-dark-muted);
}
/* ONE RECIPE FOR EVERY CONTROL IN THE BAR. .filter-pill, .toolbar-more,
   .toolbar-clear, .toolbar-refresh and .toolbar-view-settings share height
   (36px), label size (13px), hover, focus and open, so
   no two controls in the same strip answer a click differently. .toolbar-btn keeps
   the same recipe but is no longer used in the bar (v2.195). */
.toolbar-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 12px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  color: var(--ink-2);
  white-space: nowrap;
}
.toolbar-btn .mdi {
  font-size: 16px;
  color: var(--ink-3);
}
.toolbar-btn.is-active {
  background: var(--tint-accent-medium);
  border-color: var(--accent-border-soft);
  color: var(--accent-text-on-tint);
}
.toolbar-btn.is-active .mdi {
  color: var(--primary);
}
.toolbar-btn:hover {
  border-color: var(--border-stronger);
}
.dark-surface .filter-toolbar {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-medium);
  box-shadow: none;
}
.dark-surface .filter-toolbar.is-plain {
  background: transparent;
  border: none;
}
.dark-surface .toolbar-search {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .toolbar-search input {
  color: var(--text-on-dark);
}
/* The light placeholder is --ink-3; without this it stayed dark ink on petrol. */

/* RESTING boundary must clear 3:1 (WCAG 1.4.11): a filter pill and the search
   input are components identified by their edge. --border-on-dark-medium (0.25)
   measures 2.05:1 against a petrol bar and the 0.12 fill only 1.42:1, so neither
   carries the boundary — the edge steps up to 0.45 (3.41:1). */
.dark-surface .filter-pill {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
  color: var(--text-on-dark-secondary);
}
/* fp-key / fp-val are --ink-2 / --ink on light and had no petrol override, so the
   pill label rendered as dark ink on petrol. Key 0.78 white, value pure white —
   the same key/value hierarchy the light pill has, both clearing AA. */
.dark-surface .filter-pill .fp-key {
  color: var(--text-on-dark-secondary);
}
.dark-surface .filter-pill .fp-val {
  color: var(--text-on-dark);
}
.dark-surface .filter-pill:hover {
  background: var(--tint-on-dark-strong);
  border-color: var(--border-on-dark-emphasis);
}
/* ACTIVE ON PETROL: the LABEL stays white, not lifted orange. Lifted orange over
   a lifted-orange tint measures 3.49:1 — below the 4.5:1 floor for 13px text —
   because the tint lightens the backdrop the orange has to clear. Orange still
   carries the state through the border, the icon, the caret and the count (all
   graphics, which need only 3:1), plus 600 weight — so the state is never
   signalled by colour alone. */
.dark-surface .filter-pill.is-active {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
  color: var(--text-on-dark);
}
.dark-surface .filter-pill.is-active .fp-key {
  color: var(--text-on-dark-secondary);
}
.dark-surface .filter-pill.is-active .fp-val {
  color: var(--text-on-dark);
}
.dark-surface .filter-pill.is-active .fp-icon,
.dark-surface .filter-pill.is-active .fp-caret {
  color: var(--lifted-orange);
}
.dark-surface .filter-pill .fp-count {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .toolbar-more {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
  color: var(--text-on-dark);
}
.dark-surface .toolbar-more:hover {
  background: var(--tint-on-dark-strong);
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .toolbar-more .mdi {
  color: var(--lifted-orange);
}
.dark-surface .toolbar-more.is-active {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
  color: var(--text-on-dark);
}
.dark-surface .toolbar-more .fp-count {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .toolbar-clear {
  border-color: var(--lifted-orange);
  color: var(--text-on-dark);
}
.dark-surface .toolbar-clear .mdi {
  color: var(--lifted-orange);
}
.dark-surface .toolbar-clear:hover {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
}
.dark-surface .toolbar-btn {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
  color: var(--text-on-dark);
}
.dark-surface .toolbar-btn:hover {
  background: var(--tint-on-dark-strong);
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .toolbar-btn.is-active {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
  color: var(--text-on-dark);
}
.dark-surface .toolbar-btn.is-active .mdi {
  color: var(--lifted-orange);
}

/* ---------- DATE-RANGE PICKER + CALENDAR ----------
   A combined presets rail + month calendar with a highlighted range and a
   From/To footer. .date-picker is the popover panel; .date-presets the left
   rail (radio .dp-opt rows, grouped by .dp-group / .dp-divider); .calendar the
   month grid (Monday-first). Day states: .today, .in-range, .range-start,
   .range-end. .cal-foot holds the .date-input-field From/To fields and the
   Cancel / Apply-range actions. .date-field is the closed trigger (.is-open
   petrol while open, .is-active tint when a non-default value is set). */
.date-field {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 10px;
  max-width: 100%;
  min-width: 0;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
}
.date-field .mdi {
  font-size: 14px;
  color: var(--ink-3);
}
.date-field .df-key {
  color: var(--ink-3);
  font-weight: 500;
  flex-shrink: 0;
}
.date-field .df-val {
  font-weight: 600;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.date-field.is-active {
  background: var(--tint-accent-medium);
  border-color: var(--accent-border-soft);
  color: var(--accent-text-on-tint);
}
.date-field.is-active .mdi,
.date-field.is-active .df-key {
  color: var(--primary-press);
}
.date-field.is-open {
  background: var(--petrol);
  border-color: var(--petrol);
  color: var(--surface-elevated);
}
.date-field.is-open .mdi {
  color: var(--surface-elevated);
}
.date-field.is-open .df-key {
  color: var(--text-on-dark-muted);
}
.date-picker {
  position: absolute;
  z-index: 80;
  display: flex;
  width: 560px;
  max-width: calc(100vw - 32px);
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-frame);
  overflow: hidden;
}
.date-presets {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px;
  width: 200px;
  flex-shrink: 0;
  background: var(--paper);
  border-right: 0.5px solid var(--line);
}
.date-presets .dp-group {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 6px 10px 2px;
}
.date-presets .dp-divider {
  height: 1px;
  background: var(--line-2);
  margin: 6px 4px;
}
.date-presets .dp-opt {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 34px;
  padding: 0 10px;
  border: none;
  background: transparent;
  border-radius: var(--radius-xs);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
  text-align: left;
}
.date-presets .dp-opt:hover {
  background: var(--tint-neutral-soft);
}
.date-presets .dp-opt.is-selected {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  font-weight: 600;
}
.calendar {
  flex: 1;
  padding: 16px;
}
.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  position: relative;
}
.cal-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.cal-title-group {
  display: flex;
  align-items: center;
  gap: 4px;
}
.cal-title-btn {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -0.01em;
  color: var(--ink);
  background: none;
  border: none;
  padding: 3px 6px;
  border-radius: var(--radius);
  cursor: pointer;
}
.cal-title-btn .mdi {
  font-size: 16px;
  color: var(--ink-3);
}
.cal-title-btn:hover {
  background: var(--paper);
}
.cal-title-btn.is-open {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
}
.cal-title-btn.is-open .mdi {
  color: var(--primary);
}
.cal-flyout {
  position: absolute;
  z-index: 12;
  top: 36px;
  left: 50%;
  transform: translateX(-50%);
  width: 212px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-frame);
  padding: 8px;
}
.cal-flyout[hidden] {
  display: none;
}
.cal-flyout-month {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
}
.cal-flyout-year {
  width: 132px;
  max-height: 198px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.cal-opt {
  border: none;
  background: none;
  border-radius: var(--radius);
  padding: 8px 6px;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  text-align: center;
}
.cal-flyout-year .cal-opt {
  text-align: left;
  padding: 8px 12px;
}
.cal-opt:hover {
  background: var(--line-2);
}
.cal-opt.is-selected {
  background: var(--primary);
  color: var(--surface-elevated);
  font-weight: 600;
}
.cal-title-btn:focus-visible,
.cal-opt:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.cal-nav {
  width: 26px;
  height: 26px;
  border: 0.5px solid var(--line);
  background: var(--surface-elevated);
  border-radius: var(--radius);
  cursor: pointer;
  display: grid;
  place-items: center;
  color: var(--ink-2);
}
.cal-nav .mdi {
  font-size: 14px;
}
.cal-nav:hover {
  background: var(--paper);
}
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.cal-dow {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  text-align: center;
  padding: 4px 0;
}
.cal-day {
  height: 30px;
  display: grid;
  place-items: center;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
  border-radius: var(--radius);
  cursor: pointer;
  border: none;
  background: transparent;
}
.cal-day:hover {
  background: var(--line-2);
}
.cal-day.today {
  border: 1px solid var(--primary);
  font-weight: 700;
}
.cal-day.in-range {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  border-radius: 0;
}
.cal-day.range-start {
  background: var(--primary);
  color: var(--surface-elevated);
  font-weight: 700;
  border-radius: var(--radius) 0 0 var(--radius);
}
.cal-day.range-end {
  background: var(--primary);
  color: var(--surface-elevated);
  font-weight: 700;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.cal-day.today.in-range,
.cal-day.today.range-start,
.cal-day.today.range-end {
  border: none;
}
.cal-foot {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 0.5px solid var(--line-2);
}
.cal-foot-fields {
  display: flex;
  gap: 10px;
  align-items: flex-end;
}
.date-input-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 1;
  min-width: 0;
}
.date-input-field .dif-label {
  font-family: var(--font-mono);
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.date-input-field .dif-box {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  height: 38px;
  background: var(--paper);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: text;
}
.date-input-field .dif-box .mdi {
  font-size: 15px;
  color: var(--ink-3);
}
.date-input-field .dif-box:focus-within {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.date-input-field .dif-input {
  flex: 1;
  min-width: 0;
  width: 100%;
  padding: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.cal-foot-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
.cal-btn-cancel {
  padding: 0 14px;
  height: 38px;
  background: var(--surface-elevated);
  color: var(--ink-2);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
}
.cal-btn-cancel:hover {
  background: var(--paper);
}
.cal-btn-apply {
  flex: 1;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  background: var(--primary);
  color: var(--surface-elevated);
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
}
.cal-btn-apply:hover {
  background: var(--primary-press);
}
.cal-btn-apply .mdi {
  font-size: 16px;
}
.cal-day:focus-visible,
.dp-opt:focus-visible,
.cal-nav:focus-visible,
.cal-btn-cancel:focus-visible,
.cal-btn-apply:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.date-field:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.dark-surface .date-picker {
  background: var(--petrol-deep);
  border-color: rgba(255, 255, 255, 0.16);
}
.dark-surface .date-presets {
  background: rgba(0, 0, 0, 0.25);
  border-right-color: var(--tint-on-dark-soft);
}
.dark-surface .date-presets .dp-group {
  color: var(--text-on-dark-muted);
}
.dark-surface .date-presets .dp-divider {
  background: var(--tint-on-dark-soft);
}
.dark-surface .date-presets .dp-opt {
  color: rgba(255, 255, 255, 0.82);
}
.dark-surface .date-presets .dp-opt:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--surface-elevated);
}
.dark-surface .date-presets .dp-opt.is-selected {
  background: var(--tint-lifted-strong);
  color: var(--lifted-orange);
}
.dark-surface .cal-title {
  color: var(--surface-elevated);
}
.dark-surface .cal-title-btn {
  color: var(--surface-elevated);
}
.dark-surface .cal-title-btn .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .cal-title-btn:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .cal-title-btn.is-open {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .cal-title-btn.is-open .mdi {
  color: var(--lifted-orange);
}
.dark-surface .cal-flyout {
  background: var(--petrol-deep);
  border-color: var(--tint-on-dark-strong);
}
.dark-surface .cal-opt {
  color: var(--surface-elevated);
}
.dark-surface .cal-opt:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .cal-opt.is-selected {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .cal-nav {
  background: var(--tint-on-dark-faint);
  border-color: var(--tint-on-dark-strong);
  color: var(--surface-elevated);
}
.dark-surface .cal-nav:hover {
  background: var(--tint-on-dark-medium);
}
.dark-surface .cal-dow {
  color: var(--text-on-dark-muted);
}
.dark-surface .cal-day {
  color: var(--surface-elevated);
}
.dark-surface .cal-day:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .cal-day.today {
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}
.dark-surface .cal-day.in-range {
  background: rgba(255, 138, 77, 0.22);
  color: var(--surface-elevated);
}
.dark-surface .cal-day.range-start,
.dark-surface .cal-day.range-end {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .cal-foot {
  border-top-color: var(--tint-on-dark-soft);
}
.dark-surface .date-input-field .dif-label {
  color: var(--text-on-dark-muted);
}
.dark-surface .date-input-field .dif-box {
  background: rgba(0, 0, 0, 0.25);
  border-color: rgba(255, 255, 255, 0.16);
}
.dark-surface .date-input-field .dif-box .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .date-input-field .dif-box:focus-within {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .date-input-field .dif-input {
  color: var(--surface-elevated);
}
.dark-surface .cal-btn-cancel {
  background: var(--tint-on-dark-faint);
  border-color: var(--tint-on-dark-strong);
  color: rgba(255, 255, 255, 0.82);
}
.dark-surface .cal-btn-cancel:hover {
  background: var(--tint-on-dark-medium);
}
.dark-surface .cal-btn-apply {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .cal-btn-apply:hover {
  background: var(--lifted-orange-press);
}
.dark-surface .cal-day:focus-visible,
.dark-surface .dp-opt:focus-visible,
.dark-surface .cal-nav:focus-visible,
.dark-surface .cal-btn-cancel:focus-visible,
.dark-surface .cal-btn-apply:focus-visible {
  outline-color: var(--lifted-orange);
}

/* ---------- SINGLE DATE PICKER ----------
   The form-field sibling of the Date picker: ONE date, no presets rail, no
   From/To footer, no Apply — picking a day commits it and closes the popover.
   Trigger: the Select field as-is (<button class="select has-caret">, value
   <span> + a trailing calendar_today glyph); every select state carries over
   (.placeholder, .s-open, .s-error, :disabled). .single-date-picker is the
   296px popover around the shared .calendar (anchor it in a .select-wrap).
   Day states added here: .is-selected (the value), .is-reference (the other
   end of a start/end pair, tinted for orientation) and :disabled (outside the
   allowed range — struck through, never colour alone). .single-date-pair lays
   a Start and an End field side by side and stacks them when narrow.
   Calendar head: the Date picker's .cal-title-group (month + year .cal-title-btn,
   each opening a .cal-flyout) between the month arrows (v2.458).
   VARIATION — the footer. Optional field: include .single-date-picker-foot
   (Clear + Today). Required field: leave it out; dates only, no clearing.
   .simple-date-picker is a DEPRECATED alias (v2.457, merged into this
   component) kept for one release — use .single-date-picker. */
.single-date-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px 20px;
  align-items: start;
}
.single-date-picker,
.simple-date-picker {
  position: absolute;
  z-index: 80;
  top: calc(100% + 6px);
  left: 0;
  width: 296px;
  max-width: calc(100vw - 32px);
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-frame);
}
.single-date-picker[hidden],
.simple-date-picker[hidden] {
  display: none;
}
.single-date-picker .calendar,
.simple-date-picker .calendar {
  padding: 14px;
}
.cal-day.is-selected {
  background: var(--primary);
  color: var(--surface-elevated);
  font-weight: 700;
}
.cal-day.is-selected:hover {
  background: var(--primary-press);
}
.cal-day.is-reference {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  font-weight: 600;
}
.cal-day:disabled {
  color: var(--ink-3);
  text-decoration: line-through;
  cursor: not-allowed;
  background: transparent;
}
.cal-day:disabled:hover {
  background: transparent;
}
.cal-day.today.is-selected,
.cal-day.today.is-reference {
  border: none;
}
.single-date-picker-foot {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 0.5px solid var(--line-2);
}
.dark-surface .single-date-picker,
.dark-surface .simple-date-picker {
  background: var(--petrol-deep);
  border-color: var(--border-on-dark-medium);
}
.dark-surface .cal-day.is-selected {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .cal-day.is-selected:hover {
  background: var(--lifted-orange-press);
}
.dark-surface .cal-day.is-reference {
  background: var(--tint-lifted-medium);
  color: var(--surface-elevated);
}
.dark-surface .cal-day:disabled {
  color: var(--text-on-dark-disabled);
}
.dark-surface .cal-day:disabled:hover {
  background: transparent;
}
.dark-surface .single-date-picker-foot {
  border-top-color: var(--tint-on-dark-soft);
}

/* ---------- SELECTION / BULK-ACTION BAR ----------
   Appears (replacing or above the toolbar) when ≥1 row is selected. Accent-
   tinted strip: a count, then bulk actions, with a clear-selection on the
   right. Use .btn variants for the actions. */
/* v2.378 — Arc parity: radius-md (was radius-lg), and the mono count badge
   (.sel-n / .sel-noun) is gone — the label is one plain sentence
   ("3 lots selected — bulk-edit fields or open detail to compare"),
   ellipsised by .sel-text. The lifted check_circle lead carries the accent. */
.selection-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 14px;
  background: var(--tint-accent-soft);
  border: 0.5px solid var(--accent-border-soft);
  border-radius: var(--radius-md);
}
.selection-bar .sel-count {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  color: var(--accent-text-on-tint);
}
.selection-bar .sel-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.selection-bar .sel-spacer {
  flex: 1;
}
.sel-action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  white-space: nowrap;
  flex-shrink: 0;
  background: var(--surface-elevated);
  border: 0.5px solid var(--accent-border-soft);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 12.5px;
  color: var(--ink);
}
.sel-action .mdi {
  font-size: 16px;
  color: var(--primary);
}
.sel-action:hover {
  border-color: var(--primary);
}
/* Icon-only button — a compact square control carrying a single icon and no label
   (bulk-bar clear, close, nav step). Light + petrol; token-only. */
.icon-button {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  background: transparent;
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--ink-2);
  transition:
    background var(--dur-fast),
    border-color var(--dur-fast),
    color var(--dur-fast);
}
.icon-button .mdi {
  font-size: 18px;
}
.icon-button:hover {
  background: var(--tint-neutral-soft);
  color: var(--ink);
}
/* Disabled (v2.483). No hover or press on a disabled button, whatever its
   colour: pointer events are off, so :hover / :active never match. The
   compact icon button fades to 40% like every other button. */
.btn:disabled,
.btn.s-disabled,
.btn[aria-disabled='true'] {
  pointer-events: none;
}
.icon-button:disabled,
.icon-button.is-disabled,
.icon-button[aria-disabled='true'] {
  opacity: 0.4;
  pointer-events: none;
  cursor: not-allowed;
}
.icon-button:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.icon-button.is-sm {
  width: 28px;
  height: 28px;
}
.icon-button.is-sm .mdi {
  font-size: 15px;
}
.dark-surface .icon-button,
.selection-bar.is-petrol .icon-button {
  background: transparent;
  border-color: transparent;
  color: var(--surface-elevated);
}
.dark-surface .icon-button:hover,
.selection-bar.is-petrol .icon-button:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .icon-button:focus-visible,
.selection-bar.is-petrol .icon-button:focus-visible {
  outline-color: var(--lifted-orange);
}
/* Compact icon button colours: is-success = QUIET green (transparent, tints on
   hover, since v2.473; it was solid before). is-danger / is-warning = quiet
   red / amber for a destructive or cautionary row action. Pair the colour with
   an unambiguous glyph and an aria-label. */
.icon-button.is-success {
  background: transparent;
  border-color: transparent;
  color: var(--success-text);
}
.icon-button.is-success:hover {
  background: var(--tint-success-bg);
  border-color: transparent;
  color: var(--success-press);
}
.icon-button.is-danger {
  background: transparent;
  border-color: transparent;
  color: var(--error-text);
}
.icon-button.is-danger:hover {
  background: var(--tint-error-bg);
  border-color: transparent;
  color: var(--error-text);
}
/* On dark, the solid dark green (--success-text) is only ~2.6:1 against petrol-deep
   — below the 3:1 a UI control's boundary needs. So on dark the success button
   flips to a light mint fill (--success-on-dark) with a petrol-deep glyph, the same
   light-fill/dark-glyph inversion used for accent controls on petrol; both the
   button-vs-surface boundary and the glyph clear AA. */
.dark-surface .icon-button.is-success,
.selection-bar.is-petrol .icon-button.is-success {
  background: transparent;
  border-color: transparent;
  color: var(--success-on-dark);
}
.dark-surface .icon-button.is-success:hover,
.selection-bar.is-petrol .icon-button.is-success:hover {
  background: var(--tint-success-on-dark);
  border-color: transparent;
  color: var(--success-on-dark);
}
.dark-surface .icon-button.is-danger,
.selection-bar.is-petrol .icon-button.is-danger {
  background: transparent;
  border-color: transparent;
  color: var(--error-on-dark);
}
.dark-surface .icon-button.is-danger:hover,
.selection-bar.is-petrol .icon-button.is-danger:hover {
  background: var(--tint-error-on-dark);
  border-color: transparent;
  color: var(--error-on-dark);
}
/* Icon-only (v2.474): a modifier on any button, not a separate component.
   Square at the size's control height; emphasis and colour as text buttons:
   tertiary = quiet, secondary = outlined, primary = filled. Needs aria-label. */
.btn.is-icon-only {
  width: var(--control-height-md);
  padding: 0;
  gap: 0;
}
.btn.is-icon-only .mdi {
  font-size: 18px;
}
.btn.is-icon-only.btn-xs {
  width: var(--control-height-xs);
}
.btn.is-icon-only.btn-xs .mdi,
.btn.is-icon-only.btn-sm .mdi {
  font-size: 16px;
}
.btn.is-icon-only.btn-sm {
  width: var(--control-height-sm);
}
.btn.is-icon-only.btn-lg {
  width: var(--control-height-lg);
}
.btn.is-icon-only.btn-lg .mdi {
  font-size: 20px;
}
.btn.is-icon-only.btn-xl {
  width: var(--control-height-xl);
}
.btn.is-icon-only.btn-xl .mdi {
  font-size: 22px;
}
/* Compact icon button emphasis (v2.473): .icon-button.is-outlined and
   .is-filled are RETIRED since v2.474, kept as aliases of the secondary /
   primary icon-only (.btn.is-icon-only). Use .icon-button only for dense rows. */
.icon-button.is-outlined {
  box-shadow: inset 0 0 0 1px var(--border-strong);
}
.icon-button.is-filled {
  background: var(--primary);
  color: var(--surface-elevated);
}
.icon-button.is-filled:hover {
  background: var(--primary-press);
  color: var(--surface-elevated);
}
.icon-button.is-warning {
  background: transparent;
  color: var(--warning-text);
}
.icon-button.is-warning:hover {
  background: var(--tint-warning-bg);
  color: var(--warning-text);
}
.icon-button.is-outlined.is-danger {
  box-shadow: inset 0 0 0 1px var(--error-text);
}
.icon-button.is-outlined.is-warning {
  box-shadow: inset 0 0 0 1px var(--warning-text);
}
.icon-button.is-outlined.is-success,
.icon-button.is-filled.is-success {
  background: transparent;
  color: var(--success-text);
  box-shadow: inset 0 0 0 1px var(--success-text);
}
.icon-button.is-filled.is-success:hover {
  background: var(--tint-success-bg);
  color: var(--success-press);
}
.icon-button.is-filled.is-danger {
  background: var(--error-text);
  color: var(--surface-elevated);
}
.icon-button.is-filled.is-danger:hover {
  background: var(--error-press);
  color: var(--surface-elevated);
}
.icon-button.is-filled.is-warning {
  background: var(--warning);
  color: var(--ink);
}
.icon-button.is-filled.is-warning:hover {
  background: var(--warning-press);
  color: var(--ink);
}
.dark-surface .icon-button.is-outlined,
.selection-bar.is-petrol .icon-button.is-outlined {
  box-shadow: inset 0 0 0 1px var(--border-on-dark-strong);
}
.dark-surface .icon-button.is-filled,
.selection-bar.is-petrol .icon-button.is-filled {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .icon-button.is-filled:hover,
.selection-bar.is-petrol .icon-button.is-filled:hover {
  background: var(--lifted-orange-press);
  color: var(--petrol-deep);
}
.dark-surface .icon-button.is-warning,
.selection-bar.is-petrol .icon-button.is-warning {
  background: transparent;
  color: var(--warning-on-dark);
}
.dark-surface .icon-button.is-warning:hover,
.selection-bar.is-petrol .icon-button.is-warning:hover {
  background: var(--tint-warning-on-dark);
  color: var(--warning-on-dark);
}
.dark-surface .icon-button.is-outlined.is-danger,
.selection-bar.is-petrol .icon-button.is-outlined.is-danger {
  box-shadow: inset 0 0 0 1px var(--error-on-dark);
}
.dark-surface .icon-button.is-outlined.is-warning,
.selection-bar.is-petrol .icon-button.is-outlined.is-warning {
  box-shadow: inset 0 0 0 1px var(--warning-on-dark);
}
.dark-surface .icon-button.is-outlined.is-success,
.selection-bar.is-petrol .icon-button.is-outlined.is-success,
.dark-surface .icon-button.is-filled.is-success,
.selection-bar.is-petrol .icon-button.is-filled.is-success {
  background: transparent;
  color: var(--success-on-dark);
  box-shadow: inset 0 0 0 1px var(--success-on-dark);
}
.dark-surface .icon-button.is-filled.is-danger,
.selection-bar.is-petrol .icon-button.is-filled.is-danger {
  background: var(--error-on-dark);
  color: var(--petrol-deep);
}
.dark-surface .icon-button.is-filled.is-danger:hover,
.selection-bar.is-petrol .icon-button.is-filled.is-danger:hover {
  background: var(--error-on-dark-press);
  color: var(--petrol-deep);
}
.dark-surface .icon-button.is-filled.is-warning,
.selection-bar.is-petrol .icon-button.is-filled.is-warning {
  background: var(--warning-on-dark);
  color: var(--petrol-deep);
}
.dark-surface .icon-button.is-filled.is-warning:hover,
.selection-bar.is-petrol .icon-button.is-filled.is-warning:hover {
  background: var(--warning);
  color: var(--petrol-deep);
}
/* ---------- CHIP INPUT (multi-value token field) ----------
   The Input atom as a wrapping field holding removable .chip-input-token tokens
   + a bare .chip-input-field. Enter/comma commits; Backspace removes the last.
   Token-only. */
.chip-input {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-height: var(--control-height-md);
  padding: 5px 8px;
  background: var(--surface-elevated);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  cursor: text;
}
.chip-input:hover {
  border-color: var(--border-stronger);
}
.chip-input:focus-within {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.chip-input.is-error {
  border-color: var(--error);
  box-shadow: var(--focus-ring-error);
}
.chip-input.is-disabled {
  background-color: var(--input-bg-disabled);
  color: var(--ink-3);
  border-color: var(--line);
  box-shadow: none;
  cursor: not-allowed;
}
.dark-surface .chip-input.is-disabled {
  background-color: var(--input-bg-disabled-on-dark);
  color: var(--text-on-dark-disabled);
  border-color: var(--border-on-dark);
}
.chip-input-token {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: 28px;
  padding: 0 3px 0 11px;
  background: var(--paper);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--ink);
  white-space: nowrap;
}
.chip-input-token.is-invalid {
  background: var(--tint-error-medium);
  border-color: var(--error);
  color: var(--error-text);
}
.chip-input-remove {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border: none;
  background: transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  color: var(--ink-3);
  flex-shrink: 0;
}
.chip-input-remove .mdi {
  font-size: 14px;
}
.chip-input-remove:hover {
  background: var(--tint-neutral);
  color: var(--ink);
}
.chip-input-remove:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}
.chip-input-field {
  flex: 1;
  min-width: 120px;
  height: 28px;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
}
.chip-input-field::placeholder {
  color: var(--ink-3);
}
.dark-surface .chip-input {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-strong);
}
.dark-surface .chip-input:hover {
  border-color: var(--border-on-dark-hover);
}
.dark-surface .chip-input:focus-within {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .chip-input.is-error {
  border-color: var(--error-on-dark);
  box-shadow: var(--focus-ring-error-strong-on-dark);
}
.dark-surface .chip-input-token {
  background: var(--tint-on-dark-soft);
  border-color: var(--border-on-dark-medium);
  color: var(--surface-elevated);
}
.dark-surface .chip-input-token.is-invalid {
  background: var(--tint-error-on-dark);
  border-color: var(--error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .chip-input-remove {
  color: var(--text-on-dark-muted);
}
.dark-surface .chip-input-remove:hover {
  background: var(--tint-on-dark-medium);
  color: var(--surface-elevated);
}
.dark-surface .chip-input-field {
  color: var(--surface-elevated);
}
.dark-surface .chip-input-field::placeholder {
  color: var(--text-on-dark-muted);
}
/* Chips present (v2.502): the placeholder goes with the first chip and the
   typing field shrinks to a 24px stub (room for the caret), so tokens stay on one line until they
   genuinely fill it. Empty field keeps the 120px minimum and the placeholder. */
.chip-input:has(.chip-input-token) .chip-input-field {
  min-width: 24px;
}
.chip-input:has(.chip-input-token) .chip-input-field::placeholder {
  color: transparent;
}
/* ---------- CHIP SELECT (multi-value pick from a known list) ----------
   The Chip input shell + tokens over a .select-panel listbox. Typing only
   filters the list; values always come from it, never free text. Picked
   options stay in the list with the tick, so a second click un-picks.
   Token-only. */
.form-select.is-multiple,
.chip-select,
.select-wrap.is-multiple {
  position: relative;
}
.form-select.is-multiple .chip-input,
.chip-select .chip-input,
.select-wrap.is-multiple .chip-input {
  padding-right: 40px;
  cursor: text;
}
.chip-select-toggle {
  position: absolute;
  top: 6px;
  right: 6px;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  border-radius: var(--radius-pill);
  color: var(--ink-3);
  cursor: pointer;
}
.chip-select-toggle .mdi {
  font-size: 20px;
}
.chip-select-toggle:hover {
  background: var(--tint-neutral);
  color: var(--ink);
}
.chip-select-toggle:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}
.form-select.is-multiple.is-open .chip-select-toggle .mdi,
.chip-select.is-open .chip-select-toggle .mdi,
.select-wrap.is-multiple.is-open .chip-select-toggle .mdi {
  transform: rotate(180deg);
}
.form-select.is-multiple > .select-panel,
.chip-select > .select-panel,
.select-wrap.is-multiple > .select-panel {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 40;
}
.form-select.is-multiple > .select-panel.is-inline,
.chip-select > .select-panel.is-inline,
.select-wrap.is-multiple > .select-panel.is-inline {
  position: static;
}
.form-select.is-multiple.is-disabled .chip-select-toggle,
.chip-select.is-disabled .chip-select-toggle,
.select-wrap.is-multiple.is-disabled .chip-select-toggle {
  cursor: not-allowed;
  opacity: 0.7;
}
.dark-surface .chip-select-toggle {
  color: var(--text-on-dark-muted);
}
.dark-surface .chip-select-toggle:hover {
  background: var(--tint-on-dark-medium);
  color: var(--surface-elevated);
}
.dark-surface .chip-select-toggle:focus-visible {
  outline-color: var(--lifted-orange);
}
/* ---------- COMPOSABLE FORMS (v2.441) ----------
   Forms build up in four layers: Field shell (.field — label, control, help,
   message) → Control box (.input / .select / .chip-input — one border, one size
   scale, one state vocabulary) → Option list (.select-panel — rows, search,
   groups, multiple, loading, empty, clear) → FormSelect / FormInput, whose
   variations are MODIFIERS on the root (.select-wrap / .input-wrap) and combine
   freely: .is-searchable + .is-multiple + .is-clearable is a valid Select.
   State vocabulary on every control box: .is-open, .is-error, .is-disabled,
   .is-locked (the older .s-* demo classes stay as aliases).
   Sizes: .is-sm (32px) / default (40px) / .is-lg (48px) on the control box. */
.input.is-error,
.select.is-error {
  border-color: var(--error);
  box-shadow: var(--focus-ring-error);
}
.select.is-open {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.select.is-open .mdi {
  color: var(--primary);
}
.dark-surface .input.is-error,
.dark-surface .select.is-error {
  border-color: var(--error-on-dark);
  box-shadow: var(--focus-ring-error-strong-on-dark);
}
.dark-surface .select.is-open {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .select.is-open .mdi {
  color: var(--lifted-orange);
}
.input.is-sm,
.select.is-sm {
  min-height: var(--control-height-sm);
  padding-top: 6px;
  padding-bottom: 6px;
  font-size: 13px;
}
.input.is-lg,
.select.is-lg {
  min-height: var(--control-height-lg);
  padding-top: 12px;
  padding-bottom: 12px;
  font-size: var(--text-input-min);
}
/* v2.447: the select is a fixed-height box at every size. Padding no longer sets the height, so a 24px caret glyph cannot push the default past large. */
.select {
  min-height: var(--control-height-md);
}
.select,
.select.is-sm,
.select.is-lg {
  padding-top: 0;
  padding-bottom: 0;
}
.select .mdi {
  line-height: 1;
}
.chip-input.is-sm {
  min-height: var(--control-height-sm);
  padding: 2px 6px;
  gap: 4px;
}
.chip-input.is-sm .chip-input-token {
  height: 24px;
}
/* One line, always (v2.504). A multiple field never grows past one row: when
   the chips don't fit (most often after Select all), the ones that overflow are
   hidden and a "+N" chip sums them up. Clicking "+N" opens the list, where every
   pick is visible and removable. Add .is-collapsed to the .chip-input; product
   logic (or the reference helper in the visualisation) hides the overflowing
   tokens and writes the count. */
.chip-input.is-collapsed {
  flex-wrap: nowrap;
  overflow: hidden;
}
.chip-input.is-collapsed .chip-input-token {
  flex-shrink: 0;
}
.chip-input.is-collapsed .chip-input-field {
  flex: 1 0 24px;
}
.chip-input-more {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  height: 28px;
  padding: 0 10px;
  background: var(--tint-accent-soft);
  border: none;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--accent-text-on-tint);
  white-space: nowrap;
  cursor: pointer;
}
.chip-input-more:hover {
  background: var(--tint-accent-medium);
}
.chip-input-more:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
.chip-input.is-sm .chip-input-more {
  height: 24px;
}
.dark-surface .chip-input-more {
  background: var(--tint-lifted-medium);
  color: var(--surface-elevated);
}
.dark-surface .chip-input-more:hover {
  background: var(--tint-lifted-strong);
}
.dark-surface .chip-input-more:focus-visible {
  outline-color: var(--lifted-orange);
}
.chip-input.is-sm .chip-input-field {
  height: 24px;
}
.chip-input.is-lg {
  min-height: var(--control-height-lg);
}
/* Input variations — a leading icon and a clear button, on any .input. */
.input-icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 18px;
  color: var(--ink-3);
  pointer-events: none;
}
.form-input.has-icon .input,
.input-wrap.has-icon .input {
  padding-left: 40px;
}
.form-input.has-icon .input.is-sm,
.input-wrap.has-icon .input.is-sm {
  padding-left: 34px;
}
.dark-surface .input-icon {
  color: var(--text-on-dark-muted);
}
/* Unit input (v2.513, restyled v2.514) — a FormInput with a unit ("hours/day",
   "kg", "%") shown as a segmented-control pill inside the right edge: a raised
   chip on the segmented track. The wrapper draws the box (border, fill, focus /
   error ring); the input sits borderless inside it. The unit is announced
   through aria-describedby. Does not combine with .is-clearable. */
.form-input.has-unit,
.input-wrap.has-unit {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 7px;
  box-sizing: border-box;
  background: var(--surface-elevated);
  border: var(--input-border);
  border-radius: var(--radius);
  transition:
    border-color 150ms,
    box-shadow 150ms;
}
.has-unit > .input,
.has-unit > .input:hover,
.has-unit > .input:focus,
.has-unit > .input.is-error,
.has-unit > .input:disabled,
.has-unit > .input.is-disabled,
.dark-surface .has-unit > .input,
.dark-surface .has-unit > .input:hover,
.dark-surface .has-unit > .input:focus,
.dark-surface .has-unit > .input.is-error,
.dark-surface .has-unit > .input:disabled,
.dark-surface .has-unit > .input.is-disabled {
  border-color: transparent;
  background-color: transparent;
  box-shadow: none;
}
.has-unit > .input {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  margin: -1px 0 -1px -1px;
}
.has-unit:hover {
  border-color: var(--border-stronger);
}
.has-unit:focus-within {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.has-unit.is-error,
.has-unit:has(> .input.is-error) {
  border-color: var(--error);
  box-shadow: var(--focus-ring-error);
}
.has-unit:has(> .input:disabled, > .input.is-disabled) {
  background: var(--input-bg-disabled);
  cursor: not-allowed;
}
.input-unit {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 12px;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--ink);
  background: var(--surface-elevated);
  border-radius: var(--radius-pill);
  box-shadow:
    0 0 0 3px var(--line-2),
    var(--shadow-segmented-on);
}
.input.is-sm + .input-unit {
  height: 20px;
  padding: 0 9px;
  font-size: 11.5px;
}
.input.is-lg + .input-unit {
  height: 32px;
  padding: 0 14px;
  font-size: 13.5px;
}
.has-unit:has(> .input:disabled, > .input.is-disabled) > .input-unit {
  color: var(--ink-3);
  box-shadow: 0 0 0 3px var(--line-2);
}
.dark-surface .has-unit {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .has-unit:hover {
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .has-unit:focus-within {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .has-unit.is-error,
.dark-surface .has-unit:has(> .input.is-error) {
  border-color: var(--error-on-dark);
  box-shadow: var(--focus-ring-error-strong-on-dark);
}
.dark-surface .has-unit:has(> .input:disabled, > .input.is-disabled) {
  background: var(--input-bg-disabled-on-dark);
}
.dark-surface .input-unit {
  color: var(--surface-elevated);
  background: var(--petrol-deep);
  box-shadow:
    0 0 0 3px var(--tint-on-dark-soft),
    var(--shadow-segmented-on-dark);
}
.dark-surface .has-unit:has(> .input:disabled, > .input.is-disabled) > .input-unit {
  color: var(--text-on-dark-disabled);
  box-shadow: 0 0 0 3px var(--tint-on-dark-soft);
}
/* The shared clear button — one control for FormInput, FormSelect and multiple
   FormSelect. Render it only while there is a value (hidden otherwise). */
.control-clear {
  position: absolute;
  z-index: 1;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
}
.control-clear .mdi {
  font-size: 16px;
}
.control-clear:hover {
  background: var(--tint-neutral);
  color: var(--ink);
}
.control-clear:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.control-clear[hidden] {
  display: none;
}
.form-input.is-clearable .input,
.input-wrap.is-clearable .input {
  padding-right: 40px;
}
/* The input fills its wrapper, so the clear button sits at the input's own edge,
   not the edge of a wider field. Multiple (chips) keeps the clear pinned to the
   first row, as the chip field grows downward. */
.form-input > .input,
.input-wrap > .input {
  width: 100%;
  box-sizing: border-box;
}
.form-input.is-multiple.is-clearable .chip-input,
.input-wrap.is-multiple.is-clearable .chip-input {
  padding-right: 40px;
}
.form-input.is-multiple .control-clear,
.input-wrap.is-multiple .control-clear {
  top: 6px;
  transform: none;
}
.form-input.is-multiple .chip-input.is-sm ~ .control-clear,
.input-wrap.is-multiple .chip-input.is-sm ~ .control-clear {
  top: 2px;
}
.form-input.is-multiple .chip-input.is-lg ~ .control-clear,
.input-wrap.is-multiple .chip-input.is-lg ~ .control-clear {
  top: 10px;
}
.form-select.is-clearable .select > span:first-child,
.select-wrap.is-clearable .select > span:first-child {
  padding-right: 30px;
}
.form-select.is-multiple.is-clearable .chip-input,
.chip-select.is-clearable .chip-input,
.select-wrap.is-multiple.is-clearable .chip-input {
  padding-right: 70px;
}
/* Locked select: a block box with no vertical padding, so centre the text on the
   control height to line up with the lock glyph. */
.select.is-locked {
  line-height: calc(var(--control-height-md) - 2px);
}
.select.is-locked.is-sm {
  line-height: calc(var(--control-height-sm) - 2px);
}
.select.is-locked.is-lg {
  line-height: calc(var(--control-height-lg) - 2px);
}
/* Clearable multiple: the clear sits left of the chevron toggle, pinned to the first row. */
.form-select.is-multiple .control-clear,
.chip-select .control-clear,
.select-wrap.is-multiple .control-clear {
  top: 6px;
  right: 38px;
  transform: none;
}
.dark-surface .control-clear {
  color: var(--text-on-dark-muted);
}
.dark-surface .control-clear:hover {
  background: var(--tint-on-dark-medium);
  color: var(--surface-elevated);
}
.dark-surface .control-clear:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* Disabled clear: follows the field — no hover fill, disabled ink, not-allowed cursor. */
.control-clear:disabled,
.control-clear.is-disabled {
  background: transparent;
  color: var(--ink-3);
  box-shadow: none;
  cursor: not-allowed;
}
.dark-surface .control-clear:disabled,
.dark-surface .control-clear.is-disabled {
  background: transparent;
  color: var(--text-on-dark-disabled);
}
/* Option list parts. */
.select-panel[hidden] {
  display: none;
}
.select-panel.is-floating {
  position: absolute;
  z-index: 40;
  top: 100%;
  left: 0;
  right: 0;
}
.select-panel.is-floating.is-wide {
  right: auto;
  min-width: 260px;
}
.select-group-label {
  padding: 10px 10px 4px;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--ink-3);
}
.select-group-label:first-child {
  padding-top: 4px;
}
.select-option-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}
.select-option-description {
  font-size: 12px;
  line-height: 1.35;
  color: var(--ink-3);
}
.select-option.is-selected .select-option-description {
  color: var(--ink-2);
}
.select-panel:is(.is-multiple, [aria-multiselectable='true'], :has(.is-select-all)) .select-option {
  justify-content: flex-start;
}
.select-panel:is(.is-multiple, [aria-multiselectable='true'], :has(.is-select-all)) .select-option::before {
  content: '';
  flex-shrink: 0;
  box-sizing: border-box;
  width: var(--checkbox-size);
  height: var(--checkbox-size);
  border: 1px solid var(--border-stronger);
  border-radius: var(--checkbox-radius);
  background: var(--surface-elevated);
}
.select-panel:is(.is-multiple, [aria-multiselectable='true'], :has(.is-select-all)) .select-option.is-selected::before {
  background:
    var(--icon-check-thin-on-fill) center / 12px no-repeat,
    var(--primary);
  border-color: var(--primary);
}
.select-panel:is(.is-multiple, [aria-multiselectable='true'], :has(.is-select-all)) .select-option.is-mixed::before {
  background:
    var(--icon-dash-thin-on-fill) center / 12px no-repeat,
    var(--primary);
  border-color: var(--primary);
}
.select-panel:is(.is-multiple, [aria-multiselectable='true'], :has(.is-select-all)) .select-option.is-selected::after {
  content: none;
}
.select-option.is-select-all {
  font-weight: 600;
  margin-bottom: 6px;
  border-bottom: 0.5px solid var(--line);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
/* Select all (v2.499): set apart by weight, a stronger rule and a trailing
   .select-option-meta count of what is picked ("3 of 6"). */
.select-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 72px;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink-2);
}
.select-loading .spinner {
  width: 16px;
  height: 16px;
}
.dark-surface .select-group-label {
  color: var(--text-on-dark-muted);
}
.dark-surface .select-option-description {
  color: var(--text-on-dark-muted);
}
.dark-surface .select-option.is-selected .select-option-description {
  color: var(--text-on-dark-secondary);
}
.dark-surface .select-panel:is(.is-multiple, [aria-multiselectable='true'], :has(.is-select-all)) .select-option::before {
  background: transparent;
  border-color: var(--border-on-dark-hover);
}
.dark-surface .select-panel:is(.is-multiple, [aria-multiselectable='true'], :has(.is-select-all)) .select-option.is-selected::before {
  background:
    var(--icon-check-thin-on-lifted) center / 12px no-repeat,
    var(--lifted-orange);
  border-color: var(--lifted-orange);
}
.dark-surface .select-panel:is(.is-multiple, [aria-multiselectable='true'], :has(.is-select-all)) .select-option.is-mixed::before {
  background:
    var(--icon-dash-thin-on-lifted) center / 12px no-repeat,
    var(--lifted-orange);
  border-color: var(--lifted-orange);
}
.dark-surface .select-option.is-select-all {
  border-bottom-color: var(--border-on-dark-medium);
}
/* Select all never takes the selected-row tint (v2.500): when everything is
   picked only its checkbox fills, so it stays a control above the options
   instead of merging into them. A select-all row with no checkbox means the
   panel is missing .is-multiple. */
.select-option.is-select-all.is-selected {
  background: transparent;
  color: var(--ink);
  font-weight: 600;
}
.select-option.is-select-all.is-selected:hover,
.select-option.is-select-all.is-selected.is-highlighted {
  background: var(--tint-neutral-soft);
}
.dark-surface .select-option.is-select-all.is-selected {
  background: transparent;
  color: var(--text-on-dark);
}
.dark-surface .select-option.is-select-all.is-selected:hover,
.dark-surface .select-option.is-select-all.is-selected.is-highlighted {
  background: var(--tint-on-dark-medium);
}
.dark-surface .select-option.is-selected .select-option-meta {
  color: var(--text-on-dark-secondary);
}
.dark-surface .select-loading {
  color: var(--text-on-dark-secondary);
}
.dark-surface .selection-bar {
  background: var(--tint-lifted-selected);
  border-color: var(--lifted-orange);
}
.dark-surface .selection-bar .sel-count {
  color: var(--lifted-orange);
}
.dark-surface .sel-action {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-hover);
  color: var(--surface-elevated);
}
.dark-surface .sel-action .mdi {
  color: var(--lifted-orange);
}
/* petrol variant — recommended default on light pages; matches the list-view treatment (v2.20) */
.selection-bar.is-petrol {
  background: var(--petrol);
  border-color: var(--petrol-mid);
  color: var(--surface-elevated);
}
.selection-bar.is-petrol .sel-lead {
  color: var(--lifted-orange);
}
.selection-bar.is-petrol .sel-count {
  color: var(--surface-elevated);
}
/* v2.343 a11y — a .sel-action is a control identified by its edge, so its RESTING
   border must clear 3:1 (WCAG 1.4.11). --border-on-dark-medium (0.25) is 2.05:1 and
   cannot carry it; the sibling filter toolbar in this same module already resolved
   this to 0.45 resting / 0.62 hover, and the selection bar now agrees. The bar's own
   outline stays at 0.25 by decision — a decorative grouping edge, not a control. */
.selection-bar.is-petrol .sel-action {
  background: var(--tint-on-dark-medium);
  border-color: var(--border-on-dark-hover);
  color: var(--surface-elevated);
}
.selection-bar.is-petrol .sel-action .mdi {
  color: var(--lifted-orange);
}
.selection-bar.is-petrol .sel-action:hover {
  border-color: var(--border-on-dark-emphasis);
}

/* ---------- SELECTION BAR · v2.343 — persistent, emphasis tier, compact overflow ----------
   Adopted from the Arc dashboard prototype. The bar is now PERSISTENT: it sits
   above the table at all times and carries an empty prompt state, so the bulk
   actions are discoverable BEFORE anything is selected. .is-empty is that
   no-selection state. Persistent + .is-petrol is the documented default; the
   light accent-tinted strip stays contextual (an accent fill with nothing
   selected reads as an active state that isn't).
   The count line ellipsises rather than pushing the actions off the bar. */
/* v2.359 — ALL controls anchor right: the count line carries margin-right:auto, so every
   button group after it (actions, overflow, tail) clusters against the right edge as one
   run. The auto margin lives on the count, not on a button group, because the groups
   appear and disappear (.is-empty hides .sel-actions; the container query swaps it for
   .sel-overflow-wrap) and the count is the one child always present. */
/* v2.379 — the sentence label must SHRINK (ellipsis) rather than push the button
   groups onto a second row: flex-basis 0 — the label takes the leftover space and ellipsises; a content-sized
   basis (auto) wraps instead of shrinking once the sentence outgrows the line. */
.selection-bar .sel-count {
  flex: 1 1 0px;
  min-width: 0;
  margin-right: auto;
}
.selection-bar .sel-count .sel-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.selection-bar.is-empty {
  background: var(--paper);
  border-color: var(--line);
}
.selection-bar.is-empty .sel-lead {
  color: var(--ink-3);
}
.selection-bar.is-empty .sel-count {
  color: var(--ink-2);
  font-weight: 500;
}
.selection-bar.is-petrol.is-empty {
  background: var(--petrol);
  border-color: var(--petrol-mid);
}
.selection-bar.is-petrol.is-empty .sel-lead,
.dark-surface .selection-bar.is-empty .sel-lead {
  color: var(--text-on-dark-secondary);
}
.selection-bar.is-petrol.is-empty .sel-count,
.dark-surface .selection-bar.is-empty .sel-count {
  color: var(--text-on-dark-secondary);
}
/* The export control still sits last — it is the one action that works with or without
   a selection, so it does not live in the group that appears and disappears with one —
   but it is no longer pushed away from the bulk actions: as of v2.359 it sits directly
   beside them in the right-hand run. The auto margin that used to live here moved to
   .sel-count (above), which is why this rule carries no margin. A flex:1 spacer is
   still wrong for either job (v2.346): the bar wraps, and in a wrapping flex row a
   flex:1 spacer takes a whole line to itself. .sel-spacer keeps working for anyone
   already using it, but it is no longer the mechanism and is out of the markup. */
.sel-tail {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
/* Nothing selected, nothing for the bulk actions to act on — so the group goes, and
   the overflow that holds it goes with it. Driven by the class, never by omitting the
   markup, so one authored bar covers both states. */
.selection-bar.is-empty .sel-actions,
.selection-bar.is-empty .sel-overflow-wrap {
  display: none;
}
/* .sel-action had no focus ring before v2.343. */
.sel-action:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.selection-bar.is-petrol .sel-action:focus-visible,
.dark-surface .sel-action:focus-visible {
  outline-color: var(--lifted-orange);
}
/* ONE emphasis step per bar — the action the bar exists to offer. On light the fill
   is --primary-press under white, NOT --primary: the bounded primary-under-white
   exemption covers .btn-primary at rest and .chip.selected only, and is not
   extended here. On petrol it is the standard accent inversion. */
.sel-action.is-primary {
  background: var(--primary-press);
  border-color: transparent;
  color: var(--surface-elevated);
  font-weight: 700;
}
.sel-action.is-primary .mdi {
  color: var(--surface-elevated);
}
.sel-action.is-primary:hover {
  background: var(--primary-active);
  border-color: transparent;
}
.selection-bar.is-petrol .sel-action.is-primary,
.dark-surface .sel-action.is-primary {
  background: var(--lifted-orange);
  border-color: transparent;
  color: var(--petrol-deep);
  box-shadow: var(--shadow-cta-lifted);
}
.selection-bar.is-petrol .sel-action.is-primary .mdi,
.dark-surface .sel-action.is-primary .mdi {
  color: var(--petrol-deep);
}
.selection-bar.is-petrol .sel-action.is-primary:hover,
.dark-surface .sel-action.is-primary:hover {
  background: var(--lifted-orange-press);
  border-color: transparent;
  box-shadow: var(--shadow-cta-lifted-hover);
}
/* A .sel-action that opens a format menu carries a trailing caret. */
.sel-action .sel-caret {
  font-size: 15px;
  margin-left: 2px;
}
.sel-menu-wrap {
  position: relative;
  display: inline-flex;
}
.sel-menu-wrap .menu {
  top: calc(100% + 6px);
  right: 0;
  left: auto;
}
/* Compact — the actions collapse into one overflow menu and the count drops its noun
   ("3 lots selected" becomes "3 selected"); the tail stays put, so export is reachable
   at every width. Keyed to the BAR's own width, not the viewport (v2.346): this bar
   lives in a content column beside a sidebar, so a 924px viewport can hand it a 532px
   column — a viewport media query never fires and the bar wraps instead. The threshold
   sits above the wrap point so the actions yield before the layout breaks. */
.selection-bar {
  container-type: inline-size;
}
/* The overflow button needs a menu, and a <button> cannot contain one — so it sits in
   a .sel-menu-wrap like the download control does, and .sel-overflow-wrap is what
   appears and disappears. Hiding the button alone would leave the wrapper behind,
   collecting one of the bar's 12px gaps for nothing. The wrapper is the ONLY hook:
   v2.347 left a .sel-overflow class on the button that matched no rule at all, and
   v2.351 removed it rather than inventing a rule to justify it. The button inside is
   styled by .icon-button. */
.sel-overflow-wrap {
  display: none;
}
/* v2.359 — compact folds EVERY control, export included: .sel-actions and .sel-tail both
   go, and the overflow menu is the single control left, sitting hard right (the count's
   margin-right:auto puts it there). Download therefore becomes a .menu-item inside that
   menu at compact width — it is one tap away rather than one click, which is the trade a
   420px bar makes. The .is-empty override matters: without a selection the bulk group is
   hidden and so is its overflow, which at compact width would leave the bar with no
   control at all — so compact re-shows the overflow even when empty, because Download
   works with or without a selection. */
@container (max-width: 620px) {
  .selection-bar .sel-actions,
  .selection-bar .sel-tail {
    display: none;
  }
  .selection-bar .sel-overflow-wrap,
  .selection-bar.is-empty .sel-overflow-wrap {
    display: inline-flex;
  }
}

/* ---------- ROOM CARD ----------
   One storage room: what it is, how full it is, and how much of it is committed.
   Rebuilt in v2.348 from the Rooms Version B prototype.

   THE CARD IS NO LONGER THE CONTROL. It carries an editable status badge, and a
   <button> cannot legally contain another button — so .room-card is a <div> and
   .room-card-title is the control. The whole surface stays clickable as a pointer
   affordance, but the title is what the keyboard reaches and what the focus ring
   is drawn around (via :has), so the card still has exactly one accessible name.

   ONE FIGURE, NOT TWO. The figure is what is LEFT in the room (loads remaining
   after packing); percentage full moved to the capacity label, where it sits
   against the bar that draws it. The older card put "1,840 of 2,200 bins · 84%"
   in the figures row, which made the reader do the subtraction the card should
   have done. */
.room-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex: 0 1 300px;
  max-width: 300px;
  min-width: 0;
  box-sizing: border-box;
  padding: 20px;
  background: var(--surface-elevated);
  border: none;
  border-radius: var(--radius-lg);
  text-align: left;
  font-family: var(--font-body);
  cursor: pointer;
  box-shadow: var(--dp-shadow);
  transition:
    background 150ms,
    box-shadow 150ms,
    opacity 150ms;
}
.room-card:hover {
  box-shadow: var(--dp-shadow-hi);
}
/* The ring is drawn on the CARD but earned by the TITLE — the focused thing and the
   thing outlined have to be the same thing to the reader. */
.room-card:has(.room-card-title:focus-visible) {
  box-shadow: var(--focus-ring-accent-strong);
}
.room-card.is-selected {
  box-shadow:
    0 0 0 2px var(--primary),
    var(--dp-shadow);
}
/* The grid the cards live in. minmax(300px,1fr) fills the row and lets each card
   stretch, so the card's own 300px flex cap is released inside it. */
.room-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 14px;
}
.room-card-grid > .room-card {
  max-width: none;
}
.room-card-head-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.room-card-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.room-card-title {
  flex: 1;
  min-width: 0;
  padding: 0;
  border: none;
  background: none;
  text-align: left;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.15;
  color: var(--ink);
  overflow-wrap: anywhere;
  cursor: pointer;
}
.room-card-title:focus-visible {
  outline: none;
}
.room-card-head > .room-card-state,
.room-card-head > .badge-select-wrap {
  flex-shrink: 0;
}
/* Site · type — body type, not the mono caps this row used before v2.348: it is a
   place and a kind of room, read as words. Mono caps is for machine metadata. */
.room-card-site {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.room-card-site .mdi {
  flex-shrink: 0;
  font-size: 14px;
  color: var(--ink-3);
}
.room-card-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.room-card-since {
  flex-shrink: 0;
  font-size: 12.5px;
  color: var(--ink-3);
  white-space: nowrap;
}
.room-card-varieties {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink-2);
}
.room-card-figures {
  display: flex;
  align-items: baseline;
  gap: 4px;
}
.room-card-figures .rc-value {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.room-card-figures .rc-unit {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-3);
}
/* Pinned to the bottom, so a grid of cards with different amounts of content still
   lines its bars up on one row. */
.room-card-foot {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: auto;
}
.room-card-sold {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 14px;
  border-bottom: 0.5px solid var(--line-2);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-2);
}
.room-card-sold .mdi {
  font-size: 16px;
  color: var(--primary);
}
.room-card-sold .rc-sold-value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.room-card-capacity .rc-cap-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
  font-size: 12px;
  color: var(--ink-3);
}
/* No capacity set — orthogonal to the four room states: a Filling room can have no
   capacity recorded, and a room with 455 loads in it still cannot show a percentage
   until someone says how much fits. The track stays (a bar belongs here once capacity
   is set) but carries no fill, because 0% full and "not yet known" are different
   facts and must not look alike. The label is the whole signal, so it is TEXT, never
   a colour: it steps to --ink-2 at 600 while its sibling note goes quiet, because it
   is a prompt to go and set something, not a statistic. */
.room-card-capacity.is-unset .rc-cap-head {
  color: var(--ink-2);
  font-weight: 600;
}
.dark-surface .room-card-capacity.is-unset .rc-cap-head {
  color: var(--text-on-dark);
}
/* Empty — a room with nothing assigned. Dimmed as a whole so a grid reads at a
   glance, and every text colour inside steps UP one rung to pay for it: 0.72 over
   paper costs roughly a third of the contrast, which would drop --ink-3 body text
   to ~3.1:1 and fail 1.4.3. Compensated, not accepted. */
.room-card.is-empty {
  opacity: 0.72;
}
.room-card.is-empty .room-card-varieties,
.room-card.is-empty .room-card-figures .rc-value {
  color: var(--ink-2);
}
.room-card.is-empty .room-card-site,
.room-card.is-empty .room-card-site .mdi,
.room-card.is-empty .room-card-since,
.room-card.is-empty .room-card-figures .rc-unit,
.room-card.is-empty .room-card-capacity .rc-cap-head {
  color: var(--ink-2);
}
.dark-surface .room-card {
  background: var(--tint-on-dark-faint);
  box-shadow: none;
}
.dark-surface .room-card:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .room-card:has(.room-card-title:focus-visible) {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .room-card.is-selected {
  box-shadow: 0 0 0 2px var(--lifted-orange);
}
.dark-surface .room-card-title,
.dark-surface .room-card-figures .rc-value,
.dark-surface .room-card-sold .rc-sold-value {
  color: var(--text-on-dark);
}
/* --text-on-dark-muted (0.55 white) clears 4.5:1 on PLAIN petrol but not on a
   translucent card fill: the card's var(--tint-on-dark-faint) lightens the background,
   and the same text measures 4.29:1 there — below 1.4.3 for 12px/400. v2.350 fixed
   exactly this on .is-empty and scoped it to .is-empty, leaving the DEFAULT card
   worse than the number that triggered the fix. 0.78 measures 6.83:1 here. */
.dark-surface .room-card-site,
.dark-surface .room-card-site .mdi,
.dark-surface .room-card-since,
.dark-surface .room-card-capacity .rc-cap-head {
  color: var(--text-on-dark-secondary);
}
.dark-surface .room-card-figures .rc-unit,
.dark-surface .room-card-varieties,
.dark-surface .room-card-sold {
  color: var(--text-on-dark-secondary);
}
.dark-surface .room-card-sold {
  border-bottom-color: var(--border-on-dark);
}
.dark-surface .room-card-sold .mdi {
  color: var(--lifted-orange);
}
.dark-surface .room-card.is-empty .room-card-varieties,
.dark-surface .room-card.is-empty .room-card-figures .rc-value {
  color: var(--text-on-dark);
}
/* On petrol the step-up has to go all the way to full white. --text-on-dark-secondary
   (0.78) under 0.72 opacity measures 4.46:1 — 0.04 short of 1.4.3's 4.5 for normal
   text, which is a fail, not a rounding error (v2.350). */
.dark-surface .room-card.is-empty .room-card-site,
.dark-surface .room-card.is-empty .room-card-site .mdi,
.dark-surface .room-card.is-empty .room-card-since,
.dark-surface .room-card.is-empty .room-card-figures .rc-unit,
.dark-surface .room-card.is-empty .room-card-capacity .rc-cap-head {
  color: var(--text-on-dark);
}
/* ---------- CARD LIST ----------
   What a data table becomes below its narrowest usable width. Not a scrolling
   table: a horizontally scrolling table hides the columns that decide whether a
   row matters, and on a phone the reader cannot see them without losing the row
   they were reading.
   Each row is a card: a title line, a value line, and up to four label/value
   pairs. Anything beyond four belongs in the record, not the list — the fallback
   is a way to FIND a row, not to read it in full. */
.card-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.card-list-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-6) var(--space-7);
  background: var(--surface-elevated);
  border: none;
  border-radius: var(--radius-lg);
  text-align: left;
  font-family: var(--font-body);
  cursor: pointer;
  box-shadow: var(--dp-shadow);
  transition:
    background 150ms,
    box-shadow 150ms;
}
.card-list-row:hover {
  box-shadow: var(--dp-shadow-hi);
}
.card-list-row:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.card-list-row.is-selected {
  background: var(--tint-accent-faint);
  box-shadow:
    0 0 0 2px var(--primary),
    var(--dp-shadow);
}
.card-list-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
}
.card-list-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card-list-sub {
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--ink-3);
  margin-top: 3px;
}
.card-list-head > :last-child {
  margin-left: auto;
  flex-shrink: 0;
}
/* Label left, value right, one per line — the Label value row pattern, because a
   column of these scans down two straight edges. The earlier two-column grid of
   label-above-value stacks put four 10px captions and four 13px figures in a
   16px-gapped grid: eight things competing at two sizes, and no edge to read down. */
.card-list-facts {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.card-list-fact {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  min-width: 0;
  padding: 5px 0;
  border-top: 0.5px solid var(--line-2);
}
.card-list-fact .cl-label {
  flex: 1;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.card-list-fact .cl-value {
  flex-shrink: 0;
  font-family: var(--font-display);
  font-size: 13.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.dark-surface .card-list-row {
  background: var(--tint-on-dark-faint);
  box-shadow: none;
}
.dark-surface .card-list-row:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .card-list-row.is-selected {
  background: var(--tint-lifted-soft);
  box-shadow: 0 0 0 2px var(--lifted-orange);
}
.dark-surface .card-list-title,
.dark-surface .card-list-fact .cl-value {
  color: var(--text-on-dark);
}
.dark-surface .card-list-sub {
  color: var(--text-on-dark-secondary);
}
.dark-surface .card-list-fact {
  border-top-color: var(--border-on-dark);
}
.dark-surface .card-list-fact .cl-label {
  color: var(--text-on-dark-secondary);
}

/* ---------- INSIGHT CARD ----------
   One finding worth reading, with the trend that makes it a finding. Not a stat
   block: a stat reports a number, an insight reports a CHANGE and says what it
   means. Never more than three on a page — the fourth stops being notable. */
.insight-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: 16px 18px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-left: 3px solid var(--primary);
  border-radius: var(--radius-lg);
  font-family: var(--font-body);
}
.insight-card.is-good {
  border-left-color: var(--success);
}
.insight-card.is-bad {
  border-left-color: var(--error);
}
.insight-card-kicker {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.insight-card-kicker .mdi {
  font-size: 14px;
}
.insight-card-figure {
  display: flex;
  align-items: baseline;
  gap: 9px;
  flex-wrap: wrap;
}
.insight-card-figure .ic-value {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.insight-card-figure .ic-delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.insight-card-figure .ic-delta .mdi {
  font-size: 15px;
}
.insight-card.is-good .ic-delta {
  color: var(--success-text);
}
.insight-card.is-bad .ic-delta {
  color: var(--error-press);
}
.insight-card-body {
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
}
.dark-surface .insight-card {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
  border-left-color: var(--lifted-orange);
}
.dark-surface .insight-card.is-good {
  border-left-color: var(--success-on-dark);
}
.dark-surface .insight-card.is-bad {
  border-left-color: var(--error-on-dark);
}
.dark-surface .insight-card-kicker {
  color: var(--text-on-dark-secondary);
}
.dark-surface .insight-card-figure .ic-value {
  color: var(--text-on-dark);
}
.dark-surface .insight-card.is-good .ic-delta {
  color: var(--success-on-dark);
}
.dark-surface .insight-card.is-bad .ic-delta {
  color: var(--error-on-dark);
}
.dark-surface .insight-card-body {
  color: var(--text-on-dark-secondary);
}

/* ---------- SHORTCUT CARD ----------
   A route to somewhere else, sized like a card so it can sit in a grid of them.
   The whole card is the control and the arrow is the only affordance it needs —
   a shortcut card with a button inside it has two targets and one purpose. */
.shortcut-card {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  background: var(--surface-elevated);
  border: none;
  border-radius: var(--radius-lg);
  text-align: left;
  font-family: var(--font-body);
  cursor: pointer;
  box-shadow: var(--dp-shadow);
  transition:
    background 150ms,
    box-shadow 150ms;
}
.shortcut-card:hover {
  box-shadow: var(--dp-shadow-hi);
}
.shortcut-card:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.shortcut-card-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: var(--tint-accent-soft);
  color: var(--accent-text-on-tint);
}
.shortcut-card-icon .mdi {
  font-size: 20px;
}
.shortcut-card-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.shortcut-card-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.shortcut-card-sub {
  font-size: 12px;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.shortcut-card-arrow {
  margin-left: auto;
  flex-shrink: 0;
  color: var(--ink-3);
  transition:
    transform 150ms,
    color 150ms;
}
.shortcut-card-arrow .mdi {
  font-size: 19px;
}
.shortcut-card:hover .shortcut-card-arrow {
  color: var(--primary);
  transform: translateX(2px);
}
.dark-surface .shortcut-card {
  background: var(--tint-on-dark-faint);
  box-shadow: none;
}
.dark-surface .shortcut-card:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .shortcut-card-icon {
  background: var(--tint-lifted-soft);
  color: var(--lifted-orange);
}
.dark-surface .shortcut-card-title {
  color: var(--text-on-dark);
}
.dark-surface .shortcut-card-sub {
  color: var(--text-on-dark-secondary);
}
.dark-surface .shortcut-card-arrow {
  color: var(--text-on-dark-muted);
}
.dark-surface .shortcut-card:hover .shortcut-card-arrow {
  color: var(--lifted-orange);
}
/* A row of them. Quick actions are shortcuts, not buttons — they are always
   available and never the primary action of the page. */
.shortcut-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px;
}

/* ---------- FEEDBACK CARD ----------
   "Was this right?" — a rating on the system's own output, asked in place.
   Five radio inputs, not five buttons, because they are one exclusive choice and
   a radio group already answers to arrow keys and announces its position.
   Asked ONCE per record and never re-asked after an answer; a question that
   returns is a nag, and the answer stops meaning anything. */
/* Success tint, not accent: the card asks about the quality of a result, and the
   accent tint reads as "this needs your action". The scale keeps --primary when
   checked — the selection is a control, and the accent is what controls use here. */
.feedback-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 18px;
  background: var(--tint-success-bg);
  border: 0.5px solid var(--success-border);
  border-radius: var(--radius-lg);
  font-family: var(--font-body);
}
.feedback-card-question {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}
.feedback-card-hint {
  font-size: 12px;
  color: var(--ink-2);
  margin-top: -6px;
}
.feedback-scale {
  display: flex;
  align-items: center;
  gap: 6px;
}
.feedback-scale-item {
  position: relative;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-elevated);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
  cursor: pointer;
  transition:
    border-color 150ms,
    background 150ms,
    color 150ms;
}
.feedback-scale-item input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.feedback-scale-item:hover {
  border-color: var(--primary);
  color: var(--ink);
}
.feedback-scale-item:has(input:focus-visible) {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
  border-color: var(--primary);
}
/* --primary-press, not --primary: white on #EA5D2E is 3.44:1, and the v2.215
   exemption is bounded to .btn-primary at rest, .chip.selected and two day cells —
   anything added after it that needs white on an accent fill takes --primary-press. */
.feedback-scale-item:has(input:checked) {
  background: var(--primary-press);
  border-color: transparent;
  color: var(--surface-elevated);
}
.feedback-scale-ends {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.feedback-card-done {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--success-text);
}
.feedback-card-done .mdi {
  font-size: 18px;
}
.dark-surface .feedback-card {
  background: var(--tint-success-on-dark);
  border-color: var(--success-border-on-dark);
}
.dark-surface .feedback-card-question {
  color: var(--text-on-dark);
}
.dark-surface .feedback-card-hint {
  color: var(--text-on-dark-secondary);
}
.dark-surface .feedback-scale-item {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
  color: var(--text-on-dark-secondary);
}
.dark-surface .feedback-scale-item:hover {
  border-color: var(--lifted-orange);
  color: var(--surface-elevated);
}
.dark-surface .feedback-scale-item:has(input:checked) {
  background: var(--lifted-orange);
  border-color: transparent;
  color: var(--petrol-deep);
}
.dark-surface .feedback-scale-ends {
  color: var(--text-on-dark-muted);
}
.dark-surface .feedback-card-done {
  color: var(--success-on-dark);
}

/* ---------- PROMPT CARD ----------
   Ask the system a question in words. Petrol by default on BOTH surfaces: it is
   the one place the product speaks rather than reports, and it should not be
   mistaken for a search field sitting in the same layout.
   Suggestions are the point. An empty prompt with a blinking cursor asks the
   reader to guess what it can do; three real questions teach it in one glance. */
.prompt-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 20px;
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--petrol) 0%, var(--petrol-deep) 100%);
  color: var(--text-on-dark);
  font-family: var(--font-body);
}
.prompt-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: 600;
  letter-spacing: var(--tr-meta);
  text-transform: uppercase;
  color: var(--lifted-orange);
}
.prompt-card-head .mdi {
  font-size: 16px;
}
.prompt-card-field {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius);
  background: var(--tint-on-dark-soft);
  border: 1px solid var(--border-on-dark);
}
.prompt-card-field:focus-within {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.prompt-card-field input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  outline: none;
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--surface-elevated);
}
.prompt-card-field input::placeholder {
  color: var(--text-on-dark-muted);
}
.prompt-card-send {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: var(--radius-sm);
  background: var(--lifted-orange);
  color: var(--petrol-deep);
  cursor: pointer;
}
.prompt-card-send .mdi {
  font-size: 17px;
}
.prompt-card-send:hover {
  background: var(--lifted-orange-press);
}
.prompt-card-send:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.prompt-card-send:disabled {
  background: var(--tint-on-dark-medium);
  color: var(--text-on-dark-disabled);
  cursor: not-allowed;
}
.prompt-suggestions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.prompt-suggestion {
  padding: 6px 11px;
  border: 1px solid var(--border-on-dark);
  border-radius: var(--radius-pill);
  background: transparent;
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--text-on-dark-secondary);
  cursor: pointer;
  text-align: left;
}
.prompt-suggestion:hover {
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
  background: var(--tint-lifted-soft);
}
.prompt-suggestion:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}

/* ---------- TRANSFER LIST ----------
   Two lists and a direction: what is available, what is assigned. Used for
   assigning tickets to a room, columns to a view, people to a shift.
   Moving an item is a CLICK on the item, not a pair of arrow buttons between the
   columns — the arrows are a second thing to find and they tell the reader
   nothing the item's own position does not. The counts are in the headers,
   because "assign 40 of 128" is the question the dialog exists to answer. */
.transfer-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-6);
}
.transfer-column {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-elevated);
  overflow: hidden;
}
.transfer-column-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 0.5px solid var(--line);
  background: var(--paper);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.transfer-column-head .tc-count {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}
.transfer-column-body {
  flex: 1;
  overflow-y: auto;
  max-height: 260px;
  padding: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.transfer-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  text-align: left;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink);
  cursor: pointer;
}
.transfer-item .mdi {
  font-size: 16px;
  color: var(--ink-3);
  flex-shrink: 0;
}
.transfer-item .ti-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.transfer-item .ti-meta {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  color: var(--ink-3);
  flex-shrink: 0;
}
.transfer-item:hover {
  background: var(--tint-neutral-soft);
}
.transfer-item:hover .mdi {
  color: var(--primary);
}
.transfer-item:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
/* Pinned — an item that cannot be moved, and says why. */
.transfer-item.is-pinned {
  background: var(--tint-accent-faint);
  color: var(--accent-text-on-tint);
  cursor: default;
}
.transfer-item.is-pinned .mdi {
  color: var(--accent-text-on-tint);
}
.transfer-item.is-pinned:hover {
  background: var(--tint-accent-faint);
}
.dark-surface .transfer-column {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
/* v2.355 — every rule below sets NORMAL-SIZE text on a translucent fill over petrol.
   NOTE (v2.356): the underlying cause was the TOKEN, not these rules — --text-on-dark-muted
   was raised 0.55 -> 0.68 and now clears 4.5:1 on every fill, so these 0.78 overrides are
   no longer required for compliance. They are left in place pending a decision on the tier
   split they create (tertiary metadata reading one step brighter than its peers).
   where 0.55 white measures 3.54–4.47:1 and fails 1.4.3. Stepped to 0.78 (6.86:1 in
   the same context). v2.354 fixed three components and asserted the rest sat on plain
   petrol; that was read off a grep, not measured, and 18 more were failing. The one
   deliberate hold-out is the DISABLED filter pill: 1.4.3 exempts inactive controls,
   and raising it would make disabled read as enabled. */
.dark-surface .transfer-column-head {
  background: var(--tint-on-dark-soft);
  border-bottom-color: var(--border-on-dark);
  color: var(--text-on-dark-secondary);
}
.dark-surface .transfer-column-head .tc-count {
  color: var(--text-on-dark-secondary);
}
.dark-surface .transfer-item {
  color: var(--text-on-dark);
}
.dark-surface .transfer-item .mdi,
.dark-surface .transfer-item .ti-meta {
  color: var(--text-on-dark-secondary);
}
.dark-surface .transfer-item:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .transfer-item:hover .mdi {
  color: var(--lifted-orange);
}
.dark-surface .transfer-item.is-pinned {
  background: var(--tint-lifted-soft);
  color: var(--surface-elevated);
}
.dark-surface .transfer-item.is-pinned .mdi {
  color: var(--lifted-orange);
}
@container (max-width: 520px) {
  .transfer-list {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- MANAGE ROW ----------
   A list the reader edits in place: rename, reorder, delete. For the small
   reference sets a packhouse owns — sites, rooms, varieties, grades.
   Delete confirms INLINE, in the row. A modal on top of a modal to remove one
   list item loses the list, and the reader has to remember which row they were
   on to know what they just agreed to. */
.manage-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.manage-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-elevated);
  font-family: var(--font-body);
}
.manage-row .mr-name {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.manage-row .mr-meta {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
  flex-shrink: 0;
}
.manage-row-actions {
  display: flex;
  gap: 2px;
  flex-shrink: 0;
}
.manage-row.is-confirming {
  border-color: var(--error);
  background: var(--tint-error-bg);
}
/* Cancel sits on an ERROR tint, not paper, and the accent label was only ever
   tuned against paper: --primary there is 3.14:1, the worst instance of
   .btn-secondary anywhere. The v2.215 exemption is bounded to elements that
   predate it, so this takes the darker step rather than widening it. */
.manage-row.is-confirming .btn-secondary {
  color: var(--accent-text-on-tint);
  border-color: var(--accent-text-on-tint);
}
.dark-surface .manage-row.is-confirming .btn-secondary {
  color: var(--surface-elevated);
  border-color: var(--border-on-dark-hover);
}
.manage-row-confirm {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  font-size: 13px;
  color: var(--error-press);
}
.manage-row-confirm .mrc-text {
  flex: 1;
  min-width: 0;
}
.manage-row-confirm .mrc-text strong {
  font-weight: 600;
}
.dark-surface .manage-row {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
.dark-surface .manage-row .mr-name {
  color: var(--text-on-dark);
}
.dark-surface .manage-row .mr-meta {
  color: var(--text-on-dark-secondary);
}
.dark-surface .manage-row.is-confirming {
  border-color: var(--error-on-dark);
  background: var(--tint-error-on-dark);
}
.dark-surface .manage-row-confirm {
  color: var(--error-on-dark);
}

/* ---------- POPOVER FORM ----------
   A .menu that holds FIELDS rather than a list of choices — bulk-edit, email a
   report, manage columns. Same surface, same shadow, same dismissal; what
   changes is that it now has a commit, so it needs a footer and it must not
   close on an outside press without one.
   Use it when the edit is small enough that losing the page behind it would cost
   more than it saves. Past about five fields, that stops being true and it is a
   Modal or a Slide-over. */
.popover-form {
  display: flex;
  flex-direction: column;
  min-width: 260px;
  max-width: 340px;
}
.popover-form-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-6) var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.popover-form-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-1) var(--space-6) var(--space-5);
  max-height: 320px;
  overflow-y: auto;
}
.popover-form-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.popover-form-field > label {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
}
.popover-form-foot {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-top: 0.5px solid var(--line);
  background: var(--paper);
}
.popover-form-foot .pf-spacer {
  margin-left: auto;
}
/* Column manager — a width budget, because columns compete for one row.
   The count is not decoration: it is the constraint the reader is working against. */
.column-budget {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.column-budget .cb-count {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.column-budget.is-over .cb-count {
  color: var(--error-press);
}
.dark-surface .popover-form-head {
  color: var(--text-on-dark-muted);
}
.dark-surface .popover-form-field > label {
  color: var(--text-on-dark);
}
.dark-surface .popover-form-foot {
  border-top-color: var(--border-on-dark);
  background: var(--tint-on-dark-faint);
}
.dark-surface .column-budget {
  color: var(--text-on-dark-muted);
}
.dark-surface .column-budget .cb-count {
  color: var(--text-on-dark);
}
.dark-surface .column-budget.is-over .cb-count {
  color: var(--error-on-dark);
}

/* ---------- FIELD REPEATER ----------
   A group of fields the reader adds more of — sites, rooms, contacts, thresholds.
   Every row can be removed INCLUDING the last one: a repeater that refuses to go
   empty forces a reader who mis-added the first row to type over it instead of
   deleting it, and an empty state says more than a blank row does.
   Add sits at the BOTTOM, so the reading order matches the order of the work. */
.field-repeater {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.field-repeater-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}
.field-repeater-row > .input,
.field-repeater-row > .select {
  flex: 1;
  min-width: 0;
}
.field-repeater-index {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 24px;
  height: 40px;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
}
.field-repeater-remove {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
}
.field-repeater-remove .mdi {
  font-size: 18px;
}
.field-repeater-remove:hover {
  background: var(--tint-error-bg);
  color: var(--error-press);
}
.field-repeater-remove:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.field-repeater-add {
  align-self: flex-start;
  margin-top: 2px;
}
.dark-surface .field-repeater-index,
.dark-surface .field-repeater-remove {
  color: var(--text-on-dark-muted);
}
.dark-surface .field-repeater-remove:hover {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .field-repeater-remove:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}

/* ---------- RECORD PAGER ----------
   Move through a SET of records without going back to the list — bin 4 of 128.
   Not table pagination: that moves through pages of rows and lives under the
   table; this moves through records and lives in the record's own header.
   The position readout is not decoration. It is the only thing that tells a
   reader whether they are 3 records into a review or 3 from the end, and it is
   what makes the ends of the set findable — so prev/next disable AT the ends
   rather than wrapping, because silently looping a review is how a bin gets
   assessed twice. */
.record-pager {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-elevated);
}
.record-pager-btn {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  transition:
    background 150ms,
    color 150ms;
}
.record-pager-btn .mdi {
  font-size: 19px;
}
.record-pager-btn:hover {
  background: var(--tint-neutral-soft);
  color: var(--ink);
}
.record-pager-btn:active {
  background: var(--tint-neutral);
}
.record-pager-btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.record-pager-btn:disabled {
  color: var(--border-strong);
  cursor: not-allowed;
  background: transparent;
}
.record-pager-position {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  padding: 0 var(--space-4);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  white-space: nowrap;
}
.record-pager-position .rp-of {
  font-weight: 500;
  color: var(--ink-3);
}
.record-pager-label {
  padding: 0 var(--space-4) 0 var(--space-1);
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--ink-2);
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Petrol — and the pager is used inside the Photo viewer, which is dark without
   carrying .dark-surface, so it answers to both. */
.dark-surface .record-pager,
.photo-viewer:not(.is-light) .record-pager {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
.dark-surface .record-pager-btn,
.photo-viewer:not(.is-light) .record-pager-btn {
  color: var(--text-on-dark-secondary);
}
.dark-surface .record-pager-btn:hover,
.photo-viewer:not(.is-light) .record-pager-btn:hover {
  background: var(--tint-on-dark-soft);
  color: var(--surface-elevated);
}
.dark-surface .record-pager-btn:active {
  background: var(--tint-on-dark-medium);
}
.dark-surface .record-pager-btn:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .record-pager-btn:disabled,
.photo-viewer:not(.is-light) .record-pager-btn:disabled {
  color: var(--text-on-dark-disabled);
  background: transparent;
}
.dark-surface .record-pager-position,
.photo-viewer:not(.is-light) .record-pager-position {
  color: var(--surface-elevated);
}
.dark-surface .record-pager-position .rp-of {
  color: var(--text-on-dark-secondary);
}
.dark-surface .record-pager-label {
  color: var(--text-on-dark-secondary);
}

/* ---------- ACTIVITY LOG ----------
   Who changed what, when — the audit trail of one record. A vertical rail with a
   node per entry; the entry names the actor, the change, and the time.
   Newest FIRST. An audit trail is read to answer "what just happened", not to
   relive the record from the beginning.
   A value change shows both sides (from → to). "Updated maturity" is not an
   audit entry; it is a rumour of one. */
.activity-log {
  display: flex;
  flex-direction: column;
  gap: 0;
  position: relative;
}
.activity-entry {
  position: relative;
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  gap: 10px;
  padding: 0 0 16px;
}
.activity-entry:last-child {
  padding-bottom: 0;
}
.activity-entry::before {
  content: '';
  position: absolute;
  left: 8px;
  top: 18px;
  bottom: 0;
  width: 1px;
  background: var(--line-2);
}
.activity-entry:last-child::before {
  display: none;
}
.activity-node {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 17px;
  height: 17px;
  margin-top: 2px;
  border-radius: var(--radius-circle);
  background: var(--surface-elevated);
  border: 1.5px solid var(--border-strong);
  color: var(--ink-3);
}
.activity-node .mdi {
  font-size: 11px;
}
.activity-entry.is-created .activity-node {
  border-color: var(--success);
  color: var(--success-text);
}
.activity-entry.is-changed .activity-node {
  border-color: var(--primary);
  color: var(--primary-press);
}
.activity-entry.is-removed .activity-node {
  border-color: var(--error);
  color: var(--error-press);
}
.activity-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.activity-head {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink);
}
.activity-head strong {
  font-weight: 600;
}
.activity-meta {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
/* from → to. The arrow is a real character, so it survives a copy-paste of the
   audit trail into an email — which is what an audit trail is for. */
.activity-change {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.activity-change .ac-from {
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: var(--tint-neutral-soft);
  color: var(--ink-2);
  text-decoration: line-through;
}
.activity-change .ac-arrow {
  color: var(--ink-3);
}
.activity-change .ac-to {
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: var(--tint-accent-soft);
  color: var(--accent-text-on-tint);
  font-weight: 600;
}
.dark-surface .activity-entry::before {
  background: var(--border-on-dark);
}
.dark-surface .activity-node {
  background: var(--petrol);
  border-color: var(--border-on-dark-hover);
  color: var(--text-on-dark-secondary);
}
.dark-surface .activity-entry.is-created .activity-node {
  border-color: var(--success-on-dark);
  color: var(--success-on-dark);
}
.dark-surface .activity-entry.is-changed .activity-node {
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}
.dark-surface .activity-entry.is-removed .activity-node {
  border-color: var(--error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .activity-head {
  color: var(--text-on-dark);
}
.dark-surface .activity-meta {
  color: var(--text-on-dark-muted);
}
.dark-surface .activity-change .ac-from {
  background: var(--tint-on-dark-soft);
  color: var(--text-on-dark-secondary);
}
.dark-surface .activity-change .ac-arrow {
  color: var(--text-on-dark-muted);
}
/* FORBIDDEN PAIRING: --lifted-orange on --tint-lifted-soft over petrol is
   4.36:1. It has now been reached three times — .filter-pill.is-active.is-open
   (v2.197), .empty-inline.is-drop-target (v2.211), and here. On petrol the label
   goes WHITE and the lifted tint carries the accent. Do not reach for it again. */
.dark-surface .activity-change .ac-to {
  background: var(--tint-lifted-soft);
  color: var(--surface-elevated);
}

/* ---------- RESULT STATE ----------
   Named Process status until v2.333. The lifecycle readout of a job — a scan, an
   export, a sync, an upload.
   EIGHT states and no more (v2.426): scan upload pending, queued, processing, complete,
   failed, unavailable, no data, not included.
   Each carries a glyph, a word, and a measure where one is real. The icon carries
   the state; the label is always grey. Processing is one glyph everywhere since
   v2.331: a pulsing hourglass_top in neutral grey, never a spinner. Failed and
   unavailable share the red step (v2.327, matching the product) — the glyph and
   the wording separate nothing from broken, never the tone.
   FOUR placements of one component, plus an overlay:
     (default)    a row inside a list — glyph + word + measure
     .is-cell     the dense table-cell form — see the section below
     .is-block    a standalone readout — head, progress bar, and a note
     .is-panel    centred in a card, where the result would have been
     .is-overlay  laid over the thing being processed (a photo, a tile)
   A failed state ALWAYS names a reason and offers the next action. "Failed" on
   its own is the state the reader can do least with. */
.result-state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-body);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.25;
  text-align: left;
  color: var(--ink-2);
}
.result-state .result-state-icon {
  display: grid;
  place-items: center;
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}
.result-state .result-state-icon .mdi {
  font-size: 15px;
}
.result-state .result-state-measure {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--ink-3);
}
/* Upload pending (v2.426; wording v2.428) — only for scans: the scan's upload has
   not completed, so there is nothing to queue or process yet. Neutral, never red —
   a STATIC cloud_upload, no pulse: nothing is processing and nothing is wrong.
   States NO reason and NO location — the upload may come from another device, so
   the readout never claims where the captures are or why they have not arrived;
   no measure, no action. Becomes Queued once the server accepts the scan; Failed
   only if an attempted upload errors out. */
.result-state.is-upload-pending .result-state-icon {
  color: var(--ink-3);
}
.result-state.is-queued .result-state-icon {
  color: var(--ink-3);
}
.result-state.is-processing .result-state-icon {
  color: var(--ink-3);
  animation: resultStatePulse 1.4s var(--ease-in-out) infinite;
}
.result-state.is-complete .result-state-icon {
  color: var(--success-text);
}
.result-state.is-failed .result-state-icon {
  color: var(--error-press);
}
.result-state.is-unavailable .result-state-icon {
  color: var(--error-press);
}
.result-state.is-none .result-state-icon {
  color: var(--ink-3);
}
/* Not included (v2.369) — plan-gated: the capability is off for this account, so the
   result will never exist. Neutral like .is-none; the feature's own "off" glyph and
   the wording carry the meaning. No action — the fix is commercial, not operational. */
.result-state.is-not-included .result-state-icon {
  color: var(--ink-3);
}
/* Block — the standalone readout. */
/* ---------- RESULT STATE · TABLE-CELL VARIANT ----------
   The form used inside a Load-assessments list cell, where the status stands in
   for a result that is not ready. Denser than the default inline: 11.5px label,
   14px glyph, 6px gap, never wraps — a table row cannot afford a second line.
   Processing is a pulsing hourglass in neutral grey, as in every placement
   since v2.331: a blue spinner repeated down a column reads as an error state and
   pulls the eye off the rows that do have results. The larger variants keep the
   spinner, where active work is the point. */
.result-state.is-cell {
  gap: 6px;
  font-size: 11.5px;
  line-height: 1.2;
  white-space: nowrap;
  color: var(--ink-2);
}
.result-state.is-cell .result-state-icon {
  width: 14px;
  height: 14px;
}
.result-state.is-cell .result-state-icon .mdi {
  font-size: 14px;
}
.result-state.is-cell .result-state-measure {
  font-size: 10.5px;
}
@keyframes resultStatePulse {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  .result-state.is-processing .result-state-icon {
    animation: none;
  }
}
.dark-surface .result-state.is-cell {
  color: var(--text-on-dark-secondary);
}
.result-state.is-block {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-3);
  width: 100%;
}
.result-state.is-block {
  font-size: 13px;
}
.result-state.is-block .result-state-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.result-state.is-block .result-state-icon {
  width: 18px;
  height: 18px;
}
.result-state.is-block .result-state-icon .mdi {
  font-size: 17px;
}
.result-state.is-block .result-state-head .result-state-measure {
  margin-left: auto;
}
.result-state .result-state-note {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.45;
  color: var(--ink-2);
}
.result-state.is-block .result-state-actions {
  display: flex;
  gap: var(--space-3);
  margin-top: 2px;
}
/* Panel — centred in a card, where the result would have been. A card whose whole
   reason for existing is one chart has nothing to show when the job produced none,
   and a 13px inline row pinned to the top-left of 300px of empty card reads as a
   rendering fault. Reuses .result-state-head and .result-state-note; the only new part is .result-state-halo,
   which scales the state graphic up for a large empty area. The glyph keeps
   .result-state-icon, so the state colours and the grey label apply here unchanged. */
.result-state.is-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-7) var(--space-5);
  text-align: center;
}
.result-state.is-panel .result-state-halo {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--tint-neutral);
  flex-shrink: 0;
}
.result-state.is-panel .result-state-halo .result-state-icon {
  width: 24px;
  height: 24px;
}
.result-state.is-panel .result-state-halo .result-state-icon .mdi {
  font-size: 24px;
}
.result-state.is-panel .result-state-head {
  justify-content: center;
  font-size: 15px;
}
.result-state.is-panel .result-state-note {
  max-width: 34ch;
  text-align: center;
}
.result-state.is-panel .result-state-actions {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  margin-top: var(--space-3);
}
.result-state.is-panel.is-complete .result-state-halo {
  background: var(--tint-success-bg);
}
.result-state.is-panel.is-failed .result-state-halo {
  background: var(--tint-error-bg);
}
.result-state.is-panel.is-unavailable .result-state-halo {
  background: var(--tint-error-bg);
}
.result-state.is-panel.is-upload-pending .result-state-halo {
  background: var(--tint-neutral);
}
.result-state.is-panel.is-none .result-state-halo {
  background: var(--tint-neutral);
}
.result-state.is-panel.is-not-included .result-state-halo {
  background: var(--tint-neutral);
}
.dark-surface .result-state.is-panel .result-state-halo {
  background: var(--tint-on-dark-strong);
}
.dark-surface .result-state.is-panel.is-complete .result-state-halo {
  background: var(--tint-success-on-dark);
}
.dark-surface .result-state.is-panel.is-failed .result-state-halo {
  background: var(--tint-error-on-dark);
}
.dark-surface .result-state.is-panel.is-unavailable .result-state-halo {
  background: var(--tint-error-on-dark);
}
/* Overlay — over the work itself. Its own dark surface, so every colour here is
   an on-dark step; the light ones would vanish into the scrim. */
.result-state.is-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: var(--space-7);
  border-radius: inherit;
  background: var(--scrim-photo);
  color: var(--surface-elevated);
  text-align: center;
}
.result-state.is-overlay .result-state-measure {
  color: var(--text-on-dark-secondary);
}
.result-state.is-overlay .result-state-note {
  font-weight: 400;
  font-size: 12.5px;
  color: var(--text-on-dark-secondary);
}
/* The overlay glyph is 26px, so its BOX is 26px. Sized only by an inline
   font-size in demo markup it overflowed an 18px .result-state-icon (scrollWidth 26 >
   clientWidth 18) and the column's gap was measured off the wrong box, crowding
   the label beneath it. A value that lives in demo markup is not in the system. */
.result-state.is-overlay {
  font-size: 13px;
}
.result-state.is-overlay .result-state-icon {
  width: 26px;
  height: 26px;
}
.result-state.is-overlay .result-state-icon .mdi {
  font-size: 26px;
}
.result-state.is-overlay .progress-bar {
  width: 100%;
  max-width: 180px;
  background: var(--tint-on-dark-strong);
}
.result-state.is-overlay .progress-bar-fill {
  background: var(--surface-elevated);
}
/* The overlay's ground is a SCRIM, not a surface: it composites over whatever
   is behind it, so its lightness varies with the photograph. The on-dark colour
   steps are tuned for --petrol-deep and cannot be trusted here — --error-on-dark
   over a scrim on a light placeholder measures 4.15:1. So the label stays WHITE
   and the semantic colour moves to the GLYPH, where it only needs 3:1. Same
   answer as the v2.197 petrol trigger: when a ground cannot carry the accent as
   a label, the accent becomes the graphic. */
.result-state.is-overlay.is-upload-pending,
.result-state.is-overlay.is-queued,
.result-state.is-overlay.is-processing,
.result-state.is-overlay.is-complete,
.result-state.is-overlay.is-failed,
.result-state.is-overlay.is-unavailable {
  color: var(--surface-elevated);
}
.result-state.is-overlay.is-upload-pending .result-state-icon {
  color: var(--text-on-dark-secondary);
}
.result-state.is-overlay.is-queued .result-state-icon {
  color: var(--text-on-dark-secondary);
}
.result-state.is-overlay.is-processing .result-state-icon {
  color: var(--text-on-dark-secondary);
}
.result-state.is-overlay.is-complete .result-state-icon {
  color: var(--success-on-dark);
}
.result-state.is-overlay.is-failed .result-state-icon {
  color: var(--error-on-dark);
}
.result-state.is-overlay.is-unavailable .result-state-icon {
  color: var(--error-on-dark);
}
/* Overlay on a frame (v2.505). The overlay owns the frame: the empty frame's
   own placeholder glyph and note hide under it (they showed through the scrim as
   a ghost icon behind the state icon), and corner chips — the capture time, a
   count — sit ABOVE the scrim so they stay readable, not dimmed under it. */
.photo-frame:has(> .result-state.is-overlay) > .mdi,
.photo-frame:has(> .result-state.is-overlay) > .pf-note {
  visibility: hidden;
}
.photo-frame:has(> .result-state.is-overlay) > .photo-frame-corner {
  z-index: 4;
}
/* On petrol */
.dark-surface .result-state,
.photo-viewer:not(.is-light) .result-state {
  color: var(--text-on-dark-secondary);
}
.dark-surface .result-state .result-state-measure,
.photo-viewer:not(.is-light) .result-state .result-state-measure {
  color: var(--text-on-dark-muted);
}
.dark-surface .result-state.is-upload-pending .result-state-icon {
  color: var(--text-on-dark-muted);
}
.dark-surface .result-state.is-queued .result-state-icon {
  color: var(--text-on-dark-muted);
}
.dark-surface .result-state.is-processing .result-state-icon {
  color: var(--text-on-dark-muted);
}
.dark-surface .result-state.is-complete .result-state-icon {
  color: var(--success-on-dark);
}
.dark-surface .result-state.is-failed .result-state-icon {
  color: var(--error-on-dark);
}
.dark-surface .result-state.is-unavailable .result-state-icon {
  color: var(--error-on-dark);
}
.dark-surface .result-state.is-none .result-state-icon {
  color: var(--text-on-dark-muted);
}
.dark-surface .result-state.is-not-included .result-state-icon {
  color: var(--text-on-dark-muted);
}
.dark-surface .result-state .result-state-note {
  color: var(--text-on-dark-secondary);
}

/* ---------- PHOTO VIEWER ----------
   The full-record view of one capture: stage, markers, a strip of siblings, and
   a findings panel. Dark is the DEFAULT and the recommended form — a photograph
   judged against a bright surface picks up a colour cast from it, which is the
   one thing a fruit-quality assessment cannot afford. .is-light exists for a
   viewer embedded in a page rather than raised over it, where a petrol block
   would read as a modal that will not close.
   Presented over a scrim it is a Modal; embedded, it is a panel. It does not
   own the scrim itself — reuse .modal-scrim. */
.photo-viewer {
  container-type: inline-size;
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  background: var(--petrol-deep);
  border-radius: var(--radius-lg);
  overflow: hidden;
  color: var(--text-on-dark);
  font-family: var(--font-body);
}
/* v2.386 — raised over the scrim (Load / Bin assessment detail pages), the
   viewer is near-fullscreen rather than dialog-sized: capped by 1100px, by
   88vw, and by the width the remaining viewport height can carry for a 4:3
   stage once ~170px of toolbar + strip chrome is spent — so the photograph
   never pushes its own chrome off-screen. vh is deliberate, not a container
   unit: a modal is sized by the window. Embedded viewers fill their column
   and take no class. Synced from Dashboard / Assessments (was a ~720px dialog). */
.photo-viewer.is-modal {
  width: min(1100px, 88vw, calc((100vh - 170px) * 4 / 3));
  max-width: 100%;
}
/* Intrinsic wrap, not a query: main asks for 560px and the panel for 300, so
   the pair wraps below 860px of CONTAINER width on its own. A media query would
   read the window and keep them side by side inside a narrow drawer or column;
   a container query cannot style the container it is declared on. Flex basis is
   the only mechanism here that needs neither. The grow factors are 1000:1 so
   spare width lands on the stage, never on the panel. */
.photo-viewer-main {
  flex: 1000 1 560px;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.photo-viewer-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 12px;
  border-bottom: 0.5px solid var(--border-on-dark);
  min-height: 52px;
  min-width: 0;
}
.photo-viewer-toolbar > div:first-child {
  min-width: 0;
  flex: 1 1 auto;
}
.photo-viewer-title {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--surface-elevated);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.photo-viewer-sub {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--text-on-dark-muted);
  margin-top: 2px;
}
.photo-viewer-toolbar .pv-spacer {
  margin-left: auto;
}
/* v2.383 — the stage clips its zoomed canvas; the Zoom control (atom) sits bottom-right over the image. */
.photo-viewer-stage {
  position: relative;
  flex: 1;
  display: grid;
  place-items: center;
  padding: 18px;
  min-height: 260px;
  background: var(--petrol-deep);
  overflow: hidden;
}
.photo-viewer-canvas {
  position: relative;
  max-width: 100%;
  line-height: 0;
  transition: transform 150ms;
}
.photo-viewer-canvas .photo-frame {
  border: none;
  background: var(--tint-on-dark-soft);
}
.photo-viewer-strip {
  border-top: 0.5px solid var(--border-on-dark);
}
.photo-viewer-panel {
  flex: 1 1 300px;
  display: flex;
  flex-direction: column;
  min-width: 0;
  border-left: 0.5px solid var(--border-on-dark);
  background: var(--petrol);
}
.photo-viewer-panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 16px 10px;
  font-family: var(--font-mono);
  font-size: var(--text-meta);
  font-weight: 600;
  letter-spacing: var(--tr-meta);
  text-transform: uppercase;
  color: var(--text-on-dark-muted);
}
.photo-viewer-panel-body {
  flex: 1;
  overflow-y: auto;
  padding: 0 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* A finding row is the panel-side twin of a marker: same index, same severity,
   so the pin and the list read as one object. Hovering either highlights both. */
.photo-finding {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 10px;
  background: var(--tint-on-dark-faint);
  border: 0.5px solid var(--border-on-dark);
  border-radius: var(--radius);
  cursor: pointer;
  font-family: var(--font-body);
}
.photo-finding:hover {
  background: var(--tint-on-dark-soft);
  border-color: var(--border-on-dark-hover);
}
.photo-finding:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.photo-finding.is-active {
  background: var(--tint-lifted-soft);
  border-color: var(--lifted-orange);
}
.photo-finding .pf-index {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-circle);
  background: var(--primary-press);
  color: var(--surface-elevated);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  margin-top: 1px;
}
.photo-finding.is-high .pf-index {
  background: var(--error-press);
}
.photo-finding.is-medium .pf-index {
  background: var(--warning-text);
}
.photo-finding.is-low .pf-index {
  background: var(--info-text);
}
.photo-finding .pf-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-on-dark);
}
.photo-finding .pf-meta {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--text-on-dark-secondary);
  margin-top: 2px;
}
/* Light — the embedded viewer. Same geometry, paper surfaces. */
.photo-viewer.is-light {
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  color: var(--ink);
}
.photo-viewer.is-light .photo-viewer-toolbar {
  border-bottom-color: var(--line);
}
.photo-viewer.is-light .photo-viewer-title {
  color: var(--ink);
}
.photo-viewer.is-light .photo-viewer-sub,
.photo-viewer.is-light .photo-viewer-panel-head {
  color: var(--ink-3);
}
.photo-viewer.is-light .photo-viewer-stage {
  background: var(--paper);
}
.photo-viewer.is-light .photo-viewer-strip {
  border-top-color: var(--line);
}
.photo-viewer.is-light .photo-viewer-panel {
  background: var(--surface-elevated);
  border-left-color: var(--line);
}
.photo-viewer.is-light .photo-finding {
  background: var(--surface-elevated);
  border-color: var(--line);
}
.photo-viewer.is-light .photo-finding:hover {
  background: var(--tint-neutral-soft);
  border-color: var(--border-stronger);
}
.photo-viewer.is-light .photo-finding:focus-visible {
  box-shadow: var(--focus-ring-accent-strong);
}
.photo-viewer.is-light .photo-finding.is-active {
  background: var(--tint-accent-faint);
  border-color: var(--primary-press);
}
.photo-viewer.is-light .photo-finding .pf-name {
  color: var(--ink);
}
.photo-viewer.is-light .photo-finding .pf-meta {
  color: var(--ink-3);
}
/* Below 860px the panel stops being a column and becomes a section under the
   stage — a 300px panel beside a photo leaves neither of them readable.
   The wrap itself is intrinsic (see .photo-viewer-main); this @container query
   only swaps the panel's divider from a left edge to a top edge, which is legal
   because the panel is a DESCENDANT of the container. An element cannot query
   itself, so the column count can never come from here. */
@container (max-width: 860px) {
  .photo-viewer-panel {
    border-left: none;
    border-top: 0.5px solid var(--border-on-dark);
    max-height: 240px;
  }
  .photo-viewer.is-light .photo-viewer-panel {
    border-top-color: var(--line);
  }
}

/* ---------- BENTO GRID ----------
   Reorderable / resizable tile layout for detail dashboards. A 2-column grid
   (.bento) of .bento-tile cards; a tile spans one column (half) or both
   (.span-full). Drag to reorder — drop position drives ORDER, never width;
   width is set from the tile menu (.tile-menu-button beside the handle), and
   every tile carries the menu; on data-width-locked tiles both rows are
   disabled with a caption naming the reason (content that reads by horizontal
   extent — a defects table, a scan strip, a wide distribution — stays full). An odd half tile
   leaves its slot empty rather than auto-growing; the gap is a valid drop
   target. Below 640px the grid is one column and every tile renders full;
   the stored layout is not rewritten. The hover-revealed .tile-handle is the
   grab affordance. Layout is the source of truth — persist the [{id,size}]
   array. */
.bento {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.bento-tile {
  position: relative;
  grid-column: span 1;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-frame);
  padding: 18px;
}
.bento-tile.span-full {
  grid-column: span 2;
}
/* Half form — a split row (label | bar | value) restacks inside a narrow tile:
   the tile is a size container, and under 420px of tile width the row goes one
   column. Content adapts by restacking, never by scaling; a card with no
   readable restack takes data-width-locked and stays full. */
.bento-tile {
  container-type: inline-size;
}
.bento-tile .graph-h {
  grid-template-columns: 120px 1fr 90px;
  padding: 0;
}
@container (max-width: 420px) {
  .bento-tile .graph-h {
    grid-template-columns: 1fr;
    row-gap: 6px;
  }
}
/* Touch — the handle cannot be hover-revealed where hover does not exist; it is
   always visible, and a ::after hitbox grows the 24px pill to a 44px target. */
@media (hover: none) {
  .tile-handle,
  .tile-menu-button {
    opacity: 1;
  }
  .tile-handle::after,
  .tile-menu-button::after {
    content: '';
    position: absolute;
    inset: -10px;
  }
}
/* Tile menu — THE width control (v2.402; the double-click shortcut is removed:
   one gesture, one visible path). A real button beside the handle, hover/focus-
   revealed with it, always visible on touch. Opens the shared Menu / popover
   with Half width / Full width as menuitemradio rows, current one selected.
   On width-locked tiles the menu opens with both rows disabled and a caption
   naming the reason. The dark hover fill matches the
   tile-handle's existing value on the same surface. */
.tile-menu-button {
  appearance: none;
  font: inherit;
  user-select: none;
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 5;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  opacity: 0;
  transition:
    opacity 150ms,
    background 150ms,
    color 150ms;
}
.tile-menu-button .mdi {
  font-size: 16px;
}
.bento-tile:hover .tile-menu-button,
.tile-menu-button:focus-visible,
.tile-menu-button[aria-expanded='true'] {
  opacity: 1;
  background: var(--paper);
}
.tile-menu-button:hover,
.tile-menu-button[aria-expanded='true'] {
  background: var(--paper);
  color: var(--primary);
}
.tile-menu-button:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.bento-tile .menu {
  top: 36px;
  right: 8px;
}
.dark-surface .tile-menu-button {
  color: var(--text-on-dark-secondary);
}
.dark-surface .bento-tile:hover .tile-menu-button,
.dark-surface .tile-menu-button:focus-visible {
  background: rgba(255, 255, 255, 0.08);
}
.dark-surface .tile-menu-button:hover,
.dark-surface .tile-menu-button[aria-expanded='true'] {
  background: rgba(255, 255, 255, 0.08);
  color: var(--lifted-orange);
}
.dark-surface .tile-menu-button:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.bento-tile[draggable='true'] {
  cursor: default;
}
.bento-tile.is-dragging {
  opacity: 0.4;
  transform: scale(0.99);
}
.bento-tile.is-dropzone {
  outline: 2px dashed var(--primary);
  outline-offset: -2px;
}
/* v2.406 — chrome hardening. The handle and menu are real <button>s: UA button
   styling is stripped above; user-select:none lives on the two chrome buttons
   only, so tile CONTENT stays selectable. The handle gets the focus ring the
   menu button already had. */
.tile-handle:focus-visible {
  outline: none;
  opacity: 1;
  box-shadow: var(--focus-ring-accent-strong);
}
.dark-surface .tile-handle:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* An open width menu must clear the tiles after it in DOM order — and the tile
   must never clip it: clip inner content wrappers, never .bento-tile itself. */
.bento-tile.is-menu-open {
  z-index: 10;
  overflow: visible;
}
/* Bare tile — content that is already a card: the tile contributes no surface
   of its own; the inner card is the surface, and it names itself (tile title
   hidden). The chrome reserves no space: while revealed it OVERLAYS whatever
   sits under the tile's top-right corner, each button on its own plate
   (--paper; the white 0.08 fill on petrol) so it stays legible over content.
   v2.423 — Julie: chrome is hover/focus-only on desktop; .is-chrome-resting
   and the bare tile's 110px header padding are removed (touch keeps
   always-visible chrome via the hover:none rule above). */
.bento-tile.is-bare {
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
  border-radius: var(--radius-2xl);
}
.bento-tile.is-bare .tile-title {
  display: none;
}
/* Empty-slot placeholder beside an odd half tile. At REST it is invisible —
   it only reserves the gap (v2.411; it wrongly showed a permanent dashed
   outline). During a drag it takes the same .is-dropzone treatment as a tile;
   hidden in the one-column collapse. */
.bento-grid-slot {
  grid-column: span 1;
  border-radius: var(--radius-lg);
  min-height: 64px;
}
.bento-grid-slot.is-dropzone {
  outline: 2px dashed var(--primary);
  outline-offset: -2px;
}
.tile-handle {
  appearance: none;
  font: inherit;
  border: none;
  user-select: none;
  position: absolute;
  top: 8px;
  right: 38px;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 24px;
  padding: 0 8px;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-3);
  cursor: grab;
  opacity: 0;
  transition:
    opacity 120ms,
    background 120ms,
    color 120ms;
}
.tile-handle .mdi {
  font-size: 14px;
}
.tile-handle .th-label {
  font-family: var(--font-mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* The label set no colour at all, so on petrol it inherited --ink-3 — a light-surface
   token — and measured 1.97:1 at 8.5px. Sixth instance of the token-on-the-wrong-
   surface family (v2.217, v2.251, v2.269, v2.270, v2.272, v2.273). */
.dark-surface .tile-handle .th-label {
  color: var(--text-on-dark-secondary);
}
.bento-tile:hover .tile-handle {
  opacity: 1;
  background: var(--paper);
  color: var(--primary);
}
.tile-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.012em;
  color: var(--ink);
  margin: 0 0 10px;
}
.dark-surface .bento-tile {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .tile-title {
  color: var(--surface-elevated);
}
.dark-surface .bento-tile:hover .tile-handle {
  background: rgba(255, 255, 255, 0.08);
  color: var(--lifted-orange);
}
/* 640px = --bp-phone-large — media queries cannot read var(); keep in sync with tokens.css */
@media (max-width: 640px) {
  .bento {
    grid-template-columns: 1fr;
  }
  .bento-tile,
  .bento-tile.span-full {
    grid-column: span 1;
  }
  .bento-grid-slot {
    display: none;
  }
}

/* ---------- DATA TABLE · row-select / sortable / column-menu (v2.1) ----------
   Additions to the base .data-table: a leading checkbox column, a sortable
   header affordance, a selected-row state, and a column-visibility menu
   trigger (the menu itself uses .menu + .menu-check). */
.data-table .col-select {
  width: 28px;
  padding: 0 11px;
}
.row-check {
  position: relative;
  box-sizing: border-box;
  width: var(--row-check-size);
  height: var(--row-check-size);
  border-radius: var(--checkbox-radius);
  border: 1px solid rgba(0, 0, 0, 0.3);
  background: var(--surface-elevated);
  display: grid;
  place-items: center;
  color: transparent;
  cursor: pointer;
  flex-shrink: 0;
}
.row-check .mdi {
  display: none;
}
.row-check.is-checked {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--surface-elevated);
}
.row-check.is-indeterminate {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--surface-elevated);
}
/* v2.454: 14px box, 1px tick drawn by the box (the .mdi child is optional and hidden);
   ::before widens the hit area to 24px without growing the box. */
.row-check::before {
  content: '';
  position: absolute;
  inset: -5px;
}
.row-check.is-checked::after,
.row-check.is-indeterminate::after {
  content: '';
  width: 12px;
  height: 12px;
  background-color: currentColor;
  -webkit-mask: var(--icon-mask-check-thin) center / 12px no-repeat;
  mask: var(--icon-mask-check-thin) center / 12px no-repeat;
}
.row-check.is-indeterminate::after {
  -webkit-mask-image: var(--icon-mask-dash-thin);
  mask-image: var(--icon-mask-dash-thin);
}

.data-table tbody tr.is-selected {
  background: var(--table-row-selected);
}
.data-table tbody tr.is-selected:hover {
  background: var(--table-row-selected-hover);
}
/* A frozen / sticky column cell paints its own background to cover the scrolling
   body, so it must repeat the row fill or the wash breaks at that column. */
.data-table tbody tr:hover > td.is-sticky {
  background: var(--table-row-hover);
}
.data-table tbody tr.is-selected > td.is-sticky {
  background: var(--table-row-selected);
}
.data-table tbody tr.is-selected:hover > td.is-sticky {
  background: var(--table-row-selected-hover);
}
.th-sort {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
.th-sort .sort-arrow {
  font-size: 13px;
  opacity: 0.5;
  transition: opacity 120ms;
}
.th-sort:hover .sort-arrow {
  opacity: 1;
}
.data-table th.sorted .th-sort .sort-arrow {
  opacity: 1;
  color: var(--primary-press);
}
.col-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 0.5px solid var(--line);
  background: var(--surface-elevated);
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--ink-3);
}
.col-trigger:hover {
  border-color: var(--border-stronger);
  color: var(--ink);
}
.col-trigger .mdi {
  font-size: 17px;
}
.dark-surface .data-table tbody tr.is-selected {
  background: var(--table-row-selected-dark);
}
.dark-surface .data-table tbody tr.is-selected:hover {
  background: var(--table-row-selected-hover-dark);
}
.dark-surface .data-table tbody tr:hover > td.is-sticky {
  background: var(--table-row-hover-dark);
}
.dark-surface .data-table tbody tr.is-selected > td.is-sticky {
  background: var(--table-row-selected-dark);
}
.dark-surface .data-table tbody tr.is-selected:hover > td.is-sticky {
  background: var(--table-row-selected-hover-dark);
}
/* Fixed layout: column widths come from the header, never from the data, so a
   long value cannot reflow the table while it scrolls. */
.data-table.is-fixed-layout {
  table-layout: fixed;
}
.data-table td.is-code {
  font-family: var(--font-mono);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}
/* Trailing actions column: pinned right, so the open control stays in reach
   while the data columns scroll under it. Opaque fills keep rows from showing
   through; the row tints are layered on top of the surface. */
.data-table .column-actions {
  position: sticky;
  right: 0;
  z-index: 1;
  width: 44px;
  padding: 0 12px 0 4px;
  text-align: center;
  background: var(--surface-elevated);
  border-left: 0.5px solid var(--line);
  box-shadow: var(--table-sticky-shadow);
}
.data-table th.column-actions {
  z-index: 3;
  padding: 12px 12px 12px 4px;
}
.data-table .column-actions .icon-button {
  margin: 0 auto;
  color: var(--ink-3);
}
.data-table tbody tr:hover > td.column-actions {
  background: linear-gradient(var(--table-row-hover), var(--table-row-hover)), var(--surface-elevated);
}
.data-table tbody tr.is-selected > td.column-actions {
  background: linear-gradient(var(--table-row-selected), var(--table-row-selected)), var(--surface-elevated);
}
.data-table tbody tr.is-selected:hover > td.column-actions {
  background: linear-gradient(var(--table-row-selected-hover), var(--table-row-selected-hover)), var(--surface-elevated);
}
.row-check:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
/* ---------- DATA TABLE FRAME + SCROLL ----------
   The frame is the card and never moves; only the rows scroll, inside
   .data-table-scroll. The header pins to the top of the scroll area, the
   actions column to its right edge, and a docked selection bar sits above the
   scroll area so it never scrolls away. */
.data-table-frame {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--surface-elevated);
  border-radius: var(--table-radius);
  box-shadow: var(--table-shadow);
  overflow: hidden;
}
.data-table-frame > .selection-bar {
  flex: 0 0 auto;
  margin: 0;
  border-radius: 0;
}
.data-table-scroll {
  flex: 1 1 auto;
  min-height: 0;
  max-height: var(--table-scroll-max-height);
  overscroll-behavior: contain;
  scroll-padding-top: 52px;
  scroll-padding-right: 60px;
}
.data-table-scroll.scroll-area {
  overflow: auto;
}
.data-table-scroll:focus-visible {
  outline: none;
  box-shadow: inset var(--focus-ring-accent-strong);
}
.data-table-scroll > .data-table {
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}
.data-table-scroll .data-table th {
  top: 0;
}
.dark-surface .data-table-frame {
  background: var(--petrol-deep);
  box-shadow: none;
  border: 0.5px solid var(--tint-on-dark-soft);
}
.dark-surface .data-table-scroll > .data-table {
  border: none;
}
.dark-surface .data-table-scroll:focus-visible {
  box-shadow: inset var(--focus-ring-accent-strong-on-dark);
}
/* ---------- LONG LIST · page scroll (v2.482, the default for a list page) ----------
   The page scrolls, not the table. Exactly two things pin: the filter toolbar
   (.data-table-toolbar-dock, sticky at the top) and the table header (sticky
   at --table-header-offset = the dock's height). The page title, selection
   bar and pagination footer stay in the flow and scroll away, so the rows get
   the height. overflow:clip (not hidden) keeps the frame's corners without
   making it a scroll container, which would unpin the header. */
.data-table-page {
  position: relative;
}
.data-table-toolbar-dock {
  position: sticky;
  top: 0;
  z-index: 6;
  padding: 12px 0;
  background: var(--paper);
}
.data-table-frame.is-page-scroll {
  overflow: clip;
}
.data-table-frame.is-page-scroll > .data-table {
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}
.data-table-frame > .table-pagination {
  border-radius: 0;
}
.dark-surface .data-table-toolbar-dock {
  background: var(--petrol);
}
.dark-surface .data-table .column-actions {
  background: var(--petrol-deep);
  border-left-color: var(--tint-on-dark-soft);
}
.dark-surface .data-table .column-actions .icon-button {
  color: var(--text-on-dark-secondary);
}
.dark-surface .data-table tbody tr:hover > td.column-actions {
  background: linear-gradient(var(--table-row-hover-dark), var(--table-row-hover-dark)), var(--petrol-deep);
}
.dark-surface .data-table tbody tr.is-selected > td.column-actions {
  background: linear-gradient(var(--table-row-selected-dark), var(--table-row-selected-dark)), var(--petrol-deep);
}
.dark-surface .data-table tbody tr.is-selected:hover > td.column-actions {
  background: linear-gradient(var(--table-row-selected-hover-dark), var(--table-row-selected-hover-dark)), var(--petrol-deep);
}
.dark-surface .row-check:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .row-check {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--border-on-dark-strong);
}
.dark-surface .row-check.is-checked,
.dark-surface .row-check.is-indeterminate {
  background: var(--lifted-orange);
  border-color: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .col-trigger {
  background: var(--tint-on-dark-faint);
  border-color: rgba(255, 255, 255, 0.14);
  color: rgba(255, 255, 255, 0.7);
}

/* filter toolbar on dark — icons in lifted-orange so they read on petrol */
.dark-surface .toolbar-search .mdi {
  color: var(--lifted-orange);
}
.dark-surface .filter-pill .fp-icon,
.dark-surface .filter-pill .fp-caret {
  color: var(--lifted-orange);
}
.dark-surface .toolbar-btn .mdi {
  color: var(--lifted-orange);
}

/* ---------- FILTER TOOLBAR · v2.2 additions (label+value pills, More-filters popover, Sort value) ---------- */
/* Two-part pill: a muted key + a bold value (e.g. Date · Today). When a value is set the pill reads .is-active. */
.filter-pill .fp-key {
  color: var(--ink-2);
}
.filter-pill .fp-val {
  font-weight: 600;
  color: var(--ink);
  margin-left: -1px;
}
.filter-pill.is-active .fp-key {
  color: var(--accent-text-on-tint);
}
.filter-pill.is-active .fp-val {
  color: var(--accent-text-on-tint);
}
/* Petrol filter-pill label colours live with the other .dark-surface toolbar rules
   above — this section used to re-declare them with raw white literals (0.7 white,
   under AA at 13px) and, being later in the file, silently won. Removed. */
/* OPEN — the trigger's menu is showing. ONE appearance for both select-style
   triggers in the bar (filter pill, More), and it is the SAME
   appearance as .select.s-open: the label keeps its resting colour and the accent
   is carried by the border, the ring and the icon/caret. Open and focus-visible
   therefore read alike, exactly as the Select field documents — so a click never
   produces a different effect from one control to the next, or from light to
   petrol. Overrides .is-active: what is on screen now outranks what is stored.
   The ring is not cropped because .filter-toolbar / .toolbar-filters clip with an
   overflow-clip-margin wide enough to let it paint. */
.filter-pill.is-open,
.filter-pill.is-active.is-open {
  background: var(--surface-elevated);
  border-color: var(--primary);
  font-weight: 600;
  box-shadow: var(--focus-ring-accent-strong);
}
.filter-pill.is-open .fp-icon,
.filter-pill.is-open .fp-caret {
  color: var(--primary);
}
.filter-pill.is-open .fp-key {
  color: var(--ink-2);
}
.filter-pill.is-open .fp-val {
  color: var(--ink);
}
/* A pill that is BOTH selected and open keeps its accent label — open adds the
   border and ring, it must not read as a downgrade of the selected state. */
.filter-pill.is-active.is-open .fp-key,
.filter-pill.is-active.is-open .fp-val {
  color: var(--accent-text-on-tint);
}
.filter-pill.is-open .fp-count {
  background: var(--primary);
  color: var(--surface-elevated);
}
/* Petrol: lifted orange on the border, ring, icon, caret and count only. An
   orange label over the pill tint measures 3.49:1 — under the 4.5:1 floor at
   13px — so the label stays white, the same call .is-active already makes. */
.dark-surface .filter-pill.is-open {
  background: var(--tint-on-dark-medium);
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* SELECTED + OPEN on petrol. Light keeps the accent LABEL so open does not read
   as a downgrade of the selected state; petrol cannot — an orange label over the
   tint is 3.49:1 — so the selected signal is carried by the FILL instead: the
   lifted tint stays put while open adds the border and ring. Both triggers take
   the same fill, or the two controls diverge in the one state nothing else does. */
.dark-surface .filter-pill.is-active.is-open,
.dark-surface .toolbar-more.is-active.is-open {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .filter-pill.is-open .fp-icon,
.dark-surface .filter-pill.is-open .fp-caret {
  color: var(--lifted-orange);
}
.dark-surface .filter-pill.is-open .fp-key {
  color: var(--text-on-dark-secondary);
}
.dark-surface .filter-pill.is-open .fp-val {
  color: var(--text-on-dark);
}
.dark-surface .filter-pill.is-open .fp-count {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
/* .toolbar-btn is RETAINED BUT UNUSED as of v2.195 — Sort / Columns were removed
   from the bar (sorting lives on the table's column headers, column visibility on
   its .col-trigger). Its rules are kept, matching the pill, so an existing
   consumer does not break; remove the class and these rules once that is agreed. */
.toolbar-btn.is-open {
  background: var(--surface-elevated);
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.toolbar-btn.is-open .mdi {
  color: var(--primary);
}
.dark-surface .toolbar-btn.is-open {
  background: var(--tint-on-dark-medium);
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .toolbar-btn.is-open .mdi {
  color: var(--lifted-orange);
}
/* DISABLED — parity with .select.s-disabled: paper fill, muted ink, resting
   border, no ring, cursor blocked. Colour is not the only signal: the control
   also stops responding and must carry disabled / aria-disabled. */
.filter-pill:disabled,
.filter-pill.is-disabled,
.toolbar-more:disabled,
.toolbar-btn:disabled,
.toolbar-clear:disabled {
  background: var(--paper);
  color: var(--ink-3);
  border-color: var(--line);
  box-shadow: none;
  cursor: not-allowed;
}
.filter-pill:disabled .fp-icon,
.filter-pill:disabled .fp-caret,
.filter-pill:disabled .fp-key,
.filter-pill:disabled .fp-val,
.filter-pill.is-disabled .fp-icon,
.filter-pill.is-disabled .fp-caret,
.filter-pill.is-disabled .fp-key,
.filter-pill.is-disabled .fp-val,
.toolbar-more:disabled .mdi,
.toolbar-btn:disabled .mdi,
.toolbar-clear:disabled .mdi {
  color: var(--ink-3);
}
.filter-pill:disabled .fp-count,
.filter-pill.is-disabled .fp-count,
.toolbar-more:disabled .fp-count {
  background: var(--line-2);
  color: var(--ink-2);
}
.dark-surface .filter-pill:disabled,
.dark-surface .filter-pill.is-disabled,
.dark-surface .toolbar-more:disabled,
.dark-surface .toolbar-btn:disabled,
.dark-surface .toolbar-clear:disabled {
  background: var(--tint-on-dark-faint);
  color: var(--text-on-dark-muted);
  border-color: var(--border-on-dark);
}
.dark-surface .filter-pill:disabled .fp-icon,
.dark-surface .filter-pill:disabled .fp-caret,
.dark-surface .filter-pill:disabled .fp-key,
.dark-surface .filter-pill:disabled .fp-val,
.dark-surface .filter-pill.is-disabled .fp-icon,
.dark-surface .filter-pill.is-disabled .fp-caret,
.dark-surface .filter-pill.is-disabled .fp-key,
.dark-surface .filter-pill.is-disabled .fp-val,
.dark-surface .toolbar-more:disabled .mdi,
.dark-surface .toolbar-btn:disabled .mdi,
.dark-surface .toolbar-clear:disabled .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .filter-pill:disabled .fp-count,
.dark-surface .filter-pill.is-disabled .fp-count,
.dark-surface .toolbar-more:disabled .fp-count {
  background: var(--tint-on-dark-strong);
  color: var(--text-on-dark-secondary);
}
/* Static state classes for specimens, mirroring .select.s-hover / .select.s-focus.
   Interaction states must be showable side by side in the documentation, not only
   provoked with a mouse. */
.filter-pill.s-hover {
  border-color: var(--border-stronger);
}
.filter-pill.s-focus {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.dark-surface .filter-pill.s-hover {
  background: var(--tint-on-dark-strong);
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .filter-pill.s-focus {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.toolbar-more.s-hover,
.toolbar-btn.s-hover {
  border-color: var(--border-stronger);
}
.toolbar-clear.s-hover {
  border-color: var(--primary);
}
.toolbar-more.s-focus,
.toolbar-btn.s-focus,
.toolbar-clear.s-focus {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.dark-surface .toolbar-more.s-hover,
.dark-surface .toolbar-btn.s-hover {
  background: var(--tint-on-dark-strong);
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .toolbar-clear.s-hover {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
}
.dark-surface .toolbar-more.s-focus,
.dark-surface .toolbar-btn.s-focus,
.dark-surface .toolbar-clear.s-focus {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* HOVER ON A SELECTED CONTROL. :hover and .is-active have equal specificity and
   .is-active is later in source, so it won every property hover set — an active
   pill or an active More was completely INERT on hover while an inactive one
   answered. The selected state keeps its accent identity and steps up one tint:
   fill --tint-accent-strong / --tint-lifted-strong, border to the solid accent. */
.filter-pill.is-active:hover,
.filter-pill.is-active.s-hover,
.toolbar-more.is-active:hover,
.toolbar-more.is-active.s-hover,
.toolbar-btn.is-active:hover,
.toolbar-btn.is-active.s-hover {
  background: var(--tint-accent-strong);
  border-color: var(--primary);
}
.dark-surface .filter-pill.is-active:hover,
.dark-surface .filter-pill.is-active.s-hover,
.dark-surface .toolbar-more.is-active:hover,
.dark-surface .toolbar-more.is-active.s-hover,
.dark-surface .toolbar-btn.is-active:hover,
.dark-surface .toolbar-btn.is-active.s-hover {
  background: var(--tint-lifted-strong);
  border-color: var(--lifted-orange);
}

/* Toolbar button carrying an active value. Retained but unused — see above. */
.toolbar-btn .tb-key {
  color: var(--ink-3);
}
.toolbar-btn .tb-val {
  font-weight: 600;
  color: var(--ink);
}
.dark-surface .toolbar-btn .tb-key {
  color: var(--text-on-dark-secondary);
}
.dark-surface .toolbar-btn .tb-val {
  color: var(--text-on-dark);
}
/* "More filters" advanced popover — a wide panel of Select atoms + Toggle atoms
   with Reset / Apply. Capped at 70vh and scrolls internally so a long panel never
   runs off the bottom of the screen. The horizontal axis is pinned to hidden
   (overflow:hidden auto) — setting overflow-y alone forces overflow-x to auto too
   and the panel grows a stray horizontal scrollbar. overscroll-behavior keeps the
   scroll inside the panel. */
.more-filters {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 480px;
  max-width: 90vw;
  max-height: 70vh;
  overflow: hidden auto;
  overscroll-behavior: contain;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-frame);
  padding: 18px;
  z-index: 40;
}
.more-filters .mf-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 14px;
}
.more-filters .mf-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 13px 16px;
}
.more-filters .mf-toggles {
  display: flex;
  gap: 22px;
  flex-wrap: wrap;
  margin-top: 16px;
  padding-top: 15px;
  border-top: 0.5px solid var(--line);
}
.more-filters .mf-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 16px;
  padding-top: 15px;
  border-top: 0.5px solid var(--line);
}
.more-filters .mf-reset {
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13.5px;
  color: var(--ink-2);
  padding: 6px 2px;
}
.more-filters .mf-reset:hover {
  color: var(--ink);
}
.dark-surface .more-filters {
  background: var(--petrol-deep);
  border-color: rgba(255, 255, 255, 0.14);
}
.dark-surface .more-filters .mf-title {
  color: var(--surface-elevated);
}
.dark-surface .more-filters .mf-grid .field label,
.dark-surface .more-filters .mf-toggles label {
  color: rgba(255, 255, 255, 0.82);
}
.dark-surface .more-filters .mf-reset {
  color: rgba(255, 255, 255, 0.65);
}
.dark-surface .more-filters .mf-reset:hover {
  color: var(--surface-elevated);
}
/* A compact Select atom (closed) for use inside dense panels. */
.select-mini {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-elevated);
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--ink-3);
  cursor: pointer;
}
.select-mini .mdi {
  font-size: 17px;
  color: var(--ink-3);
  margin-left: auto;
}
.select-mini:hover {
  border-color: var(--border-stronger);
}
.dark-surface .select-mini {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--tint-on-dark-strong);
  color: var(--text-on-dark-muted);
}
.dark-surface .select-mini .mdi {
  color: var(--text-on-dark-muted);
}

/* ---------- SEARCH FIELD (atom) ---------- */
/* Leading-icon lookup input; .toolbar-search is this atom sized to the filter bar. */
.search-field {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-elevated);
}
.search-field .mdi {
  font-size: 18px;
  color: var(--ink-3);
}
.search-field input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
}
.search-field input::placeholder {
  color: var(--ink-3);
}
.search-field.is-focus {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.search-field .sf-clear {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: none;
  background: var(--tint-neutral);
  border-radius: 50%;
  cursor: pointer;
  color: var(--ink-2);
}
.search-field .sf-clear .mdi {
  font-size: 13px;
}
.search-field .sf-clear:hover {
  background: var(--line);
}
.dark-surface .search-field {
  background: rgba(255, 255, 255, 0.05);
  border-color: var(--tint-on-dark-strong);
}
.dark-surface .search-field .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .search-field input {
  color: var(--surface-elevated);
}
.dark-surface .search-field input::placeholder {
  color: var(--text-on-dark-secondary);
}
.dark-surface .toolbar-search input::placeholder,
.dark-surface .menu-search input::placeholder {
  color: var(--text-on-dark-secondary);
}
.dark-surface .search-field.is-focus {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .search-field .sf-clear {
  background: var(--tint-on-dark-medium);
  color: var(--surface-elevated);
}

/* ---------- TOGGLE / SWITCH (atom) ---------- */
/* A binary on/off switch — the form atom for a single instant-apply setting (used by the More-filters panel). */
.switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink);
  cursor: pointer;
}
.switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  margin: 0;
}
.switch .track {
  width: 40px;
  height: 24px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.22);
  position: relative;
  transition: background 150ms;
  flex-shrink: 0;
}
.switch .track::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--surface-elevated);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.28);
  transition: transform 150ms;
}
.switch input:checked + .track,
.switch .track.on {
  background: var(--primary);
}
.switch input:checked + .track::after,
.switch .track.on::after {
  transform: translateX(16px);
}
.switch input:focus-visible + .track,
.switch .track.is-focus {
  box-shadow: var(--focus-ring-accent-strong);
}
.switch.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.dark-surface .switch {
  color: var(--surface-elevated);
}
.dark-surface .switch .track {
  background: var(--border-on-dark-medium);
}
.dark-surface .switch input:checked + .track,
.dark-surface .switch .track.on {
  background: var(--lifted-orange);
}
.dark-surface .switch input:checked + .track::after,
.dark-surface .switch .track.on::after {
  background: var(--petrol-deep);
}
.dark-surface .switch input:focus-visible + .track,
.dark-surface .switch .track.is-focus {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}

/* =========================================================================
   v2.3 — DATA-VIZ RULEBOOK (charts, KPI, states)
   Ported from the Reports rulebook. HTML/CSS charts are intrinsically fluid;
   SVG charts (columns/area/histogram/line) are drawn by the inline renderer.
   Colour: one default hue (orange); chart series 1–4 for categories,
   status only on breach. On petrol, --chart-main flips to lifted-orange.
   ========================================================================= */
.dark-surface {
  --chart-main: var(--lifted-orange);
  --chart-track: rgba(255, 255, 255, 0.11);
  --chart-series-1: var(--chart-series-1-on-dark);
  --chart-series-2: var(--chart-series-2-on-dark);
  --chart-series-3: var(--chart-series-3-on-dark);
  --chart-series-4: var(--chart-series-4-on-dark);
  --chart-series-5: var(--chart-series-5-on-dark);
  --chart-series-6: var(--chart-series-6-on-dark);
  --chart-cherry-blush: var(--chart-cherry-blush-on-dark);
  --chart-cherry-rose: var(--chart-cherry-rose-on-dark);
  --chart-cherry-ruby: var(--chart-cherry-ruby-on-dark);
  --chart-cherry-crimson: var(--chart-cherry-crimson-on-dark);
  --chart-cherry-currant: var(--chart-cherry-currant-on-dark);
  --chart-cherry-merlot: var(--chart-cherry-merlot-on-dark);
  --chart-cherry-mahogany: var(--chart-cherry-mahogany-on-dark);
}

/* ranked bars */
.chart-ranked {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.chart-ranked-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.chart-ranked-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.chart-ranked-label {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.chart-swatch {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex-shrink: 0;
}
.chart-tag-unattributed {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: var(--line-2);
  border-radius: 3px;
  padding: 1px 5px;
}
.chart-ranked-value {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  font-feature-settings: 'tnum';
  white-space: nowrap;
}
.chart-ranked-value.hot {
  color: var(--primary-press);
}
.chart-ranked-unit {
  font-size: 10.5px;
  color: var(--ink-3);
  font-weight: 500;
}
.chart-ranked-share {
  margin-left: 8px;
  color: var(--ink-3);
  font-weight: 500;
  font-size: 11px;
}
.chart-ranked-track {
  height: 14px;
  border-radius: 5px;
  background: var(--chart-track);
  overflow: hidden;
}
.chart-ranked-fill {
  height: 100%;
  border-radius: 5px;
  background: var(--chart-main);
  transition: width 500ms var(--ease-out);
}
/* stacked */
.chart-stacked-track {
  display: flex;
  height: 24px;
  border-radius: 6px;
  overflow: hidden;
  gap: 2px;
}
.chart-stacked-segment {
  min-width: 2px;
  transition: width 500ms var(--ease-out);
}
.chart-stacked-legend {
  display: flex;
  flex-direction: column;
  margin-top: 14px;
}
.chart-stacked-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 0.5px solid var(--line-2);
}
.chart-stacked-row:last-child {
  border-bottom: none;
}
.chart-stacked-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex-shrink: 0;
}
.chart-stacked-label {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
  flex: 1;
}
.chart-stacked-value {
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 600;
  font-feature-settings: 'tnum';
  white-space: nowrap;
  color: var(--ink);
}
.chart-stacked-percent {
  color: var(--ink-3);
  font-weight: 500;
  font-size: 11px;
  margin-left: 6px;
}
.chart-mini-badge {
  padding: 2px 7px;
  font-size: 9.5px;
  margin-left: 4px;
}

/* =========================================================================
   TABLE-CELL & INLINE DATA ATOMS
   Five atoms that turn a dense table cell into something readable. All five
   are cell-scale: they inherit the row's line-height and never change the
   row height. Token-only, light + petrol.
   ========================================================================= */

/* ---------- COLOUR GRADE BAR ----------
   A segmented ordered scale — colour-grade bands, ripeness, size distribution.
   One hue at rising strength (see --chart-grade-*), 1px gaps so the step
   boundaries are structural. Give each segment a flex-grow for its share.
   ACCESSIBILITY: colour is the only visual channel here, so the bar is
   decorative on its own — put role="img" + an aria-label carrying the numbers
   on .chart-grade-bar, and show the headline value in text beside it. */
.chart-grade-bar {
  display: flex;
  align-items: stretch;
  gap: 1px;
  height: 8px;
  width: 100%;
  min-width: 48px;
  border-radius: var(--radius-xs);
  overflow: hidden;
  background: var(--chart-track);
}
.chart-grade-bar.is-tall {
  height: 12px;
}
.chart-grade-segment {
  flex: 0 1 auto;
  min-width: 2px;
  background: var(--chart-grade-4);
  transition: flex-grow var(--dur-slow) var(--ease-out);
}
.chart-grade-segment.is-grade-1 {
  background: var(--chart-grade-1);
}
.chart-grade-segment.is-grade-2 {
  background: var(--chart-grade-2);
}
.chart-grade-segment.is-grade-3 {
  background: var(--chart-grade-3);
}
.chart-grade-segment.is-grade-4 {
  background: var(--chart-grade-4);
}
.chart-grade-segment.is-unattributed {
  background: var(--ink-300);
}
.dark-surface .chart-grade-bar {
  background: var(--tint-on-dark-medium);
}
.dark-surface .chart-grade-segment {
  background: var(--chart-grade-4-on-dark);
}
.dark-surface .chart-grade-segment.is-grade-1 {
  background: var(--chart-grade-1-on-dark);
}
.dark-surface .chart-grade-segment.is-grade-2 {
  background: var(--chart-grade-2-on-dark);
}
.dark-surface .chart-grade-segment.is-grade-3 {
  background: var(--chart-grade-3-on-dark);
}
.dark-surface .chart-grade-segment.is-grade-4 {
  background: var(--chart-grade-4-on-dark);
}
.dark-surface .chart-grade-segment.is-unattributed {
  background: var(--text-on-dark-muted);
}
/* Produce colour fills (v2.491): one class per cherry grade, on a grade-bar
   segment or a column-chart bar (use .column-chart.is-categorical). The tokens
   swap on petrol, so there are no dark rules. */
.chart-grade-segment.is-cherry-blush,
.column-chart-bar.is-cherry-blush {
  background: var(--chart-cherry-blush);
}
.chart-grade-segment.is-cherry-rose,
.column-chart-bar.is-cherry-rose {
  background: var(--chart-cherry-rose);
}
.chart-grade-segment.is-cherry-ruby,
.column-chart-bar.is-cherry-ruby {
  background: var(--chart-cherry-ruby);
}
.chart-grade-segment.is-cherry-crimson,
.column-chart-bar.is-cherry-crimson {
  background: var(--chart-cherry-crimson);
}
.chart-grade-segment.is-cherry-currant,
.column-chart-bar.is-cherry-currant {
  background: var(--chart-cherry-currant);
}
.chart-grade-segment.is-cherry-merlot,
.column-chart-bar.is-cherry-merlot {
  background: var(--chart-cherry-merlot);
}
.chart-grade-segment.is-cherry-mahogany,
.column-chart-bar.is-cherry-mahogany {
  background: var(--chart-cherry-mahogany);
}
/* ---------- GRADE BAR TOOLTIP LAYER ----------
   .chart-grade-bar clips (overflow:hidden) so the rounded ends survive — which
   also cuts off any tooltip bubble raised from a segment inside it. Wrap the bar
   in .chart-grade-bar-layer to hover a per-band value: the layer is the
   positioning context, the bar stops clipping, and the end segments carry the
   radius themselves so the shape is unchanged.
   ACCESSIBILITY: a tooltip is never the only record of a number — keep the bar's
   role="img" + aria-label, and give each hoverable segment tabindex="0" and an
   aria-label so keyboard users reach the same value. */
.chart-grade-bar-layer {
  position: relative;
  overflow: visible;
  z-index: 0;
}
.chart-grade-bar-layer .chart-grade-bar {
  overflow: visible;
}
.chart-grade-bar-layer .chart-grade-segment:first-child {
  border-radius: var(--radius-xs) 0 0 var(--radius-xs);
}
.chart-grade-bar-layer .chart-grade-segment:last-child {
  border-radius: 0 var(--radius-xs) var(--radius-xs) 0;
}
.chart-grade-bar-layer .chart-grade-segment[data-tooltip] {
  cursor: default;
  transition:
    filter var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}
.chart-grade-bar-layer .chart-grade-segment[data-tooltip]:hover,
.chart-grade-bar-layer .chart-grade-segment[data-tooltip]:focus-visible {
  filter: brightness(1.08);
  box-shadow: inset 0 0 0 1px var(--surface-elevated);
  z-index: 1;
}
.chart-grade-bar-layer .chart-grade-segment[data-tooltip]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.dark-surface .chart-grade-bar-layer .chart-grade-segment[data-tooltip]:hover,
.dark-surface .chart-grade-bar-layer .chart-grade-segment[data-tooltip]:focus-visible {
  box-shadow: inset 0 0 0 1px var(--petrol-deep);
}
.dark-surface .chart-grade-bar-layer .chart-grade-segment[data-tooltip]:focus-visible {
  outline-color: var(--lifted-orange);
}
@media (prefers-reduced-motion: reduce) {
  .chart-grade-bar-layer .chart-grade-segment[data-tooltip] {
    transition: none;
  }
}

/* ---------- TARGET ZONE ----------
   A band laid over a scale to mark the range a value should fall in — the ripeness
   window, an acceptable Brix range, a contract spec. Absolutely positioned over a
   .progress-bar, a .capacity-bar or a .chart-grade-bar by left + width.
   The zone is a GRAPHIC, so it needs 3:1 against the track and it never carries
   text: the numbers live in a label beside the scale or in a tooltip on the zone.
   .is-outside marks a value that has left the zone — the one case where the band
   itself changes, because "out of spec" is the reading the zone exists to give. */
.scale-with-zone {
  position: relative;
}
.target-zone {
  position: absolute;
  top: -3px;
  bottom: -3px;
  z-index: 2;
  border-radius: 2px;
  background: var(--tint-success-medium);
  border-left: 1.5px solid var(--success);
  border-right: 1.5px solid var(--success);
  pointer-events: auto;
}
.target-zone.is-warning {
  background: var(--tint-warning-medium);
  border-left-color: var(--warning);
  border-right-color: var(--warning);
}
.target-zone.is-outside {
  background: var(--tint-error-bg);
  border-left-color: var(--error);
  border-right-color: var(--error);
}
.target-zone-label {
  position: absolute;
  top: -19px;
  left: 0;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--success-text);
  white-space: nowrap;
}
.target-zone.is-warning + .target-zone-label,
.target-zone-label.is-warning {
  color: var(--warning-text);
}
.target-zone-label.is-outside {
  color: var(--error-press);
}
/* The marker for where the value actually is. A line, not a dot — a dot on a 6px
   track is bigger than the track and reads as a handle the reader can drag. */
.scale-marker {
  position: absolute;
  top: -5px;
  bottom: -5px;
  z-index: 3;
  width: 2px;
  background: var(--ink);
  border-radius: 1px;
}
.scale-marker.is-outside {
  background: var(--error-press);
}
.dark-surface .target-zone {
  background: var(--tint-success-on-dark);
  border-left-color: var(--success-on-dark);
  border-right-color: var(--success-on-dark);
}
.dark-surface .target-zone.is-warning {
  background: var(--tint-warning-on-dark);
  border-left-color: var(--warning-on-dark);
  border-right-color: var(--warning-on-dark);
}
.dark-surface .target-zone.is-outside {
  background: var(--tint-error-on-dark);
  border-left-color: var(--error-on-dark);
  border-right-color: var(--error-on-dark);
}
.dark-surface .target-zone-label {
  color: var(--success-on-dark);
}
.dark-surface .target-zone-label.is-warning {
  color: var(--warning-on-dark);
}
.dark-surface .target-zone-label.is-outside {
  color: var(--error-on-dark);
}
.dark-surface .scale-marker {
  background: var(--surface-elevated);
}
.dark-surface .scale-marker.is-outside {
  background: var(--error-on-dark);
}

/* ---------- ZOOM CONTROL ----------
   Zoom on an image stage: out, a readout, in, and reset. The readout is a BUTTON
   that resets to 100% — a percentage that is only a label wastes the one place a
   reader looks when the zoom is wrong.
   It appears on hover and focus of the stage, like the other stage chrome, and it
   is always petrol: it sits over a photograph, and a paper control over an image
   is unreadable on half of them.
   v2.388 — positioning is part of the atom: on any [data-stage] it anchors itself
   bottom-RIGHT (right/bottom 12px), no wrapper div needed — usages had drifted
   (viewer bottom-right, atom specimens bottom-left). Freestanding specimens
   outside a stage stay in flow.
   v2.398 — DOM order is fit · out · readout · in. Fit is hidden at 100% and joins
   once zoomed; the pill is right-anchored on a stage, so joining at the LEFT grows
   the pill leftward and out/readout/in never move — joined at the right it
   displaced zoom-in from under the pointer after the first zoom click, and the
   next click landed on fit and reset. Markup order only; no CSS hook. */
.zoom-control {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  padding: 3px;
  border-radius: var(--radius);
  background: var(--scrim-photo);
  opacity: 0;
  transition: opacity 150ms;
}
[data-stage] .zoom-control {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 4;
}
.zoom-control-btn {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--surface-elevated);
  cursor: pointer;
}
.zoom-control-btn .mdi {
  font-size: 18px;
}
.zoom-control-btn:hover {
  background: var(--tint-on-dark-medium);
}
.zoom-control-btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.zoom-control-btn:disabled {
  color: var(--text-on-dark-disabled);
  cursor: not-allowed;
  background: transparent;
}
.zoom-control-level {
  min-width: 52px;
  height: 30px;
  padding: 0 6px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--surface-elevated);
  cursor: pointer;
}
.zoom-control-level:hover {
  background: var(--tint-on-dark-medium);
}
.zoom-control-level:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.zoom-control.is-always,
[data-stage]:hover .zoom-control,
[data-stage]:focus-within .zoom-control {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .zoom-control,
  .photo-viewer-canvas {
    transition: none;
  }
}

/* ---------- COLUMN GROUP ----------
   A row of vertical mini-columns inside a cell — the size-band distribution,
   a per-day sparkline.

   The label belongs INSIDE its column's cell, not in a sibling row. v2.220 and
   earlier used a separate .chart-column-label-row that had to mirror the group's
   flex geometry — same basis, same gap, same min-width — so any difference
   between the two rows (a track wrapper on one, a different min-width) slid every
   label off its column. Nesting makes the alignment structural: a label cannot
   drift from a column it is inside. Cells bottom-align, so the labels form a
   straight baseline and the bars grow up from it.

   Bars carry their own height (px or %). The group's height is a MINIMUM for the
   bar area, not a cap — a cell is bar + label, and the label lives below the box.
   height:auto is deliberate — the group's box is bar area + label, and an
   explicit height on it (as this atom carried until v2.222) clips the label out
   of the box, which then paints 19px outside its own bounds. */
.chart-column-group {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  min-width: 48px;
  height: auto;
}
.chart-column-group.is-short {
  --chart-column-h: var(--chart-column-h-short);
  height: auto;
}
/* Tall tier — the only one a printed value fits on. The detail-screen size-band
   chart prints each column's share above its bar; at 30px the number would be
   half the column. */
.chart-column-group.is-tall {
  --chart-column-h: var(--chart-column-h-tall);
  height: auto;
}
/* At 150px a .is-short cell is 18.9px and "90+" at --text-micro measures 19.2px —
   a 0.2px overflow that ellipsis resolves by eating the "+", turning a 3-character
   tick mark into a lie. Tighter tracking at this tier only; the label stays 10px,
   the documented floor. Note scrollWidth CANNOT detect this: it is an integer and
   rounds 0.2px to zero, which is why four sweeps reported "0 clipped". */
.chart-column-group.is-short .chart-column-label {
  letter-spacing: -0.02em;
}
/* The cell reserves the bar area, so a bar height of 60% means 60% of the band
   and .is-short actually scales it. v2.221 put min-height on the GROUP and left
   the bars on explicit px, which made .is-short a no-op — a minimum cannot
   shrink a bar that carries its own height. */
.chart-column-cell {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
}
.chart-column-cell > .chart-column-bar-area {
  display: flex;
  align-items: flex-end;
  height: var(--chart-column-h);
}
/* A bare column is the MUTED step. Emphasis in this atom comes from muting the
   others, not from darkening the peak — which is also the only way it can work:
   --primary is byte-identical to --chart-main, so a peak painted --primary on a
   --chart-main field is invisible. That was the v2.221 regression. */
.chart-column {
  flex: 1 1 0;
  min-width: 3px;
  min-height: 4px;
  border-radius: 2px 2px 0 0;
  background: var(--chart-grade-2);
  transition: height var(--dur-slow) var(--ease-out);
}
.chart-column-cell > .chart-column {
  width: 100%;
  flex: none;
}
.chart-column.is-muted {
  background: var(--chart-grade-1);
}
.chart-column.is-peak {
  background: var(--primary);
}
/* The label. Inside a .chart-column-cell it needs no geometry of its own. */
.chart-column-label {
  display: block;
  margin-top: 3px;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: 0.04em;
  font-feature-settings: 'tnum';
  color: var(--ink-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chart-column-label.is-peak {
  color: var(--accent-text-on-tint);
}
/* Track — OPTIONAL, and off by default. A track behind every column turns a
   sparkline into a bar chart with a visible ceiling, which is the wrong reading
   when the columns are shares of a whole. Use it only when each column is a
   value against a known maximum. */
.chart-column-track {
  flex: 1 1 0;
  min-width: 3px;
  display: flex;
  align-items: flex-end;
  height: 100%;
  background: var(--chart-track);
  border-radius: 2px;
}
.chart-column-track > .chart-column {
  width: 100%;
  flex: 0 0 auto;
}
/* Deprecated in v2.221 — kept so existing markup does not break. Use
   .chart-column-cell instead; this row cannot stay aligned to a group it is not
   part of. */
.chart-column-label-row {
  display: flex;
  gap: 3px;
  margin-top: 5px;
  min-width: 48px;
}
.chart-column-label-row > .chart-column-label {
  flex: 1 1 0;
  min-width: 3px;
  text-align: center;
  margin-top: 0;
}
/* ---------- COLUMN VALUE ----------
   The share the bar represents, printed on top of it — pulled from the Load
   Assessments detail size-band chart, where the reader needs the number and the
   shape together.

   It hangs off the BAR (bottom:100%), not the cell, so it rides the bar's top
   edge instead of floating at a fixed height. The bar area reserves the room with
   padding-top on a border-box, so every bar's percentage is measured against the
   same reduced content box and the ratios between columns survive. Padding the
   area is the only way that holds: a value placed IN the flex column would shrink
   the tall bars and not the short ones, which silently rewrites the data.

   The value is the measured quantity, the label is the category. Different jobs,
   so different faces — display for the number, mono for the tick mark below. */
.chart-column-bar-area.has-value {
  box-sizing: border-box;
  padding-top: 14px;
  overflow: visible;
}
.chart-column-bar-area.has-value > .chart-column {
  position: relative;
  overflow: visible;
}
.chart-column-value {
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translateX(-50%);
  margin-bottom: 3px;
  font-family: var(--font-display);
  font-size: var(--text-micro);
  font-weight: 700;
  line-height: 1;
  font-feature-settings: 'tnum';
  color: var(--ink-2);
  white-space: nowrap;
}
.chart-column-value.is-peak {
  color: var(--accent-text-on-tint);
}
.dark-surface .chart-column-value {
  color: var(--text-on-dark-secondary);
}
.dark-surface .chart-column-value.is-peak {
  color: var(--lifted-orange);
}
.dark-surface .chart-column-label {
  color: var(--text-on-dark-muted);
}
.dark-surface .chart-column-label.is-peak {
  color: var(--lifted-orange);
}
.dark-surface .chart-column {
  background: var(--chart-grade-2-on-dark);
}
.dark-surface .chart-column.is-muted {
  background: var(--chart-grade-1-on-dark);
}
.dark-surface .chart-column.is-peak {
  background: var(--lifted-orange);
}
/* ---------- CHART COLUMN · modern finish (v2.494) ----------
   Softer, more open bars: rounded tops (--radius-sm; --radius-xs in the small
   tiers), tall charts inset each bar inside its cell so bands breathe, grouped
   bars sit 3px apart, and a 1px baseline grounds the plot. */
.chart-column {
  border-radius: var(--radius-sm) var(--radius-sm) 1px 1px;
}
.chart-column-group.is-cell .chart-column,
.chart-column-group.is-short .chart-column {
  border-radius: var(--radius-xs) var(--radius-xs) 0 0;
}
.chart-column-track {
  border-radius: var(--radius-sm);
}
.chart-column-group.is-tall {
  gap: 6px;
}
.chart-column-group.is-tall .chart-column-cell > .chart-column-bar-area {
  box-sizing: border-box;
  padding-left: 14%;
  padding-right: 14%;
  background: linear-gradient(var(--line), var(--line)) left bottom / 100% 1px no-repeat;
}
.chart-column-group.is-tall.is-grouped .chart-column-cell > .chart-column-bar-area {
  padding-left: 8%;
  padding-right: 8%;
  gap: 3px;
}
.dark-surface .chart-column-group.is-tall .chart-column-cell > .chart-column-bar-area {
  background-image: linear-gradient(var(--tint-on-dark-medium), var(--tint-on-dark-medium));
}
/* Cell tier: the column group sized for a data-table row (24px bar area,
   9px labels). Peak, runner-up and the rest: is-peak, is-second, is-muted. */
.chart-column-group.is-cell {
  --chart-column-h: var(--chart-column-h-cell);
  height: auto;
}
.chart-column-group.is-cell .chart-column-label {
  margin-top: 2px;
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0;
}
.chart-column-group.is-cell .chart-column-label.is-peak {
  color: var(--ink-3);
}
.dark-surface .chart-column-group.is-cell .chart-column-label.is-peak {
  color: var(--text-on-dark-muted);
}
.chart-column.is-second {
  background: var(--lifted-orange);
}
.dark-surface .chart-column.is-second {
  background: var(--chart-grade-3-on-dark);
}
.dark-surface .chart-column-track {
  background: var(--tint-on-dark-medium);
}

/* ---------- COLUMN GROUP · hover ----------
   A bare .chart-column is ~6px wide — too small to hover reliably, so a tooltip
   on it is unreachable in practice. .is-hoverable dims the siblings instead of
   highlighting the target, which needs no extra chrome and reads at any width.
   Pair with data-tooltip on each column for the readout. */
.chart-column-group.is-hoverable {
  overflow: visible;
}
.chart-column-group.is-hoverable .chart-column {
  position: relative;
  transition: opacity 150ms;
}
.chart-column-group.is-hoverable:hover .chart-column {
  opacity: 0.45;
}
.chart-column-group.is-hoverable .chart-column:hover,
.chart-column-group.is-hoverable .chart-column:focus-visible {
  opacity: 1;
}
.chart-column-group.is-hoverable .chart-column:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
@media (prefers-reduced-motion: reduce) {
  .chart-column-group.is-hoverable .chart-column {
    transition: none;
  }
}

/* ---------- COLUMN GROUP · grouped series ----------
   Grouped columns (v2.431, from the Accuracy reports build): several series
   side-by-side inside ONE band cell — internal vs grader vs adjusted results
   per size band. The base atom already flexes every .chart-column in a bar
   area evenly (flex:1 1 0), so the variant only adds the intra-cell gap that
   keeps sibling series readable as separate bars. Fills take the accessory
   palette inline (background:var(--chart-series-1) / var(--chart-series-2) /
   var(--chart-series-3)) — the same categorical contract as ranked bars and
   stacked segments: one tone per series, stable in every cell, never orange,
   never petrol. Name the series with ONE .chart-legend; 2-3 series only — a
   fourth turns each bar into a sliver at the 3px minimum. .is-peak/.is-muted
   stay out of it: emphasis-by-muting is a single-series device, and here the
   colour already carries the identity.
*/
.chart-column-group.is-grouped .chart-column-bar-area {
  gap: 2px;
}

/* ---------- SERIES TOOLTIP ----------
   One flyout that explains EVERY colour in a pile, not just the one under the
   pointer: the band's name, a hairline, then one row per series (dot + name +
   value). The hover target is the WHOLE band (a grouped column cell, a grade
   bar), never a 6px bar, and the band tints while it is read. Placement:
   beside the band by default, .is-flipped for bands in the right half of a
   chart (opens leftwards), .is-below under a bar-shaped host. Hosts opt in
   with .has-series-tooltip: .chart-column-group (each .chart-column-cell is
   focusable) and .chart-grade-bar-layer (the layer is focusable).
   ACCESSIBILITY: the flyout is aria-hidden — the focusable band carries
   role="img" + an aria-label reading every series, so the numbers never live
   only in the flyout. It opens on :hover and :focus, so a tap works too. */
.chart-series-tooltip {
  display: none;
  position: absolute;
  z-index: var(--z-tooltip);
  left: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%);
  width: max-content;
  min-width: 180px;
  max-width: var(--tooltip-max-width-wide);
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-frame);
  font-family: var(--font-body);
  text-align: left;
  pointer-events: none;
}
.chart-series-tooltip.is-flipped {
  left: auto;
  right: calc(100% + 8px);
}
.chart-series-tooltip.is-below {
  top: calc(100% + 8px);
  left: 0;
  transform: none;
}
.chart-series-tooltip-title {
  padding: 9px 12px 8px;
  border-bottom: 0.5px solid var(--line);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
}
.chart-series-tooltip-rows {
  display: grid;
  grid-template-columns: 8px minmax(0, 1fr) auto;
  align-items: center;
  gap: 6px 10px;
  padding: 8px 12px 10px;
}
.chart-series-tooltip-row {
  display: contents;
}
.chart-series-tooltip-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ink-300);
}
.chart-series-tooltip-dot.is-grade-1 {
  background: var(--chart-grade-1);
}
.chart-series-tooltip-dot.is-grade-2 {
  background: var(--chart-grade-2);
}
.chart-series-tooltip-dot.is-grade-3 {
  background: var(--chart-grade-3);
}
.chart-series-tooltip-dot.is-grade-4 {
  background: var(--chart-grade-4);
}
.chart-series-tooltip-name {
  font-size: 12.5px;
  color: var(--ink-2);
}
.chart-series-tooltip-value {
  font-family: var(--font-display);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
  font-feature-settings: 'tnum';
  text-align: right;
  white-space: nowrap;
}
.chart-column-group.has-series-tooltip {
  overflow: visible;
}
.chart-column-group.has-series-tooltip .chart-column-cell {
  position: relative;
  border-radius: var(--radius-xs);
  outline: none;
  cursor: default;
  transition: background var(--dur-fast) var(--ease-out);
}
.chart-column-group.has-series-tooltip .chart-column-cell:hover,
.chart-column-group.has-series-tooltip .chart-column-cell:focus {
  background: var(--tint-neutral);
}
.chart-column-group.has-series-tooltip .chart-column-cell:focus-visible {
  box-shadow: var(--focus-ring-accent-strong);
}
.chart-column-group.has-series-tooltip .chart-column-cell:hover .chart-series-tooltip,
.chart-column-group.has-series-tooltip .chart-column-cell:focus .chart-series-tooltip {
  display: block;
}
.chart-grade-bar-layer.has-series-tooltip {
  border-radius: var(--radius-xs);
  outline: none;
  cursor: default;
}
.chart-grade-bar-layer.has-series-tooltip:focus-visible {
  box-shadow: var(--focus-ring-accent-strong);
}
.chart-grade-bar-layer.has-series-tooltip:hover .chart-series-tooltip,
.chart-grade-bar-layer.has-series-tooltip:focus .chart-series-tooltip {
  display: block;
}
.dark-surface .chart-series-tooltip {
  background: var(--petrol-deep);
  border-color: var(--border-on-dark-medium);
}
.dark-surface .chart-series-tooltip-title {
  color: var(--text-on-dark-secondary);
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .chart-series-tooltip-name {
  color: var(--text-on-dark-secondary);
}
.dark-surface .chart-series-tooltip-value {
  color: var(--text-on-dark);
}
.dark-surface .chart-series-tooltip-dot {
  background: var(--text-on-dark-muted);
}
.dark-surface .chart-series-tooltip-dot.is-grade-1 {
  background: var(--chart-grade-1-on-dark);
}
.dark-surface .chart-series-tooltip-dot.is-grade-2 {
  background: var(--chart-grade-2-on-dark);
}
.dark-surface .chart-series-tooltip-dot.is-grade-3 {
  background: var(--chart-grade-3-on-dark);
}
.dark-surface .chart-series-tooltip-dot.is-grade-4 {
  background: var(--chart-grade-4-on-dark);
}
.dark-surface .chart-column-group.has-series-tooltip .chart-column-cell:hover,
.dark-surface .chart-column-group.has-series-tooltip .chart-column-cell:focus {
  background: var(--tint-on-dark-soft);
}
.dark-surface .chart-column-group.has-series-tooltip .chart-column-cell:focus-visible,
.dark-surface .chart-grade-bar-layer.has-series-tooltip:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
@media (prefers-reduced-motion: reduce) {
  .chart-column-group.has-series-tooltip .chart-column-cell {
    transition: none;
  }
}

/* ---------- COLUMN CHART (v2.432) ----------
   The DETAILED bar chart — Column group's full-size sibling for detail pages
   (the Arc load-assessment Size bands / Color grade tiles): a real y-axis
   with % ticks, gridlines, the value printed above EVERY bar, the category
   label under it, and optionally click-a-bar-to-filter. Column group stays
   the compact in-cell preview; this atom is for a tile that IS the chart.
   Anatomy:
     .column-chart > .column-chart-y-axis (tick labels, top value first)
                   + .column-chart-plot
                       > .column-chart-gridlines > .column-chart-gridline xN
                         (.is-baseline on the last — the 0% line)
                       > .column-chart-bands > .column-chart-band xN
   A band stacks value -> bar -> label. When the chart filters, each band is
   a real <button> (aria-pressed mirrors selection; a second click clears) —
   never a div with onclick: selection must be keyboard-reachable. Bars are a
   single measured quantity by default: every bar takes the orange gradient,
   .is-peak at full strength and the rest at the muted step — the same
   emphasis-by-muting rule as Column group. Categorical charts set
   .is-categorical on the root and give each bar its category's own tone
   inline (accessory palette for named categories, the --chart-grade ramp for
   a grade mix) at full strength, no peak. Selection: .is-selected on the
   picked band, .is-dimmed on every sibling. The value + label always print,
   so colour is never the only signal. --column-chart-h (188px) is the
   minimum plot height; the 22px bottom lane is the label row.
*/
.column-chart {
  display: flex;
  gap: 10px;
  min-height: var(--column-chart-h);
}
.column-chart-y-axis {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-bottom: 22px;
  text-align: right;
}
.column-chart-y-axis span {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-micro);
  color: var(--ink-3);
  line-height: 1;
  font-feature-settings: 'tnum';
}
.column-chart-plot {
  flex: 1;
  position: relative;
}
.column-chart-gridlines {
  position: absolute;
  inset: 0 0 22px 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.column-chart-gridline {
  height: 1px;
  background: var(--line-2);
}
.column-chart-gridline.is-baseline {
  background: var(--line);
}
.column-chart-bands {
  position: relative;
  height: 100%;
  padding-bottom: 22px;
  box-sizing: border-box;
  display: flex;
  align-items: flex-end;
  gap: 10px;
}
.column-chart.is-dense .column-chart-bands {
  gap: 4px;
}
.column-chart-band {
  flex: 1 1 0;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  border-radius: var(--radius-xs);
  transition: opacity var(--dur-fast) var(--ease-out);
}
button.column-chart-band {
  cursor: pointer;
}
button.column-chart-band:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.column-chart-band.is-dimmed {
  opacity: 0.5;
}
.column-chart-value {
  font-family: var(--font-display);
  font-size: var(--text-micro);
  font-weight: 700;
  color: var(--ink-2);
  margin-bottom: 4px;
  font-feature-settings: 'tnum';
  white-space: nowrap;
  line-height: 1;
}
.column-chart-band.is-peak .column-chart-value,
.column-chart-band.is-selected .column-chart-value {
  color: var(--accent-text-on-tint);
}
.column-chart-bar {
  display: block;
  width: 100%;
  max-width: 46px;
  min-height: 1.5px;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: linear-gradient(to top, var(--primary), var(--lifted-orange));
  opacity: 0.5;
  transition:
    height var(--dur-slow) var(--ease-out),
    opacity var(--dur-fast) var(--ease-out);
}
.column-chart-band.is-peak .column-chart-bar,
.column-chart-band.is-selected .column-chart-bar {
  opacity: 1;
}
button.column-chart-band:hover .column-chart-bar {
  opacity: 0.75;
}
button.column-chart-band.is-peak:hover .column-chart-bar,
button.column-chart-band.is-selected:hover .column-chart-bar {
  opacity: 1;
}
.column-chart.is-categorical .column-chart-bar {
  opacity: 1;
}
.column-chart-label {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  font-family: var(--font-body);
  font-size: var(--text-micro);
  font-weight: 500;
  color: var(--ink-3);
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
  line-height: 1.4;
}
.column-chart-band.is-peak .column-chart-label,
.column-chart-band.is-selected .column-chart-label {
  color: var(--ink);
  font-weight: 700;
}
.dark-surface .column-chart-y-axis span {
  color: var(--text-on-dark-muted);
}
.dark-surface .column-chart-gridline {
  background: var(--tint-on-dark-faint);
}
.dark-surface .column-chart-gridline.is-baseline {
  background: var(--border-on-dark);
}
.dark-surface .column-chart-value {
  color: var(--text-on-dark-secondary);
}
.dark-surface .column-chart-band.is-peak .column-chart-value,
.dark-surface .column-chart-band.is-selected .column-chart-value {
  color: var(--lifted-orange);
}
.dark-surface .column-chart-bar {
  background: linear-gradient(to top, var(--lifted-orange-press), var(--lifted-orange));
}
/* Cherry fills on petrol (v2.498): .dark-surface .column-chart-bar sets the
   lifted-orange gradient later and at higher specificity, so each cherry fill
   is restated here. The token already swaps to its on-dark value. */
.dark-surface .column-chart-bar.is-cherry-blush {
  background: var(--chart-cherry-blush);
}
.dark-surface .column-chart-bar.is-cherry-rose {
  background: var(--chart-cherry-rose);
}
.dark-surface .column-chart-bar.is-cherry-ruby {
  background: var(--chart-cherry-ruby);
}
.dark-surface .column-chart-bar.is-cherry-crimson {
  background: var(--chart-cherry-crimson);
}
.dark-surface .column-chart-bar.is-cherry-currant {
  background: var(--chart-cherry-currant);
}
.dark-surface .column-chart-bar.is-cherry-merlot {
  background: var(--chart-cherry-merlot);
}
.dark-surface .column-chart-bar.is-cherry-mahogany {
  background: var(--chart-cherry-mahogany);
}
.dark-surface button.column-chart-band:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .column-chart-label {
  color: var(--text-on-dark-muted);
}
.dark-surface .column-chart-band.is-peak .column-chart-label,
.dark-surface .column-chart-band.is-selected .column-chart-label {
  color: var(--text-on-dark);
}
/* Categorical charts (series + produce colours) never borrow the accent: the
   bar colours ARE the data, so the peak / selected value is marked by weight
   and ink, not orange (v2.492). */
.column-chart.is-categorical .column-chart-band.is-peak .column-chart-value,
.column-chart.is-categorical .column-chart-band.is-selected .column-chart-value {
  color: var(--ink);
  font-weight: 700;
}
.dark-surface .column-chart.is-categorical .column-chart-band.is-peak .column-chart-value,
.dark-surface .column-chart.is-categorical .column-chart-band.is-selected .column-chart-value {
  color: var(--text-on-dark);
  font-weight: 700;
}
@media (prefers-reduced-motion: reduce) {
  .column-chart-band,
  .column-chart-bar {
    transition: none;
  }
}
/* Column chart · clean look (v2.496). The same chart without the reading
   apparatus: no y-axis, no gridlines (the 0% baseline stays as the floor),
   and values print only on the peak / selected band, revealed on hover or
   focus for the rest. The values stay in the DOM, so the aria-label and
   button names keep every number. Default (no modifier) is the detailed look. */
.column-chart.is-clean .column-chart-y-axis {
  display: none;
}
.column-chart.is-clean .column-chart-gridline:not(.is-baseline) {
  visibility: hidden;
}
.column-chart.is-clean .column-chart-value {
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.column-chart.is-clean .column-chart-band.is-peak .column-chart-value,
.column-chart.is-clean .column-chart-band.is-selected .column-chart-value,
.column-chart.is-clean .column-chart-band:hover .column-chart-value,
.column-chart.is-clean .column-chart-band:focus-visible .column-chart-value {
  opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
  .column-chart.is-clean .column-chart-value {
    transition: none;
  }
}
/* ---------- COLUMN CHART · GROUPED (v2.497) ----------
   Several series per band ON the detailed axis: the Column chart frame
   (y-axis + gridlines) holding a grouped Column group — the Accuracy reports
   Size band tile. Geometry, from the product: bar area
   --column-chart-grouped-h (240px) + the 22px label lane = a 262px chart;
   14px between bands; inside a band the is-tall grouped padding (8% each
   side) and 3px between series. The value prints over every bar
   (.chart-column-value); below an 880px container the values drop and the
   series tooltip carries the numbers, as does .is-clean at any width. The
   axis gridline draws the 0% line, so the bar area's own baseline is off. */
.column-chart.is-grouped {
  container-type: inline-size;
  height: calc(var(--column-chart-grouped-h) + 22px);
  min-height: 0;
}
.column-chart.is-grouped .column-chart-plot > .chart-column-group {
  position: relative;
  gap: 14px;
}
.column-chart.is-grouped .chart-column-cell > .chart-column-bar-area {
  height: var(--column-chart-grouped-h);
}
.column-chart.is-grouped .chart-column-group.is-tall .chart-column-cell > .chart-column-bar-area {
  background: none;
}
.column-chart.is-grouped .chart-column {
  position: relative;
  overflow: visible;
}
.column-chart.is-grouped .chart-column-label {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 22px;
  margin-top: 0;
}
.column-chart.is-grouped.is-clean .chart-column-value {
  display: none;
}
@container (max-width: 879px) {
  .column-chart.is-grouped .chart-column-value {
    display: none;
  }
}
/* Values replace the tooltip (v2.503). Add .has-values to a grouped chart and,
   at 880px and wider (where every bar prints its value), the series tooltip
   stays shut: hover AND keyboard focus neither open it nor tint the band, so
   mouse and keyboard users get the same chart. The focus ring stays, so a
   keyboard user still sees which band they are on. Below 880px the values drop
   and the tooltip comes back. .is-clean prints no values at any width, so
   .is-clean.has-values keeps the tooltip on everywhere. */
@container (min-width: 880px) {
  .column-chart.is-grouped.has-values:not(.is-clean) .chart-column-group.has-series-tooltip .chart-column-cell:hover .chart-series-tooltip,
  .column-chart.is-grouped.has-values:not(.is-clean) .chart-column-group.has-series-tooltip .chart-column-cell:focus .chart-series-tooltip {
    display: none;
  }
  .column-chart.is-grouped.has-values:not(.is-clean) .chart-column-group.has-series-tooltip .chart-column-cell:hover,
  .column-chart.is-grouped.has-values:not(.is-clean) .chart-column-group.has-series-tooltip .chart-column-cell:focus {
    background: none;
  }
}

/* ---------- ALERT BANNER · metric ----------
   .has-metric swaps the meta line for a measured readout: the actual as a large
   tabular number, the signed shortfall, and the target it is measured against.
   Use when the banner exists BECAUSE a number crossed a threshold; a banner that
   only reports an event keeps the plain .alert-banner-meta line. */
.alert-banner.has-metric {
  align-items: start;
}
.alert-banner-metric {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-top: 7px;
}
.alert-banner-metric .am-value {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.alert-banner-metric .am-delta {
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.alert-banner-metric .am-target {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.alert-banner.success .alert-banner-metric .am-delta {
  color: var(--success-text);
}
.alert-banner.warning .alert-banner-metric .am-delta {
  color: var(--warning-text);
}
.alert-banner.error .alert-banner-metric .am-delta {
  color: var(--error-press);
}
.alert-banner.info .alert-banner-metric .am-delta {
  color: var(--info-text);
}
.dark-surface .alert-banner-metric .am-value {
  color: var(--surface-elevated);
}
.dark-surface .alert-banner-metric .am-target {
  color: var(--text-on-dark-secondary);
}
.dark-surface .alert-banner.success .alert-banner-metric .am-delta {
  color: var(--success-on-dark);
}
.dark-surface .alert-banner.warning .alert-banner-metric .am-delta {
  color: var(--warning-on-dark);
}
.dark-surface .alert-banner.error .alert-banner-metric .am-delta {
  color: var(--error-on-dark);
}
.dark-surface .alert-banner.info .alert-banner-metric .am-delta {
  color: var(--info-on-dark);
}

/* ---------- ATTRIBUTE TOOLTIP ----------
   The zero-markup tooltip: put data-tooltip="…" on ANY element and the bubble
   is drawn with ::after / ::before, no wrapper and no bubble element. Same
   tokens and look as the .tooltip-wrap / .tooltip atom — reach for that one
   when the content is rich or must be announced; reach for this one in dense
   tables where two extra nodes per cell is too much.
   ACCESSIBILITY: generated content is not reliably announced and cannot be
   bound with aria-describedby, so this variant is VISUAL ONLY. The element
   must carry its own accessible name (aria-label or real text) saying the same
   thing. Never put information here that exists nowhere else. */
[data-tooltip] {
  position: relative;
}
[data-tooltip]::after,
[data-tooltip]::before {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--dur-fast) var(--ease-out),
    visibility var(--dur-fast) var(--ease-out);
  transition-delay: 0s;
}
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  z-index: var(--z-tooltip);
  left: 50%;
  bottom: calc(100% + var(--tooltip-offset));
  transform: translateX(-50%);
  width: max-content;
  max-width: var(--tooltip-max-width);
  white-space: normal;
  text-align: left;
  padding: var(--tooltip-pad-y) var(--tooltip-pad-x);
  background: var(--tooltip-bg);
  color: var(--tooltip-text);
  border-radius: var(--tooltip-radius);
  box-shadow: var(--tooltip-shadow);
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: 0;
}
[data-tooltip]::before {
  content: '';
  position: absolute;
  z-index: var(--z-tooltip);
  left: 50%;
  bottom: calc(100% + var(--tooltip-offset) - 4px);
  width: 8px;
  height: 8px;
  margin-left: -4px;
  background: var(--tooltip-bg);
  transform: rotate(45deg);
}
[data-tooltip]:hover::after,
[data-tooltip]:hover::before,
[data-tooltip]:focus-visible::after,
[data-tooltip]:focus-visible::before,
[data-tooltip].is-open::after,
[data-tooltip].is-open::before {
  opacity: 1;
  visibility: visible;
  transition-delay: var(--dur-fast);
}
[data-tooltip][data-tooltip-position='bottom']::after {
  bottom: auto;
  top: calc(100% + var(--tooltip-offset));
}
[data-tooltip][data-tooltip-position='bottom']::before {
  bottom: auto;
  top: calc(100% + var(--tooltip-offset) - 4px);
}
[data-tooltip][data-tooltip-position='right']::after {
  left: calc(100% + var(--tooltip-offset));
  bottom: auto;
  top: 50%;
  transform: translateY(-50%);
}
[data-tooltip][data-tooltip-position='right']::before {
  left: calc(100% + var(--tooltip-offset) - 4px);
  bottom: auto;
  top: 50%;
  margin: -4px 0 0;
}
[data-tooltip][data-tooltip-position='left']::after {
  left: auto;
  right: calc(100% + var(--tooltip-offset));
  bottom: auto;
  top: 50%;
  transform: translateY(-50%);
}
[data-tooltip][data-tooltip-position='left']::before {
  left: auto;
  right: calc(100% + var(--tooltip-offset) - 4px);
  bottom: auto;
  top: 50%;
  margin: -4px 0 0;
}
[data-tooltip].is-terse::after {
  white-space: nowrap;
  max-width: none;
}
.dark-surface [data-tooltip]::after {
  background: var(--tooltip-bg-on-dark);
  color: var(--tooltip-text-on-dark);
}
.dark-surface [data-tooltip]::before {
  background: var(--tooltip-bg-on-dark);
}
@media (prefers-reduced-motion: reduce) {
  [data-tooltip]::after,
  [data-tooltip]::before {
    transition: none;
  }
}

/* ---------- TWO-LINE CELL ----------
   A value plus its qualifier, stacked: bold primary line, muted mono line
   under it. The date cell (day over time) is the common case, but it fits
   anywhere a cell shows a value AND something that qualifies it — an ID under
   a name, a unit under a total, a code under a label. */
.cell-two-line {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  min-width: 0;
}
.cell-two-line .cell-primary {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cell-two-line .cell-qualifier {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.35;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cell-two-line.is-numeric .cell-primary,
.cell-two-line.is-numeric .cell-qualifier {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
}
.cell-two-line.is-end {
  align-items: flex-end;
  text-align: right;
}
.dark-surface .cell-two-line .cell-primary {
  color: var(--text-on-dark);
}
.dark-surface .cell-two-line .cell-qualifier {
  color: var(--text-on-dark-muted);
}
/* ---------- NAME CELL ----------
   The record's name in a data-table row: 14px semibold, an optional trailing
   indicator glyph (scans attached). Ellipsises; never wraps. */
.cell-name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
}
.cell-name-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cell-name .mdi {
  flex-shrink: 0;
  font-size: 14px;
  color: var(--ink-3);
}
.dark-surface .cell-name {
  color: var(--text-on-dark);
}
.dark-surface .cell-name .mdi {
  color: var(--text-on-dark-muted);
}
/* ---------- GRADE CELL ----------
   Colour grade in a data-table row: a 10px pill grade bar over a readout of
   the leading grade (share + grade name). */
.cell-grade {
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-width: 0;
}
.cell-grade > .chart-grade-bar {
  height: 10px;
  border-radius: var(--radius-pill);
}
.cell-grade-readout {
  display: flex;
  align-items: baseline;
  gap: 5px;
  min-width: 0;
}
.cell-grade-value {
  font-family: var(--font-display);
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.cell-grade-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-body);
  font-size: 11.5px;
  color: var(--ink-3);
}
.dark-surface .cell-grade-value {
  color: var(--text-on-dark);
}
.dark-surface .cell-grade-label {
  color: var(--text-on-dark-muted);
}
/* Grade cell with hover tooltips: the bar sits inside .chart-grade-bar-layer,
   so these rules reach it as a descendant. Pill ends move onto the end
   segments, because the tooltip layer has to let the bar overflow. */
.cell-grade .chart-grade-bar,
.cell-grade .chart-grade-bar.is-tall {
  height: 10px;
  border-radius: var(--radius-pill);
}
.cell-grade .chart-grade-bar-layer.has-series-tooltip {
  border-radius: var(--radius-pill);
}
.cell-grade .chart-grade-bar-layer .chart-grade-segment:first-child {
  border-radius: var(--radius-pill) 0 0 var(--radius-pill);
}
.cell-grade .chart-grade-bar-layer .chart-grade-segment:last-child {
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}
.cell-grade .chart-grade-bar-layer .chart-grade-segment:only-child {
  border-radius: var(--radius-pill);
}
/* In a table the open tooltip rises above the next rows, the pinned header
   and the pinned actions column. */
.data-table .chart-grade-bar-layer.has-series-tooltip:hover,
.data-table .chart-grade-bar-layer.has-series-tooltip:focus {
  z-index: 4;
}
/* Size bands with hover tooltips in a table: the hovered band rises above
   later rows and the pinned header / actions column. Dot shades follow the
   bar states. */
.chart-series-tooltip-dot.is-second {
  background: var(--lifted-orange);
}
.dark-surface .chart-series-tooltip-dot.is-second {
  background: var(--chart-grade-3-on-dark);
}
.data-table .chart-column-group.has-series-tooltip .chart-column-cell:hover,
.data-table .chart-column-group.has-series-tooltip .chart-column-cell:focus {
  z-index: 4;
}

/* ---------- STATUS CELL ----------
   A table cell whose whole area carries the status tint — for when the status
   IS the value (a maturity band, a pass/fail verdict), not a note attached to
   one. Tint + icon + label together: colour is never the only signal.
   Text follows the same pairing as the status chips, so error takes
   --error-press (--error-text on --tint-error-bg is 4.41:1, under the floor). */
.cell-status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px;
  border-radius: var(--radius);
  border: 1px solid;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
}
.cell-status .mdi {
  font-size: 15px;
  flex-shrink: 0;
}
.cell-status.is-success {
  background: var(--tint-success-bg);
  border-color: var(--success-border);
  color: var(--success-text);
}
.cell-status.is-warning {
  background: var(--tint-warning-bg);
  border-color: var(--warning-border);
  color: var(--warning-text);
}
.cell-status.is-error {
  background: var(--tint-error-bg);
  border-color: var(--error-border);
  color: var(--error-press);
}
.cell-status.is-info {
  background: var(--tint-info-bg);
  border-color: var(--info-border);
  color: var(--info-text);
}
.cell-status.is-neutral {
  background: var(--tint-neutral-soft);
  border-color: var(--line);
  color: var(--ink-2);
}
.cell-status.is-block {
  display: flex;
  width: 100%;
  justify-content: flex-start;
}
.dark-surface .cell-status.is-success {
  background: var(--tint-success-on-dark);
  border-color: var(--success-border-on-dark);
  color: var(--success-on-dark);
}
.dark-surface .cell-status.is-warning {
  background: var(--tint-warning-on-dark);
  border-color: var(--warning-border-on-dark);
  color: var(--warning-on-dark);
}
.dark-surface .cell-status.is-error {
  background: var(--tint-error-on-dark);
  border-color: var(--error-border-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .cell-status.is-info {
  background: var(--tint-info-on-dark);
  border-color: var(--info-border-on-dark);
  color: var(--info-on-dark);
}
.dark-surface .cell-status.is-neutral {
  background: var(--tint-on-dark-soft);
  border-color: var(--border-on-dark);
  color: var(--text-on-dark-secondary);
}

/* ---------- SEVERITY STACK ----------
   Several graded findings in ONE cell — defects by severity, deductions by
   reason. Each row is a dot + name + right-aligned tabular count; the dot
   repeats the severity the name already states. Cap at three rows and put the
   remainder in .cell-severity-more so row height stays predictable. */
.cell-severity-stack {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  padding: 3px 0;
}
.cell-severity-row {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-body);
  font-size: 12px;
  line-height: 1.3;
  color: var(--ink-2);
}
.cell-severity-row .sev-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-circle);
  flex-shrink: 0;
}
.cell-severity-row .sev-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cell-severity-row .sev-value {
  margin-left: auto;
  padding-left: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.cell-severity-row.is-high .sev-dot {
  background: var(--error);
}
.cell-severity-row.is-medium .sev-dot {
  background: var(--warning);
}
.cell-severity-row.is-low .sev-dot {
  background: var(--info);
}
.cell-severity-row.is-none .sev-dot {
  background: var(--border-strong);
}
.cell-severity-more {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 1px;
}
.dark-surface .cell-severity-row {
  color: var(--text-on-dark-secondary);
}
.dark-surface .cell-severity-row .sev-value {
  color: var(--text-on-dark);
}
.dark-surface .cell-severity-more {
  color: var(--text-on-dark-muted);
}

/* ---------- PHOTO FRAME ----------
   The image container. A fixed aspect ratio so a grid of them never reflows as
   images load, and object-fit:cover so a portrait and a landscape capture read
   as the same tile. .is-empty is the state BEFORE an image exists — a capture
   slot, a failed load — a glyph on a neutral field, never a stretched image.
   Corner slots hold chips and counts; .has-scrim lays a gradient under them so
   white text survives a bright photograph. */
.photo-frame {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--tint-neutral-soft);
  border: 0.5px solid var(--line);
}
.photo-frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.photo-frame.is-square {
  aspect-ratio: 1;
}
.photo-frame.is-wide {
  aspect-ratio: 16 / 9;
}
.photo-frame.is-empty {
  display: grid;
  place-items: center;
  color: var(--ink-3);
  gap: 6px;
}
.photo-frame.is-empty .mdi {
  font-size: 30px;
}
.photo-frame.is-empty .pf-note {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
}
.photo-frame.has-scrim::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 46%;
  background: linear-gradient(to top, var(--scrim-photo), transparent);
  pointer-events: none;
}
.photo-frame-corner {
  position: absolute;
  display: flex;
  align-items: center;
  gap: 6px;
  z-index: 2;
}
.photo-frame-corner.is-top-left {
  top: 8px;
  left: 8px;
}
.photo-frame-corner.is-top-right {
  top: 8px;
  right: 8px;
}
.photo-frame-corner.is-bottom-left {
  bottom: 8px;
  left: 8px;
}
.photo-frame-corner.is-bottom-right {
  bottom: 8px;
  right: 8px;
}
.photo-frame-caption {
  position: absolute;
  left: 10px;
  right: 10px;
  bottom: 8px;
  z-index: 2;
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--surface-elevated);
  text-shadow: 0 1px 2px var(--scrim-photo);
}
.dark-surface .photo-frame,
.photo-viewer:not(.is-light) .photo-frame {
  background: var(--tint-on-dark-soft);
  border-color: var(--border-on-dark);
}
.dark-surface .photo-frame.is-empty,
.photo-viewer:not(.is-light) .photo-frame.is-empty {
  color: var(--text-on-dark-secondary);
}

/* ---------- IMAGE MARKER ----------
   A pin dropped on a photograph at a normalised position — one finding, one
   marker. It is a BUTTON: it opens the finding it refers to, so it carries a
   focus ring and a full 44px hit area even though the dot itself is 18px.
   The fill uses the -press / -text steps, not the raw semantic colour, so the
   numeral inside clears 4.5:1 (white on --error is only 3.7:1). */
.image-marker {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
}
.image-marker .im-dot {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-circle);
  border: 2px solid var(--surface-elevated);
  box-shadow: var(--shadow-card);
  background: var(--primary-press);
  color: var(--surface-elevated);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  transition:
    transform 150ms,
    box-shadow 150ms;
}
.image-marker.is-high .im-dot {
  background: var(--error-press);
}
.image-marker.is-medium .im-dot {
  background: var(--warning-text);
}
.image-marker.is-low .im-dot {
  background: var(--info-text);
}
.image-marker:hover .im-dot {
  transform: scale(1.2);
}
.image-marker.is-active .im-dot {
  transform: scale(1.25);
  box-shadow: 0 0 0 4px var(--tint-accent-medium);
}
.image-marker:focus-visible {
  outline: none;
}
.image-marker:focus-visible .im-dot {
  box-shadow: var(--focus-ring-accent-strong);
}
.image-marker.is-draft .im-dot {
  background: var(--surface-elevated);
  color: var(--ink-2);
  border-style: dashed;
  border-color: var(--primary);
}
@media (prefers-reduced-motion: reduce) {
  .image-marker .im-dot {
    transition: none;
  }
}

/* ---------- LABEL VALUE ROW ----------
   A label and its number on one line: label ellipsises on the left, the value
   is right-aligned mono tabular-nums and NEVER shrinks, so a column of these
   reads as a straight edge of digits. Stack them for a mini key-value list. */
.label-value-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.label-value-row .label-value-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
}
.label-value-row .label-value-amount {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 12.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
  white-space: nowrap;
  color: var(--ink);
  text-align: right;
}
.label-value-row .label-value-amount .label-value-unit {
  font-size: 10px;
  font-weight: 500;
  color: var(--ink-3);
  margin-left: 3px;
}
.label-value-row.is-strong .label-value-label {
  color: var(--ink);
  font-weight: 600;
}
.label-value-row.is-strong .label-value-amount {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.label-value-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
.dark-surface .label-value-row .label-value-label {
  color: var(--text-on-dark-secondary);
}
.dark-surface .label-value-row .label-value-amount {
  color: var(--text-on-dark);
}
.dark-surface .label-value-row .label-value-amount .label-value-unit {
  color: var(--text-on-dark-muted);
}
.dark-surface .label-value-row.is-strong .label-value-label {
  color: var(--text-on-dark);
}

/* bullet / target */
.chart-bullet-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.chart-bullet-track {
  position: relative;
  height: 12px;
  border-radius: 5px;
  background: var(--chart-track);
}
.chart-bullet-track.tall {
  height: 16px;
  border-radius: 6px;
}
.chart-bullet-fill {
  height: 100%;
  border-radius: 5px;
  background: var(--chart-main);
  transition: width 500ms var(--ease-out);
}
.chart-bullet-tick {
  position: absolute;
  top: -3px;
  bottom: -3px;
  width: 2px;
  border-radius: 1px;
  background: var(--ink);
  opacity: 0.55;
}
.chart-target {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.chart-target-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.chart-target-number {
  font-family: var(--font-display);
  font-size: 44px;
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
  font-feature-settings: 'tnum';
  color: var(--ink);
}
.chart-target-footer {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
/* line legend */
.chart-legend {
  display: flex;
  gap: 16px;
  margin-top: 10px;
  flex-wrap: wrap;
}
.chart-legend-key {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-2);
}
.chart-legend-line {
  width: 14px;
  height: 3px;
  border-radius: 2px;
}
/* donut */
.chart-donut-wrap {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}
.chart-donut-legend {
  flex: 1;
  min-width: 180px;
}
/* ---------- KPI CARD ----------
   The stat tile: mono label, display-size value with an optional unit, and a delta.
   .feature is the petrol emphasis variant; .is-sm the compact size. */
.kpi-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.kpi-label {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.kpi-value {
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--ink);
  font-feature-settings: 'tnum';
}
.kpi-value .kpi-unit {
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-3);
  letter-spacing: 0;
  margin-left: 6px;
}
.kpi-delta {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.kpi-delta.up {
  color: var(--success-text);
}
.kpi-delta.down {
  color: var(--error-text);
}
.kpi-card.feature {
  background: var(--petrol);
  color: var(--surface-elevated);
  border-radius: var(--radius-lg);
  padding: 18px;
}
.kpi-card.feature .kpi-value {
  color: var(--surface-elevated);
}
.kpi-card.feature .kpi-label {
  color: var(--text-on-dark-muted);
}
.kpi-card.feature .kpi-value .kpi-unit {
  color: var(--text-on-dark-muted);
}
.kpi-card.is-sm .kpi-value {
  font-size: 32px;
}
.dark-surface .kpi-card .kpi-value {
  color: var(--surface-elevated);
}
.dark-surface .kpi-card .kpi-label,
.dark-surface .kpi-card .kpi-value .kpi-unit {
  color: var(--text-on-dark-muted);
}
/* Stat tile — compact metric on a paper inset (the room-detail stat block): icon + mono label, value + unit */
.stat-tile {
  background: var(--paper);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
}
.stat-tile-head {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-bottom: 8px;
}
.stat-tile-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.stat-tile-value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 24px;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--ink);
  font-feature-settings: 'tnum';
}
.stat-tile-value .stat-tile-unit {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0;
  color: var(--ink-3);
  margin-left: 4px;
}
.dark-surface .stat-tile {
  background: var(--tint-on-dark-faint);
}
.dark-surface .stat-tile-value {
  color: var(--surface-elevated);
}
.dark-surface .stat-tile-label,
.dark-surface .stat-tile-value .stat-tile-unit {
  color: var(--text-on-dark-secondary);
}
/* Petrol overrides the base chart chrome was missing — legend keys, unit and
   share suffixes, and the target footer all sat on --ink-* tones (1.9–2.4:1 on
   petrol). */
.dark-surface .chart-legend-key {
  color: var(--text-on-dark-secondary);
}
.dark-surface .chart-ranked-unit,
.dark-surface .chart-ranked-share,
.dark-surface .chart-stacked-percent {
  color: var(--text-on-dark-secondary);
}
.dark-surface .chart-target-footer {
  color: var(--text-on-dark-secondary);
}
/* The delta pair had no petrol treatment: --success-text / --error-text measure
   1.9:1 and 3.0:1 on petrol. Lightened status tones fix both. */
.kpi-delta.flat {
  color: var(--ink-3);
}
.dark-surface .kpi-delta.up {
  color: var(--success-on-dark);
}
.dark-surface .kpi-delta.down {
  color: var(--error-on-dark);
}
.dark-surface .kpi-delta.flat {
  color: var(--text-on-dark-secondary);
}

/* ---------- COMPARISON PATTERNS ----------
   How a chart says "against what". The system does NOT overlay a second tinted
   series on a bar — two orange bands in one lane are hard to tell apart and the
   comparison is then carried by tint alone. Instead the comparison is carried
   by NUMBERS IN THEIR OWN COLUMNS, a delta, and where useful a shape that is
   not the bar: a range band or a sparkline. The bar always shows exactly one
   period — the current one.

   Four parts, used together or alone:
     .period-select   the control that says which period is being compared to
     .delta-chip      the change, as a glyph + number
     .compare-table   this period / prior period / delta, in columns
     .range-track     a value inside the range it belongs to
     .trend-spark     the shape of the run behind the number
*/

/* 1 — PERIOD SELECT. Off it is a quiet control; ON it is accent-tinted, so a
   glance at the header says a comparison is active and against what. */
.period-select {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--surface-elevated);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  outline: none;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out);
}
.period-select .mdi {
  font-size: 17px;
  color: var(--ink-3);
}
.period-select:hover {
  border-color: var(--border-stronger);
}
.period-select:focus-visible {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
/* ON keeps the accent tint, the accent border and accent icons — but the LABEL
   stays --ink. --primary-press on --tint-accent-medium measures 4.10:1, under
   the 4.5:1 AA needs for 13.5px text; the icons clear the 3:1 asked of a
   graphic at the same pair. So the pill still reads as accented, and the word
   it is accenting stays legible. */
.period-select.is-on {
  background: var(--tint-accent-medium);
  border-color: var(--accent-border-soft);
  color: var(--ink);
}
.period-select.is-on .mdi {
  color: var(--primary-press);
}
.dark-surface .period-select {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-hover);
  color: var(--text-on-dark);
}
.dark-surface .period-select .mdi {
  color: var(--text-on-dark-secondary);
}
.dark-surface .period-select:hover {
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .period-select:focus-visible {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .period-select.is-on {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}
.dark-surface .period-select.is-on .mdi {
  color: var(--lifted-orange);
}

/* 2 — DELTA CHIP. Neutral by default: in a packhouse "up" is good for pack-out
   and bad for defects, so the chip states the change and lets the label carry
   the judgement. Tone is opt-in via .is-good / .is-bad, only where the
   direction has one unambiguous reading. .is-quiet drops the chip for an
   immaterial change, so the emphasised ones are the ones worth looking at. */
.delta-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  background: var(--line-2);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--ink);
  font-feature-settings: 'tnum';
  white-space: nowrap;
}
.delta-chip .dc-glyph {
  font-size: 9px;
  line-height: 1;
}
.delta-chip.is-quiet {
  background: transparent;
  padding-left: 0;
  padding-right: 0;
  color: var(--ink-3);
}
.delta-chip.is-good {
  background: var(--tint-success-bg);
  color: var(--success-text);
}
/* --error-text on the error tint is 4.23:1 — under AA. --error-press is the
   darker red already in the token layer (the pair --success-text / --warning-text
   were darkened for the same reason; --error-text was not). */
.delta-chip.is-bad {
  background: var(--tint-error-bg);
  color: var(--error-press);
}
.delta-chip.is-flat {
  color: var(--ink-3);
}
.dark-surface .delta-chip {
  background: var(--tint-on-dark-medium);
  color: var(--text-on-dark);
}
.dark-surface .delta-chip.is-quiet {
  background: transparent;
  color: var(--text-on-dark-secondary);
}
.dark-surface .delta-chip.is-good {
  background: var(--tint-on-dark-faint);
  color: var(--success-on-dark);
}
.dark-surface .delta-chip.is-bad {
  background: var(--tint-on-dark-faint);
  color: var(--error-on-dark);
}
.dark-surface .delta-chip.is-flat {
  color: var(--text-on-dark-secondary);
}

/* 3 — COMPARE TABLE. The two periods live in their own numeric columns; the
   bar is only ever the current period. Mono, tabular figures, right-aligned,
   so the columns can be read down. */
.compare-table {
  width: 100%;
  border-collapse: collapse;
}
.compare-table th {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  text-align: right;
  padding: 0 0 10px 14px;
  vertical-align: bottom;
  white-space: nowrap;
}
.compare-table td {
  padding: 10px 0 10px 14px;
  border-top: 0.5px solid var(--line-2);
  text-align: right;
  vertical-align: middle;
}
.compare-table th:first-child,
.compare-table td:first-child {
  text-align: left;
  padding-left: 0;
}
.compare-table td.compare-bar-cell {
  width: 44%;
  padding-right: 6px;
}
.compare-label {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
}
.compare-value {
  font-family: var(--font-mono);
  font-size: 13.5px;
  font-weight: 700;
  color: var(--ink);
  font-feature-settings: 'tnum';
  white-space: nowrap;
}
.compare-value .cv-unit {
  font-size: 10.5px;
  font-weight: 500;
  color: var(--ink-3);
  margin-left: 1px;
}
/* The prior period is quieter than the current one — same column treatment,
   less weight, so "now" always reads first. */
.compare-value.is-prior {
  font-weight: 500;
  color: var(--ink-3);
}
.compare-value.is-focus {
  color: var(--primary-press);
}
.dark-surface .compare-table th {
  color: var(--text-on-dark-secondary);
}
.dark-surface .compare-table td {
  border-top-color: var(--border-on-dark);
}
.dark-surface .compare-label {
  color: var(--text-on-dark);
}
.dark-surface .compare-value {
  color: var(--text-on-dark);
}
.dark-surface .compare-value .cv-unit,
.dark-surface .compare-value.is-prior {
  color: var(--text-on-dark-secondary);
}
.dark-surface .compare-value.is-focus {
  color: var(--lifted-orange);
}

/* 4 — RANGE TRACK. A value against the range it sits in — this load's spread
   measured on the scale of every assessment to date. The band is the range,
   the notch is the value; the scale ends are labelled, so the reading does not
   depend on judging a length. */
.range-track {
  position: relative;
  height: 14px;
  border-radius: 5px;
  background: var(--chart-track);
}
.range-band {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: 5px;
  background: var(--chart-main);
}
/* The notch is cut in the surface colour, not drawn in ink — a gap in the band
   rather than a line over it, which keeps the shape clean at 14px. */
.range-notch {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2.5px;
  border-radius: 1px;
  background: var(--surface-elevated);
}
.dark-surface .range-notch {
  background: var(--petrol);
}
.range-scale {
  display: flex;
  justify-content: space-between;
  margin-top: 5px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  color: var(--ink-3);
  font-feature-settings: 'tnum';
}
.dark-surface .range-scale {
  color: var(--text-on-dark-secondary);
}
.range-row {
  display: grid;
  grid-template-columns: minmax(118px, 1fr) auto minmax(200px, 1.6fr);
  align-items: center;
  gap: 18px;
  padding: 14px 0;
  border-top: 0.5px solid var(--line-2);
}
.range-row:first-of-type {
  border-top: 0;
}
.dark-surface .range-row {
  border-top-color: var(--border-on-dark);
}
.range-name {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink);
}
.range-name .mdi {
  font-size: 18px;
  color: var(--primary);
}
.dark-surface .range-name {
  color: var(--text-on-dark);
}
.dark-surface .range-name .mdi {
  color: var(--lifted-orange);
}
.range-reading {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
  color: var(--ink);
  font-feature-settings: 'tnum';
  white-space: nowrap;
}
.range-reading .rr-unit {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--ink-3);
  margin-left: 3px;
}
.dark-surface .range-reading {
  color: var(--text-on-dark);
}
.dark-surface .range-reading .rr-unit {
  color: var(--text-on-dark-secondary);
}

/* 5 — TREND SPARK. The shape of the run behind the number: no axes, no grid,
   no labels — it answers "steady or swinging?", never "how much?". The last
   point carries a dot so the end of the run is unambiguous. */
.trend-spark {
  display: block;
  width: 66px;
  height: 26px;
  color: var(--chart-main);
  overflow: visible;
}
.trend-spark .ts-area {
  fill: var(--chart-grade-1);
}
.trend-spark .ts-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.trend-spark .ts-dot {
  fill: currentColor;
}
.dark-surface .trend-spark .ts-area {
  fill: var(--chart-grade-1-on-dark);
}

/* rulebook layout helpers */
.datavisualization-rule-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-top: 14px;
}
.visualization-rule {
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
  background: var(--surface-elevated);
}
.visualization-rule .datavisualization-rule-heading {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: 700;
  font-size: 13.5px;
  margin-bottom: 8px;
}
.visualization-rule .datavisualization-rule-heading .mdi {
  font-size: 18px;
}
.visualization-rule.good .datavisualization-rule-heading {
  color: var(--success-text);
}
.visualization-rule.bad .datavisualization-rule-heading {
  color: var(--error-text);
}
.visualization-rule ul {
  margin: 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.visualization-rule li {
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.5;
}
.visualization-swatch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
  margin-top: 12px;
}
.visualization-swatch-card {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  padding: 12px;
  background: var(--surface-elevated);
}
.visualization-swatch-chip {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}
.visualization-swatch-name {
  font-weight: 600;
  font-size: 13px;
  color: var(--ink);
}
.visualization-swatch-value {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--ink-3);
  margin-top: 1px;
}
.visualization-swatch-job {
  font-size: 12px;
  color: var(--ink-2);
  line-height: 1.45;
  margin-top: 5px;
}
.visualization-spec {
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-top: 14px;
  background: var(--surface-elevated);
}
.datavisualization-spec-heading {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 14px 16px;
  border-bottom: 0.5px solid var(--line-2);
}
.datavisualization-spec-icon {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-md);
  background: var(--tint-accent-medium);
  color: var(--primary-press);
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
.datavisualization-spec-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  color: var(--ink);
}
.datavisualization-spec-description {
  font-size: 12.5px;
  color: var(--ink-2);
  margin-top: 1px;
  line-height: 1.4;
}
.visualization-when {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--line-2);
}
.visualization-when > div {
  background: var(--surface-elevated);
  padding: 11px 16px;
  font-size: 12.5px;
  color: var(--ink-2);
  line-height: 1.45;
}
.visualization-when b {
  display: block;
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 3px;
}
.visualization-when .use b {
  color: var(--success-text);
}
.visualization-when .avoid b {
  color: var(--error-text);
}
.visualization-demo {
  padding: 18px 16px;
}
.datavisualization-demo-grid {
  display: grid;
  gap: 14px;
}
.demo-card {
  background: var(--paper);
  border: 0.5px solid var(--line-2);
  border-radius: var(--radius-md);
  padding: 14px;
}
.demo-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 10px;
}
.zero-stat {
  display: flex;
  align-items: center;
  gap: 10px;
}
.zero-num {
  font-family: var(--font-display);
  font-size: 34px;
  font-weight: 700;
  color: var(--ink-3);
  letter-spacing: -0.03em;
}
.empty-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--ink-3);
  padding: 12px 14px;
  background: var(--paper);
  border: 0.5px dashed var(--line);
  border-radius: var(--radius);
}
/* Data-quality note (v2.479): renamed from .note-card, which is the Note card component. */
.data-quality-note {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 13px;
  color: var(--ink-2);
  line-height: 1.5;
  padding: 12px 14px;
  background: var(--tint-info-bg, var(--tint-info-bg));
  border: 0.5px solid var(--info-border);
  border-radius: var(--radius);
}
.data-quality-note .mdi {
  font-size: 18px;
  color: var(--info-text);
  flex-shrink: 0;
}
.breach-row {
  display: flex;
  gap: 10px;
  align-items: flex-start;
}
.breach-who {
  font-weight: 600;
  font-size: 13.5px;
  color: var(--ink);
}
.breach-what {
  font-size: 12.5px;
  color: var(--ink-2);
  margin-top: 1px;
}
.breach-when {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-3);
  margin-top: 2px;
}
/* dark-surface chart treatments */
.dark-surface .chart-ranked-label,
.dark-surface .chart-stacked-label,
.dark-surface .chart-ranked-value,
.dark-surface .chart-stacked-value,
.dark-surface .chart-target-number {
  color: var(--surface-elevated);
}
.dark-surface .chart-ranked-value.hot {
  color: var(--lifted-orange);
}
.dark-surface .chart-bullet-tick {
  background: var(--surface-elevated);
  opacity: 0.7;
}
.dark-surface .chart-tag-unattributed {
  background: var(--tint-on-dark-medium);
  color: rgba(255, 255, 255, 0.7);
}
.dark-surface .visualization-rule,
.dark-surface .visualization-spec,
.dark-surface .visualization-swatch-card {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--tint-on-dark-medium);
}
.dark-surface .datavisualization-spec-heading {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .visualization-when {
  background: var(--tint-on-dark-soft);
}
.dark-surface .visualization-when > div {
  background: transparent;
  color: rgba(255, 255, 255, 0.75);
}
.dark-surface .datavisualization-spec-name,
.dark-surface .visualization-swatch-name,
.dark-surface .breach-who {
  color: var(--surface-elevated);
}
.dark-surface .demo-card {
  background: rgba(0, 0, 0, 0.18);
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .datavisualization-spec-icon {
  background: var(--tint-lifted-strong);
  color: var(--lifted-orange);
}
@media (max-width: 720px) {
  .datavisualization-rule-columns,
  .visualization-when {
    grid-template-columns: 1fr;
  }
}
/* ---------- EYEBROW · card edge highlight ----------
   The Hectre eyebrow: an orange accent stripe along ONE edge of a card that
   follows the rounded corners. A card highlight (not a label). Left edge by
   default; add .eyebrow-top / .eyebrow-right / .eyebrow-bottom for other sides.
   On petrol it lifts to --lifted-orange. Built as a border on an inset overlay
   so it hugs the card's border-radius. */
.card-eyebrow {
  position: relative;
}
.card-eyebrow::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  border-left: 4px solid var(--primary);
}
.card-eyebrow.eyebrow-top::before {
  border-left: none;
  border-top: 4px solid var(--primary);
}
.card-eyebrow.eyebrow-right::before {
  border-left: none;
  border-right: 4px solid var(--primary);
}
.card-eyebrow.eyebrow-bottom::before {
  border-left: none;
  border-bottom: 4px solid var(--primary);
}
.dark-surface .card-eyebrow::before {
  border-color: var(--lifted-orange);
}
/* card selected / highlight = the eyebrow stripe */
.card.selected {
  position: relative;
}
.card.selected::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  border-left: 4px solid var(--primary);
}
.dark-surface .card.selected::before {
  border-color: var(--lifted-orange);
}

/* =========================================================================
   v2.25 — SOFT CARD + MOBILE/APP COMPONENTS
   Card restyled borderless (soft shadow). New: segmented control, white
   button, mobile bottom nav, mobile toast, mobile overview tiles, platform
   badge. Wrap any block in .dark-surface for the petrol treatment.
   ========================================================================= */

/* ---------- CARD (base) — borderless, lifts on a soft shadow ---------- */
.card {
  background: var(--surface-elevated);
  border: none;
  border-radius: 20px;
  padding: 24px;
  position: relative;
  box-shadow: var(--dp-shadow);
  transition: box-shadow var(--dur-base, 200ms) var(--ease-out);
}
.card:hover {
  box-shadow: var(--dp-shadow-hi);
}
.dark-surface .card {
  background: var(--petrol-deep);
  border: 0.5px solid var(--tint-on-dark-soft);
  box-shadow: none;
}

/* ---------- SEGMENTED CONTROL — enclosed pill mode-toggle (Both) ---------- */
.segmented {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  background: var(--line-2);
  border-radius: var(--radius-pill);
}
.segmented button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  box-sizing: border-box;
  appearance: none;
  border: none;
  cursor: pointer;
  background: transparent;
  padding: 9px 22px;
  border-radius: var(--radius-pill);
  font: 600 14px / 1 var(--font-body);
  color: var(--ink-3);
  transition:
    color 150ms,
    background 150ms,
    box-shadow 150ms;
}
.segmented button:hover {
  color: var(--ink-2);
}
.segmented button .mdi {
  font-size: 16px;
}
/* Size scale — shared control-height tiers. Default (no class) is the roomy standalone size; is-medium is the recommended in-line default (30px, aligns with chips/tags/badges). */
.segmented.is-small button {
  height: 24px;
  padding: 0 10px;
  font-size: 11.5px;
}
.segmented.is-small button .mdi {
  font-size: 14px;
}
.segmented.is-medium button {
  height: 30px;
  padding: 0 12px;
  font-size: 12.5px;
}
.segmented.is-medium button .mdi {
  font-size: 15px;
}
.segmented.is-large button {
  height: 36px;
  padding: 0 14px;
  font-size: 13.5px;
}
.segmented.is-large button .mdi {
  font-size: 17px;
}
.segmented button.on {
  background: var(--surface-elevated);
  color: var(--ink);
  box-shadow:
    0 1px 2px rgba(20, 30, 25, 0.05),
    0 6px 14px -8px rgba(20, 30, 25, 0.22);
}
.segmented button:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.segmented.full {
  display: flex;
  width: 100%;
}
.segmented.full button {
  flex: 1;
}
.dark-surface .segmented {
  background: var(--tint-on-dark-soft);
}
.dark-surface .segmented button {
  color: rgba(255, 255, 255, 0.65);
}
.dark-surface .segmented button:hover {
  color: rgba(255, 255, 255, 0.85);
}
.dark-surface .segmented button.on {
  background: var(--petrol-deep);
  color: var(--surface-elevated);
  box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.4);
}
/* Icon-only — the label moves to aria-label + a tooltip, and the button goes
   square so the row of icons reads as one control. Never mix icon-only and
   labelled buttons in the same group. */
.segmented.is-icon-only button {
  gap: 0;
  padding: 0;
  width: 44px;
  height: 44px;
}
.segmented.is-small.is-icon-only button {
  width: 24px;
  height: 24px;
}
.segmented.is-medium.is-icon-only button {
  width: 30px;
  height: 30px;
}
.segmented.is-large.is-icon-only button {
  width: 36px;
  height: 36px;
}
/* Inverted — a petrol control on a LIGHT surface: a mode toggle sitting over a
   photo, a map, or an image toolbar where the paper track would disappear. This
   is NOT .dark-surface .segmented, which is the same control ON petrol. */
.segmented.is-inverted {
  background: var(--petrol);
}
.segmented.is-inverted button {
  color: var(--text-on-dark-secondary);
}
.segmented.is-inverted button:hover {
  color: var(--surface-elevated);
}
.segmented.is-inverted button.on {
  background: var(--surface-elevated);
  color: var(--ink);
  box-shadow: none;
}
.segmented.is-inverted button:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}

/* ---------- LOADING SPINNER — indeterminate progress atom (Both) ----------
   A rotating ring for in-progress work. Reuses the button loading keyframes.
   Always pair with role="status" + an aria-label (or visually-hidden text) so it
   is announced; colour is never the only signal. Respects reduced-motion. */
.spinner {
  display: inline-block;
  box-sizing: border-box;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--line);
  border-top-color: var(--primary);
  animation: btn-spin 600ms linear infinite;
  vertical-align: middle;
}
.spinner.is-sm {
  width: 16px;
  height: 16px;
}
.spinner.is-lg {
  width: 28px;
  height: 28px;
  border-width: 3px;
}
.dark-surface .spinner {
  border-color: var(--tint-on-dark-strong);
  border-top-color: var(--lifted-orange);
}
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation-duration: 1400ms;
  }
}

/* ---------- WHITE BUTTON — the light action button (Both) ---------- */
.btn-white {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-sizing: border-box;
  min-height: var(--control-height-md);
  padding: 0 18px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 14px;
  line-height: 1;
  white-space: nowrap;
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  background: var(--surface-elevated);
  color: var(--ink);
  box-shadow: var(--dp-shadow);
  transition: box-shadow 150ms;
}
.btn-white:hover {
  box-shadow: var(--dp-shadow-hi);
}
.btn-white:active {
  transform: translateY(0.5px);
}
.btn-white:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.btn-white .mdi {
  font-size: 18px;
}

/* ---------- MOBILE · BOTTOM NAV (App) ---------- */
.bottom-nav {
  display: flex;
  align-items: stretch;
  background: var(--surface-elevated);
  border-top: 0.5px solid var(--line);
  height: 76px;
  padding-bottom: var(--safe-bottom);
}
.bottom-nav .bn-tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--ink-3);
  padding-top: 6px;
}
.bottom-nav .bn-tab .lab {
  font: 500 11px / 1 var(--font-body);
  letter-spacing: 0.01em;
}
.bottom-nav .bn-tab.on {
  color: var(--primary-press);
}
.bottom-nav .bn-tab:focus-visible {
  outline: none;
  box-shadow: inset var(--focus-ring-accent-strong);
  border-radius: 12px;
}
.dark-surface .bottom-nav {
  background: var(--petrol-deep);
  border-top-color: var(--tint-on-dark-soft);
}
.dark-surface .bottom-nav .bn-tab {
  color: var(--text-on-dark-muted);
}
.dark-surface .bottom-nav .bn-tab.on {
  color: var(--lifted-orange);
}

/* ---------- MOBILE · TOAST (App) — tone chip + optional Undo ---------- */
.toastbar {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  width: max-content;
  max-width: 360px;
  min-width: 200px;
  padding: 12px 16px 12px 12px;
  border-radius: 18px;
  background: var(--surface-elevated);
  box-shadow:
    0 1px 2px rgba(20, 30, 25, 0.06),
    0 16px 34px -14px rgba(20, 30, 25, 0.42);
  color: var(--ink);
}
.toastbar .tb-icon {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--surface-elevated);
}
.toastbar .tb-icon .mdi {
  font-size: 17px;
  font-variation-settings:
    'FILL' 1,
    'wght' 500,
    'GRAD' 0,
    'opsz' 20;
}
.toastbar .tb-msg {
  flex: 1;
  min-width: 0;
  font: 600 14px / 1.3 var(--font-body);
  color: var(--ink);
}
.toastbar .tb-undo {
  flex-shrink: 0;
  background: none;
  border: none;
  cursor: pointer;
  font: 700 13px / 1 var(--font-body);
  color: var(--primary-press);
  padding: 4px 2px 4px 8px;
}
.toastbar.success .tb-icon {
  background: var(--success);
}
.toastbar.info .tb-icon {
  background: var(--info);
}
.toastbar.warning .tb-icon {
  background: var(--warning);
}
.toastbar.error .tb-icon {
  background: var(--error);
}

/* ---------- MOBILE · OVERVIEW TILES (App) — horizontal-scroll KPI tiles ---------- */
.metric-tiles {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  padding: 4px 2px 8px;
  scrollbar-width: none;
}
.metric-tiles::-webkit-scrollbar {
  height: 0;
}
.metric-tile {
  flex: 0 0 auto;
  width: 132px;
  min-height: 150px;
  border-radius: 20px;
  padding: 17px 15px;
  background: var(--surface-elevated);
  box-shadow: var(--dp-shadow);
  display: flex;
  flex-direction: column;
  cursor: pointer;
  transition: box-shadow 150ms;
}
.metric-tile:hover {
  box-shadow: var(--dp-shadow-hi);
}
.metric-tile .mt-ic {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--tint-accent-medium);
  color: var(--primary-press);
}
.metric-tile .mt-ic.amber {
  background: var(--tint-amber-medium);
  color: var(--amber-text);
}
.metric-tile .mt-ic.violet {
  background: var(--tint-violet-medium);
  color: var(--violet-text);
}
.metric-tile .mt-ic.teal {
  background: var(--tint-teal-medium);
  color: var(--teal-text);
}
.metric-tile .mt-val {
  margin-top: auto;
  font: 700 22px / 1 var(--font-display);
  letter-spacing: -0.025em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.metric-tile .mt-val .u {
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-3);
  margin-left: 2px;
}
.metric-tile .mt-label {
  font: 600 12.5px / 1.2 var(--font-body);
  color: var(--ink);
  margin-top: 9px;
}
.metric-tile .mt-cap {
  font: 400 11px / 1.2 var(--font-body);
  color: var(--ink-3);
  margin-top: 4px;
}
.dark-surface .metric-tile {
  background: var(--tint-on-dark-faint);
  box-shadow: none;
  border: 0.5px solid var(--tint-on-dark-soft);
}
.dark-surface .metric-tile .mt-val,
.dark-surface .metric-tile .mt-label {
  color: var(--surface-elevated);
}
.dark-surface .metric-tile .mt-cap {
  color: var(--text-on-dark-secondary);
}
/* Icon discs on petrol: the light pairs (0.14 accessory tint + dark ink glyph) go
   dull to invisible on a dark surface, so the fill lifts and the glyph flips to
   the on-dark accessory colour — every pair clears 3:1 as a graphic on petrol. */
.dark-surface .metric-tile .mt-ic {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .metric-tile .mt-ic.amber {
  background: var(--tint-amber-on-dark);
  color: var(--amber-on-dark);
}
.dark-surface .metric-tile .mt-ic.violet {
  background: var(--tint-violet-on-dark);
  color: var(--violet-on-dark);
}
.dark-surface .metric-tile .mt-ic.teal {
  background: var(--tint-teal-on-dark);
  color: var(--teal-on-dark);
}

/* ---------- PLATFORM BADGE — flags an element Dashboard / App / Both ---------- */
.plat-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 3px 8px 3px 6px;
  border-radius: var(--radius-pill);
  vertical-align: middle;
}
.plat-badge .mdi {
  font-size: 13px;
}
.plat-badge.is-dashboard {
  background: var(--petrol-soft);
  color: var(--petrol);
}
.plat-badge.is-app {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
}
.plat-badge.is-both {
  background: var(--tint-neutral);
  color: var(--ink-2);
}
.dark-surface .plat-badge.is-dashboard {
  background: var(--tint-on-dark-medium);
  color: var(--surface-elevated);
}
.dark-surface .plat-badge.is-app {
  background: var(--tint-lifted-strong);
  color: var(--lifted-orange);
}
.dark-surface .plat-badge.is-both {
  background: var(--tint-on-dark-soft);
  color: rgba(255, 255, 255, 0.8);
}

/* =========================================================================
   v2.26 — SELECT-CARD FIX + MORE MOBILE/APP MEMBERS
   Select cards borderless (match the base card). New: avatar, list row,
   stepper, status hero card, bottom sheet, wheel time picker, keypad.
   ========================================================================= */

/* ---------- AVATAR (atom · Both) ---------- */
.avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--petrol-soft);
  color: var(--petrol);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0.04em;
  flex-shrink: 0;
  box-shadow: inset 0 0 0 1px rgba(20, 30, 40, 0.04);
}
.avatar.sm {
  width: 32px;
  height: 32px;
  font-size: 12px;
}
.avatar.lg {
  width: 60px;
  height: 60px;
  font-size: 20px;
}
/* Solid — a petrol disc with white initials. The default .avatar is a soft
   --petrol-soft tint with dark initials, which is right in a list or a comment
   thread where a dozen of them sit in a column and a dozen solid discs would read
   as a row of holes. It is wrong for the ONE avatar that is a persistent control:
   in the app header the profile has to hold its own against the page beneath it,
   and the product renders it solid. Added as a variant rather than changed on the
   base, so the 17 existing instances keep the treatment they were designed with.
   The initials are --surface-elevated on --petrol: 12.03:1. */
.avatar.solid {
  background: var(--petrol);
  color: var(--surface-elevated);
  box-shadow: none;
}
/* The petrol rule below LOOKS like a duplicate of .dark-surface .avatar and is not
   optional. v2.262 deleted it on exactly that reasoning and made the disc invisible:
   .avatar.solid and .dark-surface .avatar are BOTH specificity (0,2,0), and .solid is
   declared later, so on petrol .solid won and painted the disc --petrol on a --petrol
   header — 1:1, with .solid's box-shadow:none also removing the base's inset hairline,
   leaving floating initials and no disc. A duplicate of VALUES can still be
   load-bearing in the CASCADE.
   It keeps an inset hairline rather than box-shadow:none. A 0.12 white tint on petrol
   is a 1.44:1 boundary, and this disc is the profile menu's target — 1.4.11 asks 3:1
   of a component boundary that carries meaning, so the ring supplies it while the
   fill stays subordinate to the petrol chrome around it. */
.dark-surface .avatar.solid {
  background: var(--tint-on-dark-medium);
  color: var(--surface-elevated);
  box-shadow: inset 0 0 0 1px var(--border-on-dark-emphasis);
}
.dark-surface .avatar {
  background: var(--tint-on-dark-medium);
  color: var(--surface-elevated);
  box-shadow: none;
}

/* ---------- LIST ROW (mobile card row · App) ---------- */
.list-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 15px 16px;
  background: var(--surface-elevated);
  border: none;
  border-left: 3px solid transparent;
  border-radius: 20px;
  box-shadow: var(--dp-shadow);
  cursor: pointer;
  transition:
    box-shadow 150ms,
    transform 150ms,
    border-color 150ms;
}
.list-row:hover {
  box-shadow: var(--dp-shadow-hi);
  transform: translateY(-1px);
}
.list-row.selected {
  border-left-color: var(--primary);
  box-shadow: var(--dp-shadow-hi);
}
.list-row .lr-name {
  font: 600 16px / 1.2 var(--font-body);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.list-row .lr-meta {
  font: 500 13px / 1.2 var(--font-body);
  color: var(--ink-3);
  margin-top: 5px;
}
.list-row .lr-trail {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  text-align: right;
}
.list-row .lr-hours {
  font: 700 18px / 1 var(--font-display);
  letter-spacing: -0.014em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.list-row .lr-hours.live {
  color: var(--success-text);
}
.dark-surface .list-row {
  background: rgba(255, 255, 255, 0.05);
  box-shadow: none;
  border: 0.5px solid var(--tint-on-dark-soft);
  border-left: 3px solid transparent;
}
.dark-surface .list-row.selected {
  border-left-color: var(--lifted-orange);
}
.dark-surface .list-row .lr-name {
  color: var(--surface-elevated);
}
.dark-surface .list-row .lr-meta {
  color: var(--text-on-dark-muted);
}
.dark-surface .list-row .lr-hours {
  color: var(--surface-elevated);
}

/* ---------- STEPPER (Both) ---------- */
.stepper {
  display: inline-flex;
  align-items: center;
  border-radius: 14px;
  overflow: hidden;
  background: var(--surface-elevated);
  box-shadow: inset 0 0 0 1px var(--line-2);
}
.stepper button {
  width: 42px;
  height: 42px;
  border: none;
  background: var(--surface-elevated);
  cursor: pointer;
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
}
.stepper button:hover {
  background: var(--petrol-soft);
}
.stepper button:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.stepper button:focus-visible {
  outline: none;
  box-shadow: inset var(--focus-ring-accent-strong);
}
.stepper .val {
  min-width: 70px;
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 700 16px / 1 var(--font-display);
  font-variant-numeric: tabular-nums;
  box-shadow:
    inset 1px 0 0 var(--line-2),
    inset -1px 0 0 var(--line-2);
}
.dark-surface .stepper {
  background: transparent;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}
.dark-surface .stepper button {
  background: transparent;
  color: var(--surface-elevated);
}
.dark-surface .stepper button:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .stepper .val {
  color: var(--surface-elevated);
  box-shadow:
    inset 1px 0 0 rgba(255, 255, 255, 0.16),
    inset -1px 0 0 rgba(255, 255, 255, 0.16);
}

/* ---------- BOTTOM SHEET (App) ---------- */
.sheet-scrim {
  position: absolute;
  inset: 0;
  background: rgba(14, 41, 53, 0.55);
  z-index: 30;
  display: flex;
  align-items: flex-end;
}
.sheet {
  background: var(--surface-elevated);
  width: 100%;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}
.sheet-handle {
  width: 36px;
  height: 4px;
  background: rgba(0, 0, 0, 0.18);
  border-radius: 999px;
  margin: 10px auto 4px;
  flex-shrink: 0;
}
.sheet-header {
  padding: 6px 20px 14px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  flex-shrink: 0;
  border-bottom: 0.5px solid var(--line-2);
}
.sheet-eyebrow {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 4px;
}
.sheet-title {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.018em;
  line-height: 1.1;
  color: var(--ink);
}
.sheet-body {
  padding: 16px 20px;
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}
.sheet-footer {
  padding: 14px 20px;
  padding-bottom: calc(14px + var(--safe-bottom));
  background: var(--surface-elevated);
  border-top: 0.5px solid var(--line);
}
.dark-surface .sheet {
  background: var(--petrol-deep);
  border-top: 0.5px solid var(--tint-on-dark-soft);
}
.dark-surface .sheet-header {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .sheet-title {
  color: var(--surface-elevated);
}
.dark-surface .sheet-footer {
  background: var(--petrol-deep);
  border-top-color: var(--tint-on-dark-soft);
}
/* A drag handle is a UI graphic, so 1.4.11 wants 3:1 — 0.30 white measures 2.48:1 on
   petrol and fails. 0.45 is the value the border scale already documents as the minimum
   visible boundary on petrol, and measures 3.70:1 (v2.357). */
.dark-surface .sheet-handle {
  background: var(--border-on-dark-hover);
}

/* ---------- CALLOUT CARD ----------
   Points AT something and tells you about it. The first-time experience uses two
   forms: a "what's new" callout beside the nav item it renames, and a tour tip
   beside each feature it teaches.

   NOT A DIALOG, which is why it is no longer named one. A dialog asks — it puts a
   decision in front of the reader and waits. This ANNOUNCES: there is nothing to
   decide, and dismissing it is the whole interaction. Naming it a dialog invited
   aria-modal on both forms, when only the announcing one blocks.

   Not a .tooltip either: it carries a heading, a dismiss and its own actions, and
   it takes focus. A tooltip is a label — no buttons, nothing to commit. If it can
   be acted on, it is this.

   It REUSES the .modal anatomy (header, body, footer, close, title) because it is
   the same card, and that reuse is the reason the class stays .modal.is-callout
   rather than forking a second set of parts. Two declarations are undone from the
   centred .modal: transform:none, because the scrim's translate fights the host's
   coordinates, and overflow:visible, because the beak hangs outside the box.

   The announcing form still needs a SCRIM — what is behind it must be visibly out
   of reach — plus a .modal-spotlight on the target: the ring says "this is the
   thing I mean", which a beak alone cannot carry across a busy screen. The tour
   tip does not block, so it takes role="dialog" WITHOUT aria-modal. */
.modal.is-callout,
.modal.is-anchored {
  position: fixed;
  z-index: 322;
  width: min(392px, calc(100vw - 32px));
  max-height: none;
  transform: none;
  overflow: visible;
}
.modal.is-callout.is-tip,
.modal.is-anchored.is-tip {
  width: min(340px, calc(100vw - 32px));
}
/* .is-anchored is the v2.268 name, kept as an alias so the FTU prototype does not
   break. Use .is-callout. */
/* The beak. Drawn with borders in the surface colour, so it reads as the card's
   own edge rather than an applied decoration — and so it needs no second colour
   to maintain. One class per side; the side names where the beak SITS, which is
   the opposite of where the target is.
   ON PETROL the beak takes --petrol-deep, because that is what .dark-surface
   .modal is actually filled with. v2.268 wrote --petrol-soft here, chosen by its
   NAME: it is #EDF2F5, a light blue-grey tint, so the beak rendered as a bright
   near-white spike out of a #0E2935 card — the exact opposite of the rule this
   comment states. Match a token's VALUE to the surface it lands on. */
.modal-beak {
  position: absolute;
  width: 0;
  height: 0;
  border: 11px solid transparent;
}
.modal-beak.is-left {
  left: -11px;
  border-right-color: var(--surface-elevated);
  border-left-width: 0;
}
.modal-beak.is-right {
  right: -11px;
  border-left-color: var(--surface-elevated);
  border-right-width: 0;
}
.modal-beak.is-top {
  top: -11px;
  border-bottom-color: var(--surface-elevated);
  border-top-width: 0;
}
.modal-beak.is-bottom {
  bottom: -11px;
  border-top-color: var(--surface-elevated);
  border-bottom-width: 0;
}
.dark-surface .modal-beak.is-left {
  border-right-color: var(--petrol-deep);
}
.dark-surface .modal-beak.is-right {
  border-left-color: var(--petrol-deep);
}
.dark-surface .modal-beak.is-top {
  border-bottom-color: var(--petrol-deep);
}
.dark-surface .modal-beak.is-bottom {
  border-top-color: var(--petrol-deep);
}
/* Eyebrow — the kicker above the title ("WHAT'S NEW"). Only where the card
   announces a CHANGE rather than asking for something: a confirm or a form has
   nothing to put here, and an eyebrow on one reads as a category label nobody
   asked for. */
/* The eyebrow carries WHERE YOU ARE — the change on .is-callout, the tour position
   on .is-tip — and sits above the title in both, so one component does not read as
   two. Both forms also carry the .modal-icon. */
.modal-eyebrow {
  margin-bottom: 4px;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--accent-text-on-tint);
}
.dark-surface .modal-eyebrow {
  color: var(--lifted-orange);
}
/* Step dots — progress through a multi-step callout or dialog. The current step is WIDER
   rather than merely a different colour, so position survives without colour
   (1.4.1) and stays legible at 7px. Dots are a summary, never the control: past
   about five steps use a labelled counter instead, because nobody counts eight
   dots. */
.modal-steps {
  display: flex;
  align-items: center;
  gap: 7px;
}
.modal-step-dot {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  transition:
    width 200ms,
    background 200ms;
}
.modal-step-dot.is-current {
  width: 20px;
  background: var(--primary);
}
.modal-step-dot.is-done {
  background: var(--accent-border-soft);
}
.dark-surface .modal-step-dot {
  background: var(--border-on-dark-hover);
}
.dark-surface .modal-step-dot.is-current {
  background: var(--lifted-orange);
}
.dark-surface .modal-step-dot.is-done {
  background: var(--tint-lifted-strong);
}
/* Spotlight — the cut-out that isolates the callout's target. One huge
   spread on the box-shadow, so the scrim IS the ring's shadow: no second
   overlay to keep in sync, and the hole cannot drift from the outline.
   .is-quiet for a target that only needs marking, not isolating. */
/* pointer-events:none, because pointer-events:auto could never block anything here:
   the scrim is a BOX-SHADOW, which is not hit-testable, so auto left the dimmed area
   fully clickable while swallowing clicks on the one element the ring exists to point
   at — the behaviour inverted. The spotlight MARKS; the modal's own scrim element is
   what blocks. */
.modal-spotlight {
  position: fixed;
  z-index: 330;
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 9999px var(--scrim);
  outline: 2px solid var(--primary);
  pointer-events: none;
}
/* Quiet marks a target without isolating it: the tint fill and 1px accent border
   stay, the scrim and the ring go. It is NOT invisible — v2.282 briefly read its
   outline:none as "no marker" and rewrote the rule to a bare box-shadow:none,
   dropping the fill, the border and the pointer-events guard with it. The marker
   here is the border, not an outline. */
.modal-spotlight.is-quiet {
  box-shadow: none;
  background: var(--tint-accent-soft);
  border: 1px solid var(--primary);
  outline: none;
  pointer-events: none;
}
/* On petrol the scrim CANNOT carry the isolation, and that is arithmetic rather
   than taste: a scrim isolates by subtraction, and --petrol-deep rgb(14,41,53) has
   almost nothing left to subtract. Measured, 50% black dims it to rgb(7,21,27) — a
   1.23:1 step — and even a PURE BLACK scrim reaches only 1.39:1, where the same 50%
   scrim on paper gives 3.94:1. Raising the opacity cannot fix it; 1.39 is the
   ceiling.
   So on petrol the target is marked by ADDITION: a 3px --lifted-orange ring
   (6.48:1 against --petrol-deep) plus a soft halo, with the scrim kept only as a
   supporting hint. Same answer the v2.197 petrol trigger and the v2.213 overlay
   glyph reached — when a ground cannot carry the signal, the signal moves to
   something that can. */
.dark-surface .modal-spotlight,
.modal-spotlight.on-dark {
  outline: 3px solid var(--lifted-orange);
  box-shadow:
    0 0 0 9999px var(--scrim),
    0 0 0 7px var(--tint-lifted-soft);
}
/* ONE petrol-quiet rule covering BOTH scoped forms, carrying the resets. Until
   v2.283 there were two byte-different copies of this selector and only the
   descendant form reset anything — so .modal-spotlight.is-quiet.on-dark fell through
   to the un-reset copy and lost on source order to .modal-spotlight.on-dark, taking
   the scrim, halo and ring it is defined to drop. Both (0,2,0): a scoped rule must
   restate what the variant beside it turns off, and it must cover every scope form. */
.dark-surface .modal-spotlight.is-quiet,
.modal-spotlight.is-quiet.on-dark {
  box-shadow: none;
  outline: none;
  background: var(--tint-lifted-soft);
  border-color: var(--lifted-orange);
}

@media (prefers-reduced-motion: reduce) {
  .modal-step-dot {
    transition: none;
  }
}

/* ══════════════════ APP · TOUCH FORM FIELD ══════════════════
   The App counterpart to .field. Dashboard fields run 36-38px for density;
   on a phone every control clears the 44px touch minimum and labels stack
   above so nothing relies on hover to explain itself. */
.app-field {
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.app-field > label,
.app-field .app-field-label {
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.app-input {
  font-family: var(--font-body);
  font-size: var(--text-input-min);
  min-height: var(--touch-comfortable);
  padding: 12px 14px;
  width: 100%;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--surface-elevated);
  color: var(--ink);
  outline: none;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out);
}
.app-input::placeholder {
  color: var(--ink-3);
}
.app-input:focus {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.app-input.is-error {
  border-color: var(--error);
  box-shadow: var(--focus-ring-error);
}
.app-input:disabled {
  background: var(--paper);
  color: var(--ink-3);
  cursor: not-allowed;
}
.app-field-help {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-3);
}
.app-field-help.is-error {
  color: var(--error-text);
}
.dark-surface .app-field > label,
.dark-surface .app-field .app-field-label {
  color: var(--surface-elevated);
}
.dark-surface .app-input {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-strong);
  color: var(--surface-elevated);
}
.dark-surface .app-input::placeholder {
  color: var(--text-on-dark-muted);
}
.dark-surface .app-input:focus {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .app-field-help {
  color: var(--text-on-dark-secondary);
}

/* ══════════════════ APP · SELECT (trigger + sheet list) ══════════════════
   A phone cannot host the dashboard's floating .select-panel: it would be
   clipped, mis-anchored and too dense to hit. The trigger opens a Bottom
   sheet whose options are 52px rows with a trailing check. */
.app-select {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: var(--touch-comfortable);
  padding: 12px 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--surface-elevated);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  text-align: left;
  cursor: pointer;
}
.app-select > span:first-child {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.app-select.is-placeholder > span:first-child {
  color: var(--ink-3);
}
.app-select > .mdi {
  font-size: 22px;
  color: var(--ink-3);
  flex-shrink: 0;
}
.app-select:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.app-option-list {
  display: flex;
  flex-direction: column;
}
.app-option {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: var(--touch-row);
  padding: 12px 4px;
  border: none;
  background: none;
  border-bottom: 0.5px solid var(--line-2);
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
.app-option:last-child {
  border-bottom: none;
}
.app-option > .app-option-label {
  flex: 1;
  min-width: 0;
}
.app-option > .mdi {
  font-size: 22px;
  color: var(--primary);
  flex-shrink: 0;
}
.app-option.is-selected {
  font-weight: 600;
  color: var(--primary-press);
}
.app-option:active {
  background: var(--tint-neutral);
}
.dark-surface .app-select {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-strong);
  color: var(--surface-elevated);
}
.dark-surface .app-select > .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .app-option {
  color: var(--surface-elevated);
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .app-option > .mdi {
  color: var(--lifted-orange);
}
.dark-surface .app-option.is-selected {
  color: var(--lifted-orange);
}

/* ══════════════════ APP · FILTER SHEET (trigger + bottom drawer of filter groups) ══════════════════
   The App counterpart to the dashboard Filter toolbar: the pill row collapses
   into ONE Filters button (with an applied-count badge) that opens a Bottom
   sheet from the bottom edge. Groups inside reuse the 52px .app-option row —
   trailing check for single-select, leading .app-option-check box for
   multi-select. The footer commits: Clear all + Show N results. */
.app-filter-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 15px;
  min-height: var(--touch-comfortable);
  padding: 11px 16px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  background: var(--surface-elevated);
  color: var(--ink);
  cursor: pointer;
  transition:
    background var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}
.app-filter-button > .mdi {
  font-size: 22px;
  color: var(--ink-2);
  flex-shrink: 0;
}
.app-filter-button:active {
  background: var(--tint-neutral);
}
.app-filter-button:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.app-filter-button.is-active {
  border-color: var(--primary);
  color: var(--primary-press);
}
.app-filter-button.is-active > .mdi {
  color: var(--primary-press);
}
.app-filter-count {
  display: grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 700;
  background: var(--tint-accent-soft);
  color: var(--accent-text-on-tint);
}
.sheet-group-label {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 16px 0 2px;
}
.sheet-group-label:first-child {
  margin-top: 0;
}
.app-option-check {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  border-radius: var(--radius-xs);
  border: 1.5px solid var(--border-strong);
  background: var(--surface-elevated);
  color: transparent;
  transition:
    background var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}
.app-option-check > .mdi {
  font-size: 16px;
  color: inherit;
}
.app-option.is-checked {
  font-weight: 600;
}
.app-option.is-checked .app-option-check {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--surface-elevated);
}
.sheet-footer-split {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px;
  align-items: center;
}
.dark-surface .app-filter-button {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-strong);
  color: var(--surface-elevated);
}
.dark-surface .app-filter-button > .mdi {
  color: var(--text-on-dark-secondary);
}
.dark-surface .app-filter-button:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .app-filter-button.is-active {
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}
.dark-surface .app-filter-button.is-active > .mdi {
  color: var(--lifted-orange);
}
/* Count badge on petrol: opaque lifted-orange fill + petrol-deep numeral (6.48:1),
   the same pairing the selected chip's count uses — NOT lifted-orange text on
   --tint-lifted-soft, a documented forbidden pairing. */
.dark-surface .app-filter-count {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .sheet-group-label {
  color: var(--text-on-dark-muted);
}
.dark-surface .app-option-check {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-strong);
}
.dark-surface .app-option.is-checked {
  color: var(--surface-elevated);
}
.dark-surface .app-option.is-checked .app-option-check {
  background: var(--lifted-orange);
  border-color: var(--lifted-orange);
  color: var(--petrol-deep);
}
/* Sheet-footer-scoped petrol treatment for the quiet Clear-all — .btn-tertiary has
   no global dark rule; scoping here avoids touching the shared button component. */
.dark-surface .sheet-footer .btn-tertiary {
  color: var(--text-on-dark-secondary);
}
.dark-surface .sheet-footer .btn-tertiary:hover,
.dark-surface .sheet-footer .btn-tertiary.s-hover {
  background: var(--tint-on-dark-faint);
  color: var(--surface-elevated);
}

/* ══════════════════ APP · DATE PICKER ══════════════════
   The dashboard picker is a 560px two-pane popover — impossible on a phone.
   The App form is a sheet: preset chips, then one month at 44px touch cells. */
.app-calendar {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.app-cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.app-cal-title {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.app-cal-nav {
  width: var(--touch-min);
  height: var(--touch-min);
  display: grid;
  place-items: center;
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-elevated);
  color: var(--ink-2);
  cursor: pointer;
}
.app-cal-nav:active {
  background: var(--paper);
}
.app-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.app-cal-dow {
  text-align: center;
  padding: 4px 0;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.app-cal-day {
  min-height: var(--touch-min);
  display: grid;
  place-items: center;
  border: none;
  background: none;
  border-radius: var(--radius);
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--ink);
  cursor: pointer;
}
.app-cal-day.is-muted {
  color: var(--ink-3);
}
.app-cal-day.is-today {
  box-shadow: inset 0 0 0 1px var(--primary);
  font-weight: 700;
}
.app-cal-day.in-range {
  background: var(--tint-accent-medium);
  color: var(--primary-press);
  border-radius: 0;
}
.app-cal-day.range-start {
  background: var(--primary);
  color: var(--surface-elevated);
  font-weight: 700;
  border-radius: var(--radius) 0 0 var(--radius);
}
.app-cal-day.range-end {
  background: var(--primary);
  color: var(--surface-elevated);
  font-weight: 700;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.app-cal-day.is-today.range-start,
.app-cal-day.is-today.range-end {
  box-shadow: none;
}
.app-cal-presets {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: none;
}
.app-cal-presets::-webkit-scrollbar {
  display: none;
}
.app-cal-preset {
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 14px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  border: 0.5px solid var(--line);
  background: var(--surface-elevated);
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
}
.app-cal-preset.is-selected {
  background: var(--tint-accent-medium);
  border-color: var(--accent-border-soft);
  color: var(--primary-press);
  font-weight: 600;
}
.dark-surface .app-cal-title {
  color: var(--surface-elevated);
}
.dark-surface .app-cal-nav {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-medium);
  color: var(--surface-elevated);
}
.dark-surface .app-cal-dow {
  color: var(--text-on-dark-muted);
}
.dark-surface .app-cal-day {
  color: var(--surface-elevated);
}
.dark-surface .app-cal-day.is-muted {
  color: var(--text-on-dark-muted);
}
.dark-surface .app-cal-day.is-today {
  box-shadow: inset 0 0 0 1px var(--lifted-orange);
  color: var(--lifted-orange);
}
.dark-surface .app-cal-day.in-range {
  background: var(--tint-lifted-medium);
  color: var(--surface-elevated);
}
.dark-surface .app-cal-day.range-start,
.dark-surface .app-cal-day.range-end {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .app-cal-preset {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-medium);
  color: var(--text-on-dark-secondary);
}
.dark-surface .app-cal-preset.is-selected {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}
/* One-day range AND the in-between state while the end is unpicked (the start cell
   carries both classes until the end lands): start-only left radius + end-only right
   radius would otherwise compose lopsided — a lone endpoint is a fully rounded pill. */
.app-cal-day.range-start.range-end {
  border-radius: var(--radius);
}

/* ══════════════════ APP · ACTION SHEET ══════════════════
   The phone answer to a destructive confirm. A Modal is wrong here: it
   centres a desktop dialog over a thumb-driven surface. Actions stack from
   the bottom, the destructive one is named and red, Cancel is separated. */
.action-sheet {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-bottom: var(--safe-bottom);
}
.action-sheet-group {
  display: flex;
  flex-direction: column;
  background: var(--surface-elevated);
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 0.5px solid var(--line);
}
.action-sheet-title {
  padding: 14px 18px;
  border-bottom: 0.5px solid var(--line-2);
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-3);
  text-align: center;
}
.action-sheet-item {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: var(--touch-row-tall);
  padding: 14px 18px;
  border: none;
  background: none;
  border-bottom: 0.5px solid var(--line-2);
  font-family: var(--font-body);
  font-size: 17px;
  color: var(--ink);
  cursor: pointer;
}
.action-sheet-item:last-child {
  border-bottom: none;
}
.action-sheet-item:active {
  background: var(--tint-neutral);
}
.action-sheet-item .mdi {
  font-size: 21px;
  color: var(--ink-3);
}
.action-sheet-item.is-danger {
  color: var(--error-text);
  font-weight: 600;
}
.action-sheet-item.is-danger .mdi {
  color: var(--error-text);
}
.action-sheet-item.is-primary {
  color: var(--primary-press);
  font-weight: 600;
}
.action-sheet-item.is-primary .mdi {
  color: var(--primary);
}
.action-sheet-item:disabled {
  color: var(--ink-3);
  cursor: not-allowed;
}
.action-sheet-cancel {
  min-height: var(--touch-row-tall);
  width: 100%;
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-elevated);
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
}
.action-sheet-cancel:active {
  background: var(--tint-neutral);
}
.dark-surface .action-sheet-group,
.dark-surface .action-sheet-cancel {
  background: var(--petrol-mid);
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .action-sheet-title {
  color: var(--text-on-dark-secondary);
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .action-sheet-item {
  color: var(--surface-elevated);
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .action-sheet-item .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .action-sheet-item.is-danger,
.dark-surface .action-sheet-item.is-danger .mdi {
  color: var(--error-on-dark);
}
.dark-surface .action-sheet-item.is-primary {
  color: var(--lifted-orange);
}
.dark-surface .action-sheet-item.is-primary .mdi {
  color: var(--lifted-orange);
}
.dark-surface .action-sheet-cancel {
  color: var(--surface-elevated);
}

/* ══════════════════ APP · HEADER BAR ══════════════════
   The screen's top chrome: an optional back affordance, the screen title, and
   up to two trailing actions. 56px tall so it clears the touch minimum without
   eating the screen; .is-large stacks a display-size title beneath the row for
   a top-level screen. Sticky, with a hairline that appears only once scrolled. */
.app-header {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: var(--touch-row-tall);
  padding: 8px 6px 8px 6px;
  padding-top: calc(8px + var(--safe-top));
  padding-left: calc(6px + var(--safe-left));
  padding-right: calc(6px + var(--safe-right));
  background: var(--surface-elevated);
  position: sticky;
  top: 0;
  z-index: 40;
}
.app-header.is-scrolled {
  border-bottom: 0.5px solid var(--line);
}
.app-header-action {
  width: var(--touch-min);
  height: var(--touch-min);
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border: none;
  background: none;
  border-radius: var(--radius-md);
  color: var(--ink-2);
  cursor: pointer;
}
.app-header-action .mdi {
  font-size: 24px;
}
.app-header-action:active {
  background: var(--tint-neutral);
}
.app-header-action:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.app-header-title {
  flex: 1;
  min-width: 0;
  padding: 0 6px;
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--ink);
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.app-header-title.is-start {
  text-align: left;
}
.app-header-large {
  padding: 2px 16px 14px;
  background: var(--surface-elevated);
}
.app-header-large .app-header-large-title {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.022em;
  line-height: 1.1;
  color: var(--ink);
}
.app-header-large .app-header-large-sub {
  margin-top: 3px;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--ink-3);
}
/* Screen description — one short paragraph under the header row for a top-level screen
   that needs a sentence of context. It explains the SCREEN, not the record: a pushed
   record screen never carries one. One or two lines, then stop. */
.app-header-note {
  margin: 0;
  padding: 2px 16px 12px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-3);
  background: var(--surface-elevated);
}
.dark-surface .app-header,
.dark-surface .app-header-large {
  background: var(--petrol);
}
.dark-surface .app-header.is-scrolled {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .app-header-action {
  color: var(--surface-elevated);
}
.dark-surface .app-header-action:active {
  background: var(--tint-on-dark-soft);
}
.dark-surface .app-header-action:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .app-header-title,
.dark-surface .app-header-large-title {
  color: var(--surface-elevated);
}
.dark-surface .app-header-large-sub {
  color: var(--text-on-dark-secondary);
}
.dark-surface .app-header-note {
  background: var(--petrol);
  color: var(--text-on-dark-secondary);
}

/* ══════════════════ APP · SEARCH ══════════════════
   Full-width search, not the dashboard's fixed 220px toolbar slot. Sits under
   the header; while focused a text Cancel appears beside it, because a phone
   has no Escape key and the clear × alone does not exit the search. */
.app-search-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px 12px;
  background: var(--surface-elevated);
}
.app-search {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--touch-min);
  padding: 0 12px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: var(--paper);
  color: var(--ink);
}
.app-search > .mdi {
  font-size: 20px;
  color: var(--ink-3);
  flex-shrink: 0;
}
.app-search input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: none;
  font-family: var(--font-body);
  font-size: var(--text-input-min);
  color: var(--ink);
}
.app-search input::placeholder {
  color: var(--ink-3);
}
.app-search.is-focused {
  background: var(--surface-elevated);
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.app-search-clear {
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--tint-neutral);
  color: var(--ink-2);
  cursor: pointer;
}
.app-search-clear .mdi {
  font-size: 16px;
}
.app-search-cancel {
  flex-shrink: 0;
  min-height: var(--touch-min);
  padding: 0 4px;
  border: none;
  background: none;
  font-family: var(--font-body);
  font-size: 16px;
  color: var(--primary);
  cursor: pointer;
}
.dark-surface .app-search-row {
  background: var(--petrol);
}
.dark-surface .app-search {
  background: var(--tint-on-dark-faint);
  color: var(--surface-elevated);
}
.dark-surface .app-search > .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .app-search input {
  color: var(--surface-elevated);
}
.dark-surface .app-search input::placeholder {
  color: var(--text-on-dark-muted);
}
.dark-surface .app-search.is-focused {
  background: var(--tint-on-dark-faint);
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .app-search-clear {
  background: var(--tint-on-dark-soft);
  color: var(--surface-elevated);
}
.dark-surface .app-search-cancel {
  color: var(--lifted-orange);
}

/* ══════════════════ APP · SWIPE ACTIONS ══════════════════
   A row's actions on a phone. There is no hover, so the dashboard's reveal-on-
   hover row actions have no equivalent: the actions live behind the row and are
   revealed by dragging it aside. .swipe-row-content is what moves; the action
   track sits underneath, right-anchored. .is-open is the revealed state (the
   demo state here — in product it follows the drag).
   NEVER swipe-only: every action must also be reachable from the row's overflow
   or an action sheet, because a swipe is invisible and undiscoverable. */
.swipe-row {
  position: relative;
  overflow: hidden;
  background: var(--surface-elevated);
  border-bottom: 0.5px solid var(--line-2);
}
.swipe-row-actions {
  position: absolute;
  inset: 0 0 0 auto;
  display: flex;
  align-items: stretch;
}
.swipe-action {
  width: 76px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  border: none;
  background: var(--petrol-mid);
  color: var(--surface-elevated);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.swipe-action .mdi {
  font-size: 21px;
}
.swipe-action.is-danger {
  background: var(--error);
}
.swipe-action.is-accent {
  background: var(--primary);
}
.swipe-action:active {
  filter: brightness(0.94);
}
.swipe-row-content {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 64px;
  padding: 12px 16px;
  background: var(--surface-elevated);
  transform: translateX(0);
  transition: transform var(--dur-base) var(--ease-out);
  touch-action: pan-y;
}
.swipe-row.is-open .swipe-row-content {
  transform: translateX(-152px);
}
.swipe-row.is-open-single .swipe-row-content {
  transform: translateX(-76px);
}
.dark-surface .swipe-row,
.dark-surface .swipe-row-content {
  background: var(--petrol-deep);
}
.dark-surface .swipe-row {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .swipe-action.is-danger {
  background: var(--error);
}
.dark-surface .swipe-action.is-accent {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
}
@media (prefers-reduced-motion: reduce) {
  .swipe-row-content {
    transition: none;
  }
}

/* ══════════════════ APP · PULL TO REFRESH ══════════════════
   The phone's answer to a Refresh button. A list has no toolbar to hang one on,
   so the gesture is the control — but the gesture is invisible, so the indicator
   must narrate every step: pull (idle) → release (armed) → spinning (refreshing)
   → a result the reader can trust. NEVER the only way to refresh: pair it with
   a Refresh item in the screen's overflow, or the action is unreachable by
   screen reader and switch control. */
.pull-refresh {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  overflow: hidden;
  background: var(--paper);
  color: var(--ink-3);
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 600;
  height: 64px;
}
.pull-refresh .pr-icon {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  color: var(--ink-3);
}
.pull-refresh .pr-icon .mdi {
  font-size: 18px;
  transition: transform var(--dur-base) var(--ease-out);
}
.pull-refresh.is-armed .pr-icon {
  border-color: var(--accent-border-soft);
  background: var(--tint-accent-medium);
  color: var(--primary-press);
}
.pull-refresh.is-armed .pr-icon .mdi {
  transform: rotate(180deg);
}
.pull-refresh.is-armed {
  color: var(--primary-press);
}
.pull-refresh.is-refreshing .pr-icon {
  border-color: var(--accent-border-soft);
}
.pull-refresh.is-done .pr-icon {
  background: var(--tint-success-bg);
  border-color: var(--success);
  color: var(--success-text);
}
.pull-refresh.is-done {
  color: var(--success-text);
}
.pull-refresh .pr-stamp {
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.dark-surface .pull-refresh {
  background: var(--petrol);
  color: var(--text-on-dark-muted);
}
.dark-surface .pull-refresh .pr-icon {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-medium);
  color: var(--text-on-dark-secondary);
}
.dark-surface .pull-refresh.is-armed .pr-icon {
  background: var(--tint-lifted-medium);
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}
.dark-surface .pull-refresh.is-armed {
  color: var(--lifted-orange);
}
.dark-surface .pull-refresh.is-done .pr-icon {
  background: var(--tint-success-on-dark);
  border-color: var(--success-on-dark);
  color: var(--success-on-dark);
}
.dark-surface .pull-refresh.is-done {
  color: var(--success-on-dark);
}
.dark-surface .pull-refresh .pr-stamp {
  color: var(--text-on-dark-muted);
}
@media (prefers-reduced-motion: reduce) {
  .pull-refresh .pr-icon .mdi {
    transition: none;
  }
}

/* ---------- APP · LIST ROW STATES ----------
   The touch states a .list-row needs and a table row does not. There is no
   hover on a phone, so :active is the only feedback the tap itself gives —
   it must be unmistakable. Selected and disabled never rely on colour alone. */
.list-row {
  min-height: var(--touch-row-tall);
}
.list-row.is-pressed,
.list-row:active {
  background: var(--tint-neutral);
}
.list-row.is-selected {
  background: var(--tint-accent-soft);
  border-left-color: var(--primary);
}
.list-row.is-disabled,
.list-row[aria-disabled='true'] {
  opacity: 0.55;
  pointer-events: none;
}
.list-row.is-disabled .lr-name {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.list-row:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.dark-surface .list-row.is-pressed,
.dark-surface .list-row:active {
  background: var(--tint-on-dark-soft);
}
.dark-surface .list-row.is-selected {
  background: var(--tint-lifted-medium);
  border-left-color: var(--lifted-orange);
}
.dark-surface .list-row:focus-visible {
  outline-color: var(--lifted-orange);
}

/* ---------- APP · EMPTY STATE (phone width) ----------
   Same empty state, re-proportioned for a phone: the illustration disc shrinks,
   the copy column narrows to a comfortable measure, and the action goes
   full-width so it is reachable with a thumb rather than centred and small. */
.empty-state.is-app {
  padding: var(--space-7) var(--space-5);
}
.empty-state.is-app .empty-actions {
  flex-direction: column;
  align-items: stretch;
  width: 100%;
  max-width: 320px;
  gap: var(--space-3);
}
.empty-state.is-app .empty-actions .btn {
  width: 100%;
  justify-content: center;
  min-height: var(--touch-comfortable);
}
.empty-state.is-app .empty-body {
  max-width: 30ch;
}

/* ---------- SLIDE-OVER (dashboard side panel / detail drawer) ----------
   The dashboard counterpart to the app bottom sheet: a right-anchored panel
   that slides in over a scrim to show a record in place without leaving the
   page. .slide-over-scrim + .slide-over (role="dialog" aria-modal); .is-open
   toggles both. Header holds the title + close (.icon-button); body scrolls;
   optional sticky .slide-over-foot for actions. Section labels reuse .kicker.
   Token-only, light + petrol. */
.slide-over-scrim {
  position: fixed;
  inset: 0;
  background: var(--scrim-petrol);
  z-index: 90;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
}
.slide-over-scrim.is-open {
  opacity: 1;
  pointer-events: auto;
}
.slide-over {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: 460px;
  max-width: 92vw;
  display: flex;
  flex-direction: column;
  background: var(--surface-elevated);
  z-index: 100;
  box-shadow: var(--shadow-slide-over);
  transform: translateX(100%);
  transition: transform var(--dur-base) var(--ease-out);
}
.slide-over.is-open {
  transform: translateX(0);
}
.slide-over-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-shrink: 0;
  padding: 20px 22px;
  border-bottom: 0.5px solid var(--line);
}
.slide-over-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.slide-over-sub {
  margin-top: 7px;
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--ink-3);
}
.slide-over-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.slide-over-foot {
  flex-shrink: 0;
  padding: 16px 22px;
  border-top: 0.5px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.dark-surface .slide-over {
  background: var(--petrol-deep);
}
.dark-surface .slide-over-header {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .slide-over-title {
  color: var(--surface-elevated);
}
.dark-surface .slide-over-sub {
  color: var(--text-on-dark-muted);
}
.dark-surface .slide-over-foot {
  border-top-color: var(--tint-on-dark-soft);
}
@media (prefers-reduced-motion: reduce) {
  .slide-over,
  .slide-over-scrim {
    transition: none;
  }
}

/* ---------- WHEEL TIME PICKER (App) ---------- */
.wheel {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: stretch;
  gap: 2px;
  height: 200px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-2xl);
  padding: 0 10px;
  overflow: hidden;
  max-width: 220px;
}
.wheel-band {
  position: absolute;
  left: 10px;
  right: 10px;
  top: 80px;
  height: 40px;
  border-radius: 10px;
  background: var(--petrol-soft);
  pointer-events: none;
}
.wheel-colon {
  align-self: center;
  font: 600 22px / 1 var(--font-display);
  color: var(--ink-3);
  padding-bottom: 2px;
}
.wheelcol {
  height: 100%;
  overflow-y: auto;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
  text-align: center;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 32%, #000 68%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 32%, #000 68%, transparent);
}
.wheelcol::-webkit-scrollbar {
  display: none;
}
.wheelcell {
  scroll-snap-align: center;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 500 23px / 1 var(--font-display);
  font-variant-numeric: tabular-nums;
  color: var(--ink-3);
  cursor: pointer;
}
.wheelcell.sel {
  color: var(--ink);
  font-weight: 700;
}
.dark-surface .wheel {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--tint-on-dark-medium);
}
.dark-surface .wheel-band {
  background: var(--tint-on-dark-soft);
}
/* 0.5 white measured 3.98:1 on the wheel's own fill — under 1.4.3 for the unselected
   values, which are readable text and not decoration. The tertiary token is 5.92:1
   here and removes the literal (v2.357). */
.dark-surface .wheelcell {
  color: var(--text-on-dark-muted);
}
.dark-surface .wheelcell.sel {
  color: var(--surface-elevated);
}

/* ---------- KEYPAD (App) ---------- */
.keypad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  max-width: 240px;
}
.keypad button {
  height: 48px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius);
  font: 600 20px / 1 var(--font-display);
  color: var(--ink);
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: center;
  justify-content: center;
}
.keypad button:hover {
  background: var(--petrol-soft);
}
.keypad button.blank {
  background: transparent;
  border: none;
  cursor: default;
}
.dark-surface .keypad button {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--tint-on-dark-medium);
  color: var(--surface-elevated);
}
.dark-surface .keypad button:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .keypad button.blank {
  background: transparent;
}

/* ---------- SEMANTIC / ALERT BANNERS ----------
   Inline state announcement: coloured circular icon + title/meta + optional
   action. Tones .success / .warning / .error / .info. Token-only, light + petrol. */
.alert-banner-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 14px;
}
.alert-banner {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  gap: 16px;
  align-items: center;
  padding: 14px 16px 14px 14px;
  border-radius: var(--radius-lg);
  border: 0.5px solid;
}
/* The disc fill is a --*-text / --*-press step, NOT the raw semantic colour:
   white on --warning is 2.15:1 and on --success 2.54:1, which fails even the 3:1
   graphic threshold, let alone 4.5:1 for the glyph inside. Same rule the Image
   marker follows. The tone is a CLASS, so the system owns it — set as an inline
   background by demo markup it silently reverted to the raw colour. */
.alert-banner-icon {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-circle);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--surface-elevated);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  background: var(--ink-2);
}
.alert-banner-icon .mdi {
  font-size: 20px;
}
.alert-banner-icon.is-success {
  background: var(--success-text);
}
.alert-banner-icon.is-warning {
  background: var(--warning-text);
}
.alert-banner-icon.is-error {
  background: var(--error-press);
}
.alert-banner-icon.is-info {
  background: var(--info-text);
}
.alert-banner.success > .alert-banner-icon {
  background: var(--success-text);
}
.alert-banner.warning > .alert-banner-icon {
  background: var(--warning-text);
}
.alert-banner.error > .alert-banner-icon {
  background: var(--error-press);
}
.alert-banner.info > .alert-banner-icon {
  background: var(--info-text);
}
.alert-banner-title {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  letter-spacing: -0.01em;
}
.alert-banner-meta {
  font-size: 12px;
  color: var(--ink-2);
  margin-top: 2px;
}
.alert-banner-action {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-weight: 700;
  padding: 8px 12px;
  border-radius: var(--radius);
  color: var(--ink);
  background: var(--tint-neutral);
  cursor: pointer;
  border: none;
}
.alert-banner.success {
  background: var(--tint-success-bg);
  border-color: var(--success-border);
}
.alert-banner.warning {
  background: var(--tint-warning-bg);
  border-color: var(--warning-border);
}
.alert-banner.error {
  background: var(--tint-error-bg);
  border-color: var(--error-border);
}
.alert-banner.info {
  background: var(--tint-info-bg);
  border-color: var(--info-border);
}
.dark-surface .alert-banner-title {
  color: var(--surface-elevated);
}
.dark-surface .alert-banner-meta {
  color: rgba(255, 255, 255, 0.65);
}
.dark-surface .alert-banner-action {
  color: var(--surface-elevated);
  background: var(--tint-on-dark-faint);
  border-color: rgba(255, 255, 255, 0.14);
}
.dark-surface .alert-banner-action:hover {
  background: var(--tint-on-dark-soft);
}
.dark-surface .alert-banner.success {
  background: rgba(16, 185, 129, 0.14);
  border-color: var(--success-border-on-dark);
}
.dark-surface .alert-banner.warning {
  background: var(--tint-warning-medium);
  border-color: var(--warning-border-on-dark);
}
.dark-surface .alert-banner.error {
  background: rgba(239, 68, 68, 0.14);
  border-color: var(--error-border-on-dark);
}
.dark-surface .alert-banner.info {
  background: rgba(59, 130, 246, 0.14);
  border-color: var(--info-border-on-dark);
}
.slide-over-close {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  background: transparent;
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--ink-3);
}
.slide-over-close .mdi {
  font-size: 20px;
}
.slide-over-close:hover {
  background: var(--paper);
  color: var(--ink);
}
.slide-over-close:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.slide-over-action {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 11px 14px;
  border-radius: var(--radius-md);
  cursor: pointer;
  text-align: left;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--border-default);
  color: var(--ink);
}
.slide-over-action .mdi {
  font-size: 17px;
  color: var(--ink-2);
}
.slide-over-action:hover {
  background: var(--paper);
}
.slide-over-action:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.slide-over-action.is-primary {
  background: var(--primary);
  border-color: transparent;
  color: var(--surface-elevated);
  box-shadow: var(--shadow-cta-glow);
}
.slide-over-action.is-primary .mdi {
  color: var(--surface-elevated);
}
.slide-over-action.is-primary:hover {
  background: var(--primary-press);
}
.dark-surface .slide-over-close {
  color: var(--text-on-dark-muted);
}
.dark-surface .slide-over-close:hover {
  background: var(--tint-on-dark-soft);
  color: var(--surface-elevated);
}
.dark-surface .slide-over-close:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .slide-over-action {
  background: var(--tint-on-dark-soft);
  border-color: var(--border-on-dark-medium);
  color: var(--surface-elevated);
}
.dark-surface .slide-over-action .mdi {
  color: var(--text-on-dark-muted);
}
.dark-surface .slide-over-action:hover {
  background: var(--tint-on-dark-medium);
}
.dark-surface .slide-over-action.is-primary {
  background: var(--lifted-orange);
  border-color: transparent;
  color: var(--petrol-deep);
  box-shadow: var(--shadow-cta-lifted);
}
.dark-surface .slide-over-action.is-primary .mdi {
  color: var(--petrol-deep);
}
.dark-surface .slide-over-action.is-primary:hover {
  background: var(--lifted-orange-press);
}
/* slide-over body content — record chip, colour-grade meter, metric tiles (light + petrol parity) */
.slide-over-record {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--ink-2);
  background: var(--tint-neutral);
  padding: 3px 9px;
  border-radius: var(--radius-xs);
}
.slide-over-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.slide-over-grade {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 12px;
}
.slide-over-grade-value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  color: var(--ink);
}
.slide-over-grade-value .unit {
  font-size: 12px;
  color: var(--ink-3);
  font-weight: 500;
  margin-left: 3px;
}
.slide-over-meter {
  height: 9px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--chart-track);
}
.slide-over-meter-fill {
  height: 100%;
  border-radius: inherit;
  background: var(--primary);
}
.slide-over-metrics {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 10px;
}
.slide-over-metric {
  background: var(--paper);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 12px 14px;
}
.slide-over-metric-label {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.slide-over-metric-value {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 19px;
  color: var(--ink);
  margin-top: 4px;
}
.slide-over-metric-value .unit {
  font-size: 11px;
  color: var(--ink-3);
  font-weight: 500;
  margin-left: 2px;
}
.dark-surface .slide-over-record {
  color: var(--text-on-dark-secondary);
  background: var(--tint-on-dark-medium);
}
.dark-surface .slide-over-grade-value {
  color: var(--text-on-dark);
}
.dark-surface .slide-over-grade-value .unit {
  color: var(--text-on-dark-muted);
}
.dark-surface .slide-over-meter {
  background: var(--tint-on-dark-medium);
}
.dark-surface .slide-over-meter-fill {
  background: var(--lifted-orange);
}
.dark-surface .slide-over-metric {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
.dark-surface .slide-over-metric-label {
  color: var(--text-on-dark-secondary);
}
.dark-surface .slide-over-metric-value {
  color: var(--text-on-dark);
}
.dark-surface .slide-over-metric-value .unit {
  color: var(--text-on-dark-muted);
}
.dark-surface .slide-over-body .kicker {
  color: var(--surface-elevated);
  background: var(--tint-lifted-medium);
}

/* ---------- MODAL (centered dialog) ----------
   The dashboard/app centered dialog for a focused create / edit / confirm task.
   .modal-scrim (dims + centers) \u2192 .modal \u2192 .modal-header (.modal-icon accent tile +
   .modal-heading[title/sub] + close .icon-button) \u00b7 .modal-body (scrolls) \u00b7
   .modal-footer (actions). .is-open toggles the scrim; .modal.is-danger recolours the
   header icon for destructive confirms, and .modal-body.is-confirm centers a
   .modal-confirm-disc + title + body. Reuses the .icon-button close and .btn footer
   actions (pair a destructive confirm with .btn-danger). Token-only, light + petrol. */
.modal-scrim {
  position: fixed;
  inset: 0;
  z-index: 320;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: var(--scrim);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
}
.modal-scrim.is-open {
  opacity: 1;
  pointer-events: auto;
}
.modal {
  width: min(520px, 100%);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background: var(--surface-elevated);
  border-radius: var(--radius-2xl);
  box-shadow: var(--dp-shadow-hi);
  overflow: hidden;
  transform: translateY(8px) scale(0.98);
  transition: transform var(--dur-base) var(--ease-out);
}
.modal-scrim.is-open .modal {
  transform: none;
}
/* Wide band — a dialog whose BODY is a grid (visualisation picker, configure-with-preview).
   Same anatomy, same 88vh cap; only the width band changes. Never for a confirm. */
.modal.is-wide {
  width: min(920px, 100%);
}
.modal-header {
  flex-shrink: 0;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 20px 22px 16px;
  border-bottom: 0.5px solid var(--line);
  background: var(--surface-elevated);
}
.modal-header.is-compact {
  align-items: center;
  padding-bottom: 20px;
}
.modal-icon {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: var(--tint-accent-medium);
  display: grid;
  place-items: center;
  color: var(--accent-text-on-tint);
}
.modal-icon .mdi {
  font-size: 20px;
}
.modal-heading {
  flex: 1;
  min-width: 0;
}
.modal-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.modal-sub {
  margin-top: 2px;
  font-family: var(--font-body);
  font-size: 13px;
  color: var(--ink-3);
}
.modal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.modal-body.is-confirm {
  align-items: center;
  text-align: center;
  padding: 32px 26px;
  gap: 6px;
}
.modal-footer {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 22px;
  border-top: 0.5px solid var(--line);
  background: var(--surface-elevated);
}
.modal-footer.is-split {
  justify-content: space-between;
}
/* Header controls are borderless (no icon-button outline): .modal-close (×, top-right,
   always dismisses) and .modal-back (arrow_back, shown in the top-left slot in place of
   the .modal-icon tile when the dialog is one step deeper — returns to the previous step).
   The × is MANDATORY (v2.384): every dialog carries .modal-close in its header, at the
   root and at every deeper step — a footer Cancel never substitutes for it, and
   scrim-click / Esc complement it rather than replace it. */
.modal-close,
.modal-back {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--ink-3);
  transition:
    background 150ms,
    color 150ms;
}
.modal-back {
  color: var(--ink-2);
}
.modal-close .mdi,
.modal-back .mdi {
  font-size: 20px;
}
.modal-close:hover,
.modal-back:hover {
  background: var(--paper);
  color: var(--ink);
}
.modal-close:focus-visible,
.modal-back:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.dark-surface .modal-close,
.dark-surface .modal-back {
  color: var(--text-on-dark-muted);
}
.dark-surface .modal-close:hover,
.dark-surface .modal-back:hover {
  background: var(--tint-on-dark-soft);
  color: var(--surface-elevated);
}
.dark-surface .modal-close:focus-visible,
.dark-surface .modal-back:focus-visible {
  outline-color: var(--lifted-orange);
}
.modal.is-danger .modal-icon,
.modal-icon.is-danger {
  background: var(--tint-error-bg);
  color: var(--error-text);
}
.modal-confirm-disc {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--tint-error-bg);
  display: grid;
  place-items: center;
  color: var(--error-text);
  margin-bottom: 8px;
}
.modal-confirm-disc .mdi {
  font-size: 28px;
}
.modal-confirm-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.modal-confirm-body {
  font-family: var(--font-body);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-2);
  margin: 2px 0 0;
  max-width: 380px;
}
.dark-surface .modal {
  background: var(--petrol-deep);
  border: 0.5px solid var(--tint-on-dark-soft);
}
.dark-surface .modal-header,
.dark-surface .modal-footer {
  background: transparent;
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .modal-title,
.dark-surface .modal-confirm-title {
  color: var(--surface-elevated);
}
.dark-surface .modal-sub {
  color: var(--text-on-dark-muted);
}
.dark-surface .modal-confirm-body {
  color: var(--text-on-dark-secondary);
}
.dark-surface .modal-icon {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .modal.is-danger .modal-icon,
.dark-surface .modal-icon.is-danger,
.dark-surface .modal-confirm-disc {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
@media (prefers-reduced-motion: reduce) {
  .modal,
  .modal-scrim {
    transition: none;
  }
}

/* ---------- MODAL · EDIT-FIELDS BODY ----------
   The record-edit layout used by "Edit <record>" dialogs (Load assessments →
   Edit): a two-column grid of labelled fields inside .modal-body. Two columns
   keep a long record on one screen without scrolling; the grid collapses to one
   column on narrow viewports and for any field that must run full width. */
.modal-field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px 18px;
}
.modal-field-grid > .field,
.modal-field-grid > .field-stack {
  max-width: none;
  margin-bottom: 0;
  gap: 7px;
}
.modal-field-grid > .field.is-full,
.modal-field-grid > .field-stack.is-full {
  grid-column: 1 / -1;
}
@media (max-width: 640px) {
  .modal-field-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- INFO TOOLTIP ----------
   A short explanatory bubble hung off an info affordance — the plain-language
   definition of a metric, a column, or a truncated label. Reveals on hover AND
   focus-visible (the trigger is a real button, so it is keyboard reachable);
   .is-open lets JS drive it on tap. Text only, never interactive content — a
   thing you can click inside is a menu or a popover, not a tooltip. */
.tooltip-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
}
.tooltip-trigger {
  appearance: none;
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.tooltip-trigger .mdi {
  font-size: 16px;
  line-height: 1;
}
.tooltip-trigger:hover {
  background: var(--tint-accent-soft);
  color: var(--primary-press);
}
.tooltip-trigger:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  box-shadow: var(--focus-ring-accent);
  color: var(--primary);
}
.tooltip {
  position: absolute;
  z-index: var(--z-tooltip);
  left: 50%;
  bottom: calc(100% + var(--tooltip-offset));
  width: max-content;
  max-width: var(--tooltip-max-width);
  white-space: normal;
  padding: var(--tooltip-pad-y) var(--tooltip-pad-x);
  background: var(--tooltip-bg);
  color: var(--tooltip-text);
  border-radius: var(--tooltip-radius);
  box-shadow: var(--tooltip-shadow);
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.45;
  text-align: left;
  text-transform: none;
  letter-spacing: 0;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translate(-50%, 4px);
  transition:
    opacity var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out),
    visibility 0s linear var(--dur-fast);
}
.tooltip::after {
  content: '';
  position: absolute;
  width: 8px;
  height: 8px;
  background: inherit;
  transform: rotate(45deg);
  left: 50%;
  margin-left: -4px;
  top: 100%;
  margin-top: -4px;
}
.tooltip-wrap:hover > .tooltip,
.tooltip-wrap:focus-within > .tooltip,
.tooltip.is-open {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition-delay: 0s;
}
/* placement — flip when the trigger sits near an edge */
.tooltip.is-bottom {
  bottom: auto;
  top: calc(100% + var(--tooltip-offset));
  transform: translate(-50%, -4px);
}
.tooltip.is-bottom::after {
  top: auto;
  margin-top: 0;
  bottom: 100%;
  margin-bottom: -4px;
}
.tooltip-wrap:hover > .tooltip.is-bottom,
.tooltip-wrap:focus-within > .tooltip.is-bottom,
.tooltip.is-bottom.is-open {
  transform: translate(-50%, 0);
}
.tooltip.is-left,
.tooltip.is-right {
  top: 50%;
  bottom: auto;
  left: auto;
}
.tooltip.is-left {
  right: calc(100% + var(--tooltip-offset));
  transform: translate(4px, -50%);
}
.tooltip.is-right {
  left: calc(100% + var(--tooltip-offset));
  transform: translate(-4px, -50%);
}
.tooltip.is-left::after {
  top: 50%;
  margin-top: -4px;
  left: 100%;
  margin-left: -4px;
}
.tooltip.is-right::after {
  top: 50%;
  margin-top: -4px;
  left: -4px;
  margin-left: 0;
}
.tooltip-wrap:hover > .tooltip.is-left,
.tooltip-wrap:focus-within > .tooltip.is-left,
.tooltip.is-left.is-open,
.tooltip-wrap:hover > .tooltip.is-right,
.tooltip-wrap:focus-within > .tooltip.is-right,
.tooltip.is-right.is-open {
  transform: translate(0, -50%);
}
/* size band — .is-wide for a two-line definition, .is-terse for a one-word label */
.tooltip.is-wide {
  max-width: var(--tooltip-max-width-wide);
}
.tooltip.is-terse {
  max-width: none;
  padding: 5px 9px;
  font-size: 12px;
  white-space: nowrap;
}
/* on dark (petrol) — bubble inverts to paper so it still lifts off the surface */
.dark-surface .tooltip {
  background: var(--tooltip-bg-on-dark);
  color: var(--tooltip-text-on-dark);
}
.dark-surface .tooltip-trigger {
  color: var(--text-on-dark-muted);
}
.dark-surface .tooltip-trigger:hover {
  background: var(--tint-on-dark-faint);
  color: var(--lifted-orange);
}
.dark-surface .tooltip-trigger:focus-visible {
  outline-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
  color: var(--lifted-orange);
}
@media (prefers-reduced-motion: reduce) {
  .tooltip {
    transition: none;
  }
}

/* ---------- FLOATING ACTION BUTTON — screen-level create (App) ----------
   The app's one SCREEN-LEVEL create action, floating above the content in the
   thumb corner. Solid accent circle (56px); tapping it either acts directly or
   speed-dials: the glyph rotates to × (.is-open), a scrim drops behind, and the
   labelled option pills stack above it, primary nearest the thumb. One per
   screen, create-verbs only — an edit or a filter belongs in the header or bar. */
.floating-action-stack {
  position: absolute;
  right: 16px;
  bottom: calc(20px + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
  z-index: 6;
}
.floating-action-scrim {
  position: fixed;
  inset: 0;
  background: var(--scrim-petrol);
  z-index: -1;
}
.floating-action-options {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  animation: floating-action-in 140ms var(--ease-out);
}
@keyframes floating-action-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.floating-action-button {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-circle);
  border: none;
  background: var(--primary);
  color: var(--surface-elevated);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--shadow-cta-glow);
}
.floating-action-button .mdi {
  font-size: 26px;
  transition: transform 140ms var(--ease-out);
}
.floating-action-button.is-open .mdi {
  transform: rotate(45deg);
}
.floating-action-button:active {
  transform: translateY(1px);
}
.floating-action-button:focus-visible,
.floating-action-pill:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.floating-action-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--touch-comfortable);
  padding: 0 18px;
  border-radius: var(--radius-pill);
  border: none;
  background: var(--primary);
  color: var(--surface-elevated);
  font-family: var(--font-body);
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-cta-glow);
}
.floating-action-pill .mdi {
  font-size: 20px;
}
.floating-action-pill:active {
  transform: translateY(1px);
}
.floating-action-pill.is-secondary {
  background: var(--surface-elevated);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  box-shadow: var(--dp-shadow-hi);
}
.dark-surface .floating-action-button,
.dark-surface .floating-action-pill {
  background: var(--lifted-orange);
  color: var(--petrol-deep);
  box-shadow: var(--shadow-cta-lifted);
}
.dark-surface .floating-action-button:focus-visible,
.dark-surface .floating-action-pill:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .floating-action-pill.is-secondary {
  background: var(--surface-elevated);
  color: var(--ink);
  border-color: var(--border-strong);
}
@media (prefers-reduced-motion: reduce) {
  .floating-action-options {
    animation: none;
  }
  .floating-action-button .mdi {
    transition: none;
  }
}

/* ---------- REPORT CARD — navigational catalog card (Both) ----------
   A route into one report/document: tinted icon tile, mono meta line, display
   title, one line of description. NOT a .select-card (which picks an option in
   place) — this one NAVIGATES. Trailing slot is a chevron when the whole card is
   the only action, or a .report-card-kebab when the item has its own menu — never
   both. .is-grid is the dashboard form: top-aligned in a card grid, 48px icon,
   optional .report-card-tags row. */
.report-card {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  background: var(--surface-elevated);
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
  padding: 16px 12px 16px 16px;
  cursor: pointer;
  font-family: var(--font-body);
  transition:
    border-color var(--dur-fast),
    background var(--dur-fast);
}
.report-card:hover {
  border-color: var(--border-stronger);
}
.report-card:active {
  border-color: var(--primary);
  background: var(--tint-accent-faint);
}
.report-card:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.report-card-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  flex-shrink: 0;
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.report-card-icon .mdi {
  font-size: 22px;
}
.report-card-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}
.report-card-meta {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.report-card-title {
  font-family: var(--font-display);
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: -0.014em;
  color: var(--ink);
}
.report-card-description {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink-2);
  text-wrap: pretty;
}
.report-card-chevron {
  color: var(--ink-3);
  flex-shrink: 0;
}
.report-card-chevron .mdi {
  font-size: 20px;
}
.report-card-kebab {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-circle);
  border: none;
  background: none;
  color: var(--ink-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.report-card-kebab .mdi {
  font-size: 20px;
}
.report-card-kebab:hover,
.report-card-kebab:active {
  background: var(--tint-neutral);
  color: var(--ink);
}
.report-card-kebab:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.report-card.is-grid {
  align-items: flex-start;
  gap: 16px;
  padding: 20px;
  transition:
    border-color var(--dur-fast),
    transform var(--dur-fast);
}
.report-card.is-grid:hover {
  border-color: var(--primary);
  transform: translateY(-2px);
}
.report-card.is-grid .report-card-icon {
  width: 48px;
  height: 48px;
}
.report-card.is-grid .report-card-icon .mdi {
  font-size: 26px;
}
.report-card.is-grid .report-card-title {
  font-size: 17px;
  letter-spacing: -0.016em;
}
.report-card.is-grid .report-card-description {
  font-size: 13px;
  line-height: 1.5;
}
.report-card-tags {
  display: flex;
  gap: 6px;
  margin-top: 8px;
  flex-wrap: wrap;
}
.report-card-tag {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: var(--paper);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 3px 7px;
  white-space: nowrap;
}
.dark-surface .report-card {
  background: var(--tint-on-dark-faint);
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .report-card:hover {
  border-color: var(--border-on-dark-hover);
}
.dark-surface .report-card:active {
  border-color: var(--lifted-orange);
  background: var(--tint-lifted-soft);
}
.dark-surface .report-card:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .report-card.is-grid:hover {
  border-color: var(--lifted-orange);
}
.dark-surface .report-card-icon {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .report-card-title {
  color: var(--surface-elevated);
}
.dark-surface .report-card-description {
  color: var(--text-on-dark-secondary);
}
.dark-surface .report-card-meta,
.dark-surface .report-card-chevron,
.dark-surface .report-card-kebab {
  color: var(--text-on-dark-muted);
}
.dark-surface .report-card-kebab:hover,
.dark-surface .report-card-kebab:active {
  background: var(--tint-on-dark-soft);
  color: var(--surface-elevated);
}
.dark-surface .report-card-tag {
  background: var(--tint-on-dark-faint);
  border-color: var(--tint-on-dark-soft);
  color: var(--text-on-dark-secondary);
}

/* ---------- QR SHARE — share-code display (Both) ----------
   How a share code is SHOWN: the QR tile beside (or above, on a phone) the
   copyable code string. The tile stays white on every surface — a QR code is
   read by a camera, and cameras need dark-on-light. Pair with a Copy button;
   the receiving end is the Scan viewfinder. */
.qr-share {
  display: flex;
  gap: 20px;
  align-items: center;
}
.qr-share.is-stacked {
  flex-direction: column;
  align-items: stretch;
}
.qr-share.is-stacked .qr-share-tile {
  align-self: center;
}
.qr-share-tile {
  width: 148px;
  height: 148px;
  flex-shrink: 0;
  background: var(--surface-elevated);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 10px;
  box-sizing: border-box;
}
.qr-share-tile svg,
.qr-share-tile img {
  width: 100%;
  height: 100%;
  display: block;
}
.qr-share-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
  flex: 1;
}
.share-code {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--ink-2);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  word-break: break-all;
}
.dark-surface .qr-share-tile {
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .share-code {
  background: var(--tint-on-dark-faint);
  border-color: var(--tint-on-dark-soft);
  color: var(--text-on-dark-secondary);
}

/* ---------- SCAN VIEWFINDER — camera frame for code scanning (App) ----------
   The receiving end of QR share: four accent corners mark where the camera is
   looking, a centred glyph holds the space until the feed is live, one line of
   hint below. Always pair with a typed-code alternative (segmented control) —
   a camera is never the only way in. */
.scan-viewfinder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 6px 0 4px;
}
.scan-viewfinder-frame {
  position: relative;
  width: 168px;
  height: 168px;
  border-radius: var(--radius-md);
  background: var(--paper);
  display: grid;
  place-items: center;
  color: var(--ink-3);
}
.scan-viewfinder-frame .mdi {
  font-size: 44px;
}
.scan-viewfinder-corner {
  position: absolute;
  width: 26px;
  height: 26px;
  border: 3px solid var(--primary);
}
.scan-viewfinder-corner.is-top-left {
  top: 0;
  left: 0;
  border-right: none;
  border-bottom: none;
  border-radius: var(--radius-lg) 0 0 0;
}
.scan-viewfinder-corner.is-top-right {
  top: 0;
  right: 0;
  border-left: none;
  border-bottom: none;
  border-radius: 0 var(--radius-lg) 0 0;
}
.scan-viewfinder-corner.is-bottom-left {
  bottom: 0;
  left: 0;
  border-right: none;
  border-top: none;
  border-radius: 0 0 0 var(--radius-lg);
}
.scan-viewfinder-corner.is-bottom-right {
  bottom: 0;
  right: 0;
  border-left: none;
  border-top: none;
  border-radius: 0 0 var(--radius-lg) 0;
}
.scan-viewfinder-hint {
  font-size: 12.5px;
  color: var(--ink-3);
  text-align: center;
  line-height: 1.5;
  max-width: 260px;
}
.dark-surface .scan-viewfinder-frame {
  background: var(--tint-on-dark-faint);
  color: var(--text-on-dark-muted);
}
.dark-surface .scan-viewfinder-corner {
  border-color: var(--lifted-orange);
}
.dark-surface .scan-viewfinder-hint {
  color: var(--text-on-dark-secondary);
}

/* ---------- SHARE ROW — person + permission in a share list (Both) ----------
   One person with access: initials avatar, name over a mono permission line,
   one remove action. A people list inside a share dialog/sheet — NOT a
   .manage-row (which renames/reorders things the reader owns). */
.share-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: var(--touch-row);
  box-sizing: border-box;
  background: var(--paper);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  padding: 8px 10px;
}
.share-row-avatar {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-circle);
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  display: grid;
  place-items: center;
  font-weight: 600;
  font-size: 11px;
  flex-shrink: 0;
}
.share-row-name {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}
.share-row-permission {
  font-family: var(--font-mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.share-row-remove {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: var(--radius-circle);
  border: none;
  background: none;
  color: var(--ink-3);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.share-row-remove .mdi {
  font-size: 20px;
}
.share-row-remove:hover {
  background: var(--tint-neutral);
  color: var(--error-text);
}
.share-row-remove:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.dark-surface .share-row {
  background: var(--tint-on-dark-faint);
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .share-row-avatar {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .share-row-name {
  color: var(--surface-elevated);
}
.dark-surface .share-row-permission,
.dark-surface .share-row-remove {
  color: var(--text-on-dark-muted);
}
.dark-surface .share-row-remove:hover {
  background: var(--tint-on-dark-soft);
  color: var(--error-on-dark);
}
.dark-surface .share-row-remove:focus-visible {
  outline-color: var(--lifted-orange);
}

/* ---------- PILL TABS — one pill per document (Both) ----------
   A pill per REPORT/TAB the reader owns, plus a dashed add-pill. Not filter
   chips (those narrow a list) and not the underline tab bar (those switch views
   of one screen): each pill IS a document. Active is solid accent; tapping the
   active pill again renames it. .is-touch lifts every target to 44px on the app. */
.pill-tabs {
  display: flex;
  align-items: center;
  gap: 8px;
  overflow-x: auto;
  padding: 2px 0 4px;
  scrollbar-width: none;
}
.pill-tabs::-webkit-scrollbar {
  display: none;
}
.pill-tab {
  flex-shrink: 0;
  height: 36px;
  padding: 0 16px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--surface-elevated);
  color: var(--ink-2);
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.pill-tab:hover {
  border-color: var(--border-stronger);
}
.pill-tab:focus-visible,
.pill-tab-add:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.pill-tab.is-active {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--surface-elevated);
}
.pill-tab-add {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-circle);
  border: 1.5px dashed var(--border-strong);
  background: none;
  color: var(--ink-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.pill-tab-add .mdi {
  font-size: 20px;
}
.pill-tab-add:hover {
  border-color: var(--border-stronger);
  color: var(--ink);
}
.pill-tabs.is-touch .pill-tab {
  height: var(--touch-min);
}
.pill-tabs.is-touch .pill-tab-add {
  width: var(--touch-min);
  height: var(--touch-min);
}
.dark-surface .pill-tab {
  background: transparent;
  border-color: var(--border-on-dark-hover);
  color: var(--text-on-dark-secondary);
}
.dark-surface .pill-tab:hover {
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .pill-tab.is-active {
  background: var(--lifted-orange);
  border-color: var(--lifted-orange);
  color: var(--petrol-deep);
}
.dark-surface .pill-tab:focus-visible,
.dark-surface .pill-tab-add:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .pill-tab-add {
  border-color: var(--border-on-dark-hover);
  color: var(--text-on-dark-secondary);
}
.dark-surface .pill-tab-add:hover {
  border-color: var(--border-on-dark-emphasis);
  color: var(--surface-elevated);
}

/* ---------- BUILDER CANVAS — dark editing surface (Dashboard · module) ----------
   The report/document BUILDER: a petrol-deep canvas that inverts the reading
   surface so edit mode is unmistakable, holding white widget cards in a 12-col
   grid. Anatomy: pill tabs → dashed title + description inputs → icon quick-add
   rail → widget grid (hover handles: configure / layout / more) → dashed add
   tile → petrol footer bar with a mono status and the exit/commit buttons.
   .is-light embeds the same anatomy on paper for a surface that cannot invert. */
.builder-canvas {
  flex: 1;
  min-height: 0;
  overflow: auto;
  background: var(--petrol-deep);
  padding: 28px 30px 80px;
}
.builder-content {
  max-width: 1180px;
  margin: 0 auto;
}
.builder-title-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 6px;
}
.builder-title-input {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--surface-elevated);
  background: transparent;
  border: none;
  border-bottom: 1.5px dashed var(--tint-on-dark-strong);
  padding: 2px 4px;
  min-width: 280px;
}
.builder-title-input:focus {
  outline: none;
  border-bottom-color: var(--lifted-orange);
}
.builder-title-delete {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-circle);
  border: none;
  background: none;
  color: var(--text-on-dark-muted);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.builder-title-delete .mdi {
  font-size: 19px;
}
.builder-title-delete:hover {
  color: var(--error-on-dark);
  background: var(--tint-on-dark-soft);
}
.builder-title-delete:focus-visible {
  outline: 2px solid var(--lifted-orange);
  outline-offset: -2px;
}
.builder-description-input {
  display: block;
  width: 100%;
  max-width: 560px;
  box-sizing: border-box;
  background: transparent;
  border: none;
  border-bottom: 1px dashed var(--tint-on-dark-strong);
  color: var(--text-on-dark-secondary);
  font-family: var(--font-body);
  font-size: 13.5px;
  padding: 4px;
  margin: 2px 0 14px;
}
.builder-description-input::placeholder {
  color: var(--text-on-dark-disabled);
}
.builder-description-input:focus {
  outline: none;
  border-bottom-color: var(--lifted-orange);
}
.builder-hint {
  font-size: 13px;
  color: var(--text-on-dark-secondary);
  margin: 0 0 22px;
}
.builder-quick-add {
  display: flex;
  gap: 6px;
  margin: -8px 0 18px;
}
.builder-quick-add button {
  width: 34px;
  height: 34px;
  border-radius: var(--radius);
  border: 1px solid var(--border-on-dark-hover);
  background: var(--tint-on-dark-faint);
  color: var(--text-on-dark-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.builder-quick-add button .mdi {
  font-size: 17px;
}
.builder-quick-add button:hover {
  border-color: var(--lifted-orange);
  color: var(--lifted-orange);
}
.builder-quick-add button:focus-visible {
  outline: 2px solid var(--lifted-orange);
  outline-offset: 2px;
}
.builder-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 16px;
}
.builder-widget {
  grid-column: span 6;
  background: var(--surface-elevated);
  border-radius: var(--radius-2xl);
  padding: 18px 20px;
  position: relative;
  border: 1.5px solid transparent;
  transition: border-color var(--dur-fast);
}
.builder-widget.is-full {
  grid-column: 1 / -1;
}
.builder-widget:hover {
  border-color: var(--lifted-orange);
}
.builder-widget.is-hidden {
  opacity: 0.45;
}
.builder-hidden-tag {
  margin-left: 8px;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 2px 6px;
  vertical-align: middle;
}
.builder-handle {
  position: absolute;
  top: 10px;
  right: 10px;
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity var(--dur-fast);
}
.builder-widget:hover .builder-handle,
.builder-handle.is-open,
.builder-handle:focus-within {
  opacity: 1;
}
.builder-handle button {
  width: 28px;
  height: 28px;
  border-radius: var(--radius);
  border: none;
  background: var(--paper);
  color: var(--ink-3);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.builder-handle button .mdi {
  font-size: 16px;
}
.builder-handle button:hover {
  background: var(--tint-accent-soft);
  color: var(--primary);
}
.builder-handle button:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
/* Builder grid group label (v2.479). Renamed from .builder-section-heading, which
   the Builder section card also defines; the old name stays as a scoped alias
   inside the grid for one version. */
.builder-grid-heading,
.builder-grid > .builder-section-heading {
  grid-column: 1 / -1;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-on-dark-secondary);
  border-bottom: 1px solid var(--tint-on-dark-soft);
  padding-bottom: 7px;
  margin-top: 6px;
}
.builder-add-tile {
  grid-column: span 6;
  border: 1.5px dashed var(--border-on-dark-hover);
  border-radius: var(--radius-2xl);
  background: var(--tint-on-dark-faint);
  color: var(--text-on-dark-secondary);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 160px;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.builder-add-tile .mdi {
  font-size: 30px;
  color: var(--lifted-orange);
}
.builder-add-tile:hover {
  border-color: var(--lifted-orange);
  background: var(--tint-lifted-soft);
  color: var(--surface-elevated);
}
.builder-add-tile:focus-visible {
  outline: 2px solid var(--lifted-orange);
  outline-offset: 2px;
}
.builder-footer {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 24px;
  background: var(--petrol);
  border-top: 0.5px solid var(--petrol-mid);
}
.builder-footer-status {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-on-dark-muted);
}
.builder-footer-spacer {
  flex: 1;
}
/* Light embed — same anatomy on paper (rule 2 parity). */
.builder-canvas.is-light {
  background: var(--paper);
}
.builder-canvas.is-light .builder-title-input {
  color: var(--ink);
  border-bottom-color: var(--border-strong);
}
.builder-canvas.is-light .builder-title-input:focus {
  border-bottom-color: var(--primary);
}
.builder-canvas.is-light .builder-title-delete {
  color: var(--ink-3);
}
.builder-canvas.is-light .builder-title-delete:hover {
  color: var(--error-text);
  background: var(--tint-neutral);
}
.builder-canvas.is-light .builder-description-input {
  color: var(--ink-2);
  border-bottom-color: var(--border-strong);
}
.builder-canvas.is-light .builder-description-input::placeholder {
  color: var(--ink-3);
}
.builder-canvas.is-light .builder-description-input:focus {
  border-bottom-color: var(--primary);
}
.builder-canvas.is-light .builder-hint,
.builder-canvas.is-light .builder-grid-heading,
.builder-canvas.is-light .builder-grid > .builder-section-heading {
  color: var(--ink-3);
}
.builder-canvas.is-light .builder-grid-heading,
.builder-canvas.is-light .builder-grid > .builder-section-heading {
  border-bottom-color: var(--line);
}
.builder-canvas.is-light .builder-quick-add button {
  border-color: var(--border-strong);
  background: var(--surface-elevated);
  color: var(--ink-2);
}
.builder-canvas.is-light .builder-quick-add button:hover {
  border-color: var(--primary);
  color: var(--primary);
}
.builder-canvas.is-light .builder-quick-add button:focus-visible,
.builder-canvas.is-light .builder-add-tile:focus-visible,
.builder-canvas.is-light .builder-title-delete:focus-visible {
  outline-color: var(--primary);
}
.builder-canvas.is-light .builder-widget {
  border-color: var(--line);
}
.builder-canvas.is-light .builder-widget:hover {
  border-color: var(--primary);
}
.builder-canvas.is-light .builder-add-tile {
  border-color: var(--border-strong);
  background: transparent;
  color: var(--ink-2);
}
.builder-canvas.is-light .builder-add-tile .mdi {
  color: var(--primary);
}
.builder-canvas.is-light .builder-add-tile:hover {
  border-color: var(--primary);
  background: var(--tint-accent-soft);
  color: var(--ink);
}
/* Layout options — the full/half width picker inside the builder's layout modal. */
.layout-option-row {
  display: flex;
  gap: 12px;
}
.layout-option {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
  background: var(--surface-elevated);
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: 14px;
  cursor: pointer;
  font-family: var(--font-body);
  text-align: left;
}
.layout-option:hover {
  border-color: var(--border-stronger);
}
.layout-option.is-selected {
  border-color: var(--primary);
  background: var(--tint-accent-soft);
}
.layout-option:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
.layout-option-thumb {
  width: 100%;
  height: 44px;
  border-radius: var(--radius);
  background: var(--paper);
  border: 1px solid var(--line);
  display: flex;
  gap: 4px;
  padding: 4px;
  box-sizing: border-box;
}
.layout-option-bar {
  background: var(--tint-accent-medium);
  border-radius: var(--radius-xs);
  flex: 1;
}
.layout-option-bar.is-dim {
  background: var(--line);
}
.layout-option-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}
.layout-option-sub {
  font-size: 12px;
  color: var(--ink-2);
  line-height: 1.45;
}
.dark-surface .layout-option {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark-hover);
}
.dark-surface .layout-option:hover {
  border-color: var(--border-on-dark-emphasis);
}
.dark-surface .layout-option.is-selected {
  border-color: var(--lifted-orange);
  background: var(--tint-lifted-soft);
}
.dark-surface .layout-option:focus-visible {
  outline-color: var(--lifted-orange);
}
.dark-surface .layout-option-thumb {
  background: var(--petrol-deep);
  border-color: var(--tint-on-dark-soft);
}
.dark-surface .layout-option-bar {
  background: var(--tint-lifted-medium);
}
.dark-surface .layout-option-bar.is-dim {
  background: var(--tint-on-dark-soft);
}
.dark-surface .layout-option-name {
  color: var(--surface-elevated);
}
.dark-surface .layout-option-sub {
  color: var(--text-on-dark-secondary);
}
@media (max-width: 900px) {
  .builder-grid > * {
    grid-column: 1 / -1;
  }
  .builder-footer {
    flex-wrap: wrap;
    row-gap: 10px;
    padding: 12px 16px;
  }
}
@media (max-width: 560px) {
  .layout-option-row {
    flex-direction: column;
  }
}

/* ---------- HIDDEN ----------
   Popovers (.menu, .page-size-menu, the profile menu) are laid out with
   display:flex, which beats the [hidden] UA rule — so a closed popover stayed
   visible. This last-word rule makes the hidden attribute authoritative. */
[hidden] {
  display: none !important;
}

/* =========================================================================
   v2.467 — Growers Report intake. Two atoms (tenant mark, selection check),
   eight components, one module, and modifiers on six existing elements.
   ========================================================================= */

/* ---------- TENANT MARK (atom · Both) ----------
   The customer's own mark: initials or a logo on a square tile. It identifies
   WHOSE brand a surface speaks for; it is never Hectre's mark. A host sets the
   tenant's brand colour by overriding --tenant-mark-bg (and its -on-dark twin)
   on the element or an ancestor; both fall back to petrol. Decorative next to
   the tenant name, so give it aria-hidden="true" there. */
.tenant-mark {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  background: var(--tenant-mark-bg);
  color: var(--tenant-mark-text);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 0.02em;
  line-height: 1;
  text-transform: uppercase;
  overflow: hidden;
}
.tenant-mark.is-xs {
  width: 22px;
  height: 22px;
  font-size: 8.5px;
  border-radius: var(--radius-sm);
}
.tenant-mark.is-sm {
  width: 28px;
  height: 28px;
  font-size: 10px;
}
.tenant-mark.is-lg {
  width: 44px;
  height: 44px;
  font-size: 15px;
  border-radius: var(--radius-lg);
}
.tenant-mark.has-image {
  background: var(--surface-elevated);
  box-shadow: inset 0 0 0 0.5px var(--line);
}
.tenant-mark img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.dark-surface .tenant-mark,
.report-header.is-editorial .tenant-mark {
  background: var(--tenant-mark-bg-on-dark);
  color: var(--tenant-mark-text-on-dark);
}
.dark-surface .tenant-mark.has-image,
.report-header.is-editorial .tenant-mark.has-image {
  background: var(--surface-elevated);
  box-shadow: none;
}

/* ---------- SELECTION CHECK (atom · Both) ----------
   The tick that marks a tile as picked. It sits on imagery, so the resting
   state is a dark disc with a white ring: legible on any photo. Selected
   fills orange with a white tick; at a selection limit it shows a lock, so
   the state never rests on colour alone. Inherits from a selected parent. */
.selection-check {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-circle);
  background: var(--scrim-photo);
  box-shadow: inset 0 0 0 1.5px var(--surface-elevated);
  transition: background var(--dur-fast) var(--ease-out);
}
.selection-check::after {
  content: '';
  width: 14px;
  height: 14px;
  background-color: var(--surface-elevated);
  -webkit-mask: var(--icon-mask-check) center / contain no-repeat;
  mask: var(--icon-mask-check) center / contain no-repeat;
  opacity: 0;
}
.selection-check.is-selected,
.is-selected > .selection-check {
  background: var(--primary);
}
.selection-check.is-selected::after,
.is-selected > .selection-check::after {
  opacity: 1;
}
.selection-check.is-locked::after,
.is-locked > .selection-check::after {
  opacity: 1;
  width: 12px;
  height: 12px;
  -webkit-mask-image: var(--icon-mask-lock);
  mask-image: var(--icon-mask-lock);
}
.dark-surface .selection-check.is-selected,
.dark-surface .is-selected > .selection-check {
  background: var(--lifted-orange);
}
.dark-surface .selection-check.is-selected::after,
.dark-surface .is-selected > .selection-check::after {
  background-color: var(--petrol-deep);
}
@media (prefers-reduced-motion: reduce) {
  .selection-check {
    transition: none;
  }
}

/* ---------- AVATAR · xs (atom size, added for the note card byline) ---------- */
.avatar.xs {
  width: 26px;
  height: 26px;
  font-size: 10px;
}

/* ---------- SELECTABLE PHOTO CARD (modifier on Photo card) ----------
   A photo card that toggles in a picker. aria-pressed carries the state; the
   selection check carries it visually. .is-locked = the group hit its limit
   (greyscale + lock, still focusable so the title can explain why). */
.photo-card.is-selectable {
  position: relative;
}
.photo-card.is-selectable > .selection-check {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 3;
}
.photo-card.is-selectable.is-locked {
  cursor: not-allowed;
}
.photo-card.is-selectable.is-locked:hover {
  border-color: var(--line);
  box-shadow: none;
}
.photo-card.is-selectable.is-locked .photo-frame img {
  filter: grayscale(1);
  opacity: 0.55;
}
.photo-card.is-selectable:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.photo-picker-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}
@media (max-width: 560px) {
  .photo-picker-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.dark-surface .photo-card.is-selectable.is-locked:hover {
  border-color: var(--border-on-dark);
}

/* ---------- GROUPED MULTI-SELECT (modifier on the Option list) ----------
   A parent row selects all of its children; it reads .is-mixed when only some
   are picked. Wrap each parent and its children in .select-option-group; the
   disclosure button (an .icon-button) collapses the group. Children indent so
   their checkbox sits under the parent label. */
.select-option-group {
  display: flex;
  flex-direction: column;
}
.select-option-group-head {
  display: flex;
  align-items: center;
  gap: 2px;
}
.select-option-group-head .select-option {
  flex: 1;
  min-width: 0;
}
.select-option-group-head .icon-button {
  width: 28px;
  height: 28px;
}
.select-option-group-head .icon-button .mdi {
  font-size: 18px;
  transition: transform var(--dur-fast) var(--ease-out);
}
.select-option-group.is-collapsed .select-option-group-head .icon-button .mdi {
  transform: rotate(-90deg);
}
.select-option-group.is-collapsed .select-option.is-child {
  display: none;
}
.select-option.is-group {
  font-weight: 600;
}
.select-option.is-child {
  padding-left: 58px;
}
.select-option-meta {
  margin-left: auto;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
.select-option.is-selected .select-option-meta {
  color: var(--ink-2);
}
.select-panel-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 18px 12px;
  text-align: center;
  font-size: 13px;
  color: var(--ink-3);
}
.dark-surface .select-option-meta,
.dark-surface .select-panel-empty {
  color: var(--text-on-dark-secondary);
}
@media (prefers-reduced-motion: reduce) {
  .select-option-group-head .icon-button .mdi {
    transition: none;
  }
}

/* ---------- WIDE SLIDE-OVER (modifier on Slide-over) ----------
   640px for a panel that holds a whole workflow (a report builder) rather
   than one record. Full-width below 700px. .is-split puts the footer summary
   and actions on one row. */
.slide-over.is-wide {
  width: var(--slide-over-width-wide);
}
.slide-over-foot.is-split {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
@media (max-width: 700px) {
  .slide-over.is-wide {
    width: 100vw;
    max-width: 100vw;
  }
}
@media (prefers-reduced-motion: reduce) {
}

/* ---------- CONFIRM DISC TONES (modifier on Modal) ----------
   The disc at the top of a confirm or outcome dialog. Danger stays the
   default for back-compat; name the tone explicitly in new markup. Success =
   it happened. Warning = it is waiting on someone. Info = nothing changed. */
.modal-confirm-disc.is-danger {
  background: var(--tint-error-bg);
  color: var(--error-text);
}
.modal-confirm-disc.is-success {
  background: var(--tint-success-medium);
  color: var(--success-text);
}
.modal-confirm-disc.is-warning {
  background: var(--tint-warning-medium);
  color: var(--warning-text);
}
.modal-confirm-disc.is-info {
  background: var(--tint-info-medium);
  color: var(--info-text);
}
.dark-surface .modal-confirm-disc.is-danger {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .modal-confirm-disc.is-success {
  background: var(--tint-success-on-dark);
  color: var(--success-on-dark);
}
.dark-surface .modal-confirm-disc.is-warning {
  background: var(--tint-warning-on-dark);
  color: var(--warning-on-dark);
}
.dark-surface .modal-confirm-disc.is-info {
  background: var(--tint-info-on-dark);
  color: var(--info-on-dark);
}

/* ---------- SUMMARY ROW (modifiers on Label value row) ----------
   The recap before a send: icon, label, a right-aligned TEXT value, hairline
   between rows. .has-icon adds the leading icon, .is-divided the hairline,
   .is-text-value swaps the mono figure for body text that may wrap, .is-accent
   marks the rows that carry a decision (sender, approver, automation). */
.label-value-row.has-icon {
  align-items: center;
}
.label-value-row .label-value-icon {
  flex-shrink: 0;
  font-size: 18px;
  color: var(--ink-3);
}
.label-value-row.is-divided {
  padding: 11px 0;
  border-bottom: 0.5px solid var(--line-2);
}
.label-value-row.is-divided:last-child {
  border-bottom: none;
}
.label-value-row.is-text-value {
  align-items: center;
}
.label-value-row.is-text-value .label-value-amount {
  flex: 0 1 auto;
  max-width: 62%;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: normal;
  font-feature-settings: normal;
  white-space: normal;
  overflow-wrap: anywhere;
}
.label-value-row .label-value-amount.is-empty {
  color: var(--ink-3);
  font-weight: 500;
}
.label-value-row.is-accent .label-value-icon {
  color: var(--primary);
}
.dark-surface .label-value-row .label-value-icon {
  color: var(--text-on-dark-secondary);
}
.dark-surface .label-value-row.is-accent .label-value-icon {
  color: var(--lifted-orange);
}
.dark-surface .label-value-row.is-divided {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .label-value-row .label-value-amount.is-empty {
  color: var(--text-on-dark-secondary);
}
/* Cell size: the list inside a data-table row (defects) — 12px names, 11px
   mono values, 3px apart. */
.label-value-list.is-cell {
  gap: 3px;
}
.label-value-list.is-cell .label-value-row {
  gap: 6px;
  align-items: center;
}
.label-value-list.is-cell .label-value-label {
  font-size: 12px;
  font-weight: 400;
  color: var(--ink);
}
.label-value-list.is-cell .label-value-amount {
  font-size: 11px;
  font-weight: 400;
  color: var(--ink-3);
}
.dark-surface .label-value-list.is-cell .label-value-label {
  color: var(--text-on-dark);
}
.dark-surface .label-value-list.is-cell .label-value-amount {
  color: var(--text-on-dark-muted);
}

/* ---------- DETAIL CHIP (component · Dashboard) ----------
   One identifying field of a record as a pill: icon, mono label, value. Rows
   of these head a record (the ticket hero). Read-only by default; as a button
   with .is-editable it opens the field's editor. */
.detail-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}
.detail-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 30px;
  max-width: 100%;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  font-family: var(--font-body);
}
.detail-chip .mdi {
  flex-shrink: 0;
  font-size: 14px;
  color: var(--ink-3);
}
.detail-chip-label {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
.detail-chip-value {
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.detail-chip-value.is-empty {
  color: var(--ink-3);
  font-weight: 500;
}
.detail-chip.is-editable {
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.detail-chip.is-editable:hover {
  border-color: var(--border-stronger);
}
.detail-chip.is-editable:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.dark-surface .detail-chip {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
.dark-surface .detail-chip .mdi,
.dark-surface .detail-chip-label {
  color: var(--text-on-dark-secondary);
}
.dark-surface .detail-chip-value {
  color: var(--text-on-dark);
}
.dark-surface .detail-chip-value.is-empty {
  color: var(--text-on-dark-secondary);
}
.dark-surface .detail-chip.is-editable:hover {
  border-color: var(--border-on-dark-hover);
}
.dark-surface .detail-chip.is-editable:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
/* ---------- DETAIL CHIP · EDIT POPOVER (v2.486) ----------
   An editable chip opens its field's option list as a popover below it. The
   popover reuses the option-list states: picking keeps it open in the saving
   state (.select-panel.is-saving, every option disabled) while the chip keeps
   its old value and shows a spinner; success closes
   on the new value, failure unlocks the options with .select-panel-error. */
.detail-chip-edit {
  position: relative;
  display: inline-flex;
}
.detail-chip-edit > .select-panel:not(.is-inline) {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  width: max-content;
  min-width: 100%;
  max-width: 280px;
}
.detail-chip-edit > .select-panel.is-inline {
  margin-top: 6px;
}
.detail-chip.is-editable .detail-chip-caret {
  font-size: 16px;
}
.detail-chip.is-editable.is-open {
  border-color: var(--primary);
  box-shadow: var(--focus-ring-accent-strong);
}
.detail-chip.is-editable.is-saving {
  cursor: progress;
  background: var(--tint-neutral-soft);
}
.detail-chip.is-saving .detail-chip-value {
  color: var(--ink-2);
}
.detail-chip .spinner {
  flex-shrink: 0;
  width: 13px;
  height: 13px;
  border-width: 1.5px;
}
.detail-chip.is-editable:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.dark-surface .detail-chip.is-editable.is-open {
  border-color: var(--lifted-orange);
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .detail-chip.is-editable.is-saving {
  background: var(--tint-on-dark-soft);
}
.dark-surface .detail-chip.is-saving .detail-chip-value {
  color: var(--text-on-dark-secondary);
}
/* ---------- DETAIL CHIP · TEXT / NUMBER EDITOR (v2.488) ----------
   An editable chip whose value is typed opens a small editor popover: a mono
   label ("Edit consignment"), a FormInput (text or number), a secondary
   icon-only cancel and a primary icon-only confirm. Same saving system as the
   option popover: on confirm the popover stays open and the whole editor is
   disabled (input + both buttons) while the backend saves; the chip keeps its
   old value and shows the spinner. Success closes on the new value; failure
   re-enables the editor with .select-panel-error above the row. */
.detail-chip-editor {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: max-content;
  min-width: 280px;
  max-width: 360px;
  margin-top: 6px;
  padding: 12px 14px 14px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.detail-chip-edit > .detail-chip-editor:not(.is-inline) {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  margin-top: 0;
}
.detail-chip-editor-label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.detail-chip-editor-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.detail-chip-editor-row .input {
  flex: 1 1 auto;
  min-width: 0;
  height: var(--control-height-md);
  padding-top: 0;
  padding-bottom: 0;
}
.detail-chip-editor-row .btn {
  flex-shrink: 0;
}
.detail-chip-editor .select-panel-error {
  margin-bottom: 0;
}
.detail-chip-editor.is-saving {
  cursor: progress;
}
.dark-surface .detail-chip-editor {
  background: var(--petrol);
  border-color: var(--border-on-dark-medium);
  box-shadow: var(--dp-shadow-hi);
}
.dark-surface .detail-chip-editor-label {
  color: var(--text-on-dark-secondary);
}
/* Detail chip caret sits at the chip's right edge with clear space after the
   value (v2.489, matches prod). */
.detail-chip .detail-chip-caret {
  margin-left: auto;
  padding-left: 3px;
}
/* Option list · loading more (v2.489): a compact row at the END of the list
   while the next page fetches; the rows above stay usable. Distinct from the
   full-panel .select-loading (first load). */
.select-loading.is-more {
  min-height: 36px;
  gap: 8px;
  font-size: 12.5px;
  color: var(--ink-3);
}
.select-loading.is-more .spinner {
  width: 14px;
  height: 14px;
  border-width: 1.5px;
}
.dark-surface .select-loading.is-more {
  color: var(--text-on-dark-secondary);
}
/* Saving locks the search and the clear action with the options. */
.select-panel.is-saving .menu-search,
.select-panel.is-saving .select-clear {
  opacity: 0.45;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .detail-chip.is-editable {
    transition: none;
  }
}

/* ---------- BUILDER SECTION CARD (component · Dashboard) ----------
   One step of a configuration builder: icon tile, title + one-line description,
   an optional trailing control (usually a switch), then the step's fields.
   Flat (divided list) by default; .is-carded lifts each step onto a card.
   .is-off = the step is excluded: body hidden, icon neutral, description says
   so. .is-error = a required step is incomplete; pair with a .field-msg. */
.builder-section-stack {
  display: flex;
  flex-direction: column;
}
.builder-section-stack.is-carded {
  gap: 12px;
}
.builder-section {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
  padding: 18px 0;
  border-bottom: 0.5px solid var(--line-2);
}
.builder-section:last-child {
  border-bottom: none;
}
.builder-section.is-carded,
.builder-section-stack.is-carded .builder-section {
  padding: 16px 18px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-2xl);
  box-shadow: var(--shadow-card);
}
.builder-section-head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.builder-section-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
}
.builder-section-icon .mdi {
  font-size: 17px;
}
.builder-section-heading {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.builder-section-title {
  margin: 0;
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
}
.builder-section-description {
  font-size: 12px;
  line-height: 1.4;
  color: var(--ink-3);
}
.builder-section-action {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.builder-section-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}
.builder-section.is-off {
  gap: 0;
}
.builder-section.is-off .builder-section-body {
  display: none;
}
.builder-section.is-off .builder-section-icon,
.builder-section.is-disabled .builder-section-icon {
  background: var(--tint-neutral);
  color: var(--ink-3);
}
.builder-section.is-disabled .builder-section-title {
  color: var(--ink-3);
}
.builder-section.is-error .builder-section-icon {
  background: var(--tint-error-bg);
  color: var(--error-text);
}
.builder-section.is-carded.is-error,
.builder-section-stack.is-carded .builder-section.is-error {
  border-color: var(--error-border);
}
.dark-surface .builder-section {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .builder-section.is-carded,
.dark-surface .builder-section-stack.is-carded .builder-section {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
  box-shadow: none;
}
.dark-surface .builder-section-icon {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .builder-section-title {
  color: var(--text-on-dark);
}
.dark-surface .builder-section-description {
  color: var(--text-on-dark-secondary);
}
.dark-surface .builder-section.is-off .builder-section-icon,
.dark-surface .builder-section.is-disabled .builder-section-icon {
  background: var(--tint-on-dark-soft);
  color: var(--text-on-dark-secondary);
}
.dark-surface .builder-section.is-disabled .builder-section-title {
  color: var(--text-on-dark-secondary);
}
.dark-surface .builder-section.is-error .builder-section-icon {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .builder-section.is-carded.is-error,
.dark-surface .builder-section-stack.is-carded .builder-section.is-error {
  border-color: var(--error-border-on-dark);
}

/* ---------- SECTION TOGGLE ROW (component · Dashboard) ----------
   Include or leave out one section of a document: icon, name + description,
   switch. The row tints when on, so a long list reads at a glance; the switch
   still carries the state. Wire the whole row's click to the switch so the
   target is the 48px row, not the 22px track. .is-locked = always included
   (show a tag, no switch). */
.section-toggle-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.section-toggle-row {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 48px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
.section-toggle-row:hover {
  background: var(--tint-neutral-soft);
}
.section-toggle-row .section-toggle-icon {
  flex-shrink: 0;
  font-size: 17px;
  color: var(--ink-3);
}
.section-toggle-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.section-toggle-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.section-toggle-description {
  font-size: 12px;
  line-height: 1.35;
  color: var(--ink-3);
}
.section-toggle-row.is-on {
  background: var(--tint-accent-soft);
}
.section-toggle-row.is-on .section-toggle-icon {
  color: var(--primary);
}
.section-toggle-row.is-locked {
  cursor: default;
}
.section-toggle-row.is-locked:hover {
  background: transparent;
}
.section-toggle-row.is-disabled {
  cursor: not-allowed;
}
.section-toggle-row.is-disabled:hover {
  background: transparent;
}
.section-toggle-row.is-disabled .section-toggle-name {
  color: var(--ink-3);
}
.dark-surface .section-toggle-row:hover {
  background: var(--tint-on-dark-faint);
}
.dark-surface .section-toggle-row .section-toggle-icon,
.dark-surface .section-toggle-description {
  color: var(--text-on-dark-secondary);
}
.dark-surface .section-toggle-name {
  color: var(--text-on-dark);
}
.dark-surface .section-toggle-row.is-on {
  background: var(--tint-lifted-soft);
}
.dark-surface .section-toggle-row.is-on .section-toggle-icon {
  color: var(--lifted-orange);
}
.dark-surface .section-toggle-row.is-locked:hover,
.dark-surface .section-toggle-row.is-disabled:hover {
  background: transparent;
}
.dark-surface .section-toggle-row.is-disabled .section-toggle-name {
  color: var(--text-on-dark-secondary);
}
@media (prefers-reduced-motion: reduce) {
  .section-toggle-row {
    transition: none;
  }
}

/* ---------- RECEIPT ROW (component · Both) ----------
   What just happened, one line per outcome, after a send or a save: tinted
   icon tile, title, one-line detail, optional action (copy, retry, a status
   badge). Tone = outcome: success (default) done, accent a setting that is now
   on, warning waiting on someone, error failed (detail wraps; offer a retry). */
.receipt-list {
  display: flex;
  flex-direction: column;
}
.receipt-row {
  display: flex;
  align-items: center;
  gap: 13px;
  min-width: 0;
  padding: 12px 0;
  border-bottom: 0.5px solid var(--line-2);
}
.receipt-row:last-child {
  border-bottom: none;
}
.receipt-row-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: var(--tint-success-medium);
  color: var(--success-text);
}
.receipt-row-icon .mdi {
  font-size: 19px;
}
.receipt-row-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.receipt-row-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}
.receipt-row-detail {
  font-size: 12px;
  line-height: 1.35;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.receipt-row-action {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.receipt-row.is-accent .receipt-row-icon {
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
}
.receipt-row.is-warning .receipt-row-icon {
  background: var(--tint-warning-medium);
  color: var(--warning-text);
}
.receipt-row.is-error .receipt-row-icon {
  background: var(--tint-error-medium);
  color: var(--error-text);
}
.receipt-row.is-error .receipt-row-detail {
  color: var(--error-text);
  white-space: normal;
}
.dark-surface .receipt-row {
  border-bottom-color: var(--tint-on-dark-soft);
}
.dark-surface .receipt-row-title {
  color: var(--text-on-dark);
}
.dark-surface .receipt-row-detail {
  color: var(--text-on-dark-secondary);
}
.dark-surface .receipt-row-icon {
  background: var(--tint-success-on-dark);
  color: var(--success-on-dark);
}
.dark-surface .receipt-row.is-accent .receipt-row-icon {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .receipt-row.is-warning .receipt-row-icon {
  background: var(--tint-warning-on-dark);
  color: var(--warning-on-dark);
}
.dark-surface .receipt-row.is-error .receipt-row-icon {
  background: var(--tint-error-on-dark);
  color: var(--error-on-dark);
}
.dark-surface .receipt-row.is-error .receipt-row-detail {
  color: var(--error-on-dark);
}

/* ---------- TENANT STRIP (component · Dashboard) ----------
   Whose brand this output speaks for: tenant mark, name, site, an optional
   tag. Read-only by default. As a <button> with .is-switchable it opens a
   tenant menu for users who work across several customers. */
.tenant-strip {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 12px 0;
}
.tenant-strip-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.tenant-strip-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tenant-strip-site {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tenant-strip-caret {
  flex-shrink: 0;
  font-size: 18px;
  color: var(--ink-3);
}
.tenant-strip.is-sm {
  gap: 8px;
  padding: 8px 0;
}
.tenant-strip.is-sm .tenant-strip-name {
  font-size: 12.5px;
}
.tenant-strip.is-switchable {
  width: 100%;
  min-height: 52px;
  padding: 10px 12px;
  background: transparent;
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}
.tenant-strip.is-switchable:hover {
  border-color: var(--border-stronger);
  background: var(--surface-elevated);
}
.tenant-strip.is-switchable:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-accent-strong);
}
.tenant-strip.is-switchable[aria-expanded='true'] {
  border-color: var(--primary);
}
.dark-surface .tenant-strip-name {
  color: var(--text-on-dark);
}
.dark-surface .tenant-strip-site,
.dark-surface .tenant-strip-caret {
  color: var(--text-on-dark-secondary);
}
.dark-surface .tenant-strip.is-switchable {
  border-color: var(--border-on-dark);
}
.dark-surface .tenant-strip.is-switchable:hover {
  border-color: var(--border-on-dark-hover);
  background: var(--tint-on-dark-faint);
}
.dark-surface .tenant-strip.is-switchable:focus-visible {
  box-shadow: var(--focus-ring-accent-strong-on-dark);
}
.dark-surface .tenant-strip.is-switchable[aria-expanded='true'] {
  border-color: var(--lifted-orange);
}
@media (prefers-reduced-motion: reduce) {
  .tenant-strip.is-switchable {
    transition: none;
  }
}

/* ---------- TENANT LOCKUP (component · Both) ----------
   The tenant's sign-off on a document: mark, name with the full-stop dot,
   site. The dot follows the accent rule (orange on light, lifted on petrol).
   .is-end mirrors it for a right-aligned header slot. */
.tenant-lockup {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  max-width: 100%;
}
.tenant-lockup.is-end {
  flex-direction: row-reverse;
  text-align: right;
}
.tenant-lockup-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  line-height: 1.15;
}
.tenant-lockup-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tenant-lockup-dot {
  color: var(--primary);
}
.tenant-lockup-site {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dark-surface .tenant-lockup-name,
.report-header.is-editorial .tenant-lockup-name {
  color: var(--text-on-dark);
}
.dark-surface .tenant-lockup-dot,
.report-header.is-editorial .tenant-lockup-dot {
  color: var(--lifted-orange);
}
.dark-surface .tenant-lockup-site,
.report-header.is-editorial .tenant-lockup-site {
  color: var(--text-on-dark-secondary);
}

/* ---------- IDENTIFICATION STRIP (component · Both) ----------
   The record's identity under a document header: a row of label / value
   pairs (variety, lot, block, consignment, received). Five across by default;
   .is-columns-3 / -4 / -6 for other sets. .is-divided adds hairlines between
   items. IDs read better as .is-mono. Missing value = "—" with .is-empty. */
.identification-strip {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px 16px;
  padding: 2px 4px;
}
.identification-strip.is-columns-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.identification-strip.is-columns-4 {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.identification-strip.is-columns-6 {
  grid-template-columns: repeat(6, minmax(0, 1fr));
}
.identification-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.identification-strip.is-divided .identification-item + .identification-item {
  padding-left: 16px;
  border-left: 0.5px solid var(--line);
}
.identification-label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.identification-value {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.identification-value.is-mono {
  font-family: var(--font-mono);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}
.identification-value.is-empty {
  color: var(--ink-3);
  font-weight: 500;
}
@media (max-width: 640px) {
  .identification-strip,
  .identification-strip[class*='is-columns-'] {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .identification-strip.is-divided .identification-item + .identification-item {
    padding-left: 0;
    border-left: none;
  }
}
@media (max-width: 420px) {
  .identification-strip,
  .identification-strip[class*='is-columns-'] {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.dark-surface .identification-label {
  color: var(--text-on-dark-secondary);
}
.dark-surface .identification-value {
  color: var(--text-on-dark);
}
.dark-surface .identification-value.is-empty {
  color: var(--text-on-dark-secondary);
}
.dark-surface .identification-strip.is-divided .identification-item + .identification-item {
  border-left-color: var(--border-on-dark);
}

/* ---------- REPORT SECTION HEADER (component · Both) ----------
   Opens one section of a document: accent icon, title, a hairline that runs
   to the edge, an optional note tag or action. The rule is what makes a long
   report scannable; keep it even when there is no tag. */
.report-section-header {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  margin-bottom: 12px;
}
.report-section-header-icon {
  flex-shrink: 0;
  font-size: 16px;
  color: var(--primary);
}
.report-section-header-title {
  margin: 0;
  min-width: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.report-section-header-rule {
  flex: 1;
  min-width: 16px;
  height: 0.5px;
  background: var(--line);
}
.report-section-header-action {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.report-section-header.is-sm {
  gap: 7px;
  margin-bottom: 8px;
}
.report-section-header.is-sm .report-section-header-icon {
  font-size: 14px;
}
.report-section-header.is-sm .report-section-header-title {
  font-size: 13px;
}
.dark-surface .report-section-header-icon {
  color: var(--lifted-orange);
}
.dark-surface .report-section-header-title {
  color: var(--text-on-dark);
}
.dark-surface .report-section-header-rule {
  background: var(--border-on-dark);
}
/* ---------- REPORT CONTENT CARD ----------
   The flat bordered panel around one report section. No shadow and a 10px
   corner, so it prints cleanly; .is-wide spans both columns of a report grid. */
.report-content-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: 18px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
  break-inside: avoid;
}
.report-content-card.is-wide {
  grid-column: 1 / -1;
}
.report-content-card.is-compact {
  padding: 14px;
}
.dark-surface .report-content-card {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
/* ---------- NUMBERED LIST ----------
   Ordered recommendations or steps: an 18px tinted number disc beside each
   item. Use an <ol>; the disc is decorative, the list order carries meaning. */
.numbered-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.numbered-list-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
}
.numbered-list-marker {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  border-radius: var(--radius-circle);
  background: var(--tint-accent-medium);
  color: var(--accent-text-on-tint);
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.numbered-list-text {
  min-width: 0;
  font-family: var(--font-body);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
  text-wrap: pretty;
}
.dark-surface .numbered-list-marker {
  background: var(--tint-lifted-medium);
  color: var(--lifted-orange);
}
.dark-surface .numbered-list-text {
  color: var(--text-on-dark-secondary);
}
/* ---------- REPORT FOOTER ----------
   Closes a document: the sender's contact line left, the generated-by credit
   right, over a hairline. Wraps to two lines when narrow. */
.report-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  margin-top: 4px;
  padding-top: 14px;
  border-top: 0.5px solid var(--line);
}
.report-footer-contact {
  min-width: 0;
  font-family: var(--font-body);
  font-size: var(--text-body-s);
  line-height: 1.5;
  color: var(--ink-3);
}
.report-footer-credit {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.report-footer-credit .mdi {
  font-size: 11px;
  color: var(--primary);
}
.dark-surface .report-footer {
  border-top-color: var(--border-on-dark);
}
.dark-surface .report-footer-contact,
.dark-surface .report-footer-credit {
  color: var(--text-on-dark-muted);
}
.dark-surface .report-footer-credit .mdi {
  color: var(--lifted-orange);
}
/* ---------- PREVIEW TOOLBAR ----------
   The strip above a live document preview: eye icon, "Live preview" label,
   template tags, then the page format pushed right. */
.preview-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  padding: 0 0 14px;
}
.preview-toolbar > .mdi {
  font-size: 16px;
  color: var(--ink-3);
}
.preview-toolbar-label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.preview-toolbar-meta {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.dark-surface .preview-toolbar > .mdi,
.dark-surface .preview-toolbar-label,
.dark-surface .preview-toolbar-meta {
  color: var(--text-on-dark-muted);
}
/* ---------- PREVIEW WELL ----------
   The shaded area holding a scaled document. The page renders at its print
   width (--report-page-width) and is scaled to fit with --preview-scale, set
   from the well's measured width; the outer box takes the scaled height. */
.preview-well {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 22px;
  background: var(--petrol-soft);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
}
.preview-well-page {
  margin: 0 auto;
  width: fit-content;
  border-radius: var(--radius-sm);
  overflow: hidden;
  box-shadow: var(--shadow-frame);
  background: var(--surface-elevated);
}
.preview-well-scale {
  width: var(--report-page-width);
  transform: scale(var(--preview-scale, 1));
  transform-origin: top left;
}
.preview-well > .photo-frame.is-empty {
  height: 100%;
  aspect-ratio: auto;
  background: transparent;
  border: none;
}
.dark-surface .preview-well {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}

/* ---------- NOTE CARD (component · Both) ----------
   A person's own words carried into a document — the packer's note. The text
   keeps its line breaks; the byline says who and where it was captured.
   .is-truncated clamps to four lines behind a "Show more" link. */
.note-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 16px;
  background: var(--surface-elevated);
  border: 0.5px solid var(--line);
  border-radius: var(--radius-lg);
}
.note-card-text {
  margin: 0;
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.55;
  color: var(--ink);
  white-space: pre-wrap;
  text-wrap: pretty;
}
.note-card.is-truncated .note-card-text {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.note-card.is-empty .note-card-text {
  color: var(--ink-3);
}
.note-card-more {
  align-self: flex-start;
  padding: 0;
  background: none;
  border: none;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--primary-press);
  text-decoration: none;
  cursor: pointer;
}
.note-card-more:hover {
  color: var(--primary);
  text-decoration: none;
}
.note-card-more:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
.note-card-author {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.note-card-author-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.note-card-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.note-card-source {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.dark-surface .note-card {
  background: var(--tint-on-dark-faint);
  border-color: var(--border-on-dark);
}
.dark-surface .note-card-text,
.dark-surface .note-card-name {
  color: var(--text-on-dark);
}
.dark-surface .note-card.is-empty .note-card-text,
.dark-surface .note-card-source {
  color: var(--text-on-dark-secondary);
}
.dark-surface .note-card-more {
  color: var(--lifted-orange);
}
.dark-surface .note-card-more:hover {
  color: var(--surface-elevated);
}
.dark-surface .note-card-more:focus-visible {
  outline-color: var(--lifted-orange);
}

/* ---------- REPORT HEADER (module · Both) ----------
   The masthead of a grower-facing document, in three templates.
   Editorial — a petrol band: eyebrow, display title, "prepared for" line,
   lockup and date on the right. Compact — lockup left, title right, an orange
   rule underneath. Letter — lockup and date on one line, then title and an
   attention line. Editorial is petrol by design; on a petrol page it drops to
   a faint band so it does not vanish. Prints with its colours kept. */
.report-header {
  display: flex;
  gap: 16px;
  min-width: 0;
}
.report-header-main {
  min-width: 0;
}
.report-header-eyebrow {
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--accent-text-on-tint);
}
.report-header-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 26px;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-wrap: balance;
}
.report-header-meta {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
}
.report-header-meta strong {
  font-weight: 700;
  color: var(--ink);
}
.report-header-date {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  letter-spacing: var(--tr-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}
.report-header-aside {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  text-align: right;
}
.report-header-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.report-header.is-editorial {
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px;
  border-radius: var(--radius-lg);
  background: var(--petrol-deep);
}
.report-header.is-editorial .report-header-eyebrow {
  margin-bottom: 8px;
  color: var(--lifted-orange);
}
.report-header.is-editorial .report-header-title {
  color: var(--text-on-dark);
}
.report-header.is-editorial .report-header-meta {
  color: var(--text-on-dark-secondary);
}
.report-header.is-editorial .report-header-meta strong {
  color: var(--text-on-dark);
}
.report-header.is-editorial .report-header-date {
  color: var(--text-on-dark-secondary);
}
.report-header.is-compact {
  align-items: flex-start;
  justify-content: space-between;
  padding-bottom: 16px;
  border-bottom: 2px solid var(--primary);
}
.report-header.is-compact .report-header-title {
  font-size: 17px;
  color: var(--primary-press);
}
.report-header.is-compact .report-header-meta {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
}
.report-header.is-compact .report-header-date {
  margin-top: 3px;
}
.report-header.is-letter {
  flex-direction: column;
  gap: 14px;
  padding-bottom: 16px;
  border-bottom: 0.5px solid var(--line);
}
.dark-surface .report-header.is-editorial {
  background: var(--tint-on-dark-faint);
  box-shadow: inset 0 0 0 0.5px var(--border-on-dark);
}
.dark-surface .report-header-eyebrow {
  color: var(--lifted-orange);
}
.dark-surface .report-header-title {
  color: var(--text-on-dark);
}
.dark-surface .report-header-meta,
.dark-surface .report-header-date {
  color: var(--text-on-dark-secondary);
}
.dark-surface .report-header-meta strong {
  color: var(--text-on-dark);
}
.dark-surface .report-header.is-compact {
  border-bottom-color: var(--lifted-orange);
}
.dark-surface .report-header.is-compact .report-header-title {
  color: var(--lifted-orange);
}
.dark-surface .report-header.is-compact .report-header-meta {
  color: var(--text-on-dark);
}
.dark-surface .report-header.is-letter {
  border-bottom-color: var(--border-on-dark);
}
@media (max-width: 600px) {
  .report-header.is-editorial,
  .report-header.is-compact {
    flex-direction: column;
    align-items: flex-start;
  }
  .report-header-aside {
    align-items: flex-start;
    text-align: left;
  }
  .report-header-aside .tenant-lockup.is-end {
    flex-direction: row;
    text-align: left;
  }
  .report-header-title {
    font-size: 22px;
  }
}
@media print {
  .report-header.is-editorial {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

/* ---------- CHART SHEEN (v2.495) ----------
   A barely-there top light on every chart fill: a little lighter at the top,
   a touch deeper at the base, so bars read as surfaces rather than flat
   paint. Drawn as an ::after overlay, so it sits over any fill (class, inline
   colour, translucent grade step, cherry ramp) without touching it, and
   follows the bar's radius. Off under forced colours. */
.chart-grade-segment,
.column-chart-bar,
.chart-column,
.chart-ranked-fill,
.chart-bullet-fill {
  position: relative;
}
.chart-grade-segment:not([data-tooltip])::after,
.column-chart-bar:not([data-tooltip])::after,
.chart-column:not([data-tooltip])::after,
.chart-ranked-fill:not([data-tooltip])::after,
.chart-bullet-fill:not([data-tooltip])::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--chart-sheen);
  pointer-events: none;
}
.dark-surface .chart-grade-segment:not([data-tooltip])::after,
.dark-surface .column-chart-bar:not([data-tooltip])::after,
.dark-surface .chart-column:not([data-tooltip])::after,
.dark-surface .chart-ranked-fill:not([data-tooltip])::after,
.dark-surface .chart-bullet-fill:not([data-tooltip])::after {
  background: var(--chart-sheen-on-dark);
}
@media (forced-colors: active) {
  .chart-grade-segment:not([data-tooltip])::after,
  .column-chart-bar:not([data-tooltip])::after,
  .chart-column:not([data-tooltip])::after,
  .chart-ranked-fill:not([data-tooltip])::after,
  .chart-bullet-fill:not([data-tooltip])::after {
    display: none;
  }
}
