/* BakeWise design tokens - the visual SSOT (PRD S7.6, principle F-P2;
   design brief section 10). One design language, two renderers: this file
   feeds the web today and a generator can emit BakeWiseTokens.kt from it
   later, so it keeps a strict shape:
     tier 1  primitives  raw values, named by hue/step or by scale step
     tier 2  semantic    roles (--bg, --text, --accent, --danger ...), the
                         only tier components read for colour and type
     tier 3  component   per-component knobs built from tier 2
   One custom property per line, `--name: value;`, kebab-case. Dark mode
   redefines tier-2 colour roles only, in two identical blocks: the OS
   preference (unless the session forced light) and the forced-dark
   session/kiosk preference. Density swaps a handful of tier-3 sizes.
   No component may hardcode a hex, px or ms: the check_frontend_tokens
   sentinel fails CI otherwise, and tokens_test.go recomputes the WCAG
   contrast of every text/background pair defined here, in both modes.

   Every value below is the mechanical port of design/tokens/zenit.tokens.json
   (ember / navy / gold over warm paper). Change the JSON first, then port
   it here; never hand-pick a colour that is not reachable from a semantic
   role. The 3 forced blocks (OS dark, [data-theme="light"], [data-theme="dark"])
   list the same role names on purpose: any role the dark blocks override is
   restored by the forced-light block. */

@font-face {
  font-family: "InterVariable";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/InterVariable-latin.woff2?v=4d30f6c080ba") format("woff2");
}
/* Metric-matched fallback: Arial scaled to Inter's box, so the swap to
   the web font barely moves a line (CLS). */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Helvetica");
  size-adjust: 107.12%;
  ascent-override: 90.44%;
  descent-override: 22.52%;
  line-gap-override: 0%;
}
/* TODO: bundle Plus Jakarta Sans + JetBrains Mono woff2 in /static/fonts/.
   Only InterVariable ships today, so the display and mono stacks name the
   brand families first and degrade to InterVariable/system faces. No
   @font-face is declared for a file that does not exist: a 404 face would
   break the swap it is meant to mask. */

