/* ==========================================================================
   theme.css — Casino Green palette & CSS custom properties
   Blackjack Solitaire
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Typography
   --------------------------------------------------------------------------
   Two tiers.

   BRAND (tier 1) — `--font-brand: 'Titan One'` is LOCKED. It is the wordmark
   and it is never re-typed per theme; a theme may only RECOLOUR it. Do not
   add `--font-brand` to a `body.theme-*` block.

   SUPPORT (tier 2) — `--font-display`, `--font-label`, `--font-mono` carry
   everything else. `--font-label` is the one support face a theme is allowed
   to swap (see §3), because the label voice is what makes Neon Arcade read
   differently from Refined Felt without touching the wordmark.

   The five faces are loaded by the <link> in index.html <head> (not @import —
   an @import inside a stylesheet serialises the font request behind the CSS
   fetch). Font families fall back to the system stack if the request fails,
   so a blocked/offline font never prevents the UI from rendering.
   -------------------------------------------------------------------------- */

:root {
  /* ---- Tier 1: brand (locked — recolour only, never re-type) ---- */
  --font-brand: 'Titan One', 'Space Grotesk', system-ui, sans-serif;

  /* ---- Tier 2: support ---- */
  --font-display: 'Space Grotesk', system-ui, -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, sans-serif;
  --font-label: 'Space Grotesk', system-ui, sans-serif; /* themed in §3 */
  --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, 'SF Mono', Menlo,
    Consolas, 'Liberation Mono', monospace;

  /* Long-copy fallback. Not part of either tier — retained for body copy in
     modals and legacy rules that predate the two-tier split. */
  --font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    'Helvetica Neue', Arial, sans-serif;

  /* ---- Brand lockup metrics ---- */
  --brand-size-hero: 40px; /* splash / game-over headline */
  --brand-size-bar:  19px; /* top bar */
  --brand-track:     0;    /* Titan One is already tight — never track it */

  /* ---- Support scale ---- */
  --type-h2:      24px;
  --type-body:    16px;
  --type-small:   14px;
  --type-label:   11px;
  --type-caption: 12px;

  --track-label:   0.12em; /* themed in §3, alongside --font-label */
  --track-caption: 0.04em;
  --weight-label:  700;    /* themed in §3 — display faces ship 400 only */
}

/* --------------------------------------------------------------------------
   2. Classic Casino Theme — Core Palette & All Custom Properties
   -------------------------------------------------------------------------- */

