/* ============================================================
   Emerald Bay OS — Design Tokens. THE single source; nothing else
   in the app may declare a custom property.

   2026-07-27 — token sources unified. base.html carried its own
   complete :root + [data-theme="dark"] block, and because that
   out-specified this file's :root, THAT block was what actually
   rendered in dark mode while this file governed light. The two
   themes were running two different palettes out of two different
   files.

   The dark values below are the ones base.html was really producing —
   a slate/emerald palette, not the pure-black Vercel palette this
   file used to claim. They were folded in verbatim so the merge
   changed nothing visually. The prose in docs/design-system/ and
   docs/atlas/ that describes the app as "pure-black Vercel" describes
   the intended direction, not the current render; retargeting the
   values to that direction is a deliberate restyle scheduled for
   after the Aug 1 warehouse cutover.

   2026-08-07 — overnight overhaul, base+skin split (5 themes).
   Two independent attributes now live on <html>, both written by
   static/js/theme.js's applyTheme() (and mirrored, inline, by the
   pre-paint boot snippet at the top of base.html/login.html/
   change_password.html so there is no FOUC):

     data-theme       one of dark | light | muted-corporate | hardman | glass
     data-theme-base  dark | light — which GROUND that theme sits on

   Every theme that is NOT literally "light" inherits the dark ground
   declared in :root below unless it sets data-theme-base="light" (only
   muted-corporate does). This is why the ground block further down keys
   off the attribute pair below rather than repeating ~90 declarations
   per skin — see routes/profile.py's VALID_THEMES for the canonical
   theme name list; it must stay in sync with the THEMES array in
   static/js/theme.js and the skin blocks in this file.

   The theme-picker in the sidebar profile popover (base.html) previews
   each theme with three small colour bars sampled from THAT theme's
   own tokens — those bars are necessarily hardcoded (a var() there
   would resolve to the CURRENTLY active theme, not the one being
   previewed), so if you change a value below, update the matching
   .theme-swatch--<name> rule in base.html's <style> block too. */
