/*
 * BrickDb design tokens
 * ----------------------------------------------------------------------------
 * Source of truth: docs/design/design-system.md (§2, §3) and brand/README.md (§5).
 * Every token is prefixed --bd- and consumed by name (semantic role), never by
 * raw hue, from app.css / components.css.
 *
 * Theme resolution order (three states, not two):
 *   1. Bare :root            -> complete LIGHT palette (the default/fallback).
 *   2. @media (prefers-color-scheme: dark), guarded :root:not([data-theme="light"])
 *                             -> redefines tokens for OS-level dark, but an explicit
 *                                data-theme="light" on the root beats it.
 *   3. :root[data-theme="dark"]
 *                             -> redefines tokens again so an in-app toggle wins in
 *                                BOTH directions, regardless of OS preference.
 *
 * No colour is ever defined only inside a media/attribute block — the bare :root
 * always carries a complete, correct light palette on its own.
 * ----------------------------------------------------------------------------
 */

:root {
  /* ================= Colour — light theme (default) ================= */

  /* Surfaces */
  --bd-color-surface: #F7F7FB;            /* app background */
  --bd-color-surface-raised: #FFFFFF;     /* cards, list rows */
  --bd-color-surface-overlay: #FFFFFF;    /* sheets, popovers — pair with --bd-shadow-3 */
  --bd-color-surface-overlay-2: #FFFFFF;  /* modals, lightbox chrome — pair with --bd-shadow-4 */

  /* Text / icons on surface */
  --bd-color-on-surface: #1C2247;         /* primary text/icons on surface, 15.7:1 */
  /* Darkened from #5B6088 on 2026-09-05 (design-system.md §2.5). Secondary text on the
     backdrop mesh is the pair that decides how saturated the mesh may be: at #5B6088
     (L 0.1237) no light-theme mesh stop could drop below L 0.709 without failing 4.5:1,
     which caps the mesh at a near-white wash. At #4E5480 (L 0.0952) the floor moves to
     L 0.603, and that is what buys the LEGO-bright mesh below. Same indigo hue family
     (H 233°), higher contrast everywhere it was already used — this loosens nothing. */
  --bd-color-muted: #4E5480;              /* secondary text, placeholders, disabled labels, 6.77:1 */
  --bd-color-border: #E1E2EE;             /* hairlines, input borders, dividers */
  --bd-color-border-strong: #C7C9DE;      /* input focus-adjacent border, dividers on cards */

  /* Brand / primary (deep coral) — deepened design-system.md §2.3 (2026-09-04). The
     original brand coral (#FF6B45, kept below as --bd-color-primary-vivid) measures only
     2.82:1 against white and fails AA. All three sRGB channels were scaled down by the
     same factor (k=0.729) before re-hexing: scaling every channel uniformly leaves every
     channel *ratio* untouched, and HSV hue/saturation are both defined purely from those
     ratios, so this method holds hue/saturation exactly fixed and only lowers value —
     "deepen the coral," not "shift toward a different colour" (hue moved 12.26°→12.35°,
     inside hex-rounding noise). The rest of the ramp moved with it by the same method so
     hover/press still get darker relative to the new resting state, rather than
     collapsing into (or inverting) it — see design-system.md §2.3's worked table. */
  --bd-color-primary: #BA4E32;            /* CTAs, active states — white-text-safe, 4.97:1 */
  /* Deepened one more step on 2026-09-05, k 0.600 → 0.570, by the same uniform sRGB
     channel scale §2.3 uses (hue 12.32° → 12.45°, HSV saturation 73.2% → 73.1%: both
     inside hex-rounding noise). app.css routes link colour through this token, and links
     sit on the backdrop mesh; against the new, far more saturated worst-case stop
     (#BAD4F7, L 0.6424) the old #994029 measured 4.44:1 and failed AA. This is the
     "darken the token" fix, never a lowered threshold — design-system.md §2.3. */
  --bd-color-primary-dark: #913D27;       /* pressed/active + link text — white-text-safe, 7.24:1 */
  --bd-color-primary-mid: #AA472E;        /* hover/focus fill on desktop pointers — white-text-safe, 5.75:1 */
  --bd-color-primary-light: #FF9070;      /* tints, subtle fills, disabled-primary — untouched, AA-exempt (WCAG 1.4.3; never a text-bearing fill) */
  --bd-color-primary-vivid: #FF6B45;      /* the brand mark's own coral (brand/README.md) — decorative
                                              accents only, NEVER a text-bearing fill on its own; still
                                              pairs at 5.43:1 with --bd-color-on-surface where text must
                                              sit directly on it. Not white-text-safe. */

  /* CHANGED 2026-09-04 (design-system.md §2.1/§2.3) — on-primary is now white, and this
     comment replaces the old "do not fix this to white" warning because the constraint it
     described no longer holds. Previously: on-primary was dark indigo (#1C2247) because
     the *old* primary fill, raw brand coral #FF6B45, measured only 2.82:1 against white
     and failed WCAG AA — indigo was the only correct choice then, and "fixing" it to white
     would have been a live contrast bug. Now: --bd-color-primary above is a deliberately
     deepened coral (#BA4E32, same hue/saturation, lower value — see the comment on that
     token) that clears AA with WHITE text at 4.97:1, so white is the correct choice today
     and is what CTAs/FABs/filled chips should use.
     This still only holds for the three white-text-safe ramp steps above — primary,
     primary-mid, primary-dark. Do NOT put white text on --bd-color-primary-light or
     --bd-color-primary-vivid (neither was deepened; both stay non-white-text-safe), and
     never use bare --bd-color-primary as TEXT on a dark surface — it measures only 3.68:1
     there (a darker coral has less separation from a dark background); use
     --bd-color-primary-light or --bd-color-primary-vivid for that case instead. */
  --bd-color-on-primary: #FFFFFF;

  /* ================= Backdrop mesh (light theme) — §2.4.1 =================
     The KNOWN backdrop glass floats over everywhere: two soft radial glows layered over
     --bd-color-surface, forming a low-frequency mesh (no hard edges) so its worst-case
     luminance stays easy to bound even once glass + blur sit on top of it (see
     --bd-color-glass-bg below and design-system.md §2.4.3's proof). Constraint that keeps
     that proof valid, stated so it survives edits: no light-theme stop's relative
     luminance may drop below 0.62 — the three stops below sit at L 0.6424-0.7272, with
     margin. The floor was 0.85 until 2026-09-05; it moved because --bd-color-muted was
     deepened (see its own comment above), and 0.62 is where the worst stop still clears
     4.5:1 against that deeper muted with room to spare (4.77:1, not 4.51:1).
     Recompute design-system.md §2.4.1's table if any stop moves. */
  --bd-color-backdrop-warm: #FFC7AE; /* coral glow, L 0.6516 */
  --bd-color-backdrop-sun: #FFD98A;  /* yellow glow, L 0.7272 */
  --bd-color-backdrop-cool: #BAD4F7; /* blue glow, L 0.6424 — the worst-case stop */
  --bd-color-backdrop:
    radial-gradient(ellipse 900px 600px at 12% -8%, var(--bd-color-backdrop-warm) 0%, transparent 60%),
    radial-gradient(ellipse 800px 520px at 96% 14%, var(--bd-color-backdrop-sun) 0%, transparent 62%),
    radial-gradient(ellipse 900px 600px at 88% 108%, var(--bd-color-backdrop-cool) 0%, transparent 60%),
    var(--bd-color-surface);

  /* ================= Glass surface tokens (light theme) — §2.4.2 =================
     Ported from OutaStory's GlassSurface.razor / ui-v2-worlds.css, retinted to BrickDb's
     indigo/coral. Consumed by Components/GlassSurface.razor.css, which sets background/
     border/box-shadow from these UNCONDITIONALLY — only backdrop-filter itself is
     @supports-gated there, so the un-blurred state is the same translucent fill, never a
     separately-verified fallback colour. Light theme keeps OutaStory's own neutral-white
     fill alphas (0.62 / 0.78 — already white, no retint needed); the border moves from
     their near-black hairline to BrickDb's own on-surface indigo at the same low alpha. */
  --bd-color-glass-bg: rgba(255, 255, 255, 0.62);
  --bd-color-glass-bg-strong: rgba(255, 255, 255, 0.78);
  --bd-color-glass-border: rgba(28, 34, 71, 0.08);
  --bd-color-glass-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  --bd-glass-blur: 16px; /* capped per §2.4.5 — no component may raise this */

  /* RESOLVED 2026-09-04 — cards are solid (design-system.md §2.4.3 "Why cards are
     solid"): OutaStory's own content cards exclude backdrop-filter as a tested production
     constraint ("blows the GPU budget" on scrolling content), and BrickDb's Collection
     screen — the app's main screen — is itself an unbounded scrolling list, the exact
     failure mode that constraint exists to avoid. The indirection stays even though the
     decision is made: every card component reads --bd-color-card-surface, never
     --bd-color-glass-bg-strong or --bd-color-surface-raised directly, so re-opening the
     experiment is a one-line change here, not a rewrite. NEVER wrap a card, list row, or
     anything inside a scrolling region in <GlassSurface> — see §2.4.3/§2.4.5. */
  --bd-color-card-surface: #FFF7F2;

  /* Tinted section surface (NEW 2026-09-05, design-system.md §2.5) — the ground under a
     grouped block of content (a <details> disclosure, an empty state) as distinct from a
     content card. Cool indigo tint against the card's warm one, so the two read as
     different kinds of surface rather than two shades of the same one. */
  --bd-color-section-surface: #EEEFFB;

  /* ================= Photo/camera scrim floor — §2.4.4 =================
     Mandatory over UNKNOWN content (a user's photo, a live camera feed) — glass never
     sits directly on unknown content; this scrim intervenes first. Theme-independent by
     construction: it's anchored to the photo's own worst case (pure white, the hardest
     case for a dark scrim), not to either theme's surface tokens. 85%/75% opacity are
     solved minimums with a small margin (>=81.7%/>=70% is the bare floor for 4.5:1/3:1
     respectively) — see design-system.md §2.4.4 for the derivation. */
  --bd-scrim-photo-text: rgba(0, 0, 0, 0.85); /* text/labels over photo or camera content */
  --bd-scrim-photo-icon: rgba(0, 0, 0, 0.75); /* icon-only glass chips over photo or camera content */
  --bd-scrim-photo-fade: 96px;

  /* Semantic — success */
  --bd-color-success: #1B7A4A;            /* "owned/complete" states, positive deltas */
  --bd-color-on-success: #FFFFFF;         /* 5.3:1 on --bd-color-success */
  --bd-color-success-surface: #D8F2E2;    /* success chip fill — 12.94:1 with on-surface */

  /* Semantic — warning */
  --bd-color-warning: #8A5A00;            /* incomplete, needs-attention */
  --bd-color-on-warning: #FFFFFF;         /* 5.9:1 on --bd-color-warning */
  --bd-color-warning-surface: #FDEBC4;

  /* Semantic — danger */
  --bd-color-danger: #C81E3A;             /* destructive actions, missing/critical flags */
  --bd-color-on-danger: #FFFFFF;          /* 5.7:1 on --bd-color-danger */
  --bd-color-danger-surface: #FBDDE2;

  /* Chrome */
  --bd-color-scrim: rgba(14, 18, 48, 0.48);   /* modal/sheet backdrop */
  --bd-color-focus-ring: #C93B1C;             /* 3px, see app.css focus rules; tuned per theme */

  /* ================= ConditionBadge grade ramp — light theme (§6) =================
     Re-saturated 2026-09-05 (design-system.md §2.6/§6). The old ramp read muddy — grade 5
     was a blue-grey at 26% HSV saturation, grade 6 an olive at 41% — and muddy is the
     opposite of what this app is about. Every fill below is far more chromatic (66-100%
     saturation) at the SAME band structure and the same three hue families, so the
     four-channel encoding (shape / texture / colour / label) is untouched.

     What could NOT change, and why — because this is the constraint someone will try to
     relax: a light-theme fill has to stay dark. White label text needs 4.5:1, which caps
     the fill at L <= 0.1833; the chip also has to be distinguishable from the card behind
     it (>=3:1, §8), which on the #FFF7F2 card caps it at L <= 0.2717. The tighter of the
     two binds, so no LEGO-bright yellow or orange is available here at any saturation —
     on a light ground, brightness and legibility are genuinely exclusive. Chroma was the
     free axis, so chroma is the axis that moved. Dark theme (below) inverts the label to
     #0E1230 and therefore CAN go bright; it does.

     Every ratio below is asserted rather than asserted-in-a-comment. Guard:
     BrickDb.App.Shared.Tests.Design.TokenContrastTests. */
  /* Band 1 — Like-new (rounded square, solid/solid/hatch) — green family */
  --bd-grade-1: #00752F; /* Sealed    — solid          — label 5.86:1, vs card 5.53:1 */
  --bd-grade-2: #0F7F45; /* Mint      — solid          — label 5.07:1, vs card 4.79:1 */
  --bd-grade-3: #0E7E63; /* NearMint  — diagonal hatch — label 5.02:1, vs card 4.74:1 */
  /* Band 2 — Displayed (circle, solid/solid/hatch) — blue family */
  --bd-grade-4: #1265C4; /* Excellent — solid          — label 5.70:1, vs card 5.38:1 */
  --bd-grade-5: #3F63B8; /* VeryGood  — solid          — label 5.69:1, vs card 5.38:1 */
  --bd-grade-6: #7A6A00; /* Good      — diagonal hatch — label 5.40:1, vs card 5.10:1 */
  /* Band 3 — Well-used (pennant, solid/hatch/cross-hatch) — amber-red family */
  --bd-grade-7: #A3660C; /* Fair      — solid          — label 4.71:1, vs card 4.44:1 */
  --bd-grade-8: #C03A10; /* Poor      — diagonal hatch — label 5.45:1, vs card 5.15:1 */
  --bd-grade-9: #8E1B2A; /* ForParts  — cross hatch    — label 8.97:1, vs card 8.48:1 */
  --bd-color-on-grade: #FFFFFF; /* label/text colour on every grade fill, light theme */

  /* ================= LEGO-bright accent family — light theme (§2.6) =================
     Six saturated hues for the places identity colour carries meaning: a set's theme
     chip (Star Wars, Technic, City …), the empty-state illustration, a section header's
     accent rule. Deliberately NOT brand swatches — brand/README.md §5 owns the coral and
     the indigo, and this family exists precisely so nobody reaches for the brand coral to
     mean "Technic".

     Each hue ships with its own ink, and the pairing rule is the whole point:
       - ink on fill >= 4.5:1                      (the chip's label)
       - max(fill vs card, ink vs card) >= 3:1     (the chip's boundary)
     The second is what lets bright yellow and orange exist here at all. A #F7C700 chip
     cannot be told apart from a near-white card by its fill (1.51:1) — but its ink is
     dark indigo, so a 1px border in the ink colour gives the boundary 14.49:1. The deep
     hues take the opposite branch: their fill carries the boundary and their ink is
     white. Either way the chip has a visible edge, whichever half of the family it is
     from. Guard: TokenContrastTests. */
  --bd-accent-red: #D01012;        /* ink white  — 5.58:1; fill vs card 5.27:1 */
  --bd-accent-red-ink: #FFFFFF;
  --bd-accent-orange: #F5851F;     /* ink indigo — 6.03:1; ink vs card 14.49:1 */
  --bd-accent-orange-ink: #1C2247;
  --bd-accent-yellow: #F7C700;     /* ink indigo — 9.59:1; ink vs card 14.49:1 */
  --bd-accent-yellow-ink: #1C2247;
  --bd-accent-green: #0E7A34;      /* ink white  — 5.45:1; fill vs card 5.15:1 */
  --bd-accent-green-ink: #FFFFFF;
  --bd-accent-blue: #0055BF;       /* ink white  — 6.88:1; fill vs card 6.50:1 */
  --bd-accent-blue-ink: #FFFFFF;
  --bd-accent-purple: #8138A8;     /* ink white  — 6.85:1; fill vs card 6.47:1 */
  --bd-accent-purple-ink: #FFFFFF;

  /* ================= Spacing scale (4px base) ================= */
  --bd-space-0: 0;
  --bd-space-1: 4px;
  --bd-space-2: 8px;
  --bd-space-3: 12px;
  --bd-space-4: 16px;
  --bd-space-5: 20px;
  --bd-space-6: 24px;
  --bd-space-7: 32px;
  --bd-space-8: 40px;
  --bd-space-9: 48px;
  --bd-space-10: 64px;
  --bd-space-11: 80px;
  --bd-space-12: 96px;

  /* ================= Radii ================= */
  --bd-radius-sm: 8px;      /* badges, chips, input fields */
  --bd-radius-md: 12px;     /* buttons, text inputs, small tiles */
  --bd-radius-lg: 20px;     /* SetCard, MinifigCard, PhotoGallery tiles */
  --bd-radius-xl: 28px;     /* bottom sheets, modals (top corners) */
  --bd-radius-pill: 999px;  /* pill buttons, FAB, stepper */

  /* ================= Elevation / shadow (light theme relies on shadow only) ================= */
  --bd-shadow-0: none;
  --bd-shadow-1: 0 1px 2px rgba(28, 34, 71, 0.06), 0 1px 1px rgba(28, 34, 71, 0.04);
  --bd-shadow-2: 0 2px 8px rgba(28, 34, 71, 0.10), 0 1px 2px rgba(28, 34, 71, 0.06);
  --bd-shadow-3: 0 8px 24px rgba(28, 34, 71, 0.14), 0 2px 6px rgba(28, 34, 71, 0.08);
  --bd-shadow-4: 0 16px 48px rgba(14, 18, 48, 0.24);

  /* ================= Typography — see app.css for applied rules ================= */
  --bd-font-display: 'Outfit', 'Space Grotesk', 'Segoe UI', system-ui, sans-serif;
  --bd-font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
                  'Noto Sans', sans-serif;

  /* Type scale — mobile (base 0-599px). Expressed in rem (16px = 1rem) so OS/browser
     dynamic type and zoom scale the whole scale together. ≥905px overrides in app.css
     redefine the size/line-height half of each pair only; the face/weight is stable. */
  --bd-type-display-lg-size: 2.125rem;   /* 34px */
  --bd-type-display-lg-line: 2.5rem;     /* 40px */
  --bd-type-display-size: 1.75rem;       /* 28px */
  --bd-type-display-line: 2.125rem;      /* 34px */
  --bd-type-headline-size: 1.375rem;     /* 22px */
  --bd-type-headline-line: 1.75rem;      /* 28px */
  --bd-type-title-size: 1.125rem;        /* 18px */
  --bd-type-title-line: 1.5rem;          /* 24px */
  --bd-type-body-lg-size: 1.0625rem;     /* 17px */
  --bd-type-body-lg-line: 1.5rem;        /* 24px */
  --bd-type-body-size: 1rem;             /* 16px — never shrinks below this */
  --bd-type-body-line: 1.375rem;         /* 22px */
  --bd-type-body-sm-size: 0.875rem;      /* 14px */
  --bd-type-body-sm-line: 1.25rem;       /* 20px */
  --bd-type-label-size: 0.75rem;         /* 12px */
  --bd-type-label-line: 1rem;            /* 16px */
  --bd-type-label-tracking: 0.02em;
  --bd-type-caption-size: 0.75rem;       /* 12px */
  --bd-type-caption-line: 1rem;          /* 16px */

  /* ================= Motion ================= */
  --bd-duration-instant: 100ms;
  --bd-duration-short: 150ms;
  --bd-duration-medium: 250ms;
  --bd-duration-long: 400ms;
  --bd-ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --bd-ease-decelerate: cubic-bezier(0, 0, 0, 1);
  --bd-ease-accelerate: cubic-bezier(0.3, 0, 1, 1);
  --bd-ease-emphasized: cubic-bezier(0.3, 0, 0.1, 1);

  /* ================= Z-index layers ================= */
  --bd-z-base: 0;
  --bd-z-sticky-header: 10;
  --bd-z-sidebar: 20;          /* AppSidebar — the mobile top strip / drawer and the persistent rail/expanded column */
  --bd-z-dropdown: 30;         /* FilterSheet anchored popovers, menus */
  --bd-z-scrim: 40;
  --bd-z-modal: 50;            /* ConfirmSheet, PhotoCapture, modal dialogs */
  --bd-z-toast: 60;
  --bd-z-scanner-overlay: 70;  /* full-screen camera overlay — always on top */

  /* ================= Safe-area insets =================
     Not named in the design-system doc's :root block, but §5 (AppShell) explicitly
     assigns AppShell ownership of "safe-area insets" and §4.1 requires BottomNav to
     be "safe-area aware". Added here as the one token group the spec requires but
     doesn't enumerate — a thin wrapper over the CSS env() safe-area-inset-* values
     (populated by the browser on iOS Safari and by the MAUI BlazorWebView on
     notched/gesture-nav devices), with 0 fallbacks so the tokens are always valid. */
  --bd-safe-area-top: env(safe-area-inset-top, 0px);
  --bd-safe-area-right: env(safe-area-inset-right, 0px);
  --bd-safe-area-bottom: env(safe-area-inset-bottom, 0px);
  --bd-safe-area-left: env(safe-area-inset-left, 0px);
}