:root {
  /* ---- Core 5 colours ---- */
  --color-primary:   #0f4c3a; /* Casino Green */
  --color-secondary: #1a1a3e; /* Deep Navy    */
  --color-accent:    #d4a843; /* Casino Gold  */
  --color-neutral:   #f5f0e8; /* Ivory Cream  */
  --color-alert:     #8b1a2b; /* Burgundy Red */

  /* The accent as an rgb triple, for the ~22 places that need it at an alpha
     (issue #151). Those all used to be literal `rgba(212, 168, 67, α)` in
     layout.css / modals.css / animations.css, which no `body.theme-*` block
     could reach — so every theme painted gold scrollbars, a gold 21-glow and a
     gold personal-best banner regardless of its own accent.

     MUST BE A LITERAL, never `var(--color-accent)`-derived: a `:root`
     declaration substitutes against :root's own values and children inherit the
     already-resolved result, so a theme override would never reach it (the §2b
     alias trap, css/CLAUDE.md). Every theme with a non-gold accent restates it
     as a literal in its own block. Sibling token: --cta-glow-rgb below, which
     is the same triple for the primary CTA halo only. */
  --accent-rgb: 212, 168, 67;

  /* ---- Surface / Background colours ---- */
  --bg-table:         linear-gradient(135deg, #0f4c3a 0%, #1a5c47 50%, #0f4c3a 100%);
  --bg-table-solid:   #0f4c3a;          /* fallback solid for calc purposes     */
  --bg-card-face:     #ffffff;
  --bg-card-back:     linear-gradient(135deg, #1a1a3e 0%, #2a2a5e 100%);
  --bg-modal:         rgba(0, 0, 0, 0.7);
  --bg-modal-content: #1c1c2e;
  --bg-score:         rgba(0, 0, 0, 0.5);
  --bg-header:        rgba(0, 0, 0, 0.25);
  --bg-footer:        rgba(0, 0, 0, 0.2);
  --bg-overlay-lock:  rgba(0, 0, 0, 0.55); /* locked hand overlay */
  --bg-dragover:      rgba(212, 168, 67, 0.1);

  /* ---- Text colours ---- */
  --text-primary:    #f5f0e8;
  --text-secondary:  rgba(245, 240, 232, 0.7);
  --text-accent:     #d4a843;
  --text-card-black: #1a1a1a;
  --text-card-red:   #c0392b;
  --text-muted:      rgba(245, 240, 232, 0.4);
  --text-inverse:    #1a1a1a; /* dark text on light/gold surfaces */

  /* ---- Border colours ---- */
  --border-card:          #333333;
  --border-hand-valid:    #2ecc71;
  --border-hand-invalid:  #8b1a2b;
  --border-hand-empty:    rgba(245, 240, 232, 0.3);
  --border-hand-dragover: #d4a843;
  --border-hand-locked:   rgba(245, 240, 232, 0.15);
  --border-score:         #d4a843;
  --border-modal:         #d4a843;
  --border-btn-help:      #d4a843;

  /* ---- Border geometry ---- */
  --border-width:        2px;
  --border-radius:       6px;
  --border-radius-card:  9px;
  --border-radius-modal: 8px;
  --border-radius-btn:   4px;

  /* ---- Shadows ---- */
  --shadow-card:       0 2px 4px rgba(0, 0, 0, 0.3);
  --shadow-card-hover: 0 4px 8px rgba(0, 0, 0, 0.4);
  --shadow-card-lift:  0 8px 16px rgba(0, 0, 0, 0.5);
  --shadow-modal:      0 8px 32px rgba(0, 0, 0, 0.5);
  --shadow-score:      0 2px 8px rgba(0, 0, 0, 0.4);

  /* ---- Spacing scale ---- */
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  20px;
  --space-6:  24px;
  --space-8:  32px;
  --space-10: 40px;
  --space-12: 48px;

  /* ---- Layout ---- */
  --max-width:    1024px;
  --hand-gap:     16px;
  --section-gap:  32px;
  --score-gap:    16px;
  --deck-gap:     24px;
  --padding-page: 24px;

  /* ---- Card dimensions (desktop) ---- */
  --card-width:        64px;
  --card-height:       96px;
  --card-stack-offset: 25px;

  /* Deck and draw pile — slightly larger than hand cards */
  --deck-card-width:  74px;
  --deck-card-height: 110px;

  /* ---- Semantic component aliases ---- */

  /* Shared accent/surface derivatives. Each defaults to an existing token so
     themes that don't opt in are unchanged; a theme overrides them to pick up
     the softer accent, gold label, hairline rule, and accent glow. */
  --color-accent-soft: var(--color-accent);
  --color-gold:        var(--text-accent);
  --surface-line:      var(--border-hand-empty);
  --glow-accent:       transparent;

  /* Primary CTA (.btn-play-again) hover/active/halo. Its resting fill is
     --color-accent, but its three other states were hardcoded Casino Gold in
     modals.css §5, so on any theme whose accent is not gold the CTA jumped
     colour on hover. Literal defaults (the exact values that were inline), so
     no existing theme moves; a theme with a non-gold accent overrides them.
     Declared as literals, not var() references, so the §2b alias trap cannot
     apply. The halo is an rgb triple because it is consumed at three alphas. */
  --cta-hover:    #e0b84f;
  --cta-active:   #c49a30;
  --cta-glow-rgb: 212, 168, 67;

  /* Header */
  --header-bg:     var(--bg-header);
  --header-border: transparent;

  /* Deck */
  --deck-bg:          var(--bg-card-back);
  --deck-border:      var(--border-card);
  --deck-label:       var(--text-muted);
  --deck-hover-scale: 1.05;

  /* Draw pile */
  --pile-empty-border: var(--border-hand-empty);
  --pile-label:        var(--text-secondary);
  --pile-label-live:   var(--color-accent-soft);

  /* Hand slots */
  --hand-bg:             transparent;
  --hand-border:         var(--border-hand-empty);
  --hand-border-valid:   var(--border-hand-valid);
  --hand-border-invalid: var(--border-hand-invalid);
  --hand-border-dragover: var(--border-hand-dragover);
  --hand-value-color:    var(--text-primary);

  /* Score */
  --score-bg:     var(--bg-score);
  --score-text:   var(--text-primary);
  --score-border: var(--border-score);
  --score-label:  var(--color-gold);

  /* Animation timing */
  --duration-instant:  0ms;
  --duration-fast:     150ms;
  --duration-normal:   200ms;
  --duration-medium:   300ms;
  --duration-slow:     400ms;
  --duration-glow:     600ms;
  --ease-out:          cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --ease-in:           cubic-bezier(0.55, 0.055, 0.675, 0.19);
  --ease-in-out:       cubic-bezier(0.645, 0.045, 0.355, 1);
}

/* --------------------------------------------------------------------------
   2b. Component aliases — re-declared on <body> so they resolve per theme
   --------------------------------------------------------------------------
   These same aliases are declared in :root above (as the documented defaults).
   A custom property's var() references are substituted on the element that
   declares it, so a :root declaration resolves against :root — and a
   `body.theme-*` override of the BASE token it points at is silently ignored.
   That trap is why every theme except Neon Arcade rendered a Casino-Gold
   draw-pile label and score label regardless of its own accent.

   Re-declaring them here on <body> — which is where the theme class lives —
   makes the same var() references resolve against the active theme. A theme
   may still pin any alias directly: `body.theme-x` (0,1,1) beats `body` (0,0,1).
   -------------------------------------------------------------------------- */

body {
  --color-accent-soft: var(--color-accent);
  --color-gold:        var(--text-accent);
  --surface-line:      var(--border-hand-empty);

  --header-bg:         var(--bg-header);

  --deck-bg:           var(--bg-card-back);
  --deck-border:       var(--border-card);
  --deck-label:        var(--text-muted);

  --pile-empty-border: var(--border-hand-empty);
  --pile-label:        var(--text-secondary);
  --pile-label-live:   var(--color-accent-soft);

  --hand-border:          var(--border-hand-empty);
  --hand-border-valid:    var(--border-hand-valid);
  --hand-border-invalid:  var(--border-hand-invalid);
  --hand-border-dragover: var(--border-hand-dragover);
  --hand-value-color:     var(--text-primary);

  --score-bg:     var(--bg-score);
  --score-text:   var(--text-primary);
  --score-border: var(--border-score);
  --score-label:  var(--color-gold);
}

/* --------------------------------------------------------------------------
   3. Alternate Themes (variable overrides only — components stay the same)
   -------------------------------------------------------------------------- */

/* ---- Theme × label-face pairing ----------------------------------------
   Each theme gets ONE support face of its own: the label voice. It overrides
   `--font-label` and the tracking that face needs, and nothing else — the
   wordmark (`--font-brand`), body copy (`--font-display`) and numerals
   (`--font-mono`) stay constant across every theme.

   `--weight-label` rides along because the display faces (Rubik Mono One,
   Anton) ship a single 400 weight; asking for 700 there gets you a synthetic
   bold that smears the counters. Space Grotesk has a real 700.

   These are declared on `body.theme-*` and read by components further down the
   tree, so they resolve by INHERITANCE, not by the :root alias route. That is
   why the alias trap documented in §2b does not apply here.
   ------------------------------------------------------------------------ */

body.theme-refined-felt{
  --font-label:   'Space Grotesk', system-ui, sans-serif;
  --track-label:  0.12em;
  --weight-label: 700;
}

body.theme-neon-arcade{
  --font-label:   'Rubik Mono One', 'Space Grotesk', system-ui, sans-serif;
  --track-label:  -0.01em;
  --weight-label: 400;
}

body.theme-neon-navy {
  --font-label:   'Anton', 'Space Grotesk', system-ui, sans-serif;
  --track-label:  0.02em;
  --weight-label: 400;
}

body.theme-crimson-classic {
  --font-label:   'Space Grotesk', system-ui, sans-serif;
  --track-label:  0.12em;
  --weight-label: 700;
}

/* Indigo Hour's label voice is the mono face already loaded for the counters.
   It costs no extra font request, and the tracked uppercase mono is the whole
   reason the palette reads as a plotted swatch sheet rather than a table. */
body.theme-indigo-hour {
  --font-label:   'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --track-label:  0.14em;
  --weight-label: 700;
}

body.theme-refined-felt {
  /* No --accent-rgb and no --bg-dragover here on purpose: this theme's accent IS
     Casino Gold, so it inherits both :root defaults unchanged and #151 moved
     nothing about how it renders. */
  --color-primary:    #0a3a2c;
  --color-alert:      #c2453b;

  --bg-table:         radial-gradient(120% 95% at 50% -8%, #2a7d5b 0%, #0f4c3a 52%, #0a3a2c 100%);
  --bg-table-solid:   #0a3a2c;
  --bg-card-face:     #fdfbf5;
  --bg-modal-content: #122d21;

  --text-primary:     #f5f0e8;
  --text-secondary:   rgba(245, 240, 232, 0.7);
  --text-accent:      #d4a843;
  --text-card-black:  #20242b;
  --text-card-red:    #b3322a;
  --text-muted:       rgba(245, 240, 232, 0.4);
  --text-inverse:     #2a2118;

  --border-hand-valid:    #43c07a;
  --border-hand-invalid:  #c2453b;
}

body.theme-neon-arcade {
  --color-primary:    #0c0a18;
  --color-secondary:  #1c1340;
  --color-accent:     #ff5cf0;
  --accent-rgb:       255, 92, 240;  /* #ff5cf0 — see :root --accent-rgb (#151) */
  --color-neutral:    #f0eaff;
  --color-alert:      #ff5470;

  --bg-table:         radial-gradient(120% 95% at 50% -8%, #1c1340 0%, #0c0a18 70%);
  --bg-table-solid:   #07060f;
  --bg-card-face:     #221a3f;
  --bg-card-back:     linear-gradient(135deg, #1b0f44 0%, #3a1c6e 100%);
  --bg-modal-content: #110d20;
  --bg-dragover:      rgba(255, 92, 240, 0.1);

  --text-primary:     #f0eaff;
  --text-secondary:   rgba(240, 234, 255, 0.6);
  --text-accent:      #ff5cf0;
  --text-card-black:  #f3efff;
  --text-card-red:    #ff6f93;
  --text-muted:       rgba(240, 234, 255, 0.4);
  --text-inverse:     #0c0a18;

  --border-card:          #5847a4;
  --border-hand-valid:    #7df9ff;
  --border-hand-invalid:  #ff5470;
  --border-hand-empty:    rgba(240, 234, 255, 0.24);
  --border-hand-dragover: #ff5cf0;
  --border-score:     #ff5cf0;
  --border-modal:     #ff5cf0;
  --border-btn-help:  #ff5cf0;

  --shadow-card:       0 3px 7px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(125, 249, 255, 0.2), 0 0 16px rgba(125, 249, 255, 0.28);
  --shadow-card-hover: 0 6px 12px rgba(0, 0, 0, 0.75), 0 0 0 1px rgba(125, 249, 255, 0.35), 0 0 26px rgba(125, 249, 255, 0.42);

  /* Accent/surface derivatives — values taken from the Start Tutorial design
     mockup (--accent-soft, --gold, --line, --glow, --panel, --accent-2). */
  --color-accent-soft: #ff8cf6;
  --color-gold:        #ffd166;
  --surface-line:      rgba(255, 255, 255, 0.12);
  --glow-accent:       rgba(255, 92, 240, 0.55);

  --header-bg:     #181231;
  --header-border: rgba(255, 255, 255, 0.12);

  --bg-score:      #0d0a1c;
  --deck-border:   #7df9ff;

  /* Override the component aliases directly, not just the base tokens they
     point at: the aliases are declared on :root, so their var() references
     resolve against :root and never see a body-level base-token override. */
  --score-bg:      #0d0a1c;
  --score-border:  #ff5cf0;
  --score-label:   #ffd166;
  --hand-border:   rgba(240, 234, 255, 0.24);
  --pile-label-live: #ff8cf6;

  --pile-empty-border: rgba(240, 234, 255, 0.24);
  --pile-label:        rgba(240, 234, 255, 0.62);
}

body.theme-neon-navy {
  --color-primary:    #050d1f;
  --color-secondary:  #0e2045;
  --color-accent:     #ff6b2b;
  --accent-rgb:       255, 107, 43;  /* #ff6b2b — see :root --accent-rgb (#151) */
  --color-neutral:    #e8f0ff;
  --color-alert:      #ff4a6e;

  --bg-table:         radial-gradient(120% 95% at 50% -8%, #0e2660 0%, #050d1f 70%);
  --bg-table-solid:   #050d1f;
  --bg-card-face:     #0c1a38;
  --bg-card-back:     linear-gradient(135deg, #041536 0%, #0a2260 100%);
  --bg-modal-content: #060e22;
  --bg-dragover:      rgba(255, 107, 43, 0.1);

  --text-primary:     #ddeeff;
  --text-secondary:   rgba(221, 238, 255, 0.6);
  --text-accent:      #ff6b2b;
  --text-card-black:  #ddeeff;
  --text-card-red:    #ff5588;
  --text-muted:       rgba(221, 238, 255, 0.4);
  --text-inverse:     #050d1f;

  --border-card:          #1a3060;
  --border-hand-valid:    #38b6ff;
  --border-hand-invalid:  #ff4a6e;
  --border-hand-empty:    rgba(221, 238, 255, 0.22);
  --border-hand-dragover: #ff6b2b;
  --border-score:         #ff6b2b;
  --border-modal:         #ff6b2b;
  --border-btn-help:      #ff6b2b;

  --shadow-card:       0 3px 7px rgba(0,0,0,.7), 0 0 0 1px rgba(56,182,255,.2), 0 0 16px rgba(56,182,255,.25);
  --shadow-card-hover: 0 6px 12px rgba(0,0,0,.75), 0 0 0 1px rgba(56,182,255,.35), 0 0 26px rgba(56,182,255,.4);
}

body.theme-crimson-classic {
  --color-primary:    #3a0a18;
  --color-secondary:  #240812;
  --color-accent:     #c9a45a;
  --accent-rgb:       201, 164, 90;  /* #c9a45a — see :root --accent-rgb (#151) */
  --color-neutral:    #fae6dc;
  --color-alert:      #c94040;

  --bg-table:         radial-gradient(125% 100% at 50% -10%, #7a1830 0%, #3a0a18 72%);
  --bg-table-solid:   #3a0a18;
  --bg-card-face:     #fdfbf5;
  --bg-modal-content: #220a10;
  --bg-dragover:      rgba(201, 164, 90, 0.1);

  --text-primary:     #fae6dc;
  --text-secondary:   rgba(250, 230, 220, 0.7);
  --text-accent:      #c9a45a;
  --text-card-black:  #20242b;
  --text-card-red:    #b3322a;
  --text-muted:       rgba(250, 230, 220, 0.4);
  --text-inverse:     #2a1018;

  --border-card:          #333333;
  --border-hand-valid:    #4dba82;
  --border-hand-invalid:  #c94040;
  --border-hand-empty:    rgba(250, 230, 220, 0.32);
  --border-hand-dragover: #c9a45a;
  --border-score:         #c9a45a;
  --border-modal:         #c9a45a;
  --border-btn-help:      #c9a45a;
}

/* ---- Indigo Hour -------------------------------------------------------
   The one theme with no gold and no green anywhere in its chrome, which is
   the point of it: gold-on-green IS the casino signal, and dropping both is
   what moves the table off a casino floor. Apricot takes every job gold held
   (primary CTA, personal best, score to beat, the wordmark's second word),
   indigo takes the felt.

   Green survives in exactly one place — `--border-hand-valid` — because that
   is not decoration: mint means "this card can go here" and rose means bust,
   and the pair has to stay legible as a signal. Everything else that was gold
   or felt is repointed at the apricot/indigo ramp.

   Ramp: #10131F deep → #171B2E base → #242847 mid → #3B3F6D lit.
   ------------------------------------------------------------------------ */
body.theme-indigo-hour {
  --color-primary:    #171b2e; /* Indigo      */
  --color-secondary:  #10131f; /* Indigo deep */
  --color-accent:     #f0b285; /* Apricot     */
  --accent-rgb:       240, 178, 133; /* #f0b285 — see :root --accent-rgb (#151) */
  --color-neutral:    #eae8f2; /* Paper       */
  --color-alert:      #d4707f; /* Bust        */

  --bg-table:         radial-gradient(120% 95% at 50% -8%, #242847 0%, #171b2e 58%, #10131f 100%);
  --bg-table-solid:   #10131f;
  --bg-card-face:     #eae8f2;
  --bg-card-back:     linear-gradient(135deg, #242847 0%, #3b3f6d 100%);
  --bg-modal-content: #171b2e;
  --bg-dragover:      rgba(240, 178, 133, 0.12);

  --text-primary:     #eae8f2;
  --text-secondary:   rgba(234, 232, 242, 0.68);
  --text-accent:      #f0b285;
  --text-card-black:  #171b2e;
  /* Not the #d4707f bust rose: on the paper card face that only reaches 2.7:1,
     which is too weak for pips. This is the same hue driven dark enough to
     clear 4.5:1 while staying in the rose family. */
  --text-card-red:    #b3475c;
  --text-muted:       rgba(234, 232, 242, 0.42);
  --text-inverse:     #171b2e; /* dark text on apricot surfaces */

  --border-card:          #3b3f6d;
  --border-hand-valid:    #6fc9a4;
  --border-hand-invalid:  #d4707f;
  --border-hand-empty:    rgba(234, 232, 242, 0.22);
  --border-hand-dragover: #f0b285;
  --border-hand-locked:   rgba(234, 232, 242, 0.14);
  --border-score:         #f0b285;
  --border-modal:         #f0b285;
  --border-btn-help:      #f0b285;

  --shadow-card:       0 3px 7px rgba(6, 8, 16, 0.6), 0 0 0 1px rgba(59, 63, 109, 0.5);
  --shadow-card-hover: 0 6px 12px rgba(6, 8, 16, 0.7), 0 0 0 1px rgba(240, 178, 133, 0.35);

  /* Accent/surface derivatives. `--color-gold` is the token name, not a colour
     instruction — it is what the wordmark's second word, the score label and
     every link read. Leaving it at its Casino-Gold default is precisely the
     "no gold" failure this theme exists to avoid. */
  --color-accent-soft: #f5c6a5;
  --color-gold:        #f0b285;
  --surface-line:      rgba(234, 232, 242, 0.12);
  --glow-accent:       rgba(240, 178, 133, 0.45);

  --header-bg:     #171b2e;
  --header-border: rgba(234, 232, 242, 0.10);

  --bg-score:    #10131f;
  --deck-border: #3b3f6d;

  /* Pinned aliases. §2b re-declares these on <body> so the base-token overrides
     above do reach them, but the score chrome and pile labels are exactly the
     four that historically went stale, so they are stated outright here. */
  --score-bg:      #10131f;
  --score-border:  #f0b285;
  --score-label:   #f0b285;
  --hand-border:   rgba(234, 232, 242, 0.22);

  --pile-empty-border: rgba(234, 232, 242, 0.22);
  --pile-label:        rgba(234, 232, 242, 0.62);
  --pile-label-live:   #f5c6a5;

  /* The Play Again button is the "apricot is the way forward" case in the
     brief. Without these it rests apricot and hovers Casino Gold. */
  --cta-hover:    #f5c6a5;
  --cta-active:   #dd9a6c;
  --cta-glow-rgb: 240, 178, 133;
}


/* --------------------------------------------------------------------------
   3b. Living surface

   Ambient table motion. Three layers, all inert (`pointer-events: none`,
   `z-index: 0`) so they sit behind every card and never compete with them:

     body::after      — the ambient glow. Every theme now, tinted per theme.
     .living-surface  — the dust motes: real DOM nodes injected by
                        src/ui/livingSurface.js, because the design phases each
                        mote independently and one painted layer cannot.
     main::before     — corner suit watermarks (in layout.css, which owns `main`).

   The felt themes previously had a crosshatch grain on `body::after` and motes
   painted on `body::before`. Both are gone: `body` already paints a cross-hatch
   on every theme (§4), so the grain was a duplicate at a different pitch, and the
   painted motes could never be individually phased.

   Colours are per-theme tokens declared on `body.theme-*` below, so they reach
   the rules by INHERITANCE — not via a `:root` alias, which would hit the §2b
   trap. `--surface-watermark` is read from layout.css for the same reason.
   -------------------------------------------------------------------------- */

body.theme-refined-felt {
  --surface-watermark: rgba(255,255,255,.09);
  --surface-glow-a:    rgba(90,220,160,.16);   /* upper pool — table green */
  --surface-glow-b:    rgba(212,168,67,.11);   /* lower pool — brass */
}

body.theme-crimson-classic {
  --surface-watermark: rgba(255,255,255,.09);
  --surface-glow-a:    rgba(224,86,120,.16);   /* upper pool — claret */
  --surface-glow-b:    rgba(212,168,67,.11);   /* lower pool — brass */
}

body.theme-neon-arcade {
  --surface-watermark: rgba(125,249,255,.10);
  --surface-glow-a:    rgba(255,92,240,.20);   /* upper pool — magenta */
  --surface-glow-b:    rgba(125,249,255,.13);  /* lower pool — cyan */
}

body.theme-neon-navy {
  --surface-watermark: rgba(56,182,255,.10);
  --surface-glow-a:    rgba(56,182,255,.20);   /* upper pool — ice blue */
  --surface-glow-b:    rgba(255,107,43,.13);   /* lower pool — ember */
}

body.theme-indigo-hour {
  --surface-watermark: rgba(234,232,242,.08);
  --surface-glow-a:    rgba(240,178,133,.16);  /* upper pool — apricot */
  --surface-glow-b:    rgba(59,63,109,.30);    /* lower pool — indigo lit */
}

/* Living surface: ambient glow — on every theme, tinted by the tokens
   above. `--bg-table` already lays a STATIC radial glow behind each theme; that
   one is part of the palette and does not move. This layer is the living half:
   two soft accent pools that drift and swell across it, which is what makes the
   surface read as lit rather than as a flat rectangle.

   `inset: -30%` is load-bearing. The layer is transformed rather than
   repositioned, so it must overhang the viewport far enough that the drift plus
   the 1.18 scale can never pull an edge of a gradient into frame.

   Sits at `z-index: -2`, one tier below the motes, so the motes read as being
   IN the light rather than behind it. */
body.theme-refined-felt::after,
body.theme-crimson-classic::after,
body.theme-neon-arcade::after,
body.theme-neon-navy::after,
body.theme-indigo-hour::after {
  content: '';
  position: fixed;
  inset: -30%;
  pointer-events: none;
  z-index: -2;
  background-image:
    radial-gradient(38% 32% at 24% 20%, var(--surface-glow-a) 0%, transparent 70%),
    radial-gradient(44% 36% at 78% 76%, var(--surface-glow-b) 0%, transparent 72%);
  animation: glowDrift 18s ease-in-out infinite alternate;
}

/* Living surface: the dust motes. Structure is in src/ui/livingSurface.js; each
   mote carries its own size, position, duration and delay as inline style, so
   everything shared lives here.

   `--text-primary` is the design's `--on-tile` — cream on felt, near-white on
   neon. It is declared on `body.theme-*`, so it reaches these nodes by
   inheritance rather than through a `:root` alias (the §2b trap). */
.living-surface {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: -1;
}

.living-mote {
  position: absolute;
  border-radius: 50%;
  background: var(--text-primary);
  opacity: 0;
  will-change: transform, opacity;
  animation-name: drift;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}

/* With motion disabled the blanket rule in animations.css §5 collapses `drift`
   to a single 0.01ms pass, which would end every mote on its 100% keyframe —
   opacity 0, i.e. an empty layer. Hold them lit and still instead. */
@media (prefers-reduced-motion: reduce) {
  .living-mote {
    opacity: .38;
    animation: none;
  }
}

/* Smooth theme transitions */
body {
  transition: background-color 0.35s ease, color 0.35s ease;
}

/* --------------------------------------------------------------------------
   4. Global Reset & Base Styles
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

[hidden] { display: none !important; }

html {
  font-size: 16px;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  font-family: var(--font-display);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--text-primary);
  background-color: var(--bg-table-solid);
  min-height: 100dvh;
  overflow-x: hidden;

  /* ---- CSS-only felt texture: cross-hatch repeating gradient ---- */
  background-image:
    /* diagonal cross-hatch fine lines */
    repeating-linear-gradient(
      45deg,
      rgba(255, 255, 255, 0.015) 0px,
      rgba(255, 255, 255, 0.015) 1px,
      transparent 1px,
      transparent 6px
    ),
    repeating-linear-gradient(
      -45deg,
      rgba(255, 255, 255, 0.015) 0px,
      rgba(255, 255, 255, 0.015) 1px,
      transparent 1px,
      transparent 6px
    ),
    /* subtle horizontal weave grain */
    repeating-linear-gradient(
      0deg,
      rgba(0, 0, 0, 0.04) 0px,
      rgba(0, 0, 0, 0.04) 1px,
      transparent 1px,
      transparent 4px
    ),
    /* table gradient */
    var(--bg-table);
  background-size:
    12px 12px,
    12px 12px,
    4px 4px,
    100% 100%;
}

/* --------------------------------------------------------------------------
   5. Focus Management (Accessibility)
   -------------------------------------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

:focus:not(:focus-visible) {
  outline: none;
}

/* --------------------------------------------------------------------------
   6. Screen-reader-only utility
   -------------------------------------------------------------------------- */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* --------------------------------------------------------------------------
   7. Smooth scrolling (respects prefers-reduced-motion)
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* --------------------------------------------------------------------------
   8. Typographic components
   --------------------------------------------------------------------------
   The consumers of the §1 tokens. Anything that needs a specific voice should
   use one of these classes rather than re-declaring a font-family.
   -------------------------------------------------------------------------- */

/* ---- Brand wordmark ---- */
.brand-wordmark {
  font-family: var(--font-brand);
  font-weight: 400; /* Titan One ships one weight */
  font-size: var(--brand-size-bar);
  letter-spacing: var(--brand-track);
  line-height: 1.1;
  color: var(--text-primary);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

.brand-wordmark--hero {
  font-size: var(--brand-size-hero);
}

/* Second word carries the accent by COLOUR, not weight — Titan One has no
   bold, so a weight change here would only produce a synthetic one. */
.brand-wordmark b,
.brand-wordmark .alt {
  font-weight: inherit;
  color: var(--color-gold);
}

/* ---- Labels & captions ---- */
.type-label {
  font-family: var(--font-label);
  font-weight: var(--weight-label);
  font-size: var(--type-label);
  letter-spacing: var(--track-label);
  line-height: 1;
  text-transform: uppercase;
}

.type-caption {
  font-family: var(--font-display);
  font-size: var(--type-caption);
  letter-spacing: var(--track-caption);
  line-height: 1.35;
  color: var(--text-secondary);
}

/* ---- Counters ----
   Theme label face + tabular numerals, so a score ticking 9 → 10 → 11 does not
   shift the chips around it. */
.score-value,
.hand-total {
  font-family: var(--font-label);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

.score-label {
  font-family: var(--font-label);
  font-weight: var(--weight-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* ---- Links ---- */
a {
  color: var(--color-gold);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

a:hover {
  color: var(--text-primary);
  text-decoration-thickness: 2px;
}

/* Beginner Assistance — empty totals hidden by default; shown + enlarged when assist is on */
.hand-total--empty { display: none; }

/* Sized to stay a hand-total bubble: at 1.6rem it filled most of the slot width
   and read as the loudest thing on the board (issue #88). One clear step above
   the 0.875rem default is enough for the assist audience. */
body.beginner-assist .hand-total {
  font-size: 1.15rem;
  font-weight: 700;
}

/* Assist mode enlarges the bubble, which makes the dual ace total ("7 (17)") overflow
   even on desktop — needs its own step-down, since `body.beginner-assist .hand-total`
   outranks the plain `.hand-total--dual` rule in layout.css (issue #84). */
body.beginner-assist .hand-total--dual {
  font-size: 0.9rem;
}

body.beginner-assist .hand-total--empty {
  display: block;
  opacity: 0.45;
}
