/* Platform design tokens — the single themeable surface for the whole UI.
 *
 * Every color, space, radius, shadow and type value used by the shell and module views is a CSS
 * custom property declared here. A host re-skins the app by overriding these on :root (or a scope)
 * — without touching any markup (coding-standards §12; architecture → theming). The existing
 * stylesheets already reference these names with fallbacks; this file makes them the source of
 * truth so themes can override them centrally. */
:root {
    /* Ask the UA to render native controls (scrollbars, form widgets, date pickers) for the light
       theme; the dark scope flips this to `dark` (M-044). */
    color-scheme: light;

    /* Palette — the Flat-UI teal/emerald language of the golden-standard app (Q.Invoice): a green-sea
       accent, midnight-slate text and a blue-grey neutral ramp. Every value is a token so a full reskin
       (or an alternate theme — see the dark scope below) is a change here, not in markup. */
    --platform-color-text: #2c3e50;
    --platform-color-text-muted: #636e6f;
    --platform-color-surface: #ffffff;
    --platform-color-surface-muted: #ecf0f1;
    --platform-color-page-bg: #f2f2f6;
    /* Translucent veil over content (e.g. the grid's loading overlay) — surface-tinted so it flips
       with the theme instead of flashing a bright white pane over dark rows. */
    --platform-color-overlay: rgba(255, 255, 255, 0.66);
    /* Veil composited OVER a theme's background photo (M-197): a translucent surface tint laid on the
       picture, so its ALPHA IS INVERTED OPACITY — higher alpha = more wash = deader photo. Referenced ONLY
       by the per-image-theme rule the shell/partials/theme-background partial emits, so an image-less
       theme (every dark theme) never gets a stray veil.

       ⚠️ NOT AN AA GUARANTEE (D-031, Dude's call). This was once 0.66, an alpha sized against desk.webp's
       ABSOLUTE worst case — a pure-black pixel under a 0.66 white veil composites to ≈#a8a8a8, giving bare
       dark text on .platform-shell__main 4.64:1, just clear of AA. That framing is dead: no alpha can
       rescue bare text over a photograph, so legibility is a per-image VISUAL call (the M-205 rule: if a
       title is hard to read on a theme, dim the IMAGE, never re-add a box behind the header). The alpha is
       now purely a LOOK dial — 0.30 keeps desk.webp gently knocked back without bleaching it.

       EVERY theme declares its own alpha explicitly; none inherits this one. That is deliberate — qinvoice
       and solarized-light used to silently inherit :root and rendered with a value nobody had chosen for
       them, so changing this line would move themes it was never meant to touch. */
    --platform-content-bg-overlay: rgba(255, 255, 255, 0.30);
    /* Placeholder tone painted UNDER the theme's background photo while it loads (M-244): roughly the
       photo-under-veil dominant colour, so the brief gap before the (preloaded) picture decodes settles
       on that tone instead of flashing the bare body colour. Like the veil above it is consumed ONLY via
       the image-gated rule the theme-background partial emits (wired there onto the actually-painted
       `--platform-content-bg-color`), so an image-less theme never shows it — no stray colour slab. This
       :root value is the LIGHT theme's (desk.webp under a 0.30 white veil). */
    --platform-content-bg-tone: #d8d4cd;
    --platform-color-border: #cfd8dc;
    --platform-color-accent: #16a085;
    /* The accent as a bare `R, G, B` triple (matches --platform-color-accent above). Some consumers
       need the channels, not the hex, to build rgba() at call sites (e.g. Bootstrap's --bs-primary-rgb,
       which powers its translucent utilities). Kept in lockstep with the hex by hand — CSS cannot
       derive one from the other. */
    --platform-color-accent-rgb: 22, 160, 133;
    --platform-color-accent-strong: #12876e;
    --platform-color-accent-surface: #d1f2eb;
    --platform-color-on-accent: #ffffff;
    --platform-color-ok: #27ae60;
    --platform-color-error: #c0392b;
    --platform-color-error-surface: #fdecea;
    /* Text-safe error colour (M-201a). `--platform-color-error` above is tuned for BORDERS and FILLS
       and fails WCAG-AA as small text on some themes, so error *text* uses this variant: it clears
       ≥4.5:1 on all four backdrops error text renders on — surface, surface-muted, page-bg and
       error-surface (two consumers are boxes that paint error-surface as their own background). On
       themes whose base error already clears AA it repeats that hex; on the four that don't (dark,
       dracula, nord, solarized-light) its lightness is moved the minimum needed, direction per the
       theme's base (D-028: dark themes lighten, light themes darken). Guarded by ThemeContrastTest.
       Do NOT point borders/fills at this — that is what `--platform-color-error` stays for. */
    --platform-color-error-text: #c0392b;

    /* ── Data-visualisation ramp (M-254) ──────────────────────────────────────────────────────────
       The SEQUENTIAL scale the week strip paints volume with: one hue, four steps of increasing
       intensity, plus an "empty" track and a fixed "overdue" status tone. Every theme declares its own
       six — dark modes are SELECTED against their own surface, never computed by flipping the light
       ramp (the dataviz rule), which is why the dark themes run light-ward while the light themes run
       dark-ward.

       DELIBERATELY INDEPENDENT OF --platform-color-accent. Accent/accent-strong fail AA today (M-201b,
       still open), so a ramp built on them would inherit a known failure across all eight themes; and
       keeping them apart means M-201b can re-tune the accent without silently contradicting this ramp.
       The hue is also never the semantic red/green, which mean overdue/confirmed elsewhere.

       WHAT IS GUARDED, AND WHAT IS NOT (ThemeContrastTest::viz*): these cells carry NO text — the count
       lives in each cell's accessible name and tooltip, and the grid below is the table view — so the
       4.5:1 body-text bar does not apply and forcing it would flatten the ramp into near-blacks. The
       ramp's real properties are: monotone lightness, adjacent OKLCH dL >= 0.06, and a light end
       >= 2:1 on the surface (the dataviz skill's ordinal checks). `overdue` is a lone status mark with
       no neighbours to be read against, so it takes WCAG 1.4.11's 3:1 for graphical objects, and it
       ships with an icon + label rather than resting on colour alone. */
    --platform-color-viz-empty: #d9dee6;
    --platform-color-viz-1: #a0b2d5;
    --platform-color-viz-2: #6d8dce;
    --platform-color-viz-3: #4068c0;
    --platform-color-viz-4: #1f44a0;
    /* Repeats --platform-color-error: it already clears 3.2:1 as a fill here, so the strip's late cell
       is the very red the grid's late pill uses (only nord's had to move — see there). */
    --platform-color-viz-overdue: #c0392b;

    /* Spacing scale */
    --platform-space-xs: 0.25rem;
    --platform-space-sm: 0.5rem;
    --platform-space-md: 1rem;
    --platform-space-lg: 1.5rem;
    --platform-space-xl: 2.5rem;

    /* Radii — the golden standard's gentle rounding (controls ~6px, cards ~5px), a touch softer than
       before but not pill-shaped. */
    --platform-radius-sm: 0.25rem;
    --platform-radius-md: 0.375rem;
    --platform-radius-lg: 0.5rem;
    /* Fully-rounded "pill" radius — the shared shape for status/lifecycle/tag badges and progress
       tracks. A large fixed value (not a theme colour) so it stays constant across themes. */
    --platform-radius-pill: 999px;

    /* Elevation */
    --platform-shadow-panel: 0 1px 3px rgba(0, 0, 0, 0.12);
    --platform-shadow-sm: 0 1px 2px rgba(44, 62, 80, 0.06), 0 1px 3px rgba(44, 62, 80, 0.08);
    --platform-shadow-md: 0 4px 14px rgba(44, 62, 80, 0.10);

    /* Typography — self-hosted IBM Plex Sans (fonts.css), the golden-standard typeface, with a system
       fallback for the swap window; the subtle letter-spacing matches the reference's compact feel. */
    --platform-font-body: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", roboto, sans-serif;
    --platform-letter-spacing: 0.01em;
    /* Type scale is deliberately ~0.9× a conventional ramp (M-084): the whole app reads a notch denser
       (labels, body, tables, headings scale together since everything references these). xs (~10.8px) is
       the readability floor — meta/hint text only; do not push it smaller. Layout --space-* below is NOT
       scaled, so spacing/density is unchanged — only glyph size shrinks. */
    --platform-font-size-xs: 0.675rem;
    --platform-font-size-sm: 0.7875rem;
    --platform-font-size-md: 0.9rem;
    --platform-font-size-lg: 1.35rem;
    --platform-font-size-xl: 1.6875rem;

    /* Input-control vertical density (M-084) — a SEPARATE knob from the layout --space-* scale so form
       fields can be made ~80% as tall without shrinking page spacing. `--platform-input-height` is the
       shared target height every full-size control aligns to (native input/select/textarea, the
       SlimSelect control and labeled buttons); `--platform-input-padding-y` is the vertical padding that
       lands native inputs on that height. Horizontal padding stays on --space-sm (the M-074 look). */
    --platform-input-padding-y: 0.35rem;
    --platform-input-height: 1.9rem;

    /* The minimum size of anything a FINGER has to hit (M-223) — 44 CSS px, the platform accessibility floor on
       both iOS and Android. A control sized for a mouse pointer (the 1.7em icon button) is roughly half that, so a
       touch-only affordance raises itself to this token under `(pointer: coarse)` instead of inventing a number. */
    --platform-tap-target: 2.75rem;

    /* Shell metrics */
    --platform-shell-max-width: 60rem;
    /* Content uses the full available width (data-dense lists/tables want the space); forms self-cap at
       a readable width via .platform-form, so only wide grids actually fill the frame. */
    --platform-content-max-width: none;
    --platform-shell-header-height: 3.5rem;

    /* App-shell navigation chrome — the DARK icon rail and the module-owned secondary panel beside it
     * (M-035/M-036). Themed entirely from here: a host re-skins the navigation by overriding these
     * (coding-standards §12). The rail is a deep charcoal (not the accent) for a sleek, restrained look
     * — the teal accent is used sparingly on controls, not as a large filled area. Icon-only (narrow);
     * the secondary is a light text menu panel. */
    --platform-rail-width: 3.5rem;
    --platform-subnav-width: 13rem;
    --platform-topbar-height: 3.25rem;
    --platform-color-sidebar: #263238;
    --platform-color-sidebar-strong: #1b242a;
    --platform-color-sidebar-text: #ffffff;
    --platform-color-sidebar-text-muted: rgba(255, 255, 255, 0.72);
    --platform-color-sidebar-hover: rgba(255, 255, 255, 0.10);
    --platform-color-sidebar-active: rgba(255, 255, 255, 0.18);
    --platform-color-sidebar-border: rgba(255, 255, 255, 0.16);
    --platform-color-scrim: rgba(15, 23, 42, 0.45);

    /* Secondary panel surface — a light panel beside the accent rail. */
    --platform-color-subnav-surface: var(--platform-color-surface);
    --platform-color-subnav-border: var(--platform-color-border);
    --platform-color-subnav-hover: var(--platform-color-surface-muted);
    --platform-color-subnav-active: var(--platform-color-accent-surface);
}