:root {
  /* Tell the UA to paint native controls dark */
  color-scheme: dark;

  /* ---- Type scale (Master Plan §1.1) — seven steps, defined once. Nothing
     below --fs-micro may ship; half-pixel sizes (12.5px et al.) are banned.
     Existing surfaces have not been swept to consume these yet (that is a
     per-surface follow-up), but every NEW rule — including this popover —
     must use these rather than a literal px size. */
  --fs-micro: 10px;   /* uppercase eyebrow / table TH only. Never body copy. */
  --fs-xs:    11px;   /* dense metadata: timestamps, counts, chip labels */
  --fs-sm:    12px;   /* dense table cells, secondary UI */
  --fs-base:  13px;   /* DEFAULT body + primary table cells */
  --fs-md:    15px;   /* card titles, drawer headings */
  --fs-lg:    20px;   /* page titles */
  --fs-xl:    26px;   /* KPI numerals only */

  /* ---- Spacing scale (Master Plan §1.2) — 4px base. Odd literals (5, 7, 9,
     11, 13, 15, 18) are forbidden in new code; apply to surfaces you rewrite,
     not as a global find-replace. */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;

  /* Nocturne / Emerald Bay Master Design System Tokens */
  --eb-canvas:        #0f1018;
  --eb-bg-deep:       #12131e;
  --eb-panel:         #1b1d2b;
  --eb-panel-hi:      #1f2130;
  --eb-mono:          ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --radius-sm:        4px;
  --radius-md:        8px;
  --radius-lg:        14px;
  --eb-r-pill:        99px;
  /* G1 fix — templates/dashboard.html (and others) call var(--eb-r-sm) /
     var(--eb-r-md), tokens that never existed; every one of those call
     sites rendered border-radius: 0. Aliasing here is the safety net so
     they resolve correctly regardless of whether/when each call site is
     renamed to --radius-sm/--radius-md directly. */
  --eb-r-sm:          var(--radius-sm);
  --eb-r-md:          var(--radius-md);
  --eb-row:           32px;
  --eb-target:        32px;
  --eb-text:          var(--fs-base);

  /* Signal roles (low-chroma muted palette per Nocturne §1a) */
  --eb-ok:            #7fbf9a;
  --eb-ok-ink:        #2f6b4f;
  --eb-ok-tint:       #dcefe4;
  --eb-attention:     #d9b36a;
  --eb-attention-ink: #3f2f10;
  --eb-alert:         #d98b8b;

  /* Department marks */
  --eb-fab:           #7f9fd4;
  --eb-paint:         #b195cf;
  --eb-qc:            #cf94ac;
  --eb-router:        #6fb3bd;
  --eb-flatbed:       #d9a37f;

  /* Surface ramp mapped directly to Nocturne tokens */
  --bg:       #0f1018;   /* --eb-canvas page ground */
  --bg-1:     #12131e;   /* --eb-bg-deep card ground */
  --bg-2:     #1b1d2b;   /* --eb-panel raised popover/modal */
  --bg-3:     #1f2130;   /* --eb-panel-hi input/hover */
  --bg-4:     #2b2e42;   /* selected/pressed */

  /* Generic borders */
  --border:       rgba(255, 255, 255, 0.08);
  --border-2:     rgba(255, 255, 255, 0.12);
  --border-3:     #3b3e54;
  --border-brand: rgba(166, 154, 251, 0.42);
  /* G3 fix — templates/warehouse/labels.html and admin_access.html reference
     --border-1 / --text-1, which never existed, with hardcoded dark
     fallbacks (var(--border-1, #2d3142)) that render dark-on-light in every
     light theme. Aliased here rather than edited at the call sites so the
     fix lands regardless of which surface is touched first. */
  --border-1:     var(--border);

  --text:     #f8fafc;
  --text-1:   var(--text);
  --text-2:   #cbd5e1;
  --text-3:   #94a3b8;
  /* Carries timestamps and field labels, so this is body text rather than
     decoration. The former slate-500 value measured 3.88:1 on --bg-1.
     This keeps a visible step below --text-3 while clearing WCAG AA. */
  --text-4:   #888ca0;

  /* --accent means BRAND, and only brand: wordmark, active nav, primary
     action. It is emerald in both themes now — it used to be #10b981 in dark
     and #0a0a0a in light, i.e. a colour in one theme and a neutral in the
     other, so nothing could rely on it meaning the same thing twice. */
  --accent:   var(--brand);

  /* Info is BLUE in both themes. It was cyan (#06b6d4) in dark and blue
     (#2563eb) in light — the same status changed hue with the theme. True
     cyan is still available as --cyan for charts. */
  --blue:     #58c4dd;
  --blue-2:   #7dd3e7;
  --blue-bg:  rgba(88, 196, 221, 0.14);

  /* Success is GREEN, not the brand emerald. --green and --accent were both
     #10b981, so "this is a Hardman thing" and "this succeeded" rendered
     identically and neither could be read reliably. */
  --green:    #22c55e;
  --green-bg: rgba(34, 197, 94, 0.15);
  --amber:    #f59e0b;
  --amber-bg: rgba(245, 158, 11, 0.15);
  --red:      #ef4444;
  --red-bg:   rgba(239, 68, 68, 0.15);
  --orange:   #f97316;
  --orange-bg:rgba(249, 115, 22, 0.15);
  --purple:   #a855f7;
  --purple-bg:rgba(168, 85, 247, 0.15);

  /* ---- Electric Nocturne brand -------------------------------------------
     Iris-violet carries navigation, selection, focus, and primary actions.
     Cyan carries informational affordances, while green is reserved for
     completed and healthy states. The tokens keep those roles consistent
     without requiring per-template color overrides.

     (Do not write a light-theme selector literally in a comment in this file:
     tests/test_ux_hardening.py finds the light block with a non-greedy regex
     and will match the comment instead. That cost a red suite once.)
       --brand       structural: borders, dots, active indicators. Theme-stable.
       --brand-text  foreground on a dark or tinted surface. Flips for AA.
       --brand-bg    translucent tint; composites over either theme. */
  /* Iris-violet separates "current/actionable" from the warmer
     sage used for completed/healthy states. The solid stays deep enough for
     3:1 non-text contrast on light cards; the brighter text step is reserved
     for dark surfaces and soft tints. */
  --brand-rgb:   139, 124, 246;
  --brand:       #8b7cf6;
  --brand-hover: #a69afb;
  --brand-text:  #b8aeff;
  --brand-bg:    rgba(var(--brand-rgb),0.15);
  /* Secondary brand — gradients/companion accents that want two stops
     instead of one flat --brand fill (theme-swatch previews, the glass
     skin's header wash). Cyan pairs with iris violet in the default/glass
     themes; the hardman skin overrides this to its own gradient's orange
     stop so the pairing stays theme-correct. */
  --brand-2:     #22d3ee;

  /* Steel blue is the app's orientation colour: selected views and scopes,
     locations, informational counts, and controls that open or inspect
     something. Violet remains reserved for actions and the current workflow. */
  --view-accent-rgb:    139, 159, 201;
  --view-accent:        #8b9fc9;
  --view-accent-text:   #b2c1df;
  --view-accent-bg:     rgba(139, 159, 201, 0.16);
  --view-accent-border: rgba(139, 159, 201, 0.40);

  /* Frosted surface used by the page headers and `glass-card`. Was hardcoded
     as rgba(15,23,42,0.75) with an emerald border in six templates.
     --glass-blur is a token because backdrop-filter is the single most
     expensive thing this app asks of a shop-floor tablet: turn it down here
     once rather than hunting six call sites. */
  --glass-bg:   rgba(15, 23, 42, 0.75);
  --glass-blur: 12px;
  /* Glass-theme-only additions (Master Plan §2.7). Neutral/inert defaults
     here so every non-glass theme is unaffected; the [data-theme="glass"]
     skin below is what actually turns these on. */
  --glass-border: var(--border-2);
  --glass-bg-2:   var(--bg-2);
  --glass-noise:  0;

  /* Sleek Slate Sidebar tokens */
  --sidebar-bg:                 #0b0f19;
  --sidebar-border:             #161e2e;
  --sidebar-item-hover:         rgba(255, 255, 255, 0.05);
  --sidebar-item-active:        #1c2433;
  --sidebar-item-active-border: #2a364f;
  --sidebar-text:               #94a3b8;
  --sidebar-text-active:        #ffffff;
  --sidebar-icon-active:        #8eaee3;
  --pink:     #db2777;

  /* Dept colors mapped to Nocturne low-chroma tokens */
  --dept-fab:        var(--eb-fab);
  --dept-paint:      var(--eb-paint);
  --dept-electrical: var(--eb-attention);
  --dept-install:    var(--eb-ok);
  --dept-qc:         var(--eb-qc);
  --dept-router:     var(--eb-router);
  --dept-flatbed:    var(--eb-flatbed);

  /* Two shadows, both carrying a hairline ring — on a dark ground the ring does
     the work and the blur only separates. The ring on --shadow-lg was
     rgba(16,185,129,0.2), i.e. brand emerald on every raised surface in the
     app; it is neutral now. --shadow had lost its ring entirely and carried a
     32px blur, which reads as haze rather than elevation at this density. */
  --shadow:    0 1px 2px rgba(0,0,0,0.30), 0 0 0 1px rgba(255,255,255,0.06);
  --shadow-lg: 0 12px 32px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.08);
  /* G2 fix — static/css/warehouse-inventory.css:994/997 references
     --shadow-sm on the mobile inventory cards; it never existed, so those
     cards render with no elevation at all. Has its own light override
     below rather than aliasing --shadow, because --shadow's 0.30 dark /
     0.04 light split is tuned for a bigger surface than a small card. */
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.18), 0 0 0 1px var(--border);
  /* Renamed 3-step elevation scale (Master Plan §2.7) sitting alongside the
     original names rather than replacing them, so nothing that already
     reads --shadow/--shadow-lg has to change. */
  --elev-1: var(--shadow);
  --elev-2: var(--shadow-lg);

  /* Modal/drawer backdrop scrim. Heavier in dark mode where the underlying
     surface is already dark; the lighter light-theme value is below. */
  --scrim: rgba(0,0,0,0.65);

  /* Semantic foreground for text rendered on saturated coloured backgrounds
     (blue/red/green/orange buttons, avatar circles, badges). Stays white in
     both themes — the same way Tailwind's `text-white` is theme-independent. */
  --text-inverted: #fff;

  /* Lighter colored text — used on coloured *-bg surfaces in dark mode where
     the base --green/--amber/--orange/--red read too saturated. Light override
     darkens them for AA contrast on white. */
  --green-text:  #4ade80;
  --amber-text:  #fbbf24;
  --orange-text: #fb923c;
  --red-text:    #fca5a5;
  --blue-text:   #7dd3e7;
  --purple-text: #c084fc;

  /* Translucent colored borders at 0.3 alpha — state borders on selects/pills. */
  --blue-border:   rgba(88,196,221,0.34);
  --green-border:  rgba(34,197,94,0.3);
  --amber-border:  rgba(245,158,11,0.3);
  --red-border:    rgba(239,68,68,0.3);
  --orange-border: rgba(249,115,22,0.3);
  --purple-border: rgba(168,85,247,0.3);

  /* Calendar dispatch-cell tints — theme-aware. */
  --cell-past:    rgba(255,255,255,0.02);
  --cell-today:   rgba(59,130,246,0.10);
  --cell-weekend: rgba(255,255,255,0.04);

  /* Avatar palette extras (used by admin_users hash-to-color). */
  --avatar-teal:   #14b8a6;
  --avatar-violet: #8b5cf6;

  /* Darker green/orange variants for light-mode AA contrast on button text. */
  --green-strong:  #22c55e;
  --orange-strong: #f97316;

  /* Subtle inset ring used on avatar/chip surfaces. */
  --ring-inset: inset 0 0 0 1px rgba(0,0,0,0.18);

  /* Black text on amber background — stable in both themes. */
  --text-on-amber: #000;

  /* Foreground for text sitting on a SOLID --brand fill (primary buttons, the
     active segment of a view switcher). Near-black provides strong contrast
     against the iris fill. Theme-stable for the same reason
     --brand is — the fill does not change
     between themes, so its foreground must not either.

     Added 2026-07-27 because three call sites had borrowed --text-on-amber for
     this. The value was right and the name was a lie, which is the kind of thing
     that survives until someone retunes amber and silently breaks the brand
     buttons. */
  --text-on-brand: #000;

  /* Calendar status-filter swatches — dark uses translucent saturated;
     light override (below) keeps the original soft pastels. */
  --filter-swatch-scheduled:  var(--amber-bg);
  --filter-swatch-inprogress: var(--amber-bg);
  --filter-swatch-done:       var(--blue-bg);
  --filter-swatch-hold:       var(--orange-bg);
  --filter-swatch-overdue:    var(--red-bg);

  /* Extended chart palette — theme-stable saturated swatches for analytics
     bars/pies. Stable across themes because they sit on neutral backgrounds. */
  --sky:   #0ea5e9;
  --cyan:  #06b6d4;
  --slate: #94a3b8;
  /* Visualization-engine roles. These are intentionally separate from brand
     and status: cyan/indigo/violet describe data series, never success. */
  --viz-canvas:   rgba(8, 17, 31, 0.88);
  --viz-panel:    rgba(17, 28, 46, 0.82);
  --viz-border:   rgba(141, 162, 189, 0.16);
  --viz-grid:     rgba(141, 162, 189, 0.08);
  --viz-series-1: #22d3ee;
  --viz-series-2: #60a5fa;
  --viz-series-3: #818cf8;
  --viz-series-4: #a855f7;
  --viz-warning:  #f5b942;
  --viz-risk:     #fb7185;
  /* 8-series categorical extension (2026-08-08, Workstream A). Series 5-8
     stop borrowing --viz-warning/--viz-risk — semantic tokens never do
     categorical duty again. Order fixed; never reassign by rank. Hues chosen
     per the dataviz skill's method and validated (CVD-adjacent >= 10.5,
     all >= 3:1 on the composited chart surface #09111e). */
  --viz-series-5: #34d399;
  --viz-series-6: #fb923c;
  --viz-series-7: #f472b6;
  --viz-series-8: #a3e635;
  /* Semantic chart roles. positive completes the warning/risk pair;
     neutral is "no polarity" (progress-donut track, unsegmented scatter). */
  --viz-positive: #4ade80;
  --viz-neutral:  #94a3b8;
  /* 5-stop sequential ramp (single cyan hue, dim -> bright on the dark
     ground). Replaces heatmap.ts's improvised grid->series-3->series-4. */
  --viz-ramp-1:   #0e7490;
  --viz-ramp-2:   #0891b2;
  --viz-ramp-3:   #06b6d4;
  --viz-ramp-4:   #22d3ee;
  --viz-ramp-5:   #a5f3fc;
  /* 3-stop diverging set: rose -> neutral gray midpoint -> green. */
  --viz-diverge-neg: #fb7185;
  --viz-diverge-mid: #94a3b8;
  --viz-diverge-pos: #4ade80;
  /* Lightest step of the calendar's 7-stop due-date heat ramp
     (yellow -> amber -> orange -> red -> pink -> blue). Every other stop landed
     on an existing token; this one had no match and was the last raw hex left in
     calendar.html outside its print block. Named for its role rather than as
     `--yellow-2`, because there is no `--yellow` base token for it to be the
     second step of. Theme-stable: the ramp sits on neutral cells in both themes. */
  --heat-lightest: #fde047;

  /* Tooltip pill — always-dark in both themes (conventional tooltip).
     Used by PDF Review annotation hover tooltips and any future
     always-dark contextual popovers. */
  --tooltip-bg:     rgba(15, 23, 42, 0.95);
  --tooltip-text:   #f8fafc;
  --tooltip-border: rgba(255, 255, 255, 0.08);

  /* ---- Motion ------------------------------------------------------------
     Three durations and two easings, so timing is a decision made once rather
     than per-component. --ease-out overshoots slightly and is for things that
     ENTER (menus, toasts, modals); --ease is symmetric and is for state
     changes on something already on screen (hover, active, colour). Anything
     that moves must honour the reduced-motion block at the end of
     components.css. */
  --dur-1: .10s;   /* colour/opacity swaps: must feel instant */
  --dur-2: .16s;   /* the default — hover, focus, small transforms */
  --dur-3: .24s;   /* entrances: dropdown, drawer, modal */
  --ease:     cubic-bezier(.4, 0, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  /* Keyboard focus ring. Deliberately NOT the same as the hover border: a
     focus ring has to be visible against every surface in the app, so it is
     an offset outline rather than a border-colour change. */
  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--brand-text);

  /* ---- Chip palette ------------------------------------------------------
     Backing store for .dept-chip (33 variants over 27 department names — 6 are legacy slugs, see components.css) and any other
     hue-coded chip. The *-bg tints are translucent, so one value composites
     correctly over both themes; only the foreground needs a light override
     (see the light block below), because the dark foregrounds are 300/400-
     weight hues that land at ~2.2:1 on a near-white surface. */
  --chip-blue-bg:    rgba(59,130,246,0.14);  --chip-blue:    #60a5fa;
  --chip-purple-bg:  rgba(168,85,247,0.14);  --chip-purple:  #c084fc;
  --chip-amber-bg:   rgba(245,158,11,0.14);  --chip-amber:   #fbbf24;
  --chip-green-bg:   rgba(34,197,94,0.14);   --chip-green:   #4ade80;
  --chip-pink-bg:    rgba(236,72,153,0.14);  --chip-pink:    #f472b6;
  --chip-cyan-bg:    rgba(6,182,212,0.16);   --chip-cyan:    #22d3ee;
  --chip-yellow-bg:  rgba(202,138,4,0.16);   --chip-yellow:  #facc15;
  --chip-orange-bg:  rgba(249,115,22,0.14);  --chip-orange:  #fb923c;
  --chip-sky-bg:     rgba(14,165,233,0.14);  --chip-sky:     #38bdf8;
  --chip-violet-bg:  rgba(139,92,246,0.14);  --chip-violet:  #a78bfa;
  --chip-emerald-bg: rgba(16,185,129,0.14);   --chip-emerald: #5ee0ae;
  --chip-fuchsia-bg: rgba(217,70,239,0.14);  --chip-fuchsia: #e879f9;
  --chip-slate-bg:   rgba(100,116,139,0.16); --chip-slate:   #cbd5e1;
  --chip-teal-bg:    rgba(20,184,166,0.14);  --chip-teal:    #5eead4;
  --chip-rose-bg:    rgba(244,63,94,0.14);   --chip-rose:    #fb7185;
  --chip-lime-bg:    rgba(132,204,22,0.14);  --chip-lime:    #a3e635;
  --chip-stone-bg:   rgba(168,162,158,0.14); --chip-stone:   #d6d3d1;
  --chip-indigo-bg:  rgba(99,102,241,0.14);  --chip-indigo:  #818cf8;

  /* Saturated-button hover shades. Theme-stable: these sit under white text
     in both themes, exactly like --text-inverted. */
  --blue-hover:  #2563eb;
  --green-hover: #16a34a;
  --red-hover:   #dc2626;

  /* Sidebar section labels — dimmer than --sidebar-text. */
  --sidebar-text-dim: #64748b;
  /* Hairline highlight on the sidebar's right edge. Inverts with the theme. */
  --sidebar-edge: rgba(255,255,255,0.02);

  /* CR80 badge inks are print-media constants, independent of app theme. */
  --badge-ink: #1D1F20;
  --badge-steel: #5980A6;
  --badge-tint: #EEF6FF;
  --badge-keyline: #B5D9FD;
  --badge-paper: #FFFFFF;
  --badge-paper-field: #F2F2F3;
  --badge-band-operator: #1D2D3D;
  --badge-band-visitor: #A6301F;
  --badge-band-contractor: #416180;
  --badge-band-office: #2B2B2D;
  --badge-dark-plate: #2C455D;
  --badge-dark-keyline: #416180;
  --badge-dark-label: #94BCE3;

  /* Kiosk's own accent — currently a private --op-accent literal
     (#ff764d) inside kiosk.css, a second brand colour competing with
     --brand. Defined here so kiosk.css can adopt it per-theme instead of
     hardcoding; value matches --op-accent today so switching the call site
     is a no-op visually until kiosk.css is re-scoped for the 5 themes. */
  --kiosk-key: #ff764d;

  /* static/css/warehouse-inventory.css hardcodes `background: white` on QR
     previews for scannability — a legitimate choice (a QR code needs a
     real white/near-white quiet zone to scan reliably under any theme,
     including glass), but it must be a named constant, not a literal. */
  --qr-paper: #fff;
}