/* ================= Dark theme via OS preference =================
   Wins unless an explicit light choice is stamped via [data-theme="light"]. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bd-color-surface: #0E1230;
    --bd-color-surface-raised: #1C2247;
    --bd-color-surface-overlay: #242B57;
    --bd-color-surface-overlay-2: #2C3462;
    --bd-color-on-surface: #EDEEF7;
    --bd-color-muted: #A8ACC9;
    --bd-color-border: #2C3462;
    --bd-color-border-strong: #3A4479;

    --bd-color-primary-light: #FFB39E; /* focus rings / accents on dark surfaces */
    /* --bd-color-primary / -mid / -dark / on-primary (now white) are theme-invariant —
       see the note at :root. --bd-color-primary-vivid is also unchanged (decorative only). */

    /* Backdrop mesh — dark theme, §2.4.1, re-saturated 2026-09-05. Ceiling raised from
       L 0.02 to L 0.045: the binding pair is --bd-color-muted (#A8ACC9) reading on the
       LIGHTEST stop, which needs L <= 0.0546 for 4.5:1, so 0.045 keeps 4.95:1 in hand
       while giving the mesh real hue. The three stops sit at L 0.0161-0.0258. */
    --bd-color-backdrop-warm: #3A1430; /* magenta-red glow, L 0.0161 */
    --bd-color-backdrop-sun: #33260A;  /* amber glow, L 0.0211 */
    --bd-color-backdrop-cool: #0C2A60; /* blue glow, L 0.0258 — the worst-case stop */

    /* Glass surface — dark theme, §2.4.2. OutaStory's own dark variant tints its border/
       highlight toward ITS brand hue rather than neutral grey; this mirrors that
       structurally, tinted toward BrickDb's coral (--bd-color-primary-light, #FF9070)
       instead of their lavender. */
    --bd-color-glass-bg: rgba(28, 34, 71, 0.55);
    --bd-color-glass-bg-strong: rgba(28, 34, 71, 0.72);
    --bd-color-glass-border: rgba(255, 144, 112, 0.10);
    --bd-color-glass-highlight: inset 0 1px 0 rgba(255, 144, 112, 0.08);
    /* --bd-glass-blur is theme-invariant (16px), set once in :root */

    --bd-color-card-surface: #24224A; /* solid and tinted, see §2.4.3 / §2.5 */
    --bd-color-section-surface: #191E48;
    /* --bd-scrim-photo-* tokens are theme-independent — see §2.4.4 */

    --bd-color-success: #5FCB92;
    --bd-color-on-success: #0E1230;
    --bd-color-success-surface: #12452C;
    --bd-color-warning: #E3A93F;
    --bd-color-on-warning: #0E1230;
    --bd-color-warning-surface: #43330A;
    --bd-color-danger: #FF6B7A;
    --bd-color-on-danger: #0E1230;
    --bd-color-danger-surface: #451723;

    --bd-color-scrim: rgba(0, 0, 0, 0.6);
    --bd-color-focus-ring: #FF9070;

    /* ConditionBadge grades — lightened versus the light theme so they stay
       legible on --bd-color-surface-raised (#1C2247); never reuse the
       light-theme hexes unchanged on dark surfaces (design-system.md §6).
       The doc specifies "lighten by roughly one step" without giving exact
       dark-theme hexes, so these were solved (not eyeballed) to clear both
       WCAG AA thresholds simultaneously: >=4.5:1 for the on-grade label text
       against the chip fill, and >=3:1 for the chip fill as a non-text UI
       component against --bd-color-surface-raised (§8). Verified below. */
    --bd-grade-1: #1FA35C; /* label 5.62:1, vs card 4.60:1 */
    --bd-grade-2: #2FB673; /* label 7.02:1, vs card 5.75:1 */
    --bd-grade-3: #3EC4A5; /* label 8.40:1, vs card 6.88:1 */
    --bd-grade-4: #4B93E0; /* label 5.71:1, vs card 4.67:1 */
    --bd-grade-5: #7E8FD4; /* label 5.90:1, vs card 4.82:1 */
    --bd-grade-6: #A79118; /* label 5.84:1, vs card 4.78:1 */
    --bd-grade-7: #D08A0A; /* label 6.38:1, vs card 5.22:1 */
    --bd-grade-8: #F2653A; /* label 5.84:1, vs card 4.78:1 */
    --bd-grade-9: #E85C74; /* label 5.41:1, vs card 4.43:1 */
    --bd-color-on-grade: #0E1230;

    /* LEGO-bright accent family — dark theme (§2.6). Inverting the ink to #0E1230 lifts
       the cap the light theme runs into, so every hue here is genuinely bright: the
       label needs L >= 0.2083 and the chip needs L >= 0.157 against the card, and all
       six clear both by a wide margin. This is the theme where the app looks most like
       a box of bricks, and that is a consequence of the maths, not a separate decision. */
    --bd-accent-red: #FF5C5C;        /* ink #0E1230 — 6.04:1; fill vs card 4.95:1 */
    --bd-accent-red-ink: #0E1230;
    --bd-accent-orange: #FFA23D;     /* ink #0E1230 — 9.14:1; fill vs card 7.48:1 */
    --bd-accent-orange-ink: #0E1230;
    --bd-accent-yellow: #FFD426;     /* ink #0E1230 — 12.80:1; fill vs card 10.48:1 */
    --bd-accent-yellow-ink: #0E1230;
    --bd-accent-green: #54C46B;      /* ink #0E1230 — 8.26:1; fill vs card 6.76:1 */
    --bd-accent-green-ink: #0E1230;
    --bd-accent-blue: #5AA9F0;       /* ink #0E1230 — 7.29:1; fill vs card 5.97:1 */
    --bd-accent-blue-ink: #0E1230;
    --bd-accent-purple: #B983E8;     /* ink #0E1230 — 6.51:1; fill vs card 5.33:1 */
    --bd-accent-purple-ink: #0E1230;


    /* Dark theme leans on the indigo ladder for elevation, not deeper shadows */
    --bd-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
    --bd-shadow-2: 0 2px 8px rgba(0, 0, 0, 0.45);
    --bd-shadow-3: 0 8px 24px rgba(0, 0, 0, 0.5);
    --bd-shadow-4: 0 16px 48px rgba(0, 0, 0, 0.6);
  }
}