/* ── Dark theme (M-044) ─────────────────────────────────────────────────────────────────────────
 * The second palette. It re-declares ONLY the palette tokens with a slate/charcoal scheme — the
 * teal accent is kept (re-tuned for contrast on dark) and every other neutral is re-tuned. The
 * non-palette tokens (spacing, radii, type, metrics, shadows) are theme-agnostic and stay defined
 * once on :root above; a dark scope that re-declared them would just duplicate values (DRY, §3).
 *
 * Activation: the `data-theme="dark"` attribute is placed on <html> before first paint — by the
 * persisted cookie/localStorage, or the OS `prefers-color-scheme`, resolved in the pre-paint script
 * in shell/partials/assets.blade.php and flipped at runtime by the `theme` Stimulus controller. The
 * subnav tokens are intentionally omitted: they alias surface/accent tokens via var(), so they follow
 * the dark palette automatically without a second declaration here. */
:root[data-theme="dark"] {
    color-scheme: dark;

    /* Dark themes veil their background photo with a DARK wash (M-203) — a light veil would bleach the
       photo toward white and clash with the dark surfaces. Alpha is tuned so the photo reads as a photo. */
    --platform-content-bg-overlay: rgba(18, 23, 27, 0.62);
    --platform-content-bg-tone: #151a1f; /* desk-dark.webp under its veil (M-244) */

    --platform-color-text: #e6ecf1;
    --platform-color-text-muted: #97a6b0;
    --platform-color-surface: #1e272c;
    --platform-color-surface-muted: #26313a;
    --platform-color-page-bg: #161c20;
    --platform-color-overlay: rgba(16, 21, 25, 0.7);
    --platform-color-border: #37444c;
    --platform-color-accent: #159e86;
    --platform-color-accent-rgb: 21, 158, 134;
    --platform-color-accent-strong: #1abc9c;
    --platform-color-accent-surface: #123a33;
    --platform-color-on-accent: #ffffff;
    --platform-color-ok: #2ecc71;
    --platform-color-error: #e74c3c;
    --platform-color-error-surface: #3a1e1b;
    /* Text-safe error (M-201a): base #e74c3c is only 3.47:1 as text; lightened (dark theme) to clear AA. */
    --platform-color-error-text: #ec7366;

    /* Viz ramp (M-254) — the same indigo hue as the light theme, but its steps are SELECTED against
       this dark surface, so the ramp runs light-ward as volume rises (anchor flips in dark). */
    --platform-color-viz-empty: #383b41;
    --platform-color-viz-1: #495878;
    --platform-color-viz-2: #607fc0;
    --platform-color-viz-3: #80a9ff;
    --platform-color-viz-4: #c5d8fe;
    --platform-color-viz-overdue: #e74c3c;

    /* Navigation chrome: the rail drops to the deepest charcoal so it still reads as a distinct,
       recessed column against the now-dark content surfaces. */
    --platform-color-sidebar: #10171c;
    --platform-color-sidebar-strong: #0a0f12;
    --platform-color-sidebar-text: #e6ecf1;
    --platform-color-sidebar-text-muted: rgba(230, 236, 241, 0.66);
    --platform-color-sidebar-hover: rgba(255, 255, 255, 0.08);
    --platform-color-sidebar-active: rgba(255, 255, 255, 0.16);
    --platform-color-sidebar-border: rgba(255, 255, 255, 0.12);
    --platform-color-scrim: rgba(0, 0, 0, 0.6);
}