/* The GROUND for the "light" base — the "light" and "muted-corporate" skins
   both sit on this (base.html's boot script sets data-theme-base="light"
   for both). Was html[data-theme="light"] before the base+skin split;
   renamed to the attribute pair below so a second theme can share it
   without copying ~90 declarations. See tests/test_ux_hardening.py's
   test_light_theme_muted_text_meets_wcag_aa, which reads this block by
   selector — keep the two in sync if you rename this again. */
[data-theme-base="light"] {
  /* Native controls (select option popups, scrollbars, pickers) render light. */
  color-scheme: light;

  /* Same five-step model as dark, mirrored: --bg is the page, --bg-2 is the
     most-lifted surface, --bg-3/--bg-4 are interaction states. In light,
     "lifted" means closer to white rather than further from black. --bg-1 and
     --bg-2 were both #ffffff here too, so light had the same missing step. */
  --bg:       #f4f5f7;   /* page */
  --bg-1:     #fcfcfd;   /* card */
  --bg-2:     #ffffff;   /* raised: popovers, modals, sticky headers */
  --bg-3:     #eceef1;   /* input rest, row hover */
  --bg-4:     #dfe2e7;   /* pressed, selected */

  /* Legacy Emerald Bay surface names are still consumed by shared cards,
     work-order forms, PDF intake, and shop-floor shells. They must follow
     the active ground instead of retaining :root's dark literals. Without
     these aliases, a light page contains isolated navy/black panels. */
  --eb-canvas:   var(--bg);
  --eb-bg-deep:  var(--bg-1);
  --eb-panel:    var(--bg-1);
  --eb-panel-hi: var(--bg-2);
  --border:       #e4e7eb;
  --border-2:     #d3d7dd;
  --border-3:     #b5bbc4;
  --border-brand: rgba(103, 88, 217, 0.38);
  --text:     #0a0a0a;
  --text-2:   #525252;
  /* --text-3 / --text-4 darkened 2026-07-27 for WCAG AA on the light --bg-1
     (#ffffff). They were #737373 (4.6:1, passing) and #a3a3a3 (2.52:1,
     failing). --text-4 carries real content in light mode — every timestamp
     (.wod-attribution-when) and field label on the WO detail page — not just
     decoration, so it has to clear 4.5:1. The three tiers stay visually
     distinct: 7.4 / 5.7 / 4.6. */
  --text-3:   #666666;
  --text-4:   #757575;
  /* --accent is NOT redeclared here: it inherits var(--brand) from :root so the
     brand is one colour in both themes. When brand needs to be readable text on
     a light surface, use --brand-text, not --accent. */
  --shadow: 0 1px 2px rgba(0,0,0,0.04), 0 0 0 1px rgba(0,0,0,0.04);
  --shadow-lg: 0 12px 32px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.06), 0 0 0 1px var(--border);

  /* Sleek Slate Sidebar light mode tokens */
  --sidebar-bg:                 #f8fafc;
  --sidebar-border:             #e2e8f0;
  --sidebar-item-hover:         rgba(0, 0, 0, 0.04);
  --sidebar-item-active:        #e8eef8;
  --sidebar-item-active-border: #b8c9e5;
  --sidebar-text:               #64748b;
  --sidebar-text-active:        #0f172a;
  --sidebar-icon-active:        #4f6f9f;

  --view-accent-rgb:    98, 118, 159;
  --view-accent:        #62769f;
  --view-accent-text:   #52658b;
  --view-accent-bg:     rgba(98, 118, 159, 0.12);
  --view-accent-border: rgba(98, 118, 159, 0.34);

  --glass-bg: rgba(255, 255, 255, 0.85);

  /* Lighter scrim for light theme where the underlying surface is near-white. */
  --scrim: rgba(0,0,0,0.5);
  /* --text-inverted intentionally inherits from :root (#fff). */

  /* Lighter foreground tints — darken for AA contrast on white.
     green/amber/orange dropped from the 700 to the 800 weight 2026-07-27:
     measured over the 12% tint they sat at 4.35 / 4.41 / 4.41, i.e. just
     under AA. At 800 they clear 6.2. Blue, red and purple already passed at
     700 and are unchanged. */
  --green-text:  #166534;
  --amber-text:  #92400e;
  --orange-text: #9a3412;
  --red-text:    #b91c1c;
  --blue-text:   #11657d;
  --purple-text: #7e22ce;

  /* Cell tints in light mode — invert source color (dark spotting on light grid). */
  --cell-past:    rgba(0,0,0,0.02);
  --cell-today:   rgba(59,130,246,0.06);
  --cell-weekend: rgba(0,0,0,0.04);

  /* Stronger green/orange for light-mode contrast on white surfaces. */
  --green-strong:  #15803d;
  --orange-strong: #c2410c;
  --blue-hover:    #11657d;
  --green-hover:   #15803d;
  --red-hover:     #b91c1c;

  --viz-canvas:   rgba(251, 252, 254, 0.96);
  --viz-panel:    rgba(243, 246, 250, 0.94);
  --viz-border:   rgba(94, 113, 138, 0.20);
  --viz-grid:     rgba(94, 113, 138, 0.10);
  --viz-series-1: #0891b2;
  --viz-series-2: #2563eb;
  --viz-series-3: #6366f1;
  --viz-series-4: #9333ea;
  --viz-warning:  #c27803;
  --viz-risk:     #e11d48;
  /* Light-ground counterparts of the dark viz extension above — same hue
     families per slot, darkened for >= 3:1 on the near-white chart surface
     (worst slot #ea580c at 3.47:1 on #fcfcfd). Ramp runs light -> dark. */
  --viz-series-5: #059669;
  --viz-series-6: #ea580c;
  --viz-series-7: #db2777;
  --viz-series-8: #4d7c0f;
  --viz-positive: #15803d;
  --viz-neutral:  #64748b;
  --viz-ramp-1:   #0891b2;
  --viz-ramp-2:   #0e7490;
  --viz-ramp-3:   #155e75;
  --viz-ramp-4:   #0f4557;
  --viz-ramp-5:   #083344;
  --viz-diverge-neg: #e11d48;
  --viz-diverge-mid: #64748b;
  --viz-diverge-pos: #15803d;

  /* Lighter inset alpha for light surfaces. */
  --ring-inset: inset 0 0 0 1px rgba(0,0,0,0.08);

  /* Light-mode pastel filter swatches preserved from original design. */
  --filter-swatch-scheduled:  #fef9c3;
  --filter-swatch-inprogress: #fef3c7;
  --filter-swatch-done:       #dbeafe;
  --filter-swatch-hold:       #fed7aa;
  --filter-swatch-overdue:    #fee2e2;

  /* Brand violet on a light surface. The foreground deepens for AA and the
     hover step moves darker so interaction remains visible against white. */
  --brand-hover: #5647c7;
  --brand-text:  #5f52c8;
  /* --brand-2's dark value (#22d3ee, a light cyan) is a `--text-3`-tier
     ~2.3:1 on white -- darkened here the same way --chip-cyan is, rather
     than left to silently inherit dark's opaque value (which
     test_no_token_family_is_partially_themed exists to catch). */
  --brand-2:     #0e7490;

  /* Chip foregrounds darkened for WCAG AA on the light surface. The dark
     values (300/400-weight hues) measure ~2.2:1 composited over the 14% tint
     on #ffffff — the worst was dept-metal-fab-structural at 2.17:1. The tint
     backgrounds themselves are translucent and need no override. */
  --chip-blue:    #1d4ed8;
  --chip-purple:  #7e22ce;
  --chip-amber:   #92400e;
  --chip-green:   #166534;
  --chip-pink:    #be185d;
  --chip-cyan:    #0e7490;
  --chip-yellow:  #a16207;
  --chip-orange:  #9a3412;
  --chip-sky:     #0369a1;
  --chip-violet:  #6d28d9;
  --chip-emerald-bg: rgba(16,185,129,0.10);
  --chip-emerald:    #08745a;
  --chip-fuchsia: #a21caf;
  --chip-slate:   #475569;
  --chip-teal:    #0f766e;
  --chip-rose:    #be123c;
  --chip-lime:    #4d7c0f;
  --chip-stone:   #57534e;
  --chip-indigo:  #4338ca;

  /* Focus ring re-derived so the 2px halo reads against the light surface. */
  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--brand);

  --sidebar-text-dim: #64748b;
  --sidebar-edge: rgba(0,0,0,0.04);


  /* Status hues for the light surface. These were previously supplied by
     base.html's own :root block; folded in here when that block was deleted
     so the light theme renders byte-identically from one source. */
  --amber:       #d97706;
  --amber-bg:    rgba(217,119,6,0.10);
  --blue:        #167c98;
  --blue-2:      #2597b4;
  --blue-bg:     rgba(22,124,152,0.10);
  --green:       #16a34a;
  --green-bg:    rgba(22,163,74,0.10);
  --orange:      #ea580c;
  --orange-bg:   rgba(234,88,12,0.10);
  --pink:        #db2777;
  --purple:      #9333ea;
  --purple-bg:   rgba(147,51,234,0.10);
  --red:         #dc2626;
  --red-bg:      rgba(220,38,38,0.08);
}