:root {
  color-scheme: light dark;

  /* ---------- Tier 1: primitives ---------- */
  /* Warm paper (light page) */
  --paper: #FAF6F1;
  /* Warm stone greys (light) */
  --stone-0: #FFFFFF;
  --stone-50: #FAFAF9;
  --stone-100: #F5F5F4;
  --stone-200: #E7E5E4;
  --stone-300: #D6D3D1;
  --stone-400: #A8A29E;
  /* 450/550 are AA-floor midpoints of the same warm ramp, kept from the
     pre-brand file: text-3 needs 4.5:1 on paper (stone-500 reaches 4.35)
     and chart strokes need 3:1 on white (stone-400 reaches 2.52). */
  --stone-450: #8F8984;
  --stone-500: #79736E;
  --stone-550: #736C67;
  --stone-600: #57534E;
  --stone-700: #44403C;
  --stone-800: #292524;
  --stone-900: #1C1917;
  /* Warm charcoal (dark) */
  --char-950: #141312;
  --char-900: #171614;
  --char-850: #1C1B19;
  --char-800: #24221F;
  --char-700: #2F2C2A;
  --char-650: #3A3633;
  --char-500: #77716C;
  --char-400: #938D87;
  --char-200: #C2BCB6;
  --char-50: #F2F1EF;
  /* Neutral near-black (hue-free): the fixed rail's pure-black surface.
     A true neutral (R=G=B), not the warm charcoal or the blue navy, so the
     sidebar reads as BLACK. Single step; no fuller neutral ramp yet. */
  --neutral-950: #121212;
  /* Ember: the one accent. 600 is the brand signal (icons, focus, hero,
     decoration); 700 is the accessible action fill (white text 5.18:1). */
  --ember-50: #FBEDE7;
  --ember-100: #F7D9CB;
  --ember-200: #EFB49A;
  --ember-500: #F26B3D;
  --ember-600: #E85D2C;
  --ember-700: #C2410C;
  --ember-800: #9A3409;
  --ember-tint: #2E1A19; /* the JSON's warm ember-navy tint on dark surfaces */
  /* Navy: structure, headings, the info role */
  --navy-50: #E9EEF4;
  --navy-100: #D4DEE9;
  --navy-200: #A9BCCF;
  --navy-400: #3E5A82;
  --navy-500: #2B4768;
  --navy-600: #1F3A5F;
  --navy-700: #182E4B;
  --navy-800: #122338;
  --navy-900: #0C1826;
  /* Gold: small highlights only, never large fills, never text on white */
  --gold-100: #FBEEC7;
  --gold-200: #F7DE9E;
  --gold-500: #F9A825;
  --gold-600: #C77B00;
  --gold-700: #9A5F00;
  /* Data-viz supporting hues (series 4-5 of the JSON chart scale) */
  --teal-50: #E7F2EE;
  --teal-300: #7FD1BD;
  --teal-600: #2E7D6B;
  --teal-700: #256A5A;
  --teal-950: #16302A;
  --slate-500: #64748B;
  /* Status hues (hue-separate from the brand) */
  --green-50: #EEF8F0;
  --green-300: #7BD69A;
  --green-700: #15803D;
  --green-950: #17271C;
  --amber-50: #FDF5E7;
  --amber-300: #F2B85C;
  --amber-800: #A3480A;
  --amber-950: #2B2213;
  --red-50: #FDEFEF;
  --red-200: #FFB8B6;
  --red-300: #FF9A98;
  --red-700: #B91C1C;
  --red-800: #991B1B;
  --red-950: #2E1A19;
  /* Type */
  --font-family-inter: "InterVariable", "Inter Fallback", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-family-display: "Plus Jakarta Sans", var(--font-family-inter);
  --font-family-mono: "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace;
  /* Space: 4 px base */
  --space-0: 0px;
  --space-05: 2px;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  /* Durations: motion scale 100 / 200 / 300 (reduced motion zeroes them) */
  --duration-100: 100ms;
  --duration-200: 200ms;
  --duration-300: 300ms;
  /* Timing constants: behaviour thresholds, not motion - never scaled */
  --timing-feedback-delay: 1000ms; /* "slow swap" progress bar appears */
  --timing-dwell: 6000ms; /* a toast's reading time */

  /* ---------- Tier 2: semantic ---------- */
  /* Type roles (scale: px / line-height) */
  --font-sans: var(--font-family-inter);
  --font-display: var(--font-family-display);
  --font-mono: var(--font-family-mono);
  --font-figure: var(--font-family-mono);
  --fs-caption: 12px;
  --lh-caption: 16px;
  --fs-small: 13px;
  --lh-small: 18px;
  --fs-body: 14px;
  --lh-body: 20px;
  --fs-body-lg: 16px;
  --lh-body-lg: 24px;
  --fs-title-sm: 18px;
  --lh-title-sm: 26px;
  --fs-title: 20px;
  --lh-title: 28px;
  --fs-headline: 24px;
  --lh-headline: 32px;
  --fs-display: 30px;
  --lh-display: 36px;
  --fs-display-lg: 36px;
  --lh-display-lg: 44px;
  --fs-hero: 40px;
  --lh-hero: 48px;
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;
  --fw-extra: 800;
  --tracking-tight: -0.011em;
  --tracking-normal: 0em;
  --tracking-caps: 0.04em;
  /* Space roles */
  --sp-0: var(--space-0);
  --sp-05: var(--space-05);
  --sp-1: var(--space-1);
  --sp-2: var(--space-2);
  --sp-3: var(--space-3);
  --sp-4: var(--space-4);
  --sp-5: var(--space-5);
  --sp-6: var(--space-6);
  --sp-8: var(--space-8);
  --sp-10: var(--space-10);
  --sp-12: var(--space-12);
  --sp-16: var(--space-16);
  /* Shape: 4 / 8 / 12 / full */
  --radius-control: 4px;
  --radius-card: 8px;
  --radius-sheet: 12px;
  --radius-pill: 9999px;
  --border-w: 1px;
  --border-w-strong: 2px;
  --focus-w: 2px;
  --focus-offset: 2px;
  /* Motion */
  --dur-fast: var(--duration-100);
  --dur-base: var(--duration-200);
  --dur-slow: var(--duration-300);
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-enter: cubic-bezier(0.05, 0.7, 0.1, 1);
  --ease-exit: cubic-bezier(0.3, 0, 0.8, 0.15);
  /* Timing thresholds (not motion, not part of the scale above) */
  --feedback-delay: var(--timing-feedback-delay);
  --toast-dwell: var(--timing-dwell);
  /* Colour: light (default). Two embers, two jobs: --accent is the action
     fill/text (ember-700, white text 5.18:1); --brand is the decorative
     signal (ember-600, 3.23:1 on paper - UI/large only, never body). */
  --bg: var(--paper);
  --surface: var(--stone-0);
  --surface-sunken: var(--stone-100);
  --surface-hover: var(--stone-100);
  --surface-selected: var(--ember-50);
  --surface-raised: var(--stone-0);
  --text: var(--stone-900);
  --text-2: var(--stone-600);
  --text-3: var(--stone-550);
  --text-on-accent: var(--stone-0);
  --text-on-danger: var(--stone-0);
  --border-subtle: var(--stone-200);
  --border: var(--stone-300);
  --border-strong: var(--stone-500);
  --brand: var(--ember-600);
  --accent: var(--ember-700);
  --accent-hover: var(--ember-800);
  --accent-subtle: var(--ember-50);
  --focus: var(--ember-600);
  --success: var(--green-700);
  --success-subtle: var(--green-50);
  --warning: var(--amber-800);
  --warning-text: var(--amber-800);
  --warning-subtle: var(--amber-50);
  --danger: var(--red-700);
  --danger-hover: var(--red-800);
  --danger-subtle: var(--red-50);
  --info: var(--navy-600);
  --info-subtle: var(--navy-50);
  /* Temperature regimes (PRD §4.5 zone_type): a physical class, not a
     status. Cold hues (navy/teal) = cold storage; warm (stone/gold) =
     ambient. Each has a text colour and a subtle tint, light + dark. */
  --temp-frozen: var(--navy-600);
  --temp-frozen-subtle: var(--navy-50);
  --temp-refrigerated: var(--teal-700);
  --temp-refrigerated-subtle: var(--teal-50);
  --temp-ambient: var(--stone-600);
  --temp-ambient-subtle: var(--stone-100);
  --temp-dry: var(--gold-700);
  --temp-dry-subtle: var(--gold-100);
  /* Data-viz order: navy, ember deep, gold deep, muted teal, muted slate */
  --chart-1: var(--navy-600);
  --chart-2: var(--ember-700);
  --chart-3: var(--gold-600);
  --chart-4: var(--teal-600);
  --chart-5: var(--slate-500);
  --chart-muted: var(--stone-450);
  --chart-grid: var(--border-subtle);
  /* Fan chart: nested forecast bands, aliases of the navy ramp (no new
     colour). More certain = more central and darker; the "hoy" seam and
     the history stroke complete the honest band provenance. */
  --fan-95: var(--navy-100);
  --fan-80: var(--navy-200);
  --fan-50: var(--navy-400);
  --fan-median: var(--navy-600);
  --fan-hist: var(--stone-400);
  --fan-now: var(--ember-600);
  --backdrop: rgb(28 25 23 / 0.45);
  --shadow-1: 0 1px 2px rgb(28 25 23 / 0.06), 0 2px 8px rgb(28 25 23 / 0.06);
  --shadow-2: 0 4px 12px rgb(28 25 23 / 0.10), 0 16px 32px rgb(28 25 23 / 0.10);

  /* ---------- Tier 3: component ---------- */
  /* Density (comfortable default) */
  --row-h: 40px;
  --cell-px: 12px;
  --control-h: 36px;
  --target-min: 44px;
  --target-floor: 56px; /* floor primary actions: gloves, one hand (brief §6) */
  /* Layout */
  --content-max: 1440px;
  --gutter: 24px;
  --nav-w: 240px;
  --nav-w-collapsed: 64px;
  --settings-w: 224px;
  --topbar-h: 56px;
  --measure: 68ch;
  --form-max: 560px;
  --auth-w: 420px;
  --dialog-w: 480px;
  --dialog-w-sm: 360px;
  --dialog-w-lg: 640px;
  --backdrop-blur: 6px;
  --palette-w: 560px;
  --palette-list-h: 50vh;
  --menu-w: 256px;
  --toast-w: 360px;
  --table-max-h: 70vh;
  --mobile-nav-w: 288px;
  /* Sidebar: a hue-free near-black rail (the navy ramp stays for structure
     headings / info / data-viz, not the fixed rail). The sidebar is the
     same neutral black whatever the theme (structural, not a theme role),
     so these are defined once and never redefined. Text stays light on
     dark; the ember "you-are-here" dot and active fill are the only colour. */
  --side-bg: var(--neutral-950);
  --side-text: var(--stone-300);
  --side-text-hi: var(--navy-100);
  --side-text-max: var(--stone-0);
  --side-hover: rgba(255, 255, 255, 0.08);
  --side-rule: rgba(255, 255, 255, 0.12);
  /* Controls */
  --btn-h: var(--control-h);
  --btn-px: var(--sp-4);
  --input-h: calc(var(--control-h) + var(--sp-2));
  --input-border: var(--border-strong);
  --check-size: 20px;
  --icon-sm: 16px;
  --icon-md: 20px;
  --icon-lg: 24px;
  --avatar-size: 32px;
  --dot-size: 8px;
  --chip-h: 24px;
  --spark-w: 80px;
  --spark-h: 24px;
  --kpi-value-fs: var(--fs-display);
  --kpi-value-lh: var(--lh-display);
  --hero-fs: var(--fs-hero);
  --hero-lh: var(--lh-hero);
  --z-sticky: 10;
  --z-nav: 30;
  --z-menu: 40;
  --z-toast: 50;
  --z-skip: 60;
  /* Marketing: device frames, product shots, trust strips. The shot
     screens render the real components zoomed down; the phone is CSS 3D. */
  --device-bezel: var(--char-900);
  --device-edge: var(--stone-300);
  --device-side: var(--char-700);
  --bezel-w: 10px;
  --laptop-w: 1040px;
  --shot-zoom: 0.8;
  --shot-zoom-md: 0.66;
  --shot-zoom-sm: 0.5;
  --phone-w: 264px;
  --phone-w-sm: 156px;
  --phone-radius: 40px;
  --phone-zoom: 0.76;
  --phone-perspective: 1400px;
  --phone-tilt-y: -18deg;
  --phone-tilt-x: 6deg;
  --reader-w: 72px;
  --reader-w-sm: 40px;
  --qr-shot: 92px;
  --plate-h: 72px;
  --logo-h: 30px;
  --logo-h-tall: 44px;
  --badge-clear: 40px;
  /* Glyph masks (alpha only: colour comes from currentColor) */
  --mask-alert: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 16h.01M12 8v4M15.312 2a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586l-4.688-4.688A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2z'/%3E%3C/svg%3E");

  /* Wallboard (tier-3): component knobs sized for a video wall, not the
     640–1440 content ramp (D145). These are the only new numerics of the
     wallboard; colours stay reachable from the tier-1/2 ramps. */
  --wb-chrome-h: 56px;
  --wb-alarmbar-h: 64px;
  --wb-gutter: clamp(16px, 2.5vw, 40px);
  --wb-label: 13px;
  --wb-stat: clamp(22px, 2.2vw, 34px);
  --wb-hero: clamp(44px, 6vw, 96px);
  --wb-clock: clamp(16px, 1.4vw, 22px);
  --wb-radius: 8px;
  --wb-stage-pad-b: 96px;
  --wb-stage-pad-b-sm: 72px;
  --wb-panel-pad: clamp(20px, 3vw, 48px);
  --wb-panel-min-h: 72vh;
  --wb-head-mb: clamp(16px, 2.5vw, 32px);
  --wb-title: clamp(20px, 2vw, 30px);
  --wb-kpi-gap: clamp(16px, 2.5vw, 40px);
  --wb-spark-h: 40px;
  --wb-oee-gap: clamp(20px, 3vw, 48px);
  --wb-facts-pad: clamp(20px, 3vw, 32px);
  --wb-track-h: 10px;
  --wb-line-min-h: 56px;
  --wb-alarm-min-h: 52px;
  --wb-line-name: clamp(15px, 1.4vw, 20px);
  --wb-line-state: clamp(14px, 1.2vw, 17px);
  --wb-line-kg: clamp(15px, 1.4vw, 20px);
  --wb-line-gap: clamp(12px, 2vw, 28px);
  --wb-alarm-word: clamp(14px, 1.3vw, 18px);
  --wb-empty-title: clamp(18px, 1.6vw, 22px);
  --wb-empty-body: 15px;
  --wb-rot-dot: 7px;
  --wb-rot-dot-gap: 5px;
  --wb-rot-gap: 10px;
  --wb-fact-label-mb: 10px;
  --wb-problem-dot: 6px;

  /* Wallboard (tier-3) ISA-101 state -> colour. The mechanical port of
     web.wallboardStates (the one server-side block). Gray = normal. */
  /* Operations console (D75): the saga DAG (the signature) and the
     adapter directory. Geometry only — colour stays on the semantic
     roles (forward solid, compensation dashed --info, poison dashed
     --danger). */
  --dag-node-w: 150px;
  --dag-edge-w: 34px;
  --dag-edge-h: 20px;
  --dag-edge-sm-w: 22px;
  --dag-term-w: 48px;
  --dag-term-circle: 40px;
  --adapter-col-min: 280px;

  --wb-state-running-bg: var(--char-800); --wb-state-running-fg: var(--char-50); --wb-state-running-ribbon: transparent;
  --wb-state-idle-bg: var(--char-900);     --wb-state-idle-fg: var(--char-400);    --wb-state-idle-ribbon: var(--char-500);
  --wb-state-warning-bg: var(--amber-950); --wb-state-warning-fg: var(--amber-300); --wb-state-warning-ribbon: var(--amber-300);
  --wb-state-alarm-bg: var(--red-950);     --wb-state-alarm-fg: var(--red-300);     --wb-state-alarm-ribbon: var(--red-300);
  --wb-state-maint-bg: var(--navy-800);    --wb-state-maint-fg: var(--navy-200);    --wb-state-maint-ribbon: var(--navy-200);
}