/* ── Named theme styles (M-086) ───────────────────────────────────────────────────────────────────
 * Beyond the built-in light/dark pair, each entry in the `platform.themes` config registry has a
 * colour-only `[data-theme="<key>"]` block here. Like the dark scope above they re-declare ONLY the
 * `--platform-color-*` palette (plus the sidebar family) — spacing, radii, type and metrics stay
 * theme-agnostic on :root, so a theme can never change element order or sizing (DRY, §3; the M-086
 * "colours only" guarantee). The subnav tokens are again omitted: they alias surface/accent via
 * var(), so they follow each palette automatically. `data-bs-theme` (the theme's `bs_base`) is set
 * alongside `data-theme` by the pre-paint script / theme controller, so Bootstrap's own light/dark
 * layer matches. To add a theme (e.g. Burda — SLOT RESERVED at the end): add one config entry and one
 * block here; nothing else changes. */

/* Matrix — a cohesive green-on-black terminal palette (bs base: dark). Body text is a legible phosphor
 * green (not harsh #00ff00) on near-black surfaces for AA-ish contrast; the accent is a brighter green
 * with dark text on it, borders are a dim green, and the rail drops to the deepest black-green. */
:root[data-theme="matrix"] {
    color-scheme: dark;

    /* Dark veil over the photo (M-203) — near-black with a green cast to sit under Matrix's phosphor palette. */
    --platform-content-bg-overlay: rgba(5, 14, 9, 0.66);
    --platform-content-bg-tone: #09110d; /* matrix.webp under its veil (M-244) */

    /* Backgrounds are near-neutral dark greys (a touch darker than the default dark theme) with only a
       whisper of green, so the phosphor text/accent pop without the whole surface reading as green
       (Dude feedback). Green stays in the TEXT + ACCENT, not the fills. */
    --platform-color-text: #7cfc7c;
    --platform-color-text-muted: #6f9a80;
    --platform-color-surface: #141715;
    --platform-color-surface-muted: #1b1f1c;
    --platform-color-page-bg: #0b0d0c;
    --platform-color-overlay: rgba(7, 9, 8, 0.72);
    --platform-color-border: #2c352f;
    --platform-color-accent: #33ff66;
    --platform-color-accent-rgb: 51, 255, 102;
    --platform-color-accent-strong: #19e64d;
    --platform-color-accent-surface: #18271e;
    --platform-color-on-accent: #05130a;
    --platform-color-ok: #2ecc71;
    --platform-color-error: #ff5c5c;
    --platform-color-error-surface: #2a1113;
    /* Text-safe error (M-201a): base already clears AA as text (worst 5.51:1) — repeats it. */
    --platform-color-error-text: #ff5c5c;

    /* Viz ramp (M-254) — phosphor CYAN, not the theme's green: green is --platform-color-ok
       ("confirmed") everywhere in this app, and a volume ramp must not borrow that meaning. Cyan stays
       terminal-coherent while saying something else. Selected against Matrix's near-black surface. */
    --platform-color-viz-empty: #28312f;
    --platform-color-viz-1: #24564e;
    --platform-color-viz-2: #068a7c;
    --platform-color-viz-3: #08c0ac;
    --platform-color-viz-4: #1bf8df;
    --platform-color-viz-overdue: #ff5c5c;

    --platform-color-sidebar: #0a0d0b;
    --platform-color-sidebar-strong: #060807;
    --platform-color-sidebar-text: #7cfc7c;
    --platform-color-sidebar-text-muted: rgba(124, 252, 124, 0.6);
    --platform-color-sidebar-hover: rgba(51, 255, 102, 0.12);
    --platform-color-sidebar-active: rgba(51, 255, 102, 0.2);
    --platform-color-sidebar-border: rgba(51, 255, 102, 0.18);
    --platform-color-scrim: rgba(0, 0, 0, 0.7);
}