/* ================================================================
   SKINS — thin overlays on top of the two ground blocks above. "dark"
   and "light" (data-theme values) ARE the grounds and get no overlay of
   their own; the three below each set data-theme-base (in base.html's
   boot script / applyTheme()) to whichever ground they sit on and then
   override brand + a handful of surface tokens only. Do not re-declare
   the full surface/text/border/chip ramp here — that is exactly the
   duplication the base+skin split exists to avoid.
   ================================================================ */

/* ---- Skin: muted-corporate ---- base = light
   "Slate/navy executive": light content area, but the sidebar keeps its
   own deep-navy ground rather than following the light content, and the
   brand accent moves from iris violet to a muted steel/navy blue. */
[data-theme="muted-corporate"] {
  --brand-rgb:    47, 95, 143;
  --brand:        #2f5f8f;
  --brand-hover:  #3d76ad;
  --brand-text:   #2a5a86;
  --brand-bg:     rgba(var(--brand-rgb), 0.12);
  --brand-2:      #64748b;
  --border-brand: rgba(47, 95, 143, 0.35);
  --focus-ring:   0 0 0 2px var(--bg), 0 0 0 4px var(--brand);

  --sidebar-bg:                 #1b2536;
  --sidebar-border:             #101826;
  --sidebar-item-hover:         rgba(255, 255, 255, 0.06);
  --sidebar-item-active:        #24314a;
  --sidebar-item-active-border: #3a4f70;
  --sidebar-text:               #9fb0c6;
  --sidebar-text-active:        #ffffff;
  --sidebar-icon-active:        #7fa8d9;
  --sidebar-text-dim:           #6b7d99;
  --sidebar-edge:               rgba(255, 255, 255, 0.03);

  /* Chart palette (2026-08-08, Workstream A). Desaturated steel-navy family
     around the skin's #2f5f8f brand — every slot passes the full dataviz
     validator (chroma floor, CVD adjacency, 3:1 on the near-white chart
     surface). Ramp is the steel-blue hue, light -> dark. Semantic and
     diverging tokens inherit the light ground. */
  --viz-canvas:   rgba(247, 249, 252, 0.96);
  --viz-panel:    rgba(240, 244, 249, 0.94);
  --viz-border:   rgba(47, 95, 143, 0.22);
  --viz-grid:     rgba(47, 95, 143, 0.10);
  --viz-series-1: #2f639c;
  --viz-series-2: #b1592f;
  --viz-series-3: #7d5da6;
  --viz-series-4: #16794a;
  --viz-series-5: #bb4f8f;
  --viz-series-6: #93761c;
  --viz-series-7: #0784a6;
  --viz-series-8: #a34b5e;
  --viz-ramp-1:   #6089ae;
  --viz-ramp-2:   #45769f;
  --viz-ramp-3:   #2f5f8f;
  --viz-ramp-4:   #24496e;
  --viz-ramp-5:   #16324f;
}