/* Dark: tuned separately, never inverted. Raised layers get lighter.
   Accent brightens to ember-600 for text on charcoal (5.33:1 on the page)
   and the on-accent ink flips to navy-900 (5.14:1): white on ember-600 is
   only 3.48:1, the forbidden pair. Light keeps the ember-700 action fill. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: var(--char-950);
    --surface: var(--char-850);
    --surface-sunken: var(--char-900);
    --surface-hover: var(--char-800);
    --surface-selected: var(--ember-tint);
    --surface-raised: var(--char-800);
    --text: var(--char-50);
    --text-2: var(--char-200);
    --text-3: var(--char-400);
    --text-on-accent: var(--navy-900);
    --text-on-danger: var(--red-950);
    --border-subtle: var(--char-700);
    --border: var(--char-650);
    --border-strong: var(--char-500);
    --accent: var(--ember-600);
    --accent-hover: var(--ember-500);
    --accent-subtle: var(--ember-tint);
    --focus: var(--ember-500);
    --success: var(--green-300);
    --success-subtle: var(--green-950);
    --warning: var(--amber-300);
    --warning-text: var(--amber-300);
    --warning-subtle: var(--amber-950);
    --danger: var(--red-300);
    --danger-hover: var(--red-200);
    --danger-subtle: var(--red-950);
    --info: var(--navy-200);
    --info-subtle: var(--navy-800);
    --temp-frozen: var(--navy-200);
    --temp-frozen-subtle: var(--navy-800);
    --temp-refrigerated: var(--teal-300);
    --temp-refrigerated-subtle: var(--teal-950);
    --temp-ambient: var(--char-200);
    --temp-ambient-subtle: var(--char-900);
    --temp-dry: var(--amber-300);
    --temp-dry-subtle: var(--amber-950);
    --chart-muted: var(--char-500);
    --chart-grid: var(--border-subtle);
    --fan-95: var(--navy-800);
    --fan-80: var(--navy-400);
    --fan-50: var(--navy-200);
    --fan-median: var(--char-50);
    --fan-hist: var(--char-500);
    --fan-now: var(--ember-500);
    --backdrop: rgb(0 0 0 / 0.6);
    --shadow-1: none;
    --shadow-2: 0 8px 24px rgb(0 0 0 / 0.5);
    --device-bezel: var(--char-800);
    --device-edge: var(--char-500);
    --device-side: var(--char-650);
  }
}
/* Forced light: the mirror of the forced-dark block below, so a session or
   kiosk pinned to light keeps the light roles whatever the OS asks for.
   Same role names as the dark block, so nothing is left half-switched. */