/* Q.Invoice — a 1:1 clone of the current default (light) palette (bs base: light). It exists so the
 * default look can later diverge from the Q.Invoice brand without changing everyone's default: today
 * selecting it is visually identical to :root. Values MUST be kept in step with the :root palette
 * above if that ever changes (this is the deliberate divergence point). */
:root[data-theme="qinvoice"] {
    color-scheme: light;

    /* No veil — the photo renders raw (Dude's call). Declared EXPLICITLY rather than left to inherit
       :root: this theme previously had no veil of its own and silently picked up the default, so the
       value it rendered with was an accident of inheritance rather than a choice. */
    --platform-content-bg-overlay: rgba(255, 255, 255, 0);
    --platform-content-bg-tone: #c8c1b8; /* desk.webp, unveiled (M-244) */

    --platform-color-text: #2c3e50;
    --platform-color-text-muted: #636e6f;
    --platform-color-surface: #ffffff;
    --platform-color-surface-muted: #ecf0f1;
    --platform-color-page-bg: #f2f2f6;
    --platform-color-overlay: rgba(255, 255, 255, 0.66);
    --platform-color-border: #cfd8dc;
    --platform-color-accent: #16a085;
    --platform-color-accent-rgb: 22, 160, 133;
    --platform-color-accent-strong: #12876e;
    --platform-color-accent-surface: #d1f2eb;
    --platform-color-on-accent: #ffffff;
    --platform-color-ok: #27ae60;
    --platform-color-error: #c0392b;
    --platform-color-error-surface: #fdecea;
    /* Text-safe error (M-201a): base already clears AA as text (worst 4.74:1) — repeats it. */
    --platform-color-error-text: #c0392b;

    /* Viz ramp (M-254) — a 1:1 clone of the :root ramp, like the rest of this palette. */
    --platform-color-viz-empty: #d9dee6;
    --platform-color-viz-1: #a0b2d5;
    --platform-color-viz-2: #6d8dce;
    --platform-color-viz-3: #4068c0;
    --platform-color-viz-4: #1f44a0;
    --platform-color-viz-overdue: #c0392b;

    --platform-color-sidebar: #263238;
    --platform-color-sidebar-strong: #1b242a;
    --platform-color-sidebar-text: #ffffff;
    --platform-color-sidebar-text-muted: rgba(255, 255, 255, 0.72);
    --platform-color-sidebar-hover: rgba(255, 255, 255, 0.1);
    --platform-color-sidebar-active: rgba(255, 255, 255, 0.18);
    --platform-color-sidebar-border: rgba(255, 255, 255, 0.16);
    --platform-color-scrim: rgba(15, 23, 42, 0.45);
}