/* ---- Skin: hardman ---- base = dark
   "Vibrant brand palette": pulled directly from the fills in
   static/img/hardmanos-mark.svg's <linearGradient id="upper"/"lower">
   stops (#d91f32 → #ef3340 → #ef7837 → #ef9a3c), not invented separately. */
[data-theme="hardman"] {
  --brand-rgb:    239, 51, 64;
  --brand:        #ef3340;
  --brand-hover:  #ef7837;
  --brand-text:   #ff8a75;
  --brand-bg:     rgba(var(--brand-rgb), 0.16);
  --brand-2:      #ef9a3c;
  --border-brand: rgba(239, 51, 64, 0.42);
  --focus-ring:   0 0 0 2px var(--bg), 0 0 0 4px var(--brand-text);

  --sidebar-bg:                 #170a0c;
  --sidebar-border:             #2a1113;
  --sidebar-item-hover:         rgba(239, 51, 64, 0.08);
  --sidebar-item-active:        #301316;
  --sidebar-item-active-border: #5c1f24;
  --sidebar-icon-active:        #ff8a75;
  --sidebar-edge:               rgba(239, 51, 64, 0.05);

  --kiosk-key: #ef9a3c;

  /* Chart palette (2026-08-08, Workstream A). Brand-anchored: slots 1-2 are
     the hardmanos-mark gradient's own #ef3340 / #ef9a3c, the rest are hues
     that keep CVD-adjacent separation >= 10.4 and 3:1 on the red-black chart
     surface below (composites to #160b0d over the dark ground --bg). Ramp is
     the single brand-red hue, dim -> bright. Semantic (--viz-positive/
     -neutral/-warning/-risk) and diverging tokens inherit the dark ground. */
  --viz-canvas:   rgba(23, 10, 12, 0.90);
  --viz-panel:    rgba(36, 16, 19, 0.85);
  --viz-border:   rgba(239, 51, 64, 0.18);
  --viz-grid:     rgba(239, 51, 64, 0.09);
  --viz-series-1: #ef3340;
  --viz-series-2: #ef9a3c;
  --viz-series-3: #2dd4bf;
  --viz-series-4: #c084fc;
  --viz-series-5: #facc15;
  --viz-series-6: #38bdf8;
  --viz-series-7: #f472b6;
  --viz-series-8: #a3e635;
  --viz-ramp-1:   #c2313c;
  --viz-ramp-2:   #ef3340;
  --viz-ramp-3:   #f4736c;
  --viz-ramp-4:   #f89e94;
  --viz-ramp-5:   #fcc9c2;
}