:root[data-theme="light"] {
  --bg: var(--paper);
  --surface: var(--stone-0);
  --surface-sunken: var(--stone-100);
  --surface-hover: var(--stone-100);
  --surface-selected: var(--ember-50);
  --surface-raised: var(--stone-0);
  --text: var(--stone-900);
  --text-2: var(--stone-600);
  --text-3: var(--stone-550);
  --text-on-accent: var(--stone-0);
  --text-on-danger: var(--stone-0);
  --border-subtle: var(--stone-200);
  --border: var(--stone-300);
  --border-strong: var(--stone-500);
  --accent: var(--ember-700);
  --accent-hover: var(--ember-800);
  --accent-subtle: var(--ember-50);
  --focus: var(--ember-600);
  --success: var(--green-700);
  --success-subtle: var(--green-50);
  --warning: var(--amber-800);
  --warning-text: var(--amber-800);
  --warning-subtle: var(--amber-50);
  --danger: var(--red-700);
  --danger-hover: var(--red-800);
  --danger-subtle: var(--red-50);
  --info: var(--navy-600);
  --info-subtle: var(--navy-50);
  --temp-frozen: var(--navy-600);
  --temp-frozen-subtle: var(--navy-50);
  --temp-refrigerated: var(--teal-700);
  --temp-refrigerated-subtle: var(--teal-50);
  --temp-ambient: var(--stone-600);
  --temp-ambient-subtle: var(--stone-100);
  --temp-dry: var(--gold-700);
  --temp-dry-subtle: var(--gold-100);
  --chart-muted: var(--stone-450);
  --chart-grid: var(--border-subtle);
  --fan-95: var(--navy-100);
  --fan-80: var(--navy-200);
  --fan-50: var(--navy-400);
  --fan-median: var(--navy-600);
  --fan-hist: var(--stone-400);
  --fan-now: var(--ember-600);
  --backdrop: rgb(28 25 23 / 0.45);
  --shadow-1: 0 1px 2px rgb(28 25 23 / 0.06), 0 2px 8px rgb(28 25 23 / 0.06);
  --shadow-2: 0 4px 12px rgb(28 25 23 / 0.10), 0 16px 32px rgb(28 25 23 / 0.10);
  --device-bezel: var(--char-900);
  --device-edge: var(--stone-300);
  --device-side: var(--char-700);
}
:root[data-theme="dark"] {
  --bg: var(--char-950);
  --surface: var(--char-850);
  --surface-sunken: var(--char-900);
  --surface-hover: var(--char-800);
  --surface-selected: var(--ember-tint);
  --surface-raised: var(--char-800);
  --text: var(--char-50);
  --text-2: var(--char-200);
  --text-3: var(--char-400);
  --text-on-accent: var(--navy-900);
  --text-on-danger: var(--red-950);
  --border-subtle: var(--char-700);
  --border: var(--char-650);
  --border-strong: var(--char-500);
  --accent: var(--ember-600);
  --accent-hover: var(--ember-500);
  --accent-subtle: var(--ember-tint);
  --focus: var(--ember-500);
  --success: var(--green-300);
  --success-subtle: var(--green-950);
  --warning: var(--amber-300);
  --warning-text: var(--amber-300);
  --warning-subtle: var(--amber-950);
  --danger: var(--red-300);
  --danger-hover: var(--red-200);
  --danger-subtle: var(--red-950);
  --info: var(--navy-200);
  --info-subtle: var(--navy-800);
  --temp-frozen: var(--navy-200);
  --temp-frozen-subtle: var(--navy-800);
  --temp-refrigerated: var(--teal-300);
  --temp-refrigerated-subtle: var(--teal-950);
  --temp-ambient: var(--char-200);
  --temp-ambient-subtle: var(--char-900);
  --temp-dry: var(--amber-300);
  --temp-dry-subtle: var(--amber-950);
  --chart-muted: var(--char-500);
  --chart-grid: var(--border-subtle);
  --fan-95: var(--navy-800);
  --fan-80: var(--navy-400);
  --fan-50: var(--navy-200);
  --fan-median: var(--char-50);
  --fan-hist: var(--char-500);
  --fan-now: var(--ember-500);
  --backdrop: rgb(0 0 0 / 0.6);
  --shadow-1: none;
  --shadow-2: 0 8px 24px rgb(0 0 0 / 0.5);
  --device-bezel: var(--char-800);
  --device-edge: var(--char-500);
  --device-side: var(--char-650);
}

/* Density: one attribute swaps a handful of sizes, never the component.
   Compact suits scanning 200 shops; touch is automatic on coarse pointers. */
:root[data-density="compact"],
[data-density="compact"] {
  --row-h: 32px;
  --cell-px: 8px;
  --control-h: 32px;
  --fs-body: 13px;
  --lh-body: 18px;
}
@media (pointer: coarse) {
  :root {
    --row-h: 48px;
    --control-h: 44px;
    --fs-body: 16px;
    --lh-body: 24px;
  }
}
@media (max-width: 640px) {
  :root {
    --gutter: 16px;
  }
}
@media (prefers-reduced-transparency: reduce) {
  :root { --backdrop-blur: 0px; }
}
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 0ms;
    --dur-base: 0ms;
    --dur-slow: 0ms;
  }
}