/* Burda — the BurdaMedia Extra brand theme (bs base: light). Sampled from burdamediaextra.cz: the site
 * is BLUE, not magenta — its primary nav is a deep brand blue (#0a58a5) washed left-to-right with a
 * bright cyan (#41bff7), the "Extra" wordmark is that same cyan, and body copy sits on a near-white
 * (#fcfcfc). So: a cool near-white surface ramp with the deep brand blue as the accent, over a rail in
 * the same blue that carries a cyan active-tint. The accent doubles as text (7.1:1 on white), which the
 * lighter link-blue #1b8bea could not (3.5:1). */
:root[data-theme="burda"] {
    color-scheme: light;

    /* Veil over the photo (M-203). Burda previously declared none and inherited :root's generic white
       0.66 — the heaviest alpha we ship — which flattened its blue collage to a grey wash. Dialled back
       to 0.45 and tinted with the brand blue so the picture stays vivid and reads as Burda's. The photo's
       upper-left (where the bare page <h1> lands, M-205) is near-white, so dark heading text survives the
       thinner veil. */
    --platform-content-bg-overlay: rgba(244, 248, 253, 0.45);
    --platform-content-bg-tone: #dbdfe7; /* burda.webp under its veil (M-244) */

    --platform-color-text: #1a1a1a;
    --platform-color-text-muted: #676c72;
    --platform-color-surface: #ffffff;
    --platform-color-surface-muted: #f4f6f9;
    --platform-color-page-bg: #eef1f6;
    --platform-color-overlay: rgba(255, 255, 255, 0.66);
    --platform-color-border: #dde2ea;
    --platform-color-accent: #0a58a5;
    --platform-color-accent-rgb: 10, 88, 165;
    --platform-color-accent-strong: #084474;
    --platform-color-accent-surface: #e3f0fb;
    --platform-color-on-accent: #ffffff;
    --platform-color-ok: #27ae60;
    --platform-color-error: #c0392b;
    --platform-color-error-surface: #fdecea;
    /* Text-safe error (M-201a): base already clears AA as text (worst 4.76:1) — repeats it. */
    --platform-color-error-text: #c0392b;

    /* Viz ramp (M-254) — Burda's brand blue, on its own tokens rather than the accent's (M-201b). */
    --platform-color-viz-empty: #d8dee6;
    --platform-color-viz-1: #98b4d3;
    --platform-color-viz-2: #5c91cc;
    --platform-color-viz-3: #1b6ebd;
    --platform-color-viz-4: #014e8e;
    --platform-color-viz-overdue: #c0392b;

    --platform-color-sidebar: #0a58a5;
    --platform-color-sidebar-strong: #084474;
    --platform-color-sidebar-text: #ffffff;
    --platform-color-sidebar-text-muted: rgba(255, 255, 255, 0.75);
    --platform-color-sidebar-hover: rgba(255, 255, 255, 0.12);
    /* The cyan the real nav washes over its blue — carries the active row. */
    --platform-color-sidebar-active: rgba(65, 191, 247, 0.32);
    --platform-color-sidebar-border: rgba(255, 255, 255, 0.18);
    --platform-color-scrim: rgba(8, 30, 55, 0.45);
}