/* ================= Explicit in-app toggle =================
   Wins in BOTH directions over the OS preference. */
:root[data-theme="dark"] {
  --bd-color-surface: #0E1230;
  --bd-color-surface-raised: #1C2247;
  --bd-color-surface-overlay: #242B57;
  --bd-color-surface-overlay-2: #2C3462;
  --bd-color-on-surface: #EDEEF7;
  --bd-color-muted: #A8ACC9;
  --bd-color-border: #2C3462;
  --bd-color-border-strong: #3A4479;

  --bd-color-primary-light: #FFB39E;

  --bd-color-backdrop-warm: #3A1430;
  --bd-color-backdrop-sun: #33260A;
  --bd-color-backdrop-cool: #0C2A60;
  --bd-color-glass-bg: rgba(28, 34, 71, 0.55);
  --bd-color-glass-bg-strong: rgba(28, 34, 71, 0.72);
  --bd-color-glass-border: rgba(255, 144, 112, 0.10);
  --bd-color-glass-highlight: inset 0 1px 0 rgba(255, 144, 112, 0.08);
  --bd-color-card-surface: #24224A; /* solid and tinted, see §2.4.3 / §2.5 */
  --bd-color-section-surface: #191E48;

  --bd-color-success: #5FCB92;
  --bd-color-on-success: #0E1230;
  --bd-color-success-surface: #12452C;
  --bd-color-warning: #E3A93F;
  --bd-color-on-warning: #0E1230;
  --bd-color-warning-surface: #43330A;
  --bd-color-danger: #FF6B7A;
  --bd-color-on-danger: #0E1230;
  --bd-color-danger-surface: #451723;

  --bd-color-scrim: rgba(0, 0, 0, 0.6);
  --bd-color-focus-ring: #FF9070;

  /* Must stay byte-identical to the OS-preference dark block above; see there for how
     these were solved. Guard: TokenContrastTests resolves this palette separately. */
  --bd-grade-1: #1FA35C;
  --bd-grade-2: #2FB673;
  --bd-grade-3: #3EC4A5;
  --bd-grade-4: #4B93E0;
  --bd-grade-5: #7E8FD4;
  --bd-grade-6: #A79118;
  --bd-grade-7: #D08A0A;
  --bd-grade-8: #F2653A;
  --bd-grade-9: #E85C74;
  --bd-color-on-grade: #0E1230;

  --bd-accent-red: #FF5C5C;
  --bd-accent-red-ink: #0E1230;
  --bd-accent-orange: #FFA23D;
  --bd-accent-orange-ink: #0E1230;
  --bd-accent-yellow: #FFD426;
  --bd-accent-yellow-ink: #0E1230;
  --bd-accent-green: #54C46B;
  --bd-accent-green-ink: #0E1230;
  --bd-accent-blue: #5AA9F0;
  --bd-accent-blue-ink: #0E1230;
  --bd-accent-purple: #B983E8;
  --bd-accent-purple-ink: #0E1230;


  --bd-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
  --bd-shadow-2: 0 2px 8px rgba(0, 0, 0, 0.45);
  --bd-shadow-3: 0 8px 24px rgba(0, 0, 0, 0.5);
  --bd-shadow-4: 0 16px 48px rgba(0, 0, 0, 0.6);
}