/* ---- Skin: glass ---- base = dark
   "Translucent layered surfaces": --bg-1/-2/-3 stay the ordinary SOLID
   dark-ground values unless the browser can actually blur behind them —
   see the @supports block right below. Without that gate, a translucent
   surface with no blur just shows whatever is scrolled underneath, which
   reads as a bug, not glass; this is the "solid fallback" the task asked
   for. base.html applies backdrop-filter: blur(var(--glass-blur)) to the
   sidebar/popovers themselves, inside the same @supports gate. Text
   tokens are untouched: the translucent surfaces below composite to a
   colour extremely close to the opaque dark-ground values they replace,
   so the AA contrast work already done for --text-2/3/4 on --bg-1 holds. */
[data-theme="glass"] {
  --glass-blur:   20px;
  --glass-border: rgba(var(--brand-rgb), 0.35);
  --glass-bg-2:   var(--bg-2);
  --glass-noise:  0.03;
  --shadow-lg:    0 16px 48px rgba(0,0,0,0.55), 0 0 0 1px var(--glass-border);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  [data-theme="glass"] {
    --bg-1:       rgba(18, 19, 30, 0.62);
    --bg-2:       rgba(27, 29, 43, 0.58);
    --bg-3:       rgba(31, 33, 48, 0.66);
    --glass-bg-2: rgba(27, 29, 43, 0.42);

    /* Chart surfaces go translucent ONLY when the browser can blur behind
       them — same rule as --bg-1/2/3 above. Outside this gate charts keep
       the opaque dark-ground --viz-canvas/--viz-panel (solid fallback).
       Series/ramp/semantic tokens inherit the dark ground: they hold >= 3:1
       over this canvas composited on --bg (#0e111c). */
    --viz-canvas: rgba(13, 18, 32, 0.55);
    --viz-panel:  rgba(20, 26, 42, 0.50);
  }
}