/* Solarized Light — Ethan Schoonover's Solarized light palette (bs base: light). The signature warm
 * cream base tones with a blue accent, over the deep base02 teal-slate rail. */
:root[data-theme="solarized-light"] {
    color-scheme: light;

    /* No veil — the photo renders raw (Dude's call). Declared EXPLICITLY rather than left to inherit
       :root, for the same reason as qinvoice above. */
    --platform-content-bg-overlay: rgba(255, 255, 255, 0);
    --platform-content-bg-tone: #ebdfce; /* solarized.webp, unveiled (M-244) */


    --platform-color-text: #566c72;
    --platform-color-text-muted: #5c6b6d;
    --platform-color-surface: #fdf6e3;
    --platform-color-surface-muted: #eee8d5;
    --platform-color-page-bg: #fdf6e3;
    --platform-color-overlay: rgba(253, 246, 227, 0.66);
    --platform-color-border: #e6dfc8;
    --platform-color-accent: #268bd2;
    --platform-color-accent-rgb: 38, 139, 210;
    --platform-color-accent-strong: #1f6fa8;
    --platform-color-accent-surface: #dbeaf5;
    --platform-color-on-accent: #ffffff;
    --platform-color-ok: #859900;
    --platform-color-error: #dc322f;
    --platform-color-error-surface: #fbe4e2;
    /* Text-safe error (M-201a): base #dc322f is only 3.77:1 as text; darkened (light theme) to clear AA. */
    --platform-color-error-text: #c92522;

    /* Viz ramp (M-254) — Solarized blue, selected against the warm cream base. */
    --platform-color-viz-empty: #cfd7dd;
    --platform-color-viz-1: #8eaecb;
    --platform-color-viz-2: #4e90c5;
    --platform-color-viz-3: #0470b0;
    --platform-color-viz-4: #015182;
    --platform-color-viz-overdue: #dc322f;

    --platform-color-sidebar: #073642;
    --platform-color-sidebar-strong: #052832;
    --platform-color-sidebar-text: #eee8d5;
    --platform-color-sidebar-text-muted: rgba(238, 232, 213, 0.72);
    --platform-color-sidebar-hover: rgba(238, 232, 213, 0.1);
    --platform-color-sidebar-active: rgba(38, 139, 210, 0.3);
    --platform-color-sidebar-border: rgba(238, 232, 213, 0.16);
    --platform-color-scrim: rgba(0, 43, 54, 0.45);
}