/* ================= Explicit light choice =================
   Re-asserts the light values so an explicit choice beats a dark OS preference.
   Values intentionally duplicate the bare :root defaults above. */
:root[data-theme="light"] {
  --bd-color-surface: #F7F7FB;
  --bd-color-surface-raised: #FFFFFF;
  --bd-color-surface-overlay: #FFFFFF;
  --bd-color-surface-overlay-2: #FFFFFF;
  --bd-color-on-surface: #1C2247;
  --bd-color-muted: #4E5480;
  --bd-color-border: #E1E2EE;
  --bd-color-border-strong: #C7C9DE;

  --bd-color-primary-light: #FF9070;

  --bd-color-backdrop-warm: #FFC7AE;
  --bd-color-backdrop-sun: #FFD98A;
  --bd-color-backdrop-cool: #BAD4F7;
  --bd-color-glass-bg: rgba(255, 255, 255, 0.62);
  --bd-color-glass-bg-strong: rgba(255, 255, 255, 0.78);
  --bd-color-glass-border: rgba(28, 34, 71, 0.08);
  --bd-color-glass-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  --bd-color-card-surface: #FFF7F2; /* solid and tinted, see §2.4.3 / §2.5 */
  --bd-color-section-surface: #EEEFFB;

  --bd-color-success: #1B7A4A;
  --bd-color-on-success: #FFFFFF;
  --bd-color-success-surface: #D8F2E2;
  --bd-color-warning: #8A5A00;
  --bd-color-on-warning: #FFFFFF;
  --bd-color-warning-surface: #FDEBC4;
  --bd-color-danger: #C81E3A;
  --bd-color-on-danger: #FFFFFF;
  --bd-color-danger-surface: #FBDDE2;

  --bd-color-scrim: rgba(14, 18, 48, 0.48);
  --bd-color-focus-ring: #C93B1C;

  /* These MUST stay byte-identical to the bare :root ramp above. They drifted once:
     the 2026-09-04 darkening of grades 2, 3, 5 and 6 was applied to :root and not
     here, so anyone who actively chose Light kept the pre-correction hexes and read
     their badge labels at 3.93:1 / 3.40:1 / 4.35:1 / 4.26:1 against white. Guard:
     BrickDb.App.Shared.Tests.Design.TokenContrastTests, which resolves all four
     palettes rather than two and so cannot miss this class of drift again. */
  --bd-grade-1: #00752F;
  --bd-grade-2: #0F7F45;
  --bd-grade-3: #0E7E63;
  --bd-grade-4: #1265C4;
  --bd-grade-5: #3F63B8;
  --bd-grade-6: #7A6A00;
  --bd-grade-7: #A3660C;
  --bd-grade-8: #C03A10;
  --bd-grade-9: #8E1B2A;
  --bd-color-on-grade: #FFFFFF;

  --bd-accent-red: #D01012;
  --bd-accent-red-ink: #FFFFFF;
  --bd-accent-orange: #F5851F;
  --bd-accent-orange-ink: #1C2247;
  --bd-accent-yellow: #F7C700;
  --bd-accent-yellow-ink: #1C2247;
  --bd-accent-green: #0E7A34;
  --bd-accent-green-ink: #FFFFFF;
  --bd-accent-blue: #0055BF;
  --bd-accent-blue-ink: #FFFFFF;
  --bd-accent-purple: #8138A8;
  --bd-accent-purple-ink: #FFFFFF;


  --bd-shadow-1: 0 1px 2px rgba(28, 34, 71, 0.06), 0 1px 1px rgba(28, 34, 71, 0.04);
  --bd-shadow-2: 0 2px 8px rgba(28, 34, 71, 0.10), 0 1px 2px rgba(28, 34, 71, 0.06);
  --bd-shadow-3: 0 8px 24px rgba(28, 34, 71, 0.14), 0 2px 6px rgba(28, 34, 71, 0.08);
  --bd-shadow-4: 0 16px 48px rgba(14, 18, 48, 0.24);
}