/* ---- Skin: classic ---- base = dark
   The original Work Order Hub was not a material restyle of the modern app;
   it was a deliberately flat, high-density operating surface. These values
   are taken from the pre-overhaul WOHub token ramp (seed commit ddb495b).
   Layout and interaction restoration lives in classic-theme.css so this
   block remains consumable by Theme Studio and the visualization engine. */
[data-theme="classic"] {
  color-scheme: dark;

  --bg:       #000000;
  --bg-1:     #0a0a0a;
  --bg-2:     #111111;
  --bg-3:     #161616;
  --bg-4:     #1c1c1c;
  --border:   #1f1f1f;
  --border-2: #2a2a2a;
  --border-3: #383838;
  --text:     #ededed;
  --text-2:   #a1a1a1;
  --text-3:   #6b6b6b;
  --text-4:   #444444;
  --accent:   #fafafa;

  --brand:        #3b82f6;
  --brand-rgb:    59, 130, 246;
  --brand-bg:     rgba(59, 130, 246, 0.12);
  --brand-text:   #60a5fa;
  --border-brand: rgba(59, 130, 246, 0.30);

  --sidebar-bg:                 #0a0a0a;
  --sidebar-border:             #1f1f1f;
  --sidebar-item-hover:         #161616;
  --sidebar-item-active:        #1a1a1a;
  --sidebar-item-active-border: #383838;
  --sidebar-text:               #a1a1a1;
  --sidebar-text-active:        #ededed;
  --sidebar-icon-active:        #ededed;
  --sidebar-edge:               transparent;

  --eb-panel:    var(--bg-1);
  --eb-panel-hi: var(--bg-2);
  --glass-bg:    var(--bg-1);
  --glass-bg-2:  var(--bg-2);
  --glass-border: var(--border);
  --glass-blur:  0px;
  --glass-noise: 0;

  --shadow-sm: none;
  --shadow:    none;
  --shadow-md: none;
  --shadow-lg: none;

  --radius-xs: 3px;
  --radius-sm: 4px;
  --radius-md: 5px;
  --radius-lg: 6px;
  --radius-xl: 8px;
  --radius-2xl: 10px;
  --eb-r-card: 8px;
  --eb-r-control: 5px;

  --viz-canvas:   #000000;
  --viz-panel:    #0a0a0a;
  --viz-border:   #1f1f1f;
  --viz-grid:     #1f1f1f;
  --viz-series-1: #3b82f6;
  --viz-series-2: #f97316;
  --viz-series-3: #22c55e;
  --viz-series-4: #a855f7;
  --viz-series-5: #f59e0b;
  --viz-series-6: #06b6d4;
}

/* The same component scale drives desk and floor surfaces. Floor screens stay
   dark because feedback colors are tuned for glare and arm's-length use.
   2026-08-07: this used to ALSO hardcode --bg/--bg-1..4/--text/--text-2..4
   here, i.e. density and palette were entangled — a floor-density region
   forced dark colours regardless of theme, which is exactly the bug
   static/css/kiosk.css:79-97 has to fight back with its own !important
   overrides (its comment says as much). Density and palette are
   orthogonal; this block now only ever sets sizing. A device that must
   always stay dark (a wall-mounted kiosk tablet, say) should set
   data-theme="hardman"/"dark" on itself, not rely on floor density to do
   it sideways. kiosk.css:37-97 still needs re-scoping from the literal
   html[data-theme="light"] selector to [data-theme-base="light"] to
   finish this fix — out of scope for this file, flagged for whoever
   touches kiosk.css next. */
[data-density="floor"] {
  --eb-row: 56px;
  --eb-target: 56px;
  --eb-text: 16px;
}