/* Dracula — the Dracula dark palette (bs base: dark). Muted charcoal surfaces with the signature
 * purple accent (dark text on it), over the deepest Dracula background for the rail. */
:root[data-theme="dracula"] {
    color-scheme: dark;

    /* Dark veil over the photo (M-203) — Dracula's deep violet-charcoal so the image keeps the theme's cast. */
    --platform-content-bg-overlay: rgba(29, 31, 43, 0.62);
    --platform-content-bg-tone: #1c1d2d; /* dracula.webp under its veil (M-244) */

    --platform-color-text: #f8f8f2;
    --platform-color-text-muted: #9aa0be;
    --platform-color-surface: #282a36;
    --platform-color-surface-muted: #343746;
    --platform-color-page-bg: #21222c;
    --platform-color-overlay: rgba(33, 34, 44, 0.72);
    --platform-color-border: #44475a;
    --platform-color-accent: #bd93f9;
    --platform-color-accent-rgb: 189, 147, 249;
    --platform-color-accent-strong: #a97fe0;
    --platform-color-accent-surface: #3a3450;
    --platform-color-on-accent: #21222c;
    --platform-color-ok: #50fa7b;
    --platform-color-error: #ff5555;
    --platform-color-error-surface: #3a1f27;
    /* Text-safe error (M-201a): base #ff5555 is only 3.75:1 as text; lightened (dark theme) to clear AA. */
    --platform-color-error-text: #ff7676;

    /* Viz ramp (M-254) — Dracula's signature purple, selected against its charcoal surface. */
    --platform-color-viz-empty: #403e44;
    --platform-color-viz-1: #635779;
    --platform-color-viz-2: #8f76ba;
    --platform-color-viz-3: #bc96fb;
    --platform-color-viz-4: #decefe;
    --platform-color-viz-overdue: #ff5555;

    --platform-color-sidebar: #191a21;
    --platform-color-sidebar-strong: #121218;
    --platform-color-sidebar-text: #f8f8f2;
    --platform-color-sidebar-text-muted: rgba(248, 248, 242, 0.72);
    --platform-color-sidebar-hover: rgba(248, 248, 242, 0.08);
    --platform-color-sidebar-active: rgba(189, 147, 249, 0.28);
    --platform-color-sidebar-border: rgba(248, 248, 242, 0.14);
    --platform-color-scrim: rgba(10, 10, 15, 0.55);
}

/* Nord — the Nord arctic dark palette (bs base: dark). Cool polar-night surfaces with a frost-blue
 * accent (dark text on it), over the deepest polar-night tone for the rail. */
:root[data-theme="nord"] {
    color-scheme: dark;

    /* Dark veil over the photo (M-203) — Nord's Polar Night slate so the image keeps the theme's cool cast. */
    --platform-content-bg-overlay: rgba(38, 44, 56, 0.60);
    --platform-content-bg-tone: #222731; /* nord.webp under its veil (M-244) */

    --platform-color-text: #eceff4;
    --platform-color-text-muted: #b8c0cc;
    --platform-color-surface: #3b4252;
    --platform-color-surface-muted: #434c5e;
    --platform-color-page-bg: #2e3440;
    --platform-color-overlay: rgba(46, 52, 64, 0.72);
    --platform-color-border: #4c566a;
    --platform-color-accent: #88c0d0;
    --platform-color-accent-rgb: 136, 192, 208;
    --platform-color-accent-strong: #81a1c1;
    --platform-color-accent-surface: #3b4a52;
    --platform-color-on-accent: #2e3440;
    --platform-color-ok: #a3be8c;
    --platform-color-error: #bf616a;
    --platform-color-error-surface: #3a2b2e;
    /* Text-safe error (M-201a): base #bf616a is only 2.11:1 as text (fails even the 3:1 large-text bar).
       Nord's surfaces are mid-tone, so the error text must be fairly light to clear AA. #eeadb2 keeps
       Nord's Aurora red hue (≈354°) but carries enough chroma (sat ≈0.66) to still read as an *error*
       rather than a dusty pink, with ~4.62:1 worst-case (surface-muted) — the largest AA margin of the
       candidates. Lightness and chroma are independent here, so lifting one need not flatten the other. */
    --platform-color-error-text: #eeadb2;

    /* Viz ramp (M-254) — Nord's frost blue, selected against its MID-TONE surface (#3b4252, OKLCH
       L=0.38 — far lighter than the other dark themes' ~0.20-0.29), which is why these steps start
       higher than theirs. Same reason the overdue tone is the only one in the app that could not simply
       repeat --platform-color-error: Nord's #bf616a is 2.46:1 as a fill on this surface, short of WCAG
       1.4.11's 3:1, so it is lifted to 3.20:1 while holding Nord's Aurora red hue. (Exactly the finding
       M-201a made about the same token as TEXT — this surface is just hard on red.) */
    --platform-color-viz-empty: #4f565b;
    --platform-color-viz-1: #51798d;
    --platform-color-viz-2: #429fc7;
    --platform-color-viz-3: #2ac5ff;
    --platform-color-viz-4: #a6e1ff;
    --platform-color-viz-overdue: #da727c;

    --platform-color-sidebar: #2b303b;
    --platform-color-sidebar-strong: #232833;
    --platform-color-sidebar-text: #eceff4;
    --platform-color-sidebar-text-muted: rgba(236, 239, 244, 0.72);
    --platform-color-sidebar-hover: rgba(236, 239, 244, 0.08);
    --platform-color-sidebar-active: rgba(136, 192, 208, 0.28);
    --platform-color-sidebar-border: rgba(236, 239, 244, 0.14);
    --platform-color-scrim: rgba(20, 24, 30, 0.55);
}
