/* =============================================================================
   theme16 · assets/theme.css
   The one stylesheet of this template. The SPA and MPA templates ship the same
   bytes, so a class means the same thing in both.

   WHAT LIVES WHERE
   - layouts/main.ejs (one style block, selector body.theme-theme16)
     declares the six seeds, the light pole --ink-bright, the elevation ladder
     (--elev-0 1 input disabled 2 3 hover 4 5 selected), the ink ladder
     (--ink-hi mid dim faint), the three server-derived inks
     (--font-on-light, --font-on-success, --font-on-danger) and --state-warning.
     NONE of those is redeclared here.
   - This file declares every other token (contract section 12) under the same
     body selector, then the base, utilities, components, states and the MPA
     Bootstrap mapping.
   - Each token is declared exactly once across the two files. The tools
     disagree about which copy of a duplicate wins: themeResolveTokens takes
     the first (the layout), themePalette takes the last (this file), and
     themeTokenCheck R10 always ranks this file above the layout. One
     declaration per name is the only way all three read the same value.
   - Everything is scoped to body.theme-theme16 because the seeds are
     on body: custom properties inherit downward only, so a token declared on
     the document element would read seeds that do not exist there.

   CONTRAST
   Target, self-imposed:
     text a user must read                       >= 4.5:1  (WCAG 1.4.3)
     UI components, and borders or outlines that
     identify a control or show its state        >= 3:1    (WCAG 1.4.11)
     decorative dividers, disabled controls      exempt    (both criteria
                                                           exempt them)
   Why this cannot be delegated to the gate:
     - tools/themeResolveTokens.js lines 362-363 count a pair as failed only
       below 3:1; 3 to 4.5 is labelled AA-large and passes. Line 366 then
       prints "all pairs >= AA" whenever nothing is under 3:1, so a body-text
       pair at 3.2:1 is reported as AA.
     - It scores only declared surface/ink-on pairs with the same suffix.
       Text drawn with --ink-dim, --ink-faint, a semantic ink, a line or the
       focus outline is never measured by any gate.
     - Seed polarity (a light canvas under a light fg) is refused only at
       admin publish time, not by any build check.
   So the numbers below were computed by hand for the example seeds, with
   utils/seedTokens.js contrastRatio and the color-mix maths copied from
   themeResolveTokens.js (sRGB, premultiplied alpha, rounded per channel;
   HSL on the shorter hue arc). Translucent colours are composited over the
   surface they sit on. Re-measure whenever a seed changes.

   Re-measured in B3 §3 (2026-09-30), after the primary fill became the
   solid accent, the semantic inks were tuned, the soft ink got its own mix
   and the chrome got its own inks. Where B3 §3 changed a token, the column
   "lowest" gives the LOWEST ratio of that pair over every preset: the
   gate's four (p1 is the example seeds) and the 27 demo-panel presets
   (d01-d27, public/demo-panel/panel.js PRESETS in file order), each applied
   as tools/themeNewGate.js render applies it (the six seeds plus the three
   --font-on-* on the body) and measured in Chrome, which also composites
   the page wash (the accent stop of --grad-page) under elev-0. The binding
   presets are light ones whose fg is a dark colour rather than the black
   pole: d22 pastel pink, d24 ice cyan, d25 powder blue, d19 the classic
   gold with its own dark fg. Record: docs/themelog/_theme-templates.md.
   B3 §3 round 1 (the same day) changed no token. It added the badge rows
   at the end of D (a status badge inside a soft button or a tile) and
   the per-rung ladder figures in C, both measured the same way.
   B3 §3 round 2 moved one share, --ink-accent and its chrome twin from 54
   to 53 percent (D and G), and added the ghost and primary badge rows at
   the end of D, the ghost-row figures in C and the SweetAlert2 rows in E,
   measured the same way.

   Example seeds: canvas 0B0E13, ink 000000, fg FFFFFF, accent D4A537,
   success 2FA36B, danger E0435C, light pole FFFFFF.

   A. The 14 named pairs, plus the chrome dim ink. The gate scores the 14.
      ink                   surface                    ink     surface  ratio  target
      --ink-on-page         --surface-page             FFFFFF  0B0E13  19.33  4.5  ok
      --ink-on-card         --surface-card             FFFFFF  1A1C21  17.05  4.5  ok
      --ink-on-input        --surface-input            FFFFFF  16191E  17.62  4.5  ok
      --ink-on-modal        --surface-modal            FFFFFF  1F2126  16.11  4.5  ok
      --ink-on-hover        --surface-hover            FFFFFF  212428  15.58  4.5  ok
      --ink-on-selected     --surface-selected         FFFFFF  232017  16.27  4.5  ok
      --ink-on-disabled     --surface-disabled         B6B7B8  171A1F   8.68  exempt (disabled)
      --ink-on-raised       --surface-raised           FFFFFF  26292D  14.61  4.5  ok
      --ink-on-chrome       --surface-chrome           FFFFFF  000000  21.00  4.5  ok
      --ink-on-chrome-dim   --surface-chrome           B8B8B8  000000  10.59  4.5  ok
      --ink-on-qr           --surface-qr               000000  FFFFFF  21.00  4.5  ok
      --ink-on-skeleton     --surface-skeleton (7%)    FFFFFF  1C1F24  16.52  exempt (no text)
      --ink-on-gold         --surface-gold (solid)     000000  D4A537   9.24  4.5  ok
                            the solid accent since B3 §3 (it was a
                            gradient; see E); lowest 4.70 (d04)
      --ink-on-light        --surface-light            000000  FFFFFF  21.00  4.5  ok
      --ink-on-dark         --surface-dark             FFFFFF  000000  21.00  4.5  ok

   B. Fixed-pole dim and faint rungs (no gate scores these)
      --ink-on-light-dim    --surface-light            616161  FFFFFF   6.19  4.5  ok
      --ink-on-light-faint  --surface-light            8C8C8C  FFFFFF   3.36  4.5  UNDER
      --ink-on-dark-dim     --surface-dark             B8B8B8  000000  10.59  4.5  ok
      --ink-on-dark-faint   --surface-dark             8C8C8C  000000   6.25  4.5  ok
      --ink-on-light-faint is under target: large text (at least 24px, or
      at least 19px bold) or decoration only. This file does not use it.

   C. Ink ladder on every rung it can meet (no gate scores these)
      surface       elev-0  elev-1  input   disabl  elev-2  elev-3  hover   elev-4  elev-5  select
      hex           0B0E13  12151A  16191E  171A1F  1A1C21  1F2126  212428  26292D  2D3034  232017
      --ink-hi      19.33   18.29   17.62   17.44   17.05   16.11   15.58   14.61   13.26   16.27
      --ink-mid     10.16    9.62    9.26    9.17    8.96    8.47    8.19    7.68    6.97    8.56
      --ink-dim      9.62    9.11    8.77    8.68    8.49    8.02    7.76    7.27    6.60    8.10
      --ink-faint    6.27    5.93    5.71    5.66    5.53    5.22    5.05    4.74    4.30    5.28
      --ink-hi, --ink-mid and --ink-dim clear 4.5 on every rung, so they
      may carry readable text anywhere on the canvas ladder. --ink-faint is
      4.30 on --elev-5, which is UNDER. It is also the doc's decoration rung,
      so it is never used for text a user must read, on any rung. This file
      uses it nowhere. Do not use any ladder ink on the chrome, QR, light or
      dark surfaces: those are fixed and need the fixed inks in A, B and G.
      SINCE 2026-09-30 (PRE-11 item 1) the seed block (layouts/main.ejs) sets
      --ink-dim at 70 percent (it was 66, which fell under 4.5 at the light
      demo-panel presets d17 to d27, worst 3.56). At 70 the B3 29-palette
      matrix has no --ink-dim cell under 4.5: lowest 4.62 (d22, on a modal),
      then selected 4.67, card 4.80, page 4.84, elev-1 5.08. Still under 4.5
      at 70, at d22: elev-4 4.36 and elev-5 4.11 (B3 round 3 moved the one
      dim text there, the .c-result label, to --ink-mid), and a ghost-button
      row (the SPA poy ticket rows): a hovered or pressed ghost button lays
      10 percent of its own ink (--ink-hi) over the surface behind it, and
      the row's dim meta line falls to 4.02 at d22 over the page (d19 4.14,
      d26 4.31, d23 4.34, d27 4.36, d21 4.49; at rest it clears at every
      preset). That row is a PRE-11 BACKLOG item: its fix is the ghost hover
      fill, not the rung. --ink-mid falls under 4.5 only on elev-5, which is
      the .c-table head: 4.37 at d22, on every page with a table. Both shares
      live in layouts/main.ejs. 72 clears elev-4 (4.64), 74 clears elev-5
      (4.66) and the ghost overlay over the page and a card (4.56, 4.53), and
      76 the ghost overlay over a modal (4.65); 72 would equal --ink-mid.
      --ink-mid needs 74 on elev-5 as well (4.66). Sources: the PRE-11 item 1
      record rows in docs/themelog/_theme-templates.md (the B3 contrast
      harness before and after, and P4's ghost-row probe) and the B3 §3
      per-rung analysis for 72 to 76.

   D. Semantic inks (text on the canvas and on tints; no gate scores these)
      B3 §3 TUNED the seed shares (the colour doc's formula is 70 for all); page = --elev-0 without the wash, the lowest column includes it.
      ink          share  hex     elev-0  elev-1  elev-2  elev-3  elev-4  select  lowest
      --ink-accent   53   E8CF95  12.69   12.01   11.19   10.58    9.59   10.69   4.56 d24
      --ink-success  55   8DCCAE  10.48    9.91    9.24    8.73    7.92    8.82   5.11 d22
      --ink-danger   59   ED909F   8.37    7.92    7.38    6.97    6.32    7.04   5.50 d22
      --ink-warning  47   EDC9A1  12.40   11.73   10.93   10.33    9.37   10.44   5.06 d22
      --ink-info     43   BEE3A6  13.56   12.83   11.95   11.30   10.25   11.41   4.90 d25
      (lowest over elev-0 to elev-3 and select, and for --ink-accent over
      --elev-hover too: 10.23 at the example seeds, 4.56 at d24, the rung
      that set its share, since an accent countdown sits in a hovered tile;
      at 54 percent it was 4.46. On elev-4 the lowest are 4.37 accent at d24,
      which reaches elev-4 only as the large .c-result numerals, target 3;
      then 4.82, 5.19, 4.77 and 4.60.)
      on its tint, the tint composited over   page    elev-1  card    modal   select  lowest
      --ink-success  on --state-success-tint  8.88    8.26    7.62    7.20    7.30   4.51 d22
      --ink-danger   on --state-danger-tint   7.46    6.98    6.46    6.07    6.12   4.67 d22
      --ink-warning  on --state-warning-tint 10.28    9.54    8.76    8.28    8.34   4.57 d22
      --ink-info     on --state-info-tint    10.98   10.17    9.34    8.82    8.90   4.57 d25
      --ink-danger   on --soft-danger         7.30    6.82    6.30    5.97     -     4.56 d22
      --ink-on-soft  on --action-soft        11.69   10.82    9.93    9.37    9.32   5.87 d24
      --ink-on-soft  on --action-soft-hover   9.44    8.64    7.98    7.53    7.57   5.28 d22
      --ink-on-soft  on --action-soft         9.79    9.06    8.38    7.93     -     4.93 d24
                     over --soft-danger (the retry in an error slot)
      --ink-on-soft  on --action-soft-hover   7.92    7.34    6.87    6.55     -     4.56 d22
                     over --soft-danger
      --soft-danger is the .c-state--error box. --ink-on-soft (its own mix,
      share 38) is the ink of .c-btn--soft, .c-chip.is-active,
      .c-badge--soft, .c-media and a pressed numpad key. The base .c-callout
      (info) is --ink-on-selected on --surface-selected (16.27, table A);
      .c-callout--success, --warning and --danger are the tint rows; a
      .c-stat__value--success is --ink-success on the stat's own
      --surface-card (9.24, lowest 5.11). All clear 4.5. Semantic inks go on
      the canvas ladder and on tints, never on a solid fill (see E), never on
      the chrome (see G) and never as a tint over --surface-raised: no
      component puts one there, and the tuned shares fall to 4.28-4.42 on it.
      A status badge inside a control (B3 §3 rounds 1 and 2: any button, a
      chip, a tile, a list row that is a link or a button) has its tint
      composited over --surface-card whatever the control paints (the rules
      after .c-badge--info), so at rest, hovered, pressed and selected it is
      the card column of the tint rows above. Before, the soft rows were the
      MPA menu-popup coupon count and the lucky-draw point and chat badges,
      the hover row the SPA lottery hub and game tiles, the ghost row the
      SPA poy ticket rows (hovered or pressed; the ghost hover is 10 percent
      of the row's ink, --ink-hi, over the page) and the primary row the web
      chat launcher's unread count:
      badge tint composited over     success    danger     warning    info
      --surface-card (the rule)       7.62       6.46       8.76       9.34
                        lowest        4.67 d22   4.83 d22   4.73 d22   4.73 d25
      --action-soft over the card     6.02       5.05       6.82       7.30
                        lowest        4.05 d22   4.22 d22   4.13 d22   4.19 d25
      --action-soft-hover over card   4.96       4.16       5.61       6.00
                        lowest        3.65 d22   3.45 d16   3.75 d22   3.83 d25
      --surface-hover                 6.92       5.92       7.96       8.46
                        lowest        4.430 d22  4.585 d22  4.495 d22  4.487 d25
      ghost hover over the page       6.72       5.77       7.83       8.30
                        lowest        3.97 d22   4.12 d22   4.04 d22   4.06 d25
      --action-fill (primary)         1.32       1.09       1.49       1.62
                        lowest        1.10 d15   1.04 d01   1.06 d15   1.01 d16
      Over every rung the soft rows fell to 3.52 success (d19), 3.06 danger
      (d16), 3.64 warning (d22) and 3.70 info (d21).

   E. Inks on solid fills (black-or-white from the server; no gate scores these)
      --ink-on-action   on --action-fill           000000  D4A537   9.24  ok
         the primary button at rest, on hover and pressed since B3 §3
         (--grad-accent, --grad-accent-hover and its :active rule); lowest
         4.70 (d04). bestInk() guarantees at least 4.58 on the accent itself.
      --ink-on-action   on --action-fill-hover     000000  DAB253  10.47  the doc's
      --ink-on-action   on --action-fill-active    000000  DFBC6B  11.54  rungs
         no longer painted behind text (4.04 and 3.25 with the light ink at
         gate preset 2, where the dark accent takes the light pole)
      --ink-on-success  on --fill-success          000000  2FA36B   6.57  ok
      --ink-on-danger   on --fill-danger           000000  E0435C   5.13  ok
         both at rest and on hover: the hover keeps the fill since B3 §3 (the
         88 percent fg mix it replaced was 4.12 at gate preset 4)
      --ink-on-warning  on --fill-warning          000000  D88D36   7.78  ok
      --state-warning resolves to D88D36 and --state-info to 68BF30.
      SweetAlert2, a stock popup (public/css/swal-theme.css re-tones it):
      the confirm button is the --ink-on-action on --action-fill pair (9.24,
      lowest 4.70 d04) and the deny button the --ink-on-danger on
      --fill-danger pair (5.13 at every preset), at rest, on hover and
      pressed since B3 §3 round 2 (the SWEETALERT2 block). The hover and
      pressed shades swal-theme.css sets (--seed-fg 14 and 26 percent) put
      the confirm at 4.04 and 3.25 (gate preset 2) and the deny at 3.97 and
      3.15 (gate preset 4).

   F. Non-text: borders, outlines and indicators (target 3:1 where they
      identify a control or show its state)
      --line-input      on --surface-input     3.70  ok   field border
      --line-input      on --surface-card      3.58  ok   field, chip, key, cell border
      --line-input      on --surface-modal     3.39  ok
      --line-input      on --surface-page      4.06  ok
      --line-input      on --elev-1            3.85  ok
      --line-input      on --surface-hover     3.28  ok
      --line-input      on --surface-selected  3.42  ok
      --line-input      on --surface-raised    3.07  ok   the lowest; do not put
                                                          fields on --elev-5 (2.79)
      --action-fill     on --surface-page      8.50  ok   focus outline, tab and
      --action-fill     on --surface-card      7.50  ok   nav indicators, selected
      --action-fill     on --surface-modal     7.08  ok   chip, tile and cell edges
      --action-fill     on --surface-raised    6.43  ok
      --action-fill     on --elev-5            5.83  ok
      --action-fill     on --surface-chrome    9.24  ok
      --action-fill     on --surface-light     2.27  UNDER  never put a focusable
                                                          control on the light
                                                          or QR surfaces
      ghost border (currentColor 40%) on page   3.79  ok
      ghost border (currentColor 40%) on card   3.81  ok
      ghost border on chrome                    3.66  ok
      --state-danger    on --surface-input     4.30  ok   invalid-field border
      .c-note start edge, a state ink, against the note (elev-1) and the
      surface around it (page, card, modal); lowest over every preset last
         success   9.91 / 10.48 /  9.24 /  8.73   5.11 d22
         danger    7.92 /  8.37 /  7.38 /  6.97   5.50 d22
         warning  11.73 / 12.40 / 10.93 / 10.33   5.06 d22
         info     12.83 / 13.56 / 11.95 / 11.30   4.90 d25
      --ring-focus      on --surface-page      2.56  UNDER  halo only. The outline
      --ring-focus      on --surface-card      2.57  UNDER  that carries focus is
      --ring-focus      on --elev-5            2.34  UNDER  --action-fill.
      --action-soft-border on --surface-card   2.57  UNDER  doc formula. The soft
                                                          button is identified by
                                                          its label (9.93), so the
                                                          border is decoration. It
                                                          never marks a state.
      --line            on page / card         1.36 / 1.20   decorative dividers only
      --line-strong     on page / card         1.95 / 1.72   decorative dividers only
      --line-accent     on --surface-raised    1.51          decorative only
      chrome hairline (ink-on-chrome 12%)      1.27          decorative only
      --surface-selected vs --surface-card     1.05  too faint to carry a state
                                                     alone, so selected list rows
                                                     and tiles add an --action-fill
                                                     edge

   G. Chrome inks (B3 §3; no gate scores these). --surface-chrome is
      --seed-ink, dark at every preset, and these inks mix toward
      --ink-on-chrome, never --seed-fg, so they stay light on a light canvas.
      The chrome rules after .c-footer give them to the classes the header
      and footer partials put directly on the chrome.
      ink                      on                              ink     ratio  lowest
      --ink-on-chrome          --surface-chrome                FFFFFF  21.00  14.82 d22
      --ink-on-chrome-dim      --surface-chrome                B8B8B8  10.59   8.35 d22
      --ink-on-chrome-accent   --surface-chrome                E8CF95  13.79   6.93 d19
      --ink-on-chrome-accent   --action-soft over chrome               11.67   6.10 d19
      --ink-on-chrome-accent   --action-soft-hover over chrome          9.51   5.42 d22
      --ink-on-chrome-accent   --action-soft over --soft-danger         9.67   5.19 d19
                               over chrome (a retry in the footer)
      --ink-on-chrome-accent   the same, hovered                        7.83   4.62 d22
      --ink-on-chrome-success  --state-success-tint over chrome 8DCCAE 10.00   6.80 d22
      --ink-on-chrome-danger   --soft-danger over chrome       ED909F   8.05   5.35 d22
      --ink-on-chrome-danger   --state-danger-tint over chrome          8.24   5.48 d22
      Before B3 §3 the canvas inks sat on the chrome; at gate preset 4 that
      was --ink-mid 2.24, --ink-dim 2.83, --ink-accent 2.50, the soft button
      2.28 and the success badge 3.15.
   ========================================================================== */

body.theme-theme16 {
  /* ── Fixed poles (colour doc section 5). Anchored on --seed-ink and
        --ink-bright, so they never flip with the canvas. Each dim or faint
        rung mixes toward its own pair's opposite pole. ── */
  --surface-light:       var(--ink-bright);
  --ink-on-light:        var(--seed-ink);
  --ink-on-light-dim:    color-mix(in srgb, var(--seed-ink) 62%, var(--ink-bright));
  --ink-on-light-faint:  color-mix(in srgb, var(--seed-ink) 45%, var(--ink-bright));
  --surface-dark:        var(--seed-ink);
  --ink-on-dark:         var(--ink-bright);
  --ink-on-dark-dim:     color-mix(in srgb, var(--ink-bright) 72%, var(--seed-ink));
  --ink-on-dark-faint:   color-mix(in srgb, var(--ink-bright) 55%, var(--seed-ink));

  /* ── Semantic states. --state-warning lives in the layout (the parity
        test reads it there).
        --state-info. The three chromatic seeds only reach the hues on the
        shorter arcs between them. At the example seeds those arcs cover
        danger 350 through warning 32 and accent 42 to success 151, going
        through red, orange, yellow and green. The blue band from 151 round to
        350 would need a longer-hue mix, which themeResolveTokens cannot
        resolve. The only other blue-ish input is the canvas, which would make
        info flip with the canvas and turn slate grey. So info takes the middle
        of the widest gap that can be reached: success and accent, half each,
        in HSL. Measured at the example seeds: 68BF30, hue 96.5, which is 55
        degrees from success (Lab dE 41), 64 from warning (dE 71) and 54 from
        accent. The theme9 formula (success 70 percent into accent) lands at
        hue 119, only 32 degrees from success, and reads as a second green.
        With other seeds the midpoint moves with them. For example, with a
        magenta accent and a green success the short arc runs through blue. ── */
  --state-success:       var(--seed-success);
  --state-danger:        var(--seed-danger);
  --state-info:          color-mix(in hsl, var(--seed-success) 50%, var(--seed-accent));
  --state-success-tint:  color-mix(in srgb, var(--state-success) 14%, transparent);
  --state-danger-tint:   color-mix(in srgb, var(--state-danger) 14%, transparent);
  --state-warning-tint:  color-mix(in srgb, var(--state-warning) 14%, transparent);
  --state-info-tint:     color-mix(in srgb, var(--state-info) 14%, transparent);

  /* ── Semantic inks: text on a semantic TINT, never on a solid fill.
        Each is its seed pulled toward --seed-fg, so they track the canvas.
        Tuned in B3 §3 the way the themes tuned theirs (promo pack R-BATCH
        2.5 and R-RESUME 2.5): the colour doc's 70 percent seed share is
        the start, and each ink keeps the LARGEST share that still gives
        4.5:1 on every canvas rung a badge, callout or error box sits on
        (elev-0 under the page wash, elev-1, card, modal, selected) and on
        its own tint over each of them, at the example seeds, the gate's
        four presets and all 27 demo-panel presets. The binding presets are
        the light ones whose fg is a dark colour rather than the black pole
        (pastel pink, powder blue, ice cyan). --ink-accent also holds on
        --elev-hover (an accent countdown in a hovered tile), which took it
        from 54 to 53 percent (B3 §3 round 2). Table D has the numbers. ── */
  --ink-accent:          color-mix(in srgb, var(--seed-accent) 53%, var(--seed-fg));
  /* theme16 retune (DESIGN.md §6): the mint and crimson seeds are lighter than the example ones, so their
     inks take a smaller seed share. A smaller share always moves the ink toward the stronger pole, so it
     only raises contrast, on a dark canvas and on a light one. */
  --ink-success:         color-mix(in srgb, var(--seed-success) 48%, var(--seed-fg));
  --ink-danger:          color-mix(in srgb, var(--seed-danger) 52%, var(--seed-fg));
  --ink-warning:         color-mix(in srgb, var(--state-warning) 47%, var(--seed-fg));
  --ink-info:            color-mix(in srgb, var(--state-info) 43%, var(--seed-fg));

  /* ── The action set: nine tokens, the doc's names and formulas exactly,
        with ONE recorded divergence (B3 §3): --ink-on-soft is its own mix,
        not an alias of --ink-accent. The soft ink sits on an accent tint,
        and in an error slot on the danger tint as well (the retry button),
        so it needs a larger fg share than accent text on a plain rung:
        38 percent is the largest share that keeps 4.5:1 there, at rest and
        on hover, over the page, a panel, a card, a modal and the selected
        rung, in every preset (table D). The two hover and pressed rungs keep
        the doc's formulas; the primary button no longer paints them (see
        the gradients below). ── */
  --action-fill:         var(--seed-accent);
  --action-fill-hover:   color-mix(in srgb, var(--seed-accent) 86%, var(--seed-fg));
  --action-fill-active:  color-mix(in srgb, var(--seed-accent) 74%, var(--seed-fg));
  --ink-on-action:       var(--font-on-light);
  --action-soft:         color-mix(in srgb, var(--seed-accent) 14%, transparent);
  --action-soft-hover:   color-mix(in srgb, var(--seed-accent) 24%, transparent);
  --action-soft-border:  color-mix(in srgb, var(--seed-accent) 45%, transparent);
  --ink-on-soft:         color-mix(in srgb, var(--seed-accent) 38%, var(--seed-fg));
  --ring-focus:          color-mix(in srgb, var(--seed-accent) 45%, transparent);

  /* ── Solid semantic fills and their inks. The inks are the server-derived
        black-or-white choices. Warning has no derived key of its own
        (colour doc section 6), so it borrows --font-on-danger, because the warning
        colour is 74 percent danger. ── */
  --fill-success:        var(--state-success);
  --fill-danger:         var(--state-danger);
  --fill-warning:        var(--state-warning);
  --ink-on-success:      var(--font-on-success);
  --ink-on-danger:       var(--font-on-danger);
  --ink-on-warning:      var(--font-on-danger);
  --soft-success:        color-mix(in srgb, var(--state-success) 16%, transparent);
  --soft-danger:         color-mix(in srgb, var(--state-danger) 16%, transparent);
  --soft-warning:        color-mix(in srgb, var(--state-warning) 16%, transparent);

  /* ── Named surface/ink pairs. Each surface declares its ink on the same
        line. CANVAS surfaces take CANVAS inks and FIXED surfaces take FIXED
        inks. light and dark are the fixed poles above. ── */
  --surface-page:        var(--elev-0);          --ink-on-page:      var(--ink-hi);
  --surface-card:        var(--elev-2);          --ink-on-card:      var(--ink-hi);
  --surface-input:       var(--elev-input);      --ink-on-input:     var(--ink-hi);
  --surface-modal:       var(--elev-3);          --ink-on-modal:     var(--ink-hi);
  --surface-hover:       var(--elev-hover);      --ink-on-hover:     var(--ink-hi);
  --surface-selected:    var(--elev-selected);   --ink-on-selected:  var(--ink-hi);
  --surface-disabled:    var(--elev-disabled);   --ink-on-disabled:  var(--ink-dim);
  --surface-raised:      var(--elev-4);          --ink-on-raised:    var(--ink-hi);
  /* Chrome is deliberately dark on any canvas, so its inks are fixed. */
  /* The chrome sits on the canvas (DESIGN.md §3, §6): a section rung, read with the canvas inks. */
  --surface-chrome:      var(--elev-1);          --ink-on-chrome:    var(--ink-hi);
  --ink-on-chrome-dim:   var(--ink-mid);
  /* The chrome twins of the accent, success and danger inks (B3 §3), for a
     link, a soft button, a status badge or an error slot placed directly in
     the header or the footer (the chrome rules after .c-footer). Same seed
     share as the canvas ink, mixed toward --ink-on-chrome instead of
     --seed-fg: at the example seeds the fg IS the light pole, so each twin
     equals its canvas ink there, and on a light canvas it stays light on
     the dark chrome. */
  --ink-on-chrome-accent:  color-mix(in srgb, var(--seed-accent) 53%, var(--ink-on-chrome));
  --ink-on-chrome-success: color-mix(in srgb, var(--seed-success) 48%, var(--ink-on-chrome));
  --ink-on-chrome-danger:  color-mix(in srgb, var(--seed-danger) 52%, var(--ink-on-chrome));
  /* A QR must stay dark-on-light whatever the canvas does. */
  --surface-qr:          var(--surface-light);   --ink-on-qr:        var(--ink-on-light);
  /* The skeleton shows no text. The gate exempts it from the ratio but it
     must still resolve. */
  --surface-skeleton:    color-mix(in srgb, var(--seed-fg) 7%, transparent);
  --ink-on-skeleton:     var(--ink-hi);
  /* gold = the filled call-to-action surface, the primary button. It is the
     solid accent (--grad-accent below), so every pixel behind its label is
     the one colour its ink was chosen for. */
  --surface-gold:        var(--grad-accent);     --ink-on-gold:      var(--ink-on-action);

  /* ── Lines. --line and --line-strong are decorative dividers.
        --line-input is the border that identifies a field. It is fg 42
        percent into the canvas, the lowest step that clears 3:1 against the
        input, card and modal surfaces at the example seeds (measured in the
        table above). ── */
  --line:                color-mix(in srgb, var(--seed-fg) 12%, var(--seed-canvas));
  --line-strong:         color-mix(in srgb, var(--seed-fg) 22%, var(--seed-canvas));
  --line-accent:         color-mix(in srgb, var(--seed-accent) 22%, transparent);
  --line-input:          color-mix(in srgb, var(--seed-fg) 42%, var(--seed-canvas));

  /* ── Gradients, shadows and glows. Shadows mix toward --seed-ink, the
        fixed dark pole, so a shadow stays dark on a light canvas.
        --grad-accent (the primary button at rest, through --surface-gold)
        and --grad-accent-hover (on hover) are the SOLID accent since B3 §3.
        Their old stops, the doc's 86 and 74 percent rungs toward --seed-fg,
        put white text on 4.04:1 (rest) and 3.25:1 (hover, pressed) at gate
        preset 2. No fixed mix can be safe, because --ink-on-action is the
        black-or-white pick for the accent alone, and a mix of the accent
        with any token moves toward that ink in some palette (fg lightens
        on a dark canvas, darkens on a light one). The accent itself holds at least
        4.58:1 with its own ink by construction (utils/seedTokens.js
        bestInk), so hover shows the accent glow instead of a lighter fill,
        and the pressed state keeps the fill too (STATES). ── */
  --grad-accent:         var(--action-fill);
  --grad-accent-hover:   var(--action-fill);
  --grad-page:           radial-gradient(1400px 720px at 82% -12%, color-mix(in srgb, var(--seed-accent) 10%, transparent), transparent 62%), radial-gradient(1100px 640px at -12% 28%, color-mix(in srgb, var(--ink-bright) 4%, transparent), transparent 58%), var(--lattice);
  --shadow-sm:           0 2px 10px color-mix(in srgb, var(--seed-ink) 22%, transparent);
  --shadow:              0 10px 30px color-mix(in srgb, var(--seed-ink) 30%, transparent);
  --shadow-lg:           0 24px 64px color-mix(in srgb, var(--seed-ink) 45%, transparent);
  --glow-accent:         0 0 22px color-mix(in srgb, var(--seed-accent) 30%, transparent);
  --focus-ring:          0 0 0 3px var(--ring-focus);

  /* ── Type. theme9's three font token names, with the Google Fonts pairing
        the layouts load: Prompt for display, Noto Sans Thai for body text and
        Chakra Petch for numerals. ── */
  --font-thai:           "Noto Sans Thai Looped","Noto Sans Thai",system-ui,sans-serif;
  --font-bold:           "Noto Sans Thai Looped","Noto Sans Thai",system-ui,sans-serif;
  --font-display:        "Taviraj","Noto Sans Thai Looped",serif;
  --font-num:            "Noto Sans Thai Looped","Noto Sans Thai",sans-serif;

  /* ── Radii ── */
  --radius-xs: 8px;  --radius-sm: 12px;  --radius: 18px;
  --radius-lg: 24px; --radius-pill: 999px; --radius-sheet: 28px;

  /* ── Layout metrics (carried from theme9). --tap-min is the smallest hit
        area of any interactive element. ── */
  --hdr-h:               64px;
  --mbbrand-h:           52px;
  --tabbar-h:            56px;
  --bottomnav-h:         64px;
  --ticker-h:            40px;
  --sidebar-w:           248px;
  --feed-max:            none;
  --gutter:              18px;
  --gutter-lg:           32px;
  --tap-min:             44px;
  --fab-size:            56px;

  /* ── Z-index ladder. --z-notice: the back office's per-page popup (.ntf-overlay), over the overlay it belongs to and
        under the toasts (TH-1006-B2). ── */
  --z-feed:10; --z-ticker:20; --z-header:30; --z-tabbar:40; --z-bottomnav:42; --z-fab:45;
  --z-widget:50; --z-sheet:60; --z-modal:70; --z-notice:75; --z-toast:80; --z-guard:90;

  /* ── Motion ── */
  --ease-out:            cubic-bezier(.22,.8,.24,1);
  --dur-fast:            180ms;  --dur: 300ms;  --dur-slow: 460ms;
  --marquee-dur:         32s;

  /* ── Leading. Thai stacks vowels and tone marks above and below the
        baseline (for example the upper vowel, tone mark and lower vowel on a
        single consonant), so leading tuned for Latin clips them or makes them
        collide with the next line. Thai needs about 1.7, where Latin reads
        well at about 1.45. ── */
  --leading-latin:       1.45;
  --leading-thai:        1.75;
  --leading-display:     1.5;

  /* ── Type scale ── */
  --text-xs:             0.75rem;
  --text-sm:             0.875rem;
  --text-base:           1rem;
  --text-lg:             1.1875rem;
  --text-xl:             1.4375rem;
  --text-2xl:            1.875rem;
  --text-3xl:            2.25rem;

  /* ── Space scale ── */
  --space-1:             4px;
  --space-2:             8px;
  --space-3:             12px;
  --space-4:             18px;
  --space-5:             28px;
  --space-6:             40px;

  /* ── theme16 (DESIGN.md §4): the gold hairline, the crystal sheen and edge, the jade lattice, the gold glow. ── */
  --line-gold:           color-mix(in srgb, var(--seed-accent) 40%, transparent);
  --line-gold-strong:    color-mix(in srgb, var(--seed-accent) 70%, transparent);
  --sheen:               linear-gradient(165deg, color-mix(in srgb, var(--ink-bright) 5%, transparent) 0%, transparent 42%);
  --edge-hi:             inset 0 1px 0 color-mix(in srgb, var(--ink-bright) 8%, transparent);
  --edge-hi-strong:      inset 0 1px 0 color-mix(in srgb, var(--ink-bright) 35%, transparent);
  --glow-gold:           color-mix(in srgb, var(--seed-accent) 12%, transparent);
  --lattice:             repeating-linear-gradient(45deg, color-mix(in srgb, var(--seed-accent) 4%, transparent) 0 1px, transparent 1px 30px), repeating-linear-gradient(-45deg, color-mix(in srgb, var(--seed-accent) 4%, transparent) 0 1px, transparent 1px 30px);
}

/* =============================================================================
   BASE
   ========================================================================== */

body.theme-theme16 {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  font-family: var(--font-thai);
  font-size: var(--text-base);
  line-height: var(--leading-latin);
  color: var(--ink-on-page);
  background-color: var(--surface-page);
  background-image: var(--grad-page);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body.theme-theme16 *,
body.theme-theme16 *::before,
body.theme-theme16 *::after {
  box-sizing: border-box;
}

/* Leading. The document is lang="th", so the body takes Thai leading and
   every element inherits it. Numeral-only components set --leading-latin on
   themselves further down. An element that declares its own language gets
   that language's leading. */
body.theme-theme16:lang(th),
body.theme-theme16 [lang|="th"] {
  line-height: var(--leading-thai);
}
body.theme-theme16 [lang|="en"] {
  line-height: var(--leading-latin);
}

/* Element defaults. Headings, paragraphs and links are written as
   html :where(body...) element, which scores (0,0,2): enough to beat
   Bootstrap Reboot's single-element rules in the MPA, too little to beat any
   component class. Media and form elements score zero. So an element that
   carries a .c- class always takes the component's values. */
html :where(body.theme-theme16) h1,
html :where(body.theme-theme16) h2,
html :where(body.theme-theme16) h3,
html :where(body.theme-theme16) h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 600;
  line-height: var(--leading-display);
  color: inherit;
}
html :where(body.theme-theme16) h1 { font-size: var(--text-2xl); }
html :where(body.theme-theme16) h2 { font-size: var(--text-xl); }
html :where(body.theme-theme16) h3 { font-size: var(--text-lg); }
html :where(body.theme-theme16) h4 { font-size: var(--text-base); }

html :where(body.theme-theme16) p { margin: 0; }

html :where(body.theme-theme16) a {
  color: var(--ink-accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
html :where(body.theme-theme16) a:where(:hover) { color: var(--ink-hi); }

:where(body.theme-theme16) :where(img, svg, video, canvas) {
  max-width: 100%;
}
:where(body.theme-theme16) :where(img) { height: auto; }

:where(body.theme-theme16) :where(button, input, select, textarea) {
  font: inherit;
  color: inherit;
}

body.theme-theme16 ::selection {
  color: var(--ink-on-action);
  background-color: var(--action-fill);
}

/* Open overlays lock the page behind them. */
body.theme-theme16:has(.c-overlay:not([hidden])) {
  overflow: hidden;
}

/* =============================================================================
   HIDDEN. Views and overlays are toggled with the hidden attribute, so no
   component display rule may ever beat it.
   ========================================================================== */

[hidden] { display: none !important; }

/* =============================================================================
   UTILITIES (exactly the 32 in contract section 9)
   Each selector repeats its class (.u-row.u-row). That doubles the
   specificity, so a utility beats the single-class component it is combined
   with (class="c-banner u-row") even though components come later in the
   file. It adds no class names.
   ========================================================================== */

.u-stack.u-stack        { display: flex; flex-direction: column; gap: var(--space-3); }
.u-stack-tight.u-stack-tight  { display: flex; flex-direction: column; gap: var(--space-1); }
.u-stack-loose.u-stack-loose  { display: flex; flex-direction: column; gap: var(--space-5); }
.u-row.u-row          { display: flex; flex-direction: row; align-items: center; gap: var(--space-2); }
.u-row-between.u-row-between  { display: flex; flex-direction: row; align-items: center; justify-content: space-between; gap: var(--space-2); }
.u-row-end.u-row-end      { display: flex; flex-direction: row; align-items: center; justify-content: flex-end; gap: var(--space-2); }
.u-wrap.u-wrap         { flex-wrap: wrap; }
.u-grid-2.u-grid-2       { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
.u-grid-3.u-grid-3       { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-3); }
.u-grid-4.u-grid-4       { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
.u-grid-auto.u-grid-auto    { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--space-3); }
.u-center.u-center       { display: flex; align-items: center; justify-content: center; text-align: center; }
.u-pad.u-pad          { padding: var(--space-4); }
.u-pad-tight.u-pad-tight    { padding: var(--space-2); }
.u-pad-loose.u-pad-loose    { padding: var(--space-6); }
.u-gutter.u-gutter       { padding-inline: var(--gutter); }
.u-container.u-container    { width: 100%; max-width: var(--feed-max); margin-inline: auto; padding-inline: var(--gutter); }
.u-scroll-x.u-scroll-x     { display: flex; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.u-scroll-x.u-scroll-x::-webkit-scrollbar { display: none; }
.u-scroll-x.u-scroll-x > * { flex: none; scroll-snap-align: start; }
.u-grow.u-grow         { flex: 1 1 auto; min-width: 0; }
.u-full.u-full         { width: 100%; }
.u-sticky-top.u-sticky-top   { position: sticky; top: 0; z-index: var(--z-tabbar); }
.u-sticky-bottom.u-sticky-bottom {
  position: sticky;
  bottom: 0;
  z-index: var(--z-tabbar);
  padding-bottom: env(safe-area-inset-bottom);
}
.u-truncate.u-truncate     { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.u-text-mid.u-text-mid     { color: var(--ink-mid); }
.u-text-dim.u-text-dim     { color: var(--ink-dim); }
.u-text-end.u-text-end     { text-align: end; }
.u-num.u-num {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  line-height: var(--leading-latin);
  letter-spacing: 0.01em;
}
.u-thai.u-thai         { font-family: var(--font-thai); line-height: var(--leading-thai); }
.u-sr-only.u-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  border: 0;
  white-space: nowrap;
}
.u-divider.u-divider {
  height: 0;
  margin: var(--space-3) 0;
  border: 0;
  border-top: 1px solid var(--line);
}

@media (min-width: 768px) {
  .u-grid-4.u-grid-4     { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .u-gutter.u-gutter,
  .u-container.u-container  { padding-inline: var(--gutter-lg); }
}
@media (max-width: 767.98px) {
  .u-hide-mobile.u-hide-mobile  { display: none !important; }
}
@media (min-width: 768px) {
  .u-hide-desktop.u-hide-desktop { display: none !important; }
}

/* =============================================================================
   COMPONENTS
   Every interactive component keeps a hit area of at least --tap-min.
   ========================================================================== */

/* ── Page structure ── */
.c-page {
  display: block;
  min-width: 0;
  padding-block: var(--space-4) var(--space-6);
}
/* A page that carries its own sidebar lays it beside the content from the
   same 768px breakpoint that u-hide-mobile uses to show that sidebar. */
@media (min-width: 768px) {
  .c-page:has(> aside) {
    display: grid;
    grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
    align-items: start;
    gap: var(--gutter-lg);
    padding-inline: var(--gutter-lg);
  }
  .c-page > aside {
    position: sticky;
    top: calc(var(--hdr-h) + var(--space-4));
  }
}

.c-view {
  display: block;
  padding-block: var(--space-4);
  animation: c-fade-in var(--dur) var(--ease-out);
}

.c-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding-block: var(--space-4);
}
.c-section__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--tap-min);
}
.c-section__head > h2,
.c-section__head > h3 {
  font-size: var(--text-lg);
}
/* The trigger column of the section-header primitive (ดูทั้งหมด and the
   carousel arrows: the .u-row-end of partials/primitives/section-header.ejs)
   keeps its labels on one line and never shrinks; the title column beside
   it (.u-grow, min-width 0) shrinks and wraps instead. At 390px the LIVE
   shelf title used to squeeze ดูทั้งหมด onto two lines. Only that column:
   a button placed straight in a head (the MPA bank page's add-account
   button) still wraps its label inside its own box, because a one-line
   label wider than the room left would run out of the button's fill. */
.c-section__head > .u-row-end > .c-btn {
  white-space: nowrap;
  flex: none;
}

.c-card {
  color: var(--ink-on-card);
  background: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--space-4);
  box-shadow: var(--shadow-sm);
}

.c-panel {
  color: var(--ink-hi);
  background: var(--elev-1);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
}

/* ── Buttons ── */
.c-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: 0 var(--space-4);
  border: 1px solid var(--line-input);
  border-radius: var(--radius);
  color: var(--ink-on-raised);
  background: var(--surface-raised);
  font-family: var(--font-bold);
  font-size: var(--text-base);
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.c-btn--primary {
  color: var(--ink-on-gold);
  background: var(--surface-gold);
  border-color: transparent;
  box-shadow: var(--shadow-sm);
}
.c-btn--soft {
  color: var(--ink-on-soft);
  background: var(--action-soft);
  border-color: var(--action-soft-border);
}
/* Secondary text inside a soft or a filled button (a subtitle, as in the
   MPA lucky-draw chat item and the SPA support sheet's LINE buttons) takes
   the button's own ink: the canvas rungs --ink-mid and --ink-dim are never
   measured on the accent tint or on a solid fill. On the soft tint they
   fell to 3.18:1 at a light demo-panel preset (B3 §3); on the primary fill
   the LINE Official subtitle (partials/modals/support.ejs) was 1.01:1 at
   the example seeds (B3 §3 round 2). One selector per button, with no
   :is() list, because tools/themePolarityCheck.js splits a selector at
   every comma and would read a bare .c-btn--primary as this rule's
   background. */
.c-btn--soft .u-text-mid.u-text-mid,
.c-btn--soft .u-text-dim.u-text-dim,
.c-btn--primary .u-text-mid.u-text-mid,
.c-btn--primary .u-text-dim.u-text-dim,
.c-btn--danger .u-text-mid.u-text-mid,
.c-btn--danger .u-text-dim.u-text-dim,
.c-btn--success .u-text-mid.u-text-mid,
.c-btn--success .u-text-dim.u-text-dim {
  color: inherit;
}
.c-btn--ghost {
  color: inherit;
  background: transparent;
  border-color: color-mix(in srgb, currentColor 40%, transparent);
}
.c-btn--danger {
  color: var(--ink-on-danger);
  background: var(--fill-danger);
  border-color: transparent;
}
.c-btn--success {
  color: var(--ink-on-success);
  background: var(--fill-success);
  border-color: transparent;
}
.c-btn--block { display: flex; width: 100%; }
.c-btn--sm {
  padding: 0 var(--space-3);
  font-size: var(--text-sm);
  border-radius: var(--radius-sm);
}

/* ── Forms ── */
.c-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.c-label {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--ink-mid);
}
.c-input,
.c-select {
  display: block;
  width: 100%;
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  color: var(--ink-on-input);
  background-color: var(--surface-input);
  border: 1px solid var(--line-input);
  border-radius: var(--radius-sm);
  font-family: var(--font-thai);
  font-size: var(--text-base);
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
textarea.c-input {
  min-height: calc(var(--tap-min) * 2);
  padding-block: var(--space-2);
  resize: vertical;
}
.c-input::placeholder { color: var(--ink-dim); opacity: 1; }
.c-select {
  appearance: none;
  -webkit-appearance: none;
  padding-inline-end: calc(var(--space-3) * 3);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-mid) 50%),
    linear-gradient(135deg, var(--ink-mid) 50%, transparent 50%);
  background-position:
    calc(100% - 18px) 50%,
    calc(100% - 13px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.c-input[aria-invalid="true"],
.c-select[aria-invalid="true"] {
  border-color: var(--state-danger);
}
.c-help {
  font-size: var(--text-xs);
  color: var(--ink-dim);
}

/* ── Chips, tabs, badges ── */
.c-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: 0 var(--space-3);
  color: var(--ink-mid);
  background: var(--surface-card);
  border: 1px solid var(--line-input);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

.c-tabs {
  display: flex;
  gap: var(--space-1);
  overflow-x: auto;
  border-bottom: 1px solid var(--line);
  scrollbar-width: none;
}
.c-tabs::-webkit-scrollbar { display: none; }
.c-tab {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: 0 var(--space-4);
  color: var(--ink-mid);
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-family: var(--font-bold);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
/* Pill tabs (STRUCT-B3b; docs/THEME_STRUCTURE_CATALOG.md): the same ARIA tab list, each tab a pill instead of an
   underlined label. Add c-tabs--pills to the [role="tablist"]; the tabs keep c-tab, role="tab" and their ids. */
.c-tabs--pills {
  flex-wrap: wrap;
  gap: var(--space-2);
  padding-block: var(--space-1);
  border-bottom: 0;
}
.c-tabs--pills > .c-tab {
  padding: 0 var(--space-3);
  color: var(--ink-on-card);
  background: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  margin-bottom: 0;
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}

.c-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  color: var(--ink-mid);
  background: var(--surface-raised);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
}
.c-badge--success { color: var(--ink-success); background: var(--state-success-tint); }
.c-badge--danger  { color: var(--ink-danger);  background: var(--state-danger-tint); }
.c-badge--warning { color: var(--ink-warning); background: var(--state-warning-tint); }
.c-badge--info    { color: var(--ink-info);    background: var(--state-info-tint); }
/* A status badge inside a control keeps the card surface under its tint
   (B3 §3 rounds 1 and 2): inside any button (soft, ghost, primary or the
   plain one), a chip, a tile, or a list row that is a link or a button.
   Over the control's own paint it was a tint over whatever the control
   paints, and that changes with the state: a soft button IS the accent
   tint (a stronger one on hover), a tile, a chip and a list row turn
   --surface-hover on hover, a ghost button lays 10 percent of its own ink
   over the surface behind it on hover and when pressed, and a primary
   button is the solid accent. That fell to 3.27:1 (the MPA chat count, a
   danger badge in a hovered soft button, demo preset d16), 4.43:1 (the SPA
   lottery hub's ฟรี! badge in a hovered tile, d22), 3.97:1 (the SPA poy
   ticket row, a success badge in a hovered ghost button, d22) and 1.09:1
   (the web chat launcher's unread count on the primary fill, at the
   example seeds). Over the card it is the tint-over-card pair of table D
   at rest, hovered, pressed and selected. Not in the header or footer: the
   chrome rules ink those badges for tints over the chrome. The list rows
   are written a.c-list__item and button.c-list__item, not :is(a, button),
   because tools/themePolarityCheck.js splits a selector at every comma. */
.c-btn .c-badge--success:not(:is(.c-header *, .c-footer *)),
.c-chip .c-badge--success:not(:is(.c-header *, .c-footer *)),
.c-tile .c-badge--success:not(:is(.c-header *, .c-footer *)),
a.c-list__item .c-badge--success:not(:is(.c-header *, .c-footer *)),
button.c-list__item .c-badge--success:not(:is(.c-header *, .c-footer *)) {
  background: linear-gradient(var(--state-success-tint), var(--state-success-tint)), var(--surface-card);
}
.c-btn .c-badge--danger:not(:is(.c-header *, .c-footer *)),
.c-chip .c-badge--danger:not(:is(.c-header *, .c-footer *)),
.c-tile .c-badge--danger:not(:is(.c-header *, .c-footer *)),
a.c-list__item .c-badge--danger:not(:is(.c-header *, .c-footer *)),
button.c-list__item .c-badge--danger:not(:is(.c-header *, .c-footer *)) {
  background: linear-gradient(var(--state-danger-tint), var(--state-danger-tint)), var(--surface-card);
}
.c-btn .c-badge--warning:not(:is(.c-header *, .c-footer *)),
.c-chip .c-badge--warning:not(:is(.c-header *, .c-footer *)),
.c-tile .c-badge--warning:not(:is(.c-header *, .c-footer *)),
a.c-list__item .c-badge--warning:not(:is(.c-header *, .c-footer *)),
button.c-list__item .c-badge--warning:not(:is(.c-header *, .c-footer *)) {
  background: linear-gradient(var(--state-warning-tint), var(--state-warning-tint)), var(--surface-card);
}
.c-btn .c-badge--info:not(:is(.c-header *, .c-footer *)),
.c-chip .c-badge--info:not(:is(.c-header *, .c-footer *)),
.c-tile .c-badge--info:not(:is(.c-header *, .c-footer *)),
a.c-list__item .c-badge--info:not(:is(.c-header *, .c-footer *)),
button.c-list__item .c-badge--info:not(:is(.c-header *, .c-footer *)) {
  background: linear-gradient(var(--state-info-tint), var(--state-info-tint)), var(--surface-card);
}

/* ── Lists, tables, stats ── */
.c-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.c-list__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: var(--space-3) var(--space-4);
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}
.c-list__item:last-child { border-bottom: 0; }
a.c-list__item,
button.c-list__item {
  width: 100%;
  background: transparent;
  border-inline: 0;
  border-top: 0;
  text-align: start;
  cursor: pointer;
}

.c-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}
.c-table th,
.c-table td {
  padding: var(--space-2) var(--space-3);
  text-align: start;
  vertical-align: middle;
  border-bottom: 1px solid var(--line);
}
.c-table thead th {
  color: var(--ink-mid);
  background: var(--elev-5);
  font-weight: 600;
  white-space: nowrap;
}
.c-table tbody tr:last-child td { border-bottom: 0; }

.c-stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  color: var(--ink-on-card);
  background: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.c-stat__label {
  font-size: var(--text-xs);
  color: var(--ink-dim);
}
.c-stat__value {
  font-family: var(--font-num);
  font-size: var(--text-xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: var(--leading-latin);
  color: var(--ink-hi);
}

/* ── Chrome. It sits on the fixed dark pole, so everything inside it reads
      the fixed chrome inks, never the canvas ladder. ── */
.c-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--hdr-h);
  padding: 0 var(--gutter);
  padding-top: env(safe-area-inset-top);
  color: var(--ink-on-chrome);
  background: var(--surface-chrome);
  border-bottom: 1px solid color-mix(in srgb, var(--ink-on-chrome) 12%, transparent);
}

/* Sticky, not fixed: it is the last thing in the page flow, so it rides the
   bottom of the viewport without covering the footer. */
.c-bottom-nav {
  position: sticky;
  bottom: 0;
  z-index: var(--z-bottomnav);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  min-height: var(--bottomnav-h);
  padding-bottom: env(safe-area-inset-bottom);
  color: var(--ink-on-chrome-dim);
  background: var(--surface-chrome);
  border-top: 1px solid color-mix(in srgb, var(--ink-on-chrome) 12%, transparent);
}
.c-bottom-nav__item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: var(--space-1) var(--space-1);
  color: var(--ink-on-chrome-dim);
  background: transparent;
  border: 0;
  font-size: var(--text-xs);
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.c-bottom-nav__item::before {
  content: "";
  position: absolute;
  top: 0;
  left: 30%;
  right: 30%;
  height: 3px;
  border-radius: 0 0 var(--radius-xs) var(--radius-xs);
  background: transparent;
}
@media (min-width: 768px) {
  .c-bottom-nav { display: none; }
}

/* A navigation panel. It is used in the page flow (a sidebar, or inside a
   collapse) and as an overlay panel, so it does not position itself. */
.c-drawer {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
  max-height: calc(100vh - var(--hdr-h));
  max-height: calc(100dvh - var(--hdr-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  color: var(--ink-on-modal);
  background: var(--surface-modal);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.u-row > .c-drawer {
  flex: 0 0 var(--sidebar-w);
  align-self: flex-start;
}

.c-footer {
  padding: var(--space-6) var(--gutter);
  color: var(--ink-on-chrome-dim);
  background: var(--surface-chrome);
  font-size: var(--text-sm);
}
/* Plain links inside the chrome take the fixed chrome ink. A canvas ink
   (the default link colour) on a fixed surface is a mixed pair. */
.c-header :where(a:not([class])),
.c-footer :where(a:not([class])) {
  color: var(--ink-on-chrome);
}
/* The same for every other canvas-ink class the header and footer partials
   put directly on the chrome (B3 §3): the text utilities and the empty and
   zero slots take --ink-on-chrome-dim; the brand link (a link with a class
   that is not a button) and a soft button take --ink-on-chrome-accent; an
   error slot and a danger badge take --ink-on-chrome-danger; a success badge
   takes --ink-on-chrome-success. The backgrounds stay (the soft and state
   tints over the chrome); only the ink changes, so hover keeps it too.
   A canvas island inside the chrome (the balance stat, the account drawer,
   a card, a panel, a base or neutral callout, a chip at rest) paints its
   own OPAQUE canvas surface, so nothing in it matches, and a plain link in
   it takes the island's own ink instead of the chrome's (the last rule).
   Only an opaque island is safe: a tinted callout (--success, --warning,
   --danger) and an active or selected chip paint a translucent tint over
   the chrome, so their canvas inks would sit on the dark chrome at a light
   canvas. Never put one directly in the header or the footer (no partial
   does; B3 §3 round 2). A link that IS an island (a
   chip, card, panel, callout, stat or tile built as an a element, as the
   footer's client-filled provider chips may be) is left out of the link
   rules too, so it keeps its island's ink: with the chrome accent ink on
   its own card surface such a chip measured 1.32:1 (B3 §3 round 1); that
   exclusion is chained :not()s, with no comma. The island lists are written
   ".c-stat *, .c-drawer *, ..." on purpose: tools/themePolarityCheck.js
   splits a selector at every comma, and a bare island class left between
   two commas reads to it as that island's background (it then judged these
   chrome inks against the drawer's modal surface). Without these, a light canvas
   put dark canvas inks on the dark chrome: 2.24:1 for the brand and footer
   text, 3.15:1 for the footer success badge, 2.28:1 for the header soft
   button at gate preset 4, and 1.24:1 for the drawer links. */
:is(.c-header, .c-footer) :is(.u-text-mid, .u-text-dim, .c-state--empty, .c-state--zero):not(:is(.c-stat *, .c-drawer *, .c-card *, .c-panel *, .c-callout *, .c-chip *, .c-btn--primary *, .c-btn--danger *, .c-btn--success *)) {
  color: var(--ink-on-chrome-dim);
}
:is(.c-header, .c-footer) :is(.c-btn--soft, a[class]:not(.c-btn):not(.c-chip):not(.c-card):not(.c-panel):not(.c-callout):not(.c-stat):not(.c-tile)):not(:is(.c-stat *, .c-drawer *, .c-card *, .c-panel *, .c-callout *, .c-chip *)) {
  color: var(--ink-on-chrome-accent);
}
/* ...and the brand link still lightens on hover, as a canvas link does. */
:is(.c-header, .c-footer) a[class]:not(.c-btn):not(.c-chip):not(.c-card):not(.c-panel):not(.c-callout):not(.c-stat):not(.c-tile):not(:is(.c-stat *, .c-drawer *, .c-card *, .c-panel *, .c-callout *, .c-chip *)):hover {
  color: var(--ink-on-chrome);
}
:is(.c-header, .c-footer) :is(.c-state--error, .c-badge--danger):not(:is(.c-stat *, .c-drawer *, .c-card *, .c-panel *, .c-callout *, .c-chip *)) {
  color: var(--ink-on-chrome-danger);
}
:is(.c-header, .c-footer) .c-badge--success:not(:is(.c-stat *, .c-drawer *, .c-card *, .c-panel *, .c-callout *, .c-chip *)) {
  color: var(--ink-on-chrome-success);
}
:is(.c-header, .c-footer) :where(a:not([class])):is(.c-stat *, .c-drawer *, .c-card *, .c-panel *, .c-callout *, .c-chip *) {
  color: inherit;
}

.c-marquee {
  display: flex;
  align-items: center;
  min-height: var(--ticker-h);
  overflow: hidden;
  color: var(--ink-mid);
  background: var(--elev-1);
  border-block: 1px solid var(--line);
  font-size: var(--text-sm);
  white-space: nowrap;
}
.c-marquee > * {
  display: inline-block;
  flex: none;
  padding-inline-start: 100%;
  animation: c-marquee 28s linear infinite;
}

.c-ticker {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: var(--ticker-h);
  padding-inline: var(--gutter);
  overflow-x: auto;
  color: var(--ink-mid);
  background: var(--elev-1);
  font-size: var(--text-sm);
  white-space: nowrap;
  scrollbar-width: none;
}
.c-ticker::-webkit-scrollbar { display: none; }
.c-ticker > * { flex: none; }

.c-banner {
  position: relative;
  display: block;
  overflow: hidden;
  color: var(--ink-on-card);
  background: var(--surface-card);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.c-banner img {
  display: block;
  width: 100%;
  height: auto;
}

/* ── Tiles, icons, avatar, QR ── */
.c-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--space-3);
}
.c-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: var(--space-3) var(--space-2);
  color: var(--ink-on-card);
  background: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.c-tile img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-sm);
}

.c-icon {
  fill: currentColor;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.25em;
  height: 1.25em;
  line-height: 1;
}
.c-icon > svg,
.c-icon > img {
  width: 100%;
  height: 100%;
}

.c-avatar {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  overflow: hidden;
  color: var(--ink-on-raised);
  background: var(--surface-raised);
  border-radius: 50%;
  font-family: var(--font-bold);
  font-weight: 600;
}
.c-avatar > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.c-qr {
  display: inline-block;
  padding: var(--space-3);
  color: var(--ink-on-qr);
  background: var(--surface-qr);
  border-radius: var(--radius);
  line-height: 0;
}
.c-qr > img,
.c-qr > canvas,
.c-qr > svg {
  display: block;
  width: 100%;
  max-width: 240px;
  height: auto;
}

/* ── Overlays (SPA dialogs). The .c-overlay itself is the backdrop. ── */
.c-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
  background: color-mix(in srgb, var(--seed-ink) 64%, transparent);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  animation: c-fade-in var(--dur-fast) var(--ease-out);
}
.c-overlay__panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 480px;
  max-height: calc(100vh - var(--gutter) * 2);
  max-height: calc(100dvh - var(--gutter) * 2);
  overflow: hidden;
  color: var(--ink-on-modal);
  background: var(--surface-modal);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  animation: c-pop-in var(--dur) var(--ease-out);
}
.c-overlay__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: calc(var(--tap-min) + var(--space-3));
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--line);
}
.c-overlay__head > h2 {
  font-size: var(--text-lg);
}
.c-overlay__body {
  flex: 1 1 auto;
  min-height: 0;
  padding: var(--space-4);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.c-overlay__foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
}
.c-overlay__close {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  color: var(--ink-mid);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-size: var(--text-xl);
  line-height: 1;
  cursor: pointer;
}
/* An empty close button draws its own cross in the current ink. */
.c-overlay__close:empty::before,
.c-overlay__close:empty::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
}
.c-overlay__close:empty::before { transform: translate(-50%, -50%) rotate(45deg); }
.c-overlay__close:empty::after  { transform: translate(-50%, -50%) rotate(-45deg); }

.c-overlay--sheet {
  align-items: flex-end;
  padding: 0;
}
.c-overlay--sheet .c-overlay__panel {
  max-width: 640px;
  max-height: 92vh;
  max-height: 92dvh;
  border-bottom: 0;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  animation: c-sheet-up var(--dur) var(--ease-out);
}
.c-overlay--full {
  padding: 0;
}
.c-overlay--full .c-overlay__panel {
  max-width: none;
  width: 100%;
  height: 100%;
  max-height: none;
  border: 0;
  border-radius: 0;
}
@media (min-width: 768px) {
  .c-overlay--sheet {
    align-items: center;
    padding: var(--gutter);
  }
  .c-overlay--sheet .c-overlay__panel {
    border-bottom: 1px solid var(--line);
    border-radius: var(--radius-lg);
  }
}
/* A side panel (STRUCT-B3b; docs/THEME_STRUCTURE_CATALOG.md): the panel fills the height at the inline start — a
   drawer, a menu, an account "side drawer"; c-overlay--side-end puts it at the inline end. It stays an overlay
   (.c-overlay[data-overlay], opened by Tpl.overlay), only its panel's place and motion change. */
.c-overlay--side {
  align-items: stretch;
  justify-content: flex-start;
  padding: 0;
}
.c-overlay--side.c-overlay--side-end { justify-content: flex-end; }
.c-overlay--side .c-overlay__panel {
  width: min(86vw, 360px);
  max-width: none;
  height: 100%;
  max-height: none;
  border-block: 0;
  border-inline-start: 0;
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  animation: c-side-in var(--dur) var(--ease-out);
}
.c-overlay--side.c-overlay--side-end .c-overlay__panel {
  border-inline-start: 1px solid var(--line);
  border-inline-end: 0;
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  animation-name: c-side-in-end;
}

/* ── The back office's per-page popup (TH-1006-B2; layouts/main.ejs wires it). /js/page-notify.js builds it at the
      end of the body: .ntf-overlay (the backdrop) > .ntf-box (role="dialog") > .ntf-header (the title, the × button
      .ntf-close), .ntf-body (the admin's HTML, cleaned by the module), .ntf-footer (.ntf-btn--ghost "ปิด"; a member
      also gets two .ntf-btn, "ซ่อน 1 วัน" and "ซ่อน 3 วัน"). It opens over the overlay it belongs to (--z-notice,
      under the toasts). The parts look like an overlay's (.c-overlay, __panel, __head, __close, __foot), .ntf-btn like
      .c-btn--primary and .ntf-btn--ghost like a ghost button on the modal surface. Tokens only; focus: the
      :focus-visible rule below. ── */
.ntf-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-notice);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
  background: color-mix(in srgb, var(--seed-ink) 64%, transparent);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  animation: c-fade-in var(--dur-fast) var(--ease-out);
}
.ntf-box {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 480px;
  max-height: calc(100vh - var(--gutter) * 2);
  max-height: calc(100dvh - var(--gutter) * 2);
  overflow: hidden;
  color: var(--ink-on-modal);
  background: var(--surface-modal);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  animation: c-pop-in var(--dur) var(--ease-out);
}
.ntf-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: calc(var(--tap-min) + var(--space-3));
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--line);
  font-family: var(--font-bold);
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: var(--leading-thai);
  overflow-wrap: anywhere;
}
.ntf-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  color: var(--ink-mid);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  font-size: var(--text-xl);
  line-height: 1;
  cursor: pointer;
}
.ntf-body {
  flex: 1 1 auto;
  min-height: 0;
  padding: var(--space-4);
  overflow-y: auto;
  overscroll-behavior: contain;
  line-height: var(--leading-thai);
  overflow-wrap: anywhere;
}
/* page-notify.js drops iframe and video from the admin's HTML (SEC B0), so only an image needs a rule here. */
.ntf-body img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}
.ntf-body a {
  color: var(--ink-accent);
  text-decoration: underline;
}
.ntf-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
}
.ntf-btn {
  display: inline-flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  color: var(--ink-on-gold);
  background: var(--surface-gold);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  font-family: var(--font-bold);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.ntf-btn--ghost {
  color: var(--ink-on-modal);
  background: transparent;
  border-color: var(--line-input);
  box-shadow: none;
}
@media (hover: hover) {
  .ntf-close:hover     { color: var(--ink-hi); background: var(--surface-hover); }
  .ntf-btn:hover       { color: var(--ink-on-gold); background: var(--grad-accent-hover); box-shadow: var(--glow-accent); }
  .ntf-btn--ghost:hover { color: var(--ink-on-modal); background: color-mix(in srgb, currentColor 10%, transparent); box-shadow: none; }
}

/* ── Toasts. shell.js sets data-kind to success, danger, warning or info. ── */
.c-toast-rack {
  position: fixed;
  top: calc(var(--hdr-h) + var(--space-2) + env(safe-area-inset-top));
  left: var(--gutter);
  right: var(--gutter);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  pointer-events: none;
}
.c-toast {
  width: 100%;
  max-width: 420px;
  padding: var(--space-3) var(--space-4);
  color: var(--ink-on-raised);
  background: var(--surface-raised);
  border: 1px solid var(--line-strong);
  border-inline-start: 4px solid var(--ink-info);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  font-size: var(--text-sm);
  pointer-events: auto;
  animation: c-pop-in var(--dur) var(--ease-out);
}
.c-toast[data-kind="success"] { border-inline-start-color: var(--ink-success); }
.c-toast[data-kind="danger"]  { border-inline-start-color: var(--ink-danger); }
.c-toast[data-kind="warning"] { border-inline-start-color: var(--ink-warning); }
.c-toast[data-kind="info"]    { border-inline-start-color: var(--ink-info); }

/* ── Lottery: numpad, number grid, countdown, result, bet slip ── */
.c-numpad {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-2);
}
.c-numpad__key {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: calc(var(--tap-min) + var(--space-3));
  min-width: var(--tap-min);
  padding: 0;
  color: var(--ink-on-raised);
  background: var(--surface-raised);
  border: 1px solid var(--line-input);
  border-radius: var(--radius);
  font-family: var(--font-num);
  font-size: var(--text-xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: var(--leading-latin);
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

.c-numgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--tap-min), 1fr));
  gap: var(--space-1);
}
.c-numgrid__cell {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: 0;
  color: var(--ink-on-card);
  background: var(--surface-card);
  border: 1px solid var(--line-input);
  border-radius: var(--radius-sm);
  font-family: var(--font-num);
  font-size: var(--text-base);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: var(--leading-latin);
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

.c-countdown {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-1);
  color: var(--ink-accent);
  font-family: var(--font-num);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: var(--leading-latin);
  letter-spacing: 0.02em;
}

.c-result {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  color: var(--ink-on-raised);
  background: var(--surface-raised);
  border: 1px solid var(--line-accent);
  border-radius: var(--radius);
  text-align: center;
}
.c-result .u-num {
  color: var(--ink-accent);
  font-size: var(--text-2xl);
  font-weight: 700;
  letter-spacing: 0.08em;
}
/* A label under a result figure sits on --surface-raised, where --ink-dim
   read 3.86:1 at panel preset d22 at 66 percent (under 4.5 at d17-d27, MPA
   #lotto-closed) and 4.36 at 70 (PRE-11); --ink-mid holds 4.64 there (B3 §3 round 3). */
.c-result .c-stat__label {
  color: var(--ink-mid);
}
/* The results grid (partials/lottery/results.ejs, a .u-grid-auto of .c-result
   cards) from 768px: the utility's 140px tracks gave a 1280px screen six
   cards about 143px wide, too narrow for a card's own two or three stat
   cells, so the figures broke one digit per line and the cards stood about
   550px tall. A track here is at least a third of the feed less --space-6
   (368px): one card across at 768 and 1024, two at 1280 and 1440, and a
   draw's two numbers (3 ตัวหน้า 123 456) break only at their space. Below
   768px the utility's tracks stay as they are (TH-1007-C2). */
@media (min-width: 768px) {
  .u-grid-auto.u-grid-auto:has(> .c-result) {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, calc(var(--feed-max) / 3 - var(--space-6))), 1fr));
  }
}

.c-slip {
  display: flex;
  flex-direction: column;
  color: var(--ink-on-card);
  background: var(--surface-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.c-slip__line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--line);
}
.c-slip__total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--elev-1);
  border-top: 1px solid var(--line-strong);
  font-family: var(--font-bold);
  font-weight: 600;
}
.c-slip__total .u-num {
  color: var(--ink-accent);
  font-size: var(--text-lg);
}

/* ── Data displays: the container and its five state slots ── */
.c-data {
  position: relative;
  display: block;
}
.c-state {
  display: block;
}

/* ── Skeleton ── */
.c-skeleton {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-2) 0;
}
.c-skeleton__line,
.c-skeleton__block {
  display: block;
  color: var(--ink-on-skeleton);
  background-color: var(--surface-skeleton);
  background-image: linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--seed-fg) 6%, transparent) 50%, transparent 100%);
  background-size: 200% 100%;
  background-repeat: no-repeat;
  border-radius: var(--radius-xs);
  animation: c-shimmer 1.4s linear infinite;
}
.c-skeleton__line {
  height: 0.875em;
  width: 100%;
}
.c-skeleton__line:nth-child(2n) { width: 72%; }
.c-skeleton__line:nth-child(3n) { width: 56%; }
.c-skeleton__block {
  height: 96px;
  width: 100%;
  border-radius: var(--radius);
}

/* ── B3 · shared-callout ───────────────────────────────────────────────
   shared status message BOX: .c-callout base = info on --surface-selected + --ink-on-selected (BACKLOG 322, never --state-info); --success/--warning/--danger = state tint + state ink; --neutral = --surface-raised + --ink-on-raised; __title, links, a direct .c-btn. Users: spa-promo, mpa-hub, mpa-pages, spa-dash G4, mpa-dash G4 (class map: B3 css-foundation, docs/themelog/_theme-templates.md, LOG 2026-09-29 20:04)
   ── */
/* Callout: a status message BOX (a promo result, an info line, a lucky-draw
   condition, a rule violation, a page alert). The base class IS the info
   state: --ink-on-selected on --surface-selected, the selected rung, never
   --state-info (promo BACKLOG 322). --success, --warning and --danger take
   their state tint with their own state ink, the way .c-badge pairs them.
   --neutral is a plain raised box (--ink-on-raised, which is --ink-hi) for a
   fact that is neither good nor bad (turnover still to play, manual review).
   The tone modifier goes on the element that carries .c-callout; there is no
   --info class. .c-callout__title is an optional bold first line; a .c-btn
   placed directly inside gets space above it. An inline annotation line in
   a form is .c-note, not a callout. */
.c-callout {
  display: block;
  padding: var(--space-3) var(--space-4);
  color: var(--ink-on-selected);
  background: var(--surface-selected);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-size: var(--text-sm);
}
.c-callout--success {
  color: var(--ink-success);
  background: var(--state-success-tint);
  border-color: color-mix(in srgb, var(--state-success) 30%, transparent);
}
.c-callout--warning {
  color: var(--ink-warning);
  background: var(--state-warning-tint);
  border-color: color-mix(in srgb, var(--state-warning) 35%, transparent);
}
.c-callout--danger {
  color: var(--ink-danger);
  background: var(--state-danger-tint);
  border-color: color-mix(in srgb, var(--state-danger) 30%, transparent);
}
.c-callout--neutral {
  color: var(--ink-on-raised);
  background: var(--surface-raised);
  border-color: var(--line-strong);
}
.c-callout__title {
  display: block;
  margin-bottom: var(--space-1);
  font-weight: 700;
}
.c-callout a {
  color: inherit;
  font-weight: 700;
}
.c-callout > .c-btn {
  margin-top: var(--space-2);
}

/* ── B3 · shared-note ───────────────────────────────────────────────
   Monopay inline annotation LINE: .c-note[data-kind] exactly as the owner's parked proposal (Q11) defines it: --ink-hi on --elev-1, kind (success/danger/warning/info) on the start edge only; rules byte-identical to spa-mono/mpa-mono snippets/c-note.css (sha256 fdab3465...), so their preflight grep finds it and skips. Users: spa-mono, mpa-mono
   ── */
/* Note: an inline annotation LINE inside a form or a panel (the Monopay
   deposit pane: a status, a warning, an error, a long server message, its
   result). The owner's parked Monopay proposal (Q11), exactly: the text
   stays on --ink-hi over --elev-1, the .c-panel pairing; data-kind
   (success, danger, warning, info) shows only on the start edge, as
   .c-toast does, because a state ink as body text does not hold under every
   palette. overflow-wrap keeps long Thai strings and references inside the
   panel. There is no copied class: a copy button shows a toast and
   .is-active for about 1.5 s. A status BOX (a result, an alert) outside the
   Monopay pane is .c-callout. */
.c-note {
  display: block;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  color: var(--ink-hi);
  background: var(--elev-1);
  border: 1px solid var(--line);
  border-inline-start: 4px solid var(--line-strong);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}
.c-note[data-kind="success"] { border-inline-start-color: var(--ink-success); }
.c-note[data-kind="danger"]  { border-inline-start-color: var(--ink-danger); }
.c-note[data-kind="warning"] { border-inline-start-color: var(--ink-warning); }
.c-note[data-kind="info"]    { border-inline-start-color: var(--ink-info); }

/* ── B3 · shared-stat ───────────────────────────────────────────────
   money-in figure: .c-stat__value--success (--ink-success) on the value element; mpa-pages' .c-stat--success .c-stat__value maps to it. Users: spa-promo, spa-dash G4, mpa-dash G4, mpa-pages
   ── */
/* Money coming in to the member, shown on a stat value (withdrawable
   credit, a bonus to receive): the success ink. The modifier goes on the
   .c-stat__value element, never on .c-stat. It reads --ink-success, so the
   tuned state inks apply to it. */
.c-stat__value--success {
  color: var(--ink-success);
}

/* ── B3 · shared-card ───────────────────────────────────────────────
   a listed-but-unusable card: .c-card.is-disabled = the template's disabled pair (--ink-on-disabled on --surface-disabled, no border, no shadow). Users: spa-promo, mpa-hub, mpa-pages
   ── */
/* A card for an item that is listed but cannot be used now (a paused
   campaign, an offer the member cannot claim): the template's disabled
   pair, as on a disabled list item or tile. Its button is disabled on its
   own (.c-btn:disabled). */
.c-card.is-disabled {
  color: var(--ink-on-disabled);
  background: var(--surface-disabled);
  border-color: transparent;
  box-shadow: none;
}

/* ── B3 · shared-btn ───────────────────────────────────────────────
   a button beside a growing field keeps a one-line label: .c-btn--nowrap (flex none, nowrap); hit area stays --tap-min from .c-btn. Users: mpa-hub, mpa-pages
   ── */
/* A button beside a growing field (.u-grow) or at the end of a tight row
   keeps its label on one line and never shrinks. */
.c-btn--nowrap {
  flex: none;
  white-space: nowrap;
}

/* ── B3 · shared-media ───────────────────────────────────────────────
   card picture frame: .c-media (+ > img) 16:7 banner, icon-slot fallback in --ink-on-soft on --action-soft; .c-promo-card = the grid that sets a square .c-media beside the text (kept: a 88px track + start alignment needs a wrapper); dimmed in .c-card.is-disabled. mpa-hub's .c-promo-card__media and spa-promo's .c-media map here. Users: spa-promo, mpa-hub, mpa-pages
   ── */
/* Media frame: a card's server picture, cropped to fill the frame, a 16 by 7
   banner by default. Without a picture the frame holds an icon slot
   (.c-icon), drawn in the soft accent pair; a picture that fails to load
   hides the whole frame with the hidden attribute, never the img alone.
   .c-promo-card lays a card out as a square frame beside its text (the promo
   pack's coupon and game cards): put it on the .c-card, the .c-media first,
   the text second. In a disabled card the picture is dimmed. */
.c-media {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 16 / 7;
  overflow: hidden;
  color: var(--ink-on-soft);
  background: var(--action-soft);
  border-radius: var(--radius-sm);
  font-size: var(--text-2xl);
}
.c-media > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.c-promo-card {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  align-items: start;
  gap: var(--space-3);
}
.c-promo-card > .c-media {
  aspect-ratio: 1 / 1;
}
.c-card.is-disabled .c-media {
  opacity: 0.6;
  filter: saturate(0.3);
}
/* A card or list row placed in a grid track, and the text column of a promo
   card, shrink to their track (B3 §3). A grid item's automatic minimum
   width is its content, which is how the MPA legacy promotion list (a
   .c-list.u-grid-2 of .c-list__item.c-card rows) ended 1px wider than the
   page at 390px. */
:is(.u-grid-2, .u-grid-3, .u-grid-4, .u-grid-auto) > :is(.c-card, .c-list__item),
.c-promo-card > :last-child {
  min-width: 0;
}
/* A long unbroken server string (a code, a URL, a title with no spaces)
   breaks inside its card or grid row instead of running past the edge,
   where an overlay body clips it or the page scrolls sideways. break-word
   breaks only a string that cannot fit its line and leaves the min-content
   width alone, so a Thai word or a short label in a tight row never splits
   (overflow-wrap anywhere did, B3 §3 round 1: ถอนเงิน, รายงาน and KBANK
   broke mid-word in the MPA dashboard and bank rows at 390px). A row in a
   grid card wraps its items instead of squeezing them. A badge in a card
   is at most the card's width and never shrinks in a row (its one-line
   width, as before B3 §3), and a code longer than the card wraps inside
   the badge, so the member can read and copy all of it (B3 §3 round 2;
   round 1 cut it at the card edge with overflow hidden, and an ellipsis
   cannot show on the inline-flex badge). overflow-wrap anywhere is safe
   here only because the badge does not shrink: a flex item keeps its
   width, and an inline badge is narrower than its line unless it is the
   over-long one. */
.c-card,
:is(.u-grid-2, .u-grid-3, .u-grid-4, .u-grid-auto) > .c-list__item {
  overflow-wrap: break-word;
}
:is(.u-grid-2, .u-grid-3, .u-grid-4, .u-grid-auto) > :is(.c-card, .c-list__item) > .u-row-between {
  flex-wrap: wrap;
}
.c-card .c-badge:not(:is(td *, th *)) {
  flex-shrink: 0;
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}
/* A list row stacked as a card (.c-list__item with .u-stack-tight) centres
   its children at their content width, so a no-wrap title row (a truncated
   title plus a badge) still ran past both edges of the row: cap each child
   at the row's width, and the title truncates as intended. */
:is(.u-grid-2, .u-grid-3, .u-grid-4, .u-grid-auto) > .c-list__item > * {
  max-width: 100%;
}

/* ── B3 · shared-overlay-tabs ───────────────────────────────────────────────
   tab bar directly in an overlay body keeps its height (.c-overlay__body > .c-tabs flex-shrink 0). Was spa-promo's; a SECOND user found, so shared (orchestrator rule 5): with spa-mono's Monopay QR order view the cashier body overflows (390: 716 of 863 px) and its tab bar collapsed to 1 px without the rule, 44 px with it (B3 css-foundation, docs/themelog/_theme-templates.md, LOG 2026-09-29 20:04). Inert in the MPA
   ── */
/* A tab bar placed directly in an overlay body keeps its height. The body
   is a scrolling flex column and a tab bar is itself a scroll container
   (overflow-x), so its automatic minimum height is 0: once the body
   overflows (the promotions lists, the cashier's Monopay order view), the
   bar collapsed to its 1px border. Holds for every overlay whose .c-tabs
   sits directly in .c-overlay__body; inert in the MPA, which has no
   .c-overlay. */
.c-overlay__body > .c-tabs {
  flex-shrink: 0;
}

/* ── B3 · spa-promo-AB ───────────────────────────────────────────────
   promo pack A+B SPA overlay: soft badge + compact stat (the rest is the B3 shared vocabulary above)
   ── */
/* Promo pack A+B, the SPA promotions overlay (partials/modals/promotions.ejs + assets/js/promo.js).
   The shared vocabulary (B3 css-foundation, docs/themelog/_theme-templates.md) already carries the
   callout box, the money-in stat value, the media frame, the disabled card and the overlay tab-bar
   fix; this block adds only what the overlay alone needs: a soft badge (the chip pair of
   .c-btn--soft) and a compact stat, three across at 390 px. */
.c-badge--soft {
  color: var(--ink-on-soft);
  background: var(--action-soft);
}
.c-stat--sm {
  padding: var(--space-2);
}
.c-stat--sm .c-stat__value {
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

/* ── B3 · promo-b-mpa-code ───────────────────────────────────────────────
   promo pack B MPA coupon page: typed coupon code field (the rest is the B3 shared vocabulary above)
   ── */
/* Promo pack B, the MPA coupon page: a field for a typed coupon code, shown upper-case and spaced;
   the value itself is sent as typed. The callout box, the money-in stat value, the disabled card
   and the no-wrap button are in the shared vocabulary (B3 css-foundation,
   docs/themelog/_theme-templates.md). */
.c-input--code {
  font-family: var(--font-bold);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ── B3 · spa-dash-g1 ───────────────────────────────────────────────
   dashboard-drift G1: .c-group-head, the heading of one group in the SPA lottery hub's หวยทั้งหมด panel (accent ink, --line hairline); rules file sha256 c76a55ec… as the B3 spa-dash step specified it (docs/themelog/_theme-templates.md, LOG B3 §2 spa-dash)
   ── */
/* Dashboard G1 (dashboard-drift): the heading of one group in the lottery hub's
   หวยทั้งหมด panel, over that group's market cards. Accent ink on the canvas
   with a decorative hairline under it, as theme9 and theme5 draw their
   lt-all-group heads. --ink-accent clears 4.5:1 on the canvas at the example
   seeds (12.73) and at gate presets 2-4 (8.50, 10.81, 10.97), on --elev-0
   without the page wash, at its 53 percent share (B3 §3 round 2). */
.c-group-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding-block-end: var(--space-2);
  color: var(--ink-accent);
  border-bottom: 1px solid var(--line);
  font-family: var(--font-bold);
  font-size: var(--text-sm);
  font-weight: 700;
}

/* ── TW-PAY · top-winners ───────────────────────────────────────────────
   TOPWIN (docs/featurelog/topwin-pay-20260930.md): the top-winners list that /js/top-winners.js fills. Users: spa home (partials/lottery/top-winners.ejs), mpa home + dashboard (partials/top-winners.ejs)
   ── */
/* One .c-list__item per winner, filled by the shared client with textContent only (window.TopWinners);
   it sets data-rank on every row and data-top 1 to 3 on the first three. A row is the rank chip, the
   flag (a lottery flag image or the neutral icon, in a small .c-avatar frame), the member over the
   lottery (this column alone shrinks and truncates, so a row fits 390 px), then the amount over the date (never
   shrunk: it keeps its width up to 60 percent of the row).
   Every pair is one the contrast record above already measures: the member --ink-hi, the lottery and
   the date --ink-mid (clears 4.5 on every rung but --elev-5), the amount --ink-success, the money-in
   ink of .c-stat__value--success (on the card: lowest 5.11, d22), the rank chip --ink-on-raised on
   --surface-raised (table A). The first three stand out by their chip only: first the gold pair
   (--ink-on-gold on --surface-gold, lowest 4.70, d04), second and third the soft pair (--ink-on-soft
   on --action-soft, lowest 5.87, d24). */
.c-winners__row {
  gap: var(--space-2);
  padding-inline: var(--space-2);
}
.c-winners__who,
.c-winners__win {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.c-winners__who {
  flex: 1 1 auto;
}
.c-winners__win {
  flex: none;
  max-width: 60%;
  align-items: flex-end;
  text-align: end;
}
.c-winners__who > *,
.c-winners__win > * {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.c-winners__user {
  color: var(--ink-hi);
  font-family: var(--font-bold);
  font-weight: 600;
}
.c-winners__lottery,
.c-winners__date {
  color: var(--ink-mid);
  font-size: var(--text-sm);
}
.c-winners__amount {
  color: var(--ink-success);
  font-family: var(--font-bold);
  font-weight: 700;
}
.c-avatar.c-winners__flag {
  width: 32px;
  height: 32px;
}
.c-rank {
  display: inline-grid;
  place-items: center;
  flex: none;
  min-width: 28px;
  height: 28px;
  padding-inline: var(--space-1);
  color: var(--ink-on-raised);
  background: var(--surface-raised);
  border-radius: var(--radius-pill);
  font-family: var(--font-bold);
  font-size: var(--text-sm);
  font-weight: 700;
}
[data-top="1"] > .c-rank {
  color: var(--ink-on-gold);
  background: var(--surface-gold);
}
[data-top="2"] > .c-rank,
[data-top="3"] > .c-rank {
  color: var(--ink-on-soft);
  background: var(--action-soft);
}

/* ── TW-PAY · gateway-deposit ───────────────────────────────────────────────
   GGPAY, INWPAY and LOCALPAY panes ([data-gw-pane], painted by /js/gateway-deposit-ui.js): a copy button that was just used. Users: spa cashier (partials/modals/gateway-panes.ejs), mpa deposit page (partials/gateway-panes.ejs)
   ── */
/* The panes are built from the existing vocabulary (.c-note, .c-stat, .c-card, .c-qr, .c-btn). The
   shared binder marks a copy button it has just used with data-gw-copied for about 1.5 s instead of a
   class; it reads as the pressed button (.c-btn.is-active, --ink-on-selected on --surface-selected,
   table A). */
.c-btn[data-gw-copied] {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
  border-color: var(--action-fill);
}

/* =============================================================================
   THEME16 · COMPONENTS
   The look of docs/design/theme16/DESIGN.md: deep emerald and champagne gold,
   gold hairlines, a crystal sheen, soft radii, Taviraj headings, a bento home
   (HM4), a full-height left rail (DL4), the drawer from the left (D1), the
   tickets as cards (PO2). Every colour is a token above; every rule below
   restyles a template class or hook, or one of the NEW classes of the frames
   DESIGN.md §11 lists (.c-rail*, .c-poy-card*, .c-empty*). Segmented and pill
   tabs are chosen by context, never by an added class outside the frames.
   ========================================================================== */

/* ── Base surfaces ── */
.c-view { padding-block: var(--space-5); }
.c-section { gap: var(--space-4); }
.c-section__head { flex-wrap: wrap; row-gap: var(--space-1); }
.c-section__head.u-stack-tight { align-items: stretch; }
.c-section__head > :is(h1),
.c-section__head > :is(h2),
.c-section__head > .u-stack-tight > :is(h1),
.c-section__head > .u-stack-tight > :is(h2) {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
/* In a row head the title takes the room and the trigger column wraps under it when there is none
   (never in a column head, where a flex basis would become a height). */
.c-section__head:not(.u-stack-tight) > :is(h1),
.c-section__head:not(.u-stack-tight) > :is(h2),
.c-section__head:not(.u-stack-tight) > .u-stack-tight { flex: 1 1 13em; }
/* The heading ornament: a gold diamond before, a fading gold rule after. */
.c-section__head > :is(h1)::before,
.c-section__head > :is(h2)::before,
.c-section__head > .u-stack-tight > :is(h1)::before,
.c-section__head > .u-stack-tight > :is(h2)::before,
.c-group-head::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border: 1px solid var(--action-fill);
  background: radial-gradient(circle, var(--action-fill) 0 1.5px, transparent 2px);
  transform: rotate(45deg);
}
.c-section__head > :is(h1)::after,
.c-section__head > :is(h2)::after,
.c-section__head > .u-stack-tight > :is(h1)::after,
.c-section__head > .u-stack-tight > :is(h2)::after {
  content: "";
  flex: 1 1 24px;
  min-width: 24px;
  height: 1px;
  margin-inline-start: var(--space-2);
  background: linear-gradient(90deg, var(--line-gold), transparent);
}
.c-section__head > .u-stack-tight > p,
.c-section__head.u-stack-tight > p { padding-inline-start: calc(8px + var(--space-3)); }
/* Carousel arrows are for pointers: a phone swipes the shelf. */
@media (max-width: 767.98px) {
  .c-section__head :is([data-carousel-prev]),
.c-section__head :is([data-carousel-next]) { display: none; }
}
/* A LIVE or HOT mark in a heading is decoration: soft gold, never red. */
:is(h1) > .c-badge--danger,
:is(h2) > .c-badge--danger,
:is(h3) > .c-badge--danger {
  color: var(--ink-on-soft);
  background: var(--action-soft);
}

.c-card,
.c-stat,
.c-slip {
  background: var(--sheen), var(--surface-card);
  border-color: var(--line-gold);
  box-shadow: var(--edge-hi), var(--shadow-sm);
}
.c-card { padding: var(--space-4); }
.c-section.c-card { border-radius: var(--radius-lg); padding: var(--space-4); }
@media (min-width: 768px) {
  .c-section.c-card { padding: var(--space-5); }
}
.c-panel {
  background: var(--elev-1);
  border-color: var(--line-gold);
}
.c-stat { box-shadow: var(--edge-hi); }
.c-stat__label { font-size: var(--text-xs); letter-spacing: 0.02em; }
.c-stat__value { font-size: var(--text-lg); }
@media (min-width: 768px) {
  .c-stat__value { font-size: var(--text-xl); }
}

/* ── Buttons ── */
.c-btn { letter-spacing: 0; }
.c-btn--primary { box-shadow: var(--edge-hi-strong), var(--shadow-sm); }
.c-btn--ghost { border-color: var(--line-gold); }
.c-btn--block { min-height: calc(var(--tap-min) + 4px); }
/* An icon-only button (it carries aria-label and no text) is round. */
.c-btn[aria-label]:has(> .c-icon:only-child),
.c-btn[aria-label]:has(> .c-avatar:only-child) {
  width: var(--tap-min);
  padding: 0;
  border-radius: var(--radius-pill);
}
.c-btn[aria-label]:has(> .c-avatar:only-child) { border-color: var(--line-gold-strong); }
.c-btn > .c-avatar { width: 34px; height: 34px; }

/* ── Fields ── */
/* A button beside a growing field keeps its label on one line (the rule of .c-btn--nowrap). */
.c-field .u-row > .c-btn { flex: none; white-space: nowrap; }
.c-input,
.c-select { min-height: calc(var(--tap-min) + 4px); }
.c-label { color: var(--ink-mid); }
/* An invalid field: the danger border (STATES) and its help line in the danger ink, the one red in a form. */
.c-field:has(.c-input[aria-invalid="true"]) > .c-help { color: var(--ink-danger); }
fieldset.c-panel { margin: 0; min-width: 0; }
body.theme-theme16 :is(input[type="checkbox"]),
body.theme-theme16 :is(input[type="radio"]) { width: 20px; height: 20px; margin: 0; accent-color: var(--action-fill); }
.c-panel > legend { float: left; width: 100%; padding: 0; margin-bottom: var(--space-2); }

/* ── Badges, chips, tabs ── */
.c-badge { padding: 3px 10px; }
.c-chip { font-weight: 500; }
.c-tabs { border-bottom-color: var(--line-gold); }
/* Segmented control: 2-4 peer choices (the lottery tabs, the buy sheet's macro tabs, the poy scopes, the cashier, auth). */
.c-tabs:is([data-lt-tabs]),
.c-tabs:is([data-lt-macros]),
[data-overlay="cashier"] .c-tabs:is([role="tablist"]),
[data-overlay="auth"] .c-tabs:is([role="tablist"]) {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--space-1);
  padding: var(--space-1);
  overflow: visible;
  background: var(--elev-1);
  border: 1px solid var(--line-gold);
  border-radius: var(--radius-pill);
}
.c-tabs:is([data-lt-tabs]) > .c-tab,
.c-tabs:is([data-lt-macros]) > .c-tab,
[data-overlay="cashier"] .c-tabs:is([role="tablist"]) > .c-tab,
[data-overlay="auth"] .c-tabs:is([role="tablist"]) > .c-tab {
  min-width: 0;
  margin: 0;
  padding: 0 var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  white-space: nowrap;
}
.c-tabs:is([data-lt-tabs]) > .c-tab .u-text-dim,
.c-tabs:is([data-lt-macros]) > .c-tab .u-text-dim,
[data-overlay="cashier"] .c-tabs:is([role="tablist"]) > .c-tab .u-text-dim,
[data-overlay="auth"] .c-tabs:is([role="tablist"]) > .c-tab .u-text-dim { display: none; }
/* Pills: a scrolling bar of view or type choices (the category nav, lottery types, number ranges, the account segments, the poy status). */
.c-tabs:is(.c-tabs--pills),
.c-tabs:is([data-lt-types]),
.c-tabs:is([data-lt-panel-ranges]),
.c-tabs:is([data-region="category-nav"] > .c-tabs) { gap: var(--space-2); padding-block: 2px; border-bottom: 0; }
.c-tabs:is(.c-tabs--pills) > .c-tab,
.c-tabs:is([data-lt-types]) > .c-tab,
.c-tabs:is([data-lt-panel-ranges]) > .c-tab,
.c-tabs:is([data-region="category-nav"] > .c-tabs) > .c-tab {
  margin: 0;
  color: var(--ink-mid);
  background: var(--surface-card);
  border: 1px solid var(--line-input);
  border-radius: var(--radius-pill);
}
.c-tabs:is(.c-tabs--pills) > .c-tab .c-icon,
.c-tabs:is([data-lt-types]) > .c-tab .c-icon,
.c-tabs:is([data-lt-panel-ranges]) > .c-tab .c-icon,
.c-tabs:is([data-region="category-nav"] > .c-tabs) > .c-tab .c-icon { color: var(--ink-accent); }

/* ── Header: frosted glass on the canvas ── */
.c-header {
  background: color-mix(in srgb, var(--surface-chrome) 86%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.15);
  backdrop-filter: blur(16px) saturate(1.15);
  border-bottom: 1px solid var(--line-gold);
}
.c-header > .u-container { min-height: var(--hdr-h); gap: var(--space-3); }
.c-header > .u-container > .u-row:first-child { min-width: 0; }
.c-header .c-header__brand {
  min-width: 0;
  color: var(--ink-accent);
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 700;
  line-height: var(--leading-display);
  text-decoration: none;
  letter-spacing: 0.01em;
}
.c-header .c-header__brand > img { display: block; height: 36px; width: auto; }
/* DL4: from 768px the rail carries the brand, so the header's own home link steps aside. */
@media (min-width: 768px) {
  .c-header .c-header__brand { display: none; }
}
.c-header .c-stat {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-pill);
  border-color: var(--line-gold-strong);
  box-shadow: none;
}
.c-header .c-stat__value { font-size: var(--text-base); }
.c-header .c-stat .c-skeleton { width: 72px; padding: 0; }
.c-header .c-stat .c-state--zero { padding: 0; font-size: var(--text-base); }
/* D3 (TASK TH-1009-C; docs/themelog/_theme-templates.md TH-1009-A D3): the header's error state — after D1 only a network
   failure at boot — is one line under the header bar, not a row in the end column that pushed the bar to 600 px at 390.
   The header's chrome tokens (its own danger ink on its surface) and its gold rule; the balance pill steps aside. */
.c-header [data-display="header"] { position: static; }
.c-header [data-display="header"] > .c-state--error {
  position: absolute;
  inset-inline: 0;
  top: 100%;
  flex-wrap: nowrap;
  gap: var(--space-2);
  padding: var(--space-1) var(--gutter);
  color: var(--ink-on-chrome-danger);
  background: var(--surface-chrome);
  border: 0;
  border-bottom: 1px solid var(--line-gold);
  border-radius: 0;
  font-size: var(--text-xs);
  white-space: nowrap;
}
.c-header .c-stat:has(> [data-display="header"] > .c-state--error:not([hidden])) { padding: 0; min-height: 0; border-color: transparent; background: none; }
.c-header .c-btn--soft:not(:has(> .c-avatar)) { flex: 1 1 auto; justify-content: flex-start; border-radius: var(--radius-pill); }
@media (max-width: 767.98px) {
  /* On phones the account sits in the bottom bar (ฉัน), so the header keeps the brand, the balance and its refresh. */
  .c-header .c-btn[aria-label]:has(> .c-avatar:only-child) { display: none; }
  .c-header .c-stat__label {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
}

/* ── Marquee and ticker: thin strips framed in gold ── */
/* The LIVE ฝาก/ถอนล่าสุด ticker has no real data source yet (nothing fills it; the template marks it sample data):
   it stays out of sight on the site until the backend serves real rows (the owner, BUILD-0; BACKLOG T16-B11). */
.c-ticker[data-region="activity-ticker"] { display: none; }
.c-marquee {
  background: var(--elev-1);
  border-block: 1px solid var(--line-gold);
}
.c-marquee > * { animation-duration: var(--marquee-dur); }
.c-marquee .c-icon { color: var(--ink-accent); }
.c-marquee:is(:hover) > *,
.c-marquee:is(:focus-within) > * { animation-play-state: paused; }
.c-ticker {
  gap: var(--space-3);
  background: var(--elev-1);
  border-block: 1px solid var(--line-gold);
}
.c-ticker h2 { gap: var(--space-2); font-family: var(--font-bold); font-size: var(--text-sm); font-weight: 600; }
/* S1: the promotion strip is a full-width band under the ticker, not an inset card. */
[data-promo-v2-strip].c-card {
  margin: 0;
  padding: var(--space-4) var(--gutter);
  border-width: 0 0 1px;
  border-radius: 0;
  box-shadow: none;
}
@media (max-width: 479.98px) {
  [data-promo-v2-strip] .u-grid-2.u-grid-2:has(> .c-btn) { grid-template-columns: minmax(0, 1fr); }
}
@media (min-width: 768px) {
  [data-promo-v2-strip].c-card { padding-inline: var(--gutter-lg); }
}
.c-ticker .c-chip { min-height: 32px; gap: var(--space-2); background: var(--surface-card); border-color: var(--line-gold); }

/* ── Category pills: phones only (the rail replaces them) ── */
@media (min-width: 768px) {
  nav[data-region="category-nav"] { display: none; }
}

/* ── The rail (DL4): from 768px a fixed rail the full height of the screen, carrying the brand and the menu in three
      labelled groups. Everything else starts right of it; the drawer stays. ── */
@media (min-width: 768px) {
  .c-page:has(> aside) { display: block; padding-inline: 0; }
  .c-page > aside.c-rail {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    width: var(--sidebar-w);
    padding: 0 var(--space-3) var(--space-5);
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--sheen), var(--elev-1);
    border: 0;
    border-inline-end: 1px solid var(--line-gold);
    border-radius: 0;
  }
  :is(.c-header),
:is(.c-marquee),
:is(.c-ticker),
:is([data-promo-v2-strip]),
:is(.c-page),
:is(.c-footer) { margin-inline-start: var(--sidebar-w); }
  [data-promo-v2-strip].c-card { margin-inline-start: var(--sidebar-w); }
}
.c-rail__brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: none;
  min-width: 0;
  min-height: var(--hdr-h);
  margin-inline: calc(-1 * var(--space-3));
  padding-inline: var(--space-4);
  color: var(--ink-accent);
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: var(--leading-display);
  letter-spacing: 0.01em;
  text-decoration: none;
  border-bottom: 1px solid var(--line-gold);
}
.c-rail__brand > img { display: block; height: 36px; width: auto; }
/* The seal: a small jade lozenge with the lattice and a gold dot, the rail's auspicious mark. */
.c-rail__seal {
  flex: none;
  width: 22px;
  height: 22px;
  margin-inline: 3px;
  background: radial-gradient(circle, var(--action-fill) 0 2.5px, transparent 3px), var(--lattice), var(--elev-3);
  border: 1px solid var(--line-gold-strong);
  border-radius: 5px;
  box-shadow: 0 0 0 3px var(--action-soft);
  transform: rotate(45deg);
}
.c-rail__group { display: flex; flex-direction: column; gap: var(--space-2); }
.c-rail__label {
  padding-inline: var(--space-3);
  color: var(--ink-dim);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
}
.c-rail__group + .c-rail__group { padding-top: var(--space-4); border-top: 1px solid var(--line-gold); }
aside[data-region="sidebar"] .c-btn--ghost.c-btn--block {
  justify-content: flex-start;
  gap: var(--space-3);
  min-height: 48px;
  padding: 0 var(--space-3);
  color: var(--ink-mid);
  border-color: transparent;
  border-radius: 14px;
  font-weight: 500;
}
aside[data-region="sidebar"] .c-btn--ghost .c-icon { color: var(--ink-accent); }

/* ── Bottom tab bar with the raised centre (phones; B4: the gold coin that opens the lottery hub, ROUND 1 below) ── */
.c-bottom-nav {
  overflow: visible;
  background: color-mix(in srgb, var(--surface-chrome) 90%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.15);
  backdrop-filter: blur(16px) saturate(1.15);
  border-top: 1px solid var(--line-gold);
}
.c-bottom-nav__item { gap: 4px; padding-top: var(--space-3); color: var(--ink-mid); }
.c-bottom-nav__item .c-icon { width: 22px; height: 22px; }
.c-bottom-nav__item::before {
  top: 4px;
  left: 50%;
  right: auto;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  transform: translateX(-50%);
}
.c-bottom-nav__item--coin { justify-content: flex-end; padding-top: 0; padding-bottom: 6px; color: var(--ink-hi); }
.c-bottom-nav__item--coin::before { display: none; }
.c-bottom-nav__item--coin .c-icon {
  width: var(--fab-size);
  height: var(--fab-size);
  margin-top: -28px;
  padding: 16px;
  color: var(--ink-on-action);
  background: var(--action-fill);
  border-radius: 50%;
  box-shadow: 0 0 0 4px var(--elev-1), 0 0 0 5px var(--line-gold-strong), var(--shadow);
}

/* ── Drawer (D1): the template's side panel (c-overlay--side) in the theme's look ── */
.c-overlay--side .c-overlay__panel {
  width: min(86vw, 340px);
  border-inline-end: 1px solid var(--line-gold);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  animation-duration: var(--dur-slow);
}
.c-overlay--side .c-overlay__panel::before { display: none; }
/* Signing out is not money out: the template's danger button reads as a gold ghost here. */
.c-overlay[data-overlay="drawer"] .c-btn--danger[data-logout] {
  color: var(--ink-mid);
  background: transparent;
  border-color: var(--line-gold);
  box-shadow: none;
}
.c-drawer .c-list__item.c-btn {
  justify-content: flex-start;
  gap: var(--space-3);
  min-height: 48px;
  padding: 0 var(--space-3);
  color: var(--ink-hi);
  border: 0;
  border-radius: 14px;
  font-weight: 500;
}
.c-drawer .c-list__item .c-icon { color: var(--ink-accent); }

/* ── Footer: a bento of three tiles ── */
.c-footer {
  padding: var(--space-6) 0;
  background: transparent;
  border-top: 1px solid var(--line-gold);
}
.c-footer .u-grid-3.u-grid-3 { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
.c-footer .u-grid-3 > * {
  padding: var(--space-5);
  background: var(--sheen), var(--elev-1);
  border: 1px solid var(--line-gold);
  border-radius: var(--radius-lg);
}
.c-footer h2 { font-size: var(--text-lg); color: var(--ink-hi); }
.c-footer .u-stack-tight > .c-btn { align-self: flex-start; }
@media (min-width: 768px) {
  .c-footer .u-grid-3.u-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ── Home: the bento ── */
.c-view[data-view="home"] {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  gap: var(--space-4);
}
.c-view[data-view="home"] > .c-section:not(.c-card) { padding-block: 0; }
@media (min-width: 768px) {
  .c-view[data-view="home"] { grid-template-columns: repeat(6, minmax(0, 1fr)); grid-auto-flow: row dense; }
  /* spans only, never grid lines: the arrangement contract (README-TEMPLATE "The arrangement contract", the owner's #52) */
  .c-view[data-view="home"] > * { grid-column: span 6; }
  .c-view[data-view="home"] > [data-region="auth-cta"],
  .c-view[data-view="home"] > [data-topwin] { grid-column: span 3; align-self: stretch; }
}
@media (min-width: 1024px) {
  .c-view[data-view="home"] { grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--space-5); }
  .c-view[data-view="home"] > * { grid-column: span 12; }
  .c-view[data-view="home"] > [data-region="hero"] { grid-column: span 7; align-self: stretch; }
  /* The hero always spans two rows: beside it the CTA tile, then the top winners (when there are none the second row is empty and the hero is one gap taller). */
  .c-view[data-view="home"] > [data-region="hero"] { grid-row: span 2; }
  .c-view[data-view="home"] > [data-region="auth-cta"],
  .c-view[data-view="home"] > [data-topwin] { grid-column: span 5; }
}
[data-region="hero"] .c-banner {
  height: 100%;
  border: 1px solid var(--line-gold);
  border-radius: var(--radius-lg);
  box-shadow: var(--edge-hi), var(--shadow);
}
/* Gold corner brackets on the hero frame. */
[data-region="hero"] .c-banner::before,
[data-region="hero"] .c-banner::after {
  content: "";
  position: absolute;
  z-index: 1;
  width: 22px;
  height: 22px;
  pointer-events: none;
  border: 0 solid var(--action-fill);
}
[data-region="hero"] .c-banner::before { top: 12px; left: 12px; border-top-width: 2px; border-left-width: 2px; border-top-left-radius: 6px; }
[data-region="hero"] .c-banner::after { right: 12px; bottom: 12px; border-right-width: 2px; border-bottom-width: 2px; border-bottom-right-radius: 6px; }
/* The static hero card (no slides): a jade ground with the lattice and a gold glow. */
[data-region="hero"] .c-banner.c-card {
  justify-content: center;
  min-height: 240px;
  background: var(--lattice), radial-gradient(640px 320px at 100% 0%, var(--glow-gold), transparent 70%), linear-gradient(150deg, var(--elev-3), var(--elev-0) 72%);
}
[data-region="hero"] .c-banner.c-card > .c-chip { align-self: flex-start; min-height: 32px; color: var(--ink-accent); border-color: var(--line-gold-strong); }
[data-region="hero"] .c-banner.c-card > p:first-of-type {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  line-height: 1.35;
}
[data-region="hero"] .c-banner.c-card > p:first-of-type > strong { color: var(--ink-accent); font-weight: 700; }
@media (min-width: 1024px) {
  [data-region="hero"] .c-banner.c-card > p:first-of-type { font-size: var(--text-3xl); }
}
[data-region="hero"] [data-banner-dot].c-chip { min-width: 8px; min-height: 8px; width: 8px; height: 8px; padding: 0; }
[data-region="hero"] [data-banner-dot].c-chip.is-active { width: 24px; background: var(--action-fill); }
[data-region="auth-cta"] h2 { font-size: var(--text-xl); }
@media (min-width: 1024px) {
  .c-view[data-view="home"] > [data-region="auth-cta"] > .u-grid-2.u-grid-2 { grid-template-columns: minmax(0, 1fr); }
}
[data-region="site-article"] p { max-width: 72ch; }

/* Shelves: horizontal rows of cards and tiles. */
[data-shelf-list] { gap: var(--space-3); padding: 2px 2px var(--space-2); }
[data-shelf-list] > .c-card { width: min(78vw, 280px); }
[data-shelf-list] > .c-tile { width: 132px; }
@media (min-width: 1024px) {
  [data-shelf-list] > .c-tile { width: 156px; }
}

/* ── Tiles ── */
.c-tiles { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
@media (min-width: 1024px) {
  .c-tiles { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}
.c-tile {
  background: var(--sheen), var(--surface-card);
  border-color: var(--line-gold);
  box-shadow: var(--edge-hi);
}
.c-tile > .u-truncate { max-width: 100%; }
/* A game or provider tile without its picture shows a jade placeholder. */
.c-tile:is([data-card="game"]),
.c-tile:is([data-card="provider"]) { justify-content: flex-start; padding: var(--space-2) var(--space-2) var(--space-3); }
.c-tile:is([data-card="game"]):has(> img[hidden])::before,
.c-tile:is([data-card="provider"]):has(> img[hidden])::before {
  content: "";
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-sm);
  /* an auspicious coin in gold hairline at the centre of the jade square */
  background: radial-gradient(circle, var(--line-gold-strong) 0 3px, transparent 3.5px), radial-gradient(circle, transparent 0 17px, var(--line-gold-strong) 17.5px 18.5px, transparent 19px), var(--lattice), radial-gradient(circle at 30% 25%, var(--glow-gold), transparent 60%), linear-gradient(160deg, var(--elev-4), var(--elev-1));
  box-shadow: inset 0 0 0 1px var(--line-gold);
}

/* ── Lottery ── */
.c-group-head {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  border-bottom-color: var(--line-gold);
}
[data-lt-card] { gap: var(--space-3); }
[data-lt-card] > .u-row:first-child { flex-wrap: wrap; }
[data-lt-card] > .u-row:first-child > h3 { flex: 1 1 6em; }
[data-lt-card] h3 { font-size: var(--text-lg); font-weight: 600; }
[data-lt-card] [data-f="flag"],
.c-card[data-card="lottery"] .c-avatar {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  overflow: hidden;
  color: var(--ink-on-raised);
  background: var(--surface-raised);
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--line-gold-strong);
  font-size: var(--text-xs);
  font-weight: 600;
}
[data-lt-card] .c-countdown,
.c-card[data-card="lottery"] .c-countdown { font-size: var(--text-lg); }
/* Closed is a neutral fact, not money out: never red. */
[data-lt-when="closed"] .c-badge--danger {
  color: var(--ink-mid);
  background: var(--surface-raised);
}
[data-lt-quickmenu] .c-tile { align-items: flex-start; text-align: start; padding: var(--space-3); }
[data-lt-quickmenu] .c-tile strong { font-family: var(--font-display); font-size: var(--text-base); font-weight: 600; }
.u-grid-auto[data-lt-list],
.u-grid-auto[data-lt-all-list] { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--space-4); }

/* Buy sheet: the number buffer, the keys, the cells. */
.c-panel.u-num[data-lt-buffer],
.c-panel.u-num[data-lt-win-digits] {
  min-height: 64px;
  color: var(--ink-accent);
  background: var(--surface-input);
  border-color: var(--line-gold-strong);
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 600;
  letter-spacing: 0.3em;
}
.c-panel.u-num[data-lt-win-digits]:empty,
.c-panel.u-num[data-lt-win-digits] { font-size: var(--text-xl); letter-spacing: 0; }
.c-numpad { gap: var(--space-2); }
.c-numpad__key,
.c-numpad__key.u-num {
  min-height: 56px;
  background: var(--sheen), var(--surface-raised);
  box-shadow: var(--edge-hi);
  font-family: var(--font-display);
  font-size: var(--text-xl);
}
.c-numpad__key:not(.u-num) { font-family: var(--font-bold); font-size: var(--text-base); }
.c-numpad__key[aria-pressed="true"] {
  color: var(--ink-on-action);
  background: var(--action-fill);
  border-color: var(--action-fill);
}
.c-numgrid { grid-template-columns: repeat(auto-fill, minmax(48px, 1fr)); gap: var(--space-2); }
[data-lt-win-filters] > .u-row { min-height: var(--tap-min); gap: var(--space-2); padding-inline-end: var(--space-3); }
details.c-panel > summary {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  cursor: pointer;
  font-weight: 600;
}
details.c-panel[open] > summary { margin-bottom: var(--space-2); }
.c-overlay__foot.u-sticky-bottom {
  background: color-mix(in srgb, var(--surface-modal) 92%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-top-color: var(--line-gold);
}
.c-overlay__foot { border-top-color: var(--line-gold); }

/* Slip: a list without the list indent; two rows per line on phones, one from 768px. */
.c-slip { margin: 0; padding: 0; list-style: none; }
.c-slip__line { border-bottom-color: var(--line); }
.c-slip__line.u-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "num type type" "price price del";
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding-block: var(--space-3);
}
.c-slip__line.u-row > [data-f="number"] { grid-area: num; }
.c-slip__line.u-row > .u-stack-tight { grid-area: type; }
.c-slip__line.u-row > .c-field { grid-area: price; }
.c-slip__line.u-row > [data-lt-line-del] { grid-area: del; }
.c-slip__line.u-row .c-input { text-align: end; }
@media (min-width: 768px) {
  .c-slip__line.u-row { grid-template-columns: auto minmax(0, 1fr) 160px auto; grid-template-areas: "num type price del"; }
}
.c-slip [data-f="number"] {
  justify-self: start;
  padding: 2px 12px;
  color: var(--ink-accent);
  border: 1px solid var(--line-gold-strong);
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: 0.12em;
}
.c-slip__total { border-top-color: var(--line-gold); }
.c-slip__total.u-stack-tight { align-items: stretch; }
.c-slip__total :is(dt),
.c-slip__total :is(dd) { margin: 0; }
.c-slip__total .u-num { font-family: var(--font-display); }
.c-slip__total > div:last-child .u-num,
.c-slip__total.u-row-between .u-num { font-size: var(--text-xl); }
.c-slip__total [data-lt-cart-discount-row] .u-num { color: var(--ink-success); font-size: var(--text-base); }
/* Money back to the member: an applied coupon, a ticket's winnings. */
[data-lt-coupon-summary] { color: var(--ink-success); }
.c-slip__total .u-num:has([data-lt-poy-detail-win]) { color: var(--ink-success); }
[data-f="outcome"][data-tone="win"] { color: var(--ink-success); }
[data-f="outcome"][data-tone="loss"] { color: var(--ink-danger); }

/* Money in on a stat. */
.c-stat__value:has([data-lt-poy-stat="win"]),
.c-stat__value[data-stmt-deposit] { color: var(--ink-success); }
.c-winners__amount { font-family: var(--font-num); }

/* A cancelled money row is neutral; red stays for money lost and failures. */
[data-statement-rows] .c-badge--danger {
  color: var(--ink-mid);
  background: var(--surface-raised);
}

/* ── Ticket cards (PO2): each ticket a full-width card framed by the gold hairline; the whole card opens the detail. ── */
.c-poy-cards {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.c-poy-card { min-width: 0; }
.c-poy-card__btn {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--space-4);
  color: var(--ink-on-card);
  text-align: start;
  cursor: pointer;
  background: var(--sheen), var(--surface-card);
  border: 1px solid var(--line-gold);
  border-radius: var(--radius);
  box-shadow: var(--edge-hi), var(--shadow-sm);
  transition: transform var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.c-poy-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-width: 0;
}
.c-poy-card__title {
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: var(--leading-display);
}
.c-poy-card__head > .c-badge { flex: none; }
.c-poy-card__meta { color: var(--ink-dim); font-size: var(--text-sm); }
.c-poy-card__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding-top: var(--space-2);
  border-top: 1px solid var(--line);
}
.c-poy-card__label { color: var(--ink-dim); font-size: var(--text-sm); }
.c-poy-card__total {
  color: var(--ink-hi);
  font-family: var(--font-num);
  font-size: var(--text-lg);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ── Tables ── */
.u-scroll-x:has(> .c-table) {
  border: 1px solid var(--line-gold);
  border-radius: var(--radius);
  background: var(--surface-card);
}
.c-table th,
.c-table td { padding: var(--space-3); }
.c-table tbody tr { min-height: 48px; }

/* ── Results: the drawn digits in the display face ── */
.c-result .c-stat__value { font-family: var(--font-display); letter-spacing: 0.12em; }

/* ── Overlays ── */
.c-overlay {
  background: color-mix(in srgb, var(--seed-ink) 62%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.c-overlay__panel {
  max-width: 520px;
  background: var(--sheen), var(--surface-modal);
  border-color: var(--line-gold);
  box-shadow: var(--edge-hi), var(--shadow-lg);
}
.c-overlay__head {
  min-height: 60px;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-5);
  border-bottom-color: var(--line-gold);
}
.c-overlay__head > h2 { font-size: var(--text-xl); }
.c-overlay__head > h2 > .u-text-dim { font-family: var(--font-bold); font-size: var(--text-sm); font-weight: 500; }
.c-overlay__body { padding: var(--space-5); }
.c-overlay__close { border: 1px solid var(--line-gold); border-radius: var(--radius-pill); }
.c-overlay--sheet .c-overlay__panel {
  max-width: 720px;
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
}
.c-overlay--sheet .c-overlay__panel::before {
  content: "";
  flex: none;
  width: 36px;
  height: 4px;
  margin: var(--space-2) auto 0;
  border-radius: var(--radius-pill);
  background: var(--line-gold-strong);
}
@media (min-width: 768px) {
  .c-overlay--sheet .c-overlay__panel { border-radius: var(--radius-lg); }
  .c-overlay--sheet .c-overlay__panel::before { display: none; }
}

/* ── Toasts ── */
.c-toast { border-color: var(--line-gold); }

/* ── Empty states. The poy frame carries the medallion as markup (.c-empty); every other list draws a smaller ring on
      its own empty slot. The receipt and the rate-warn islands get their medallions on their bodies, by CSS only. ── */
.c-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-4);
  text-align: center;
}
.c-empty__mark {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 64px;
  height: 64px;
  margin-bottom: var(--space-2);
  color: var(--ink-accent);
  background: var(--sheen), var(--elev-2);
  border-radius: 50%;
  box-shadow: 0 0 0 1px var(--line-gold-strong), 0 0 0 6px var(--action-soft);
}
.c-empty__mark > .c-icon { width: 28px; height: 28px; }
.c-empty > h3 { font-size: var(--text-lg); }
.c-empty > p { max-width: 36ch; color: var(--ink-dim); }
.c-empty > .c-btn { margin-top: var(--space-2); }
:is(main) .c-data > .c-state--empty:not(:has(.c-empty)),
:is(.c-overlay__body) .c-data > .c-state--empty:not(:has(.c-empty)) {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-4);
  text-align: center;
}
:is(main) .c-data > .c-state--empty:not(:has(.c-empty))::before,
:is(.c-overlay__body) .c-data > .c-state--empty:not(:has(.c-empty))::before {
  content: "";
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--action-fill) 0 3px, transparent 3.5px), var(--sheen), var(--elev-2);
  box-shadow: 0 0 0 1px var(--line-gold-strong), 0 0 0 5px var(--action-soft);
}
/* The receipt's mint medallion (a drawn check) and the rate-warn's amber one (!), on the islands' bodies. */
:is([data-overlay="buy-success"]) .c-overlay__body,
:is([data-overlay="rate-warn"]) .c-overlay__body { position: relative; }
:is([data-overlay="buy-success"]) .c-overlay__body::before,
:is([data-overlay="rate-warn"]) .c-overlay__body::before {
  content: "";
  display: grid;
  place-items: center;
  align-self: center;
  flex: none;
  width: 72px;
  height: 72px;
  border-radius: 50%;
}
[data-overlay="buy-success"] .c-overlay__body::before {
  background: var(--state-success-tint);
  box-shadow: 0 0 0 1px var(--ink-success), 0 0 0 7px var(--state-success-tint);
}
[data-overlay="buy-success"] .c-overlay__body::after {
  content: "";
  position: absolute;
  top: calc(var(--space-5) + 20px);
  left: 50%;
  width: 14px;
  height: 26px;
  border: solid var(--ink-success);
  border-width: 0 3px 3px 0;
  transform: translateX(-50%) rotate(45deg);
}
[data-overlay="rate-warn"] .c-overlay__body::before {
  content: "!";
  color: var(--ink-warning);
  background: var(--state-warning-tint);
  box-shadow: 0 0 0 1px var(--ink-warning), 0 0 0 7px var(--state-warning-tint);
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  line-height: 1;
}
:is([data-overlay="buy-success"]) .c-overlay__body > .c-badge,
:is([data-overlay="rate-warn"]) .c-overlay__body > .c-badge { align-self: center; }

/* ── Account: the profile ring (the shortcuts are the AC5 list menu, ROUND 1 below) ── */
.c-avatar { box-shadow: 0 0 0 1px var(--line-gold-strong); }

/* ── Data states ── */
.c-state--error { border: 1px solid color-mix(in srgb, var(--state-danger) 30%, transparent); }
.c-state--empty { color: var(--ink-dim); }

/* ── Fit at 390 and 1280 (the mock's review, build 2) ── */
/* The header's guest buttons keep one line beside the brand. */
.c-header [data-auth-block] .c-btn { white-space: nowrap; }
@media (max-width: 479.98px) {
  .c-header [data-auth-block="guest"] .c-btn--sm { padding-inline: var(--space-3); }
}
/* Results: one card per row on a phone, the drawn numbers set smaller in the three-up row. */
/* (doubled class: it outranks the template's ≥768 results rule, which divides --feed-max, none in this theme) */
.u-grid-auto.u-grid-auto[data-lt-result-grid] { grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); gap: var(--space-4); }
.c-result > .u-row > h3 { min-width: 0; }
.c-result .c-stat__label { white-space: nowrap; }
.c-result .u-grid-3 .c-stat__value,
.c-result .u-grid-2 .c-stat__value { font-size: var(--text-xl); letter-spacing: 0.08em; }
/* Promotions: one card per row on a phone, a grid from there. */
.u-grid-auto[data-promo-grid] { grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); }
/* A slip line's number pill and badge keep their own width in a stacked column (the ticket detail). */
.c-slip .u-stack-tight > [data-f="number"] { align-self: flex-start; }
.c-slip .u-stack-tight > .c-badge { align-self: flex-end; }
/* The ticket card's rows fill the card, so the title truncates beside its badge. */
.c-poy-card__btn { align-items: stretch; }
.c-poy-card__head,
.c-poy-card__meta,
.c-poy-card__foot { width: 100%; }
.c-poy-card__title { flex: 1 1 auto; }
/* The drawer's panel sits at the inline start (D1), also where its overlay is laid out in the flow. */
.c-overlay.c-overlay--side > .c-overlay__panel { margin-inline: 0 auto; }
/* A panel stacks above the fixed rail wherever an overlay is laid out in the flow. */
.c-overlay__panel { position: relative; z-index: 1; }
/* A fieldset without a panel class is a plain group: no browser frame (its grey is not a token). */
:where(body.theme-theme16) fieldset:not(.c-panel) { min-width: 0; margin: 0; padding: 0; border: 0; }

/* =============================================================================
   THEME16 · ROUND 1 (the owner's feedback at MOCK-REVIEW, 2026-10-07): phones first.
   Header H4, bottom-nav B4 (the gold coin), market cards L2, the account as a
   "me" page (AC5), the poy scope as a selector, one status-colour scheme, and a
   little more gold geometry behind the hero. Desktop keeps working as it was.
   ========================================================================== */

/* ── Header (H4): the menu at the start, the brand centred, the guest's gold เข้าสู่ระบบ or the member's balance at the
      end. The side columns are as wide as their content at least and share the rest, so the brand sits in the centre
      and, when the room runs out, truncates with … instead of pushing the bar past the screen. ── */
.c-header__bar {
  display: grid;
  grid-template-columns: minmax(max-content, 1fr) minmax(0, auto) minmax(max-content, 1fr);
  align-items: center;
  gap: var(--space-2);
}
.c-header__start { display: flex; align-items: center; justify-self: start; }
.c-header__end { display: flex; align-items: center; justify-self: end; }
.c-header .c-header__brand {
  display: block;
  min-width: 0;
  max-width: 100%;
  justify-self: center;
  color: var(--ink-accent);
  text-align: center;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: var(--leading-display);
  letter-spacing: 0.01em;
  text-decoration: none;
}
.c-header__brand > .u-truncate { display: block; }
.c-header__brand > img { display: block; height: 36px; width: auto; max-width: 100%; margin-inline: auto; object-fit: contain; }
/* Only the search field grows; the guest's buttons keep their width. */
.c-header [data-auth-block] .c-btn--soft { flex: none; justify-content: center; }
@media (min-width: 768px) {
  .c-header__bar { grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--space-3); }
  .c-header .c-header__brand { display: none; }
}

/* ── Bottom nav (B4): หน้าหลัก a side item; the centre cell opens the lottery hub as a gold Chinese coin — round, an
      inner rim, a square hole — drawn by CSS on the tokens. ── */
.c-coin {
  position: relative;
  display: block;
  flex: none;
  width: var(--fab-size);
  height: var(--fab-size);
  margin-top: -28px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%, color-mix(in srgb, var(--ink-bright) 38%, transparent), transparent 58%), var(--action-fill);
  box-shadow: 0 0 0 4px var(--elev-1), 0 0 0 5px var(--line-gold-strong), var(--shadow);
}
.c-coin::before {
  content: "";
  position: absolute;
  inset: 6px;
  border: 1.5px solid color-mix(in srgb, var(--ink-on-action) 40%, transparent);
  border-radius: 50%;
}
.c-coin::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 16px;
  background: var(--elev-1);
  border: 2px solid color-mix(in srgb, var(--ink-on-action) 55%, transparent);
  border-radius: 2px;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink-bright) 16%, transparent);
  transform: translate(-50%, -50%);
}
.c-bottom-nav__item--coin { gap: 2px; }
.c-bottom-nav__item--coin > span:last-child { font-weight: 600; }

/* ── Market cards (L2): one compact horizontal card per market — the flag, the name with its state under it, the
      button at the end; the banner variant puts the banner first. The middle column shrinks and wraps, so no countdown
      runs over the button. ── */
.u-grid-auto[data-lt-list],
.u-grid-auto[data-lt-all-list] { grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); gap: var(--space-3); }
.c-card.c-mkt {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  min-height: 72px;
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
}
.c-mkt__main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.c-mkt .c-mkt__name { font-size: var(--text-base); font-weight: 600; line-height: 1.45; }
.c-mkt__when { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-2); min-width: 0; font-size: var(--text-sm); }
.c-mkt__when .c-countdown { min-width: 0; font-size: var(--text-base); overflow-wrap: anywhere; }
.c-mkt__when .c-badge { font-size: var(--text-xs); }
.c-mkt .c-mkt__buy { min-width: 64px; }
.c-mkt[data-lt-state="closed"] .c-mkt__name { color: var(--ink-mid); }
.c-mkt__title { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
/* The name takes the room beside the flag (a short name keeps its box wide, and a long one truncates with …). */
.c-mkt__title > .c-mkt__name { flex: 1 1 auto; }
.c-mkt__banner {
  position: relative;
  display: grid;
  place-items: center;
  width: 64px;
  height: 48px;
  overflow: hidden;
  color: var(--ink-accent);
  background: var(--surface-raised);
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px var(--line-gold);
}
/* The banner covers its box; the ticket icon is only the placeholder, so it goes whenever the image is shown. */
.c-mkt__banner > img { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; object-fit: cover; }
.c-mkt__banner:has(> img:not([hidden])) > .c-icon { display: none; }
.c-mkt--banner[data-lt-state="closed"] .c-mkt__banner > img { opacity: 0.55; }
/* The home shelf's lottery card (template markup): its countdown column shrinks and the row wraps before the button. */
.c-card[data-card="lottery"] .u-row-between { flex-wrap: wrap; gap: var(--space-2); }
.c-card[data-card="lottery"] .u-row-between > .u-stack-tight { flex: 1 1 auto; min-width: 0; }
.c-card[data-card="lottery"] .u-row-between > .c-btn { flex: none; margin-inline-start: auto; }
.c-card[data-card="lottery"] .c-countdown { overflow-wrap: anywhere; }

/* The lottery shortcuts: one scrolling row on phones (each title on one line), the three-up grid from 768. */
.c-tiles[data-lt-quickmenu] > .c-tile { align-items: flex-start; text-align: start; }
.c-tiles[data-lt-quickmenu] > .c-tile > .c-badge { align-self: flex-start; }
@media (max-width: 767.98px) {
  .c-tiles[data-lt-quickmenu] {
    display: flex;
    gap: var(--space-3);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    padding: 2px 2px var(--space-2);
  }
  .c-tiles[data-lt-quickmenu] > .c-tile { flex: 0 0 min(66vw, 236px); scroll-snap-align: start; }
  .c-tiles[data-lt-quickmenu] > .c-tile strong { white-space: nowrap; }
}

/* ── The poy scope as a selector: a labelled field with a chevron (the tab keeps its role and ids). ── */
.c-poy-scope { gap: var(--space-1); }
.c-poy-scope .c-tabs.c-poy-scope__select { display: flex; padding: 0; overflow: visible; background: none; border: 0; }
.c-poy-scope .c-poy-scope__select > .c-tab {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-width: min(100%, 220px);
  min-height: calc(var(--tap-min) + 4px);
  margin: 0;
  padding: 0 var(--space-3) 0 var(--space-4);
  color: var(--ink-hi);
  background: var(--surface-input);
  border: 1px solid var(--line-input);
  border-radius: var(--radius-sm);
  font-weight: 500;
}
.c-poy-scope__chev { flex: none; width: 18px; height: 18px; color: var(--ink-accent); }

/* ── Account (AC5): a "me" page — full height on phones, a centred dialog from 768; the shortcuts and the segments
      are list menus; the selected segment carries the gold start edge and its pane follows the list. ── */
.c-overlay--me .c-overlay__body { gap: var(--space-4); }
.c-overlay--me .c-avatar { width: 52px; height: 52px; }
.c-menu { display: flex; flex-direction: column; gap: var(--space-2); }
.c-menu__label { padding-inline: var(--space-1); color: var(--ink-dim); font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.04em; }
.c-menu__list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  overflow: hidden;
  list-style: none;
  background: var(--sheen), var(--surface-card);
  border: 1px solid var(--line-gold);
  border-radius: var(--radius);
}
.c-menu__list > li + li,
.c-menu__list > .c-menu__item + .c-menu__item { border-top: 1px solid var(--line); }
.c-menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 56px;
  padding: var(--space-2) var(--space-4);
  color: var(--ink-hi);
  text-align: start;
  cursor: pointer;
  background: transparent;
  border: 0;
}
.c-menu__icon { flex: none; width: 36px; height: 36px; padding: 8px; color: var(--ink-accent); background: var(--action-soft); border-radius: var(--radius-sm); }
.c-menu__text { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; font-weight: 500; line-height: 1.5; }
.c-menu__sub { color: var(--ink-dim); font-size: var(--text-sm); font-weight: 400; }
.c-menu__chev { flex: none; width: 18px; height: 18px; color: var(--ink-dim); }
.c-tabs.c-menu__tabs { flex-direction: column; gap: 0; overflow: hidden; }
.c-menu__tabs > .c-tab.c-menu__item {
  justify-content: flex-start;
  margin: 0;
  white-space: normal;
  border: 0;
  border-radius: 0;
}
.c-menu__tabs > .c-tab.c-menu__item + .c-tab.c-menu__item { border-top: 1px solid var(--line); }
.c-menu__tabs > .c-tab.c-menu__item:is(.is-active),
.c-menu__tabs > .c-tab.c-menu__item:is([aria-selected="true"]) {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
  box-shadow: inset 3px 0 0 var(--action-fill);
}
.c-menu__tabs > .c-tab.c-menu__item:is(.is-active) .c-menu__chev,
.c-menu__tabs > .c-tab.c-menu__item:is([aria-selected="true"]) .c-menu__chev { color: var(--ink-accent); }
@media (hover: hover) {
  .c-menu__item:hover { background: var(--surface-hover); }
}
@media (min-width: 768px) {
  .c-overlay--full.c-overlay--me { align-items: center; justify-content: center; padding: var(--gutter); }
  .c-overlay--full.c-overlay--me .c-overlay__panel {
    width: 100%;
    max-width: 720px;
    height: auto;
    max-height: 92dvh;
    border: 1px solid var(--line-gold);
    border-radius: var(--radius-lg);
  }
}

/* ── One status scheme, the same everywhere: mint = open, won, money in; amber = waiting; a dim neutral = closed;
      red = lost, money out, errors. A label that is not a status (a provider, ฟรี!, a code, the rank, the rate, a
      completed action) is soft gold or neutral. ── */
/* waiting: the template's info badge carries ยังไม่ถึงเวลาเปิดรับ, the shelf's ยังไม่เปิดรับ and the data layer's รอผล */
body.theme-theme16 .c-badge.c-badge--info { color: var(--ink-warning); background: var(--state-warning-tint); }
/* labels, not statuses */
body.theme-theme16 .c-badge[data-f="provider"],
body.theme-theme16 .c-ticker > .u-row > .c-badge--warning { color: var(--ink-mid); background: var(--surface-raised); }
body.theme-theme16 [data-lt-quickmenu] .c-badge--success,
body.theme-theme16 [data-profile-rank].c-badge,
body.theme-theme16 [data-lt-rate-readout].c-badge,
body.theme-theme16 [data-promo-grid] [data-f="code"].c-badge,
body.theme-theme16 [data-web-chat-badge].c-badge { color: var(--ink-on-soft); background: var(--action-soft); }
/* a completed action is done, not money in */
body.theme-theme16 [data-overlay="buy-success"] .c-badge--success { color: var(--ink-on-soft); background: var(--action-soft); }
body.theme-theme16 [data-statement-rows] .c-badge--success,
body.theme-theme16 [data-overlay="affiliate"] .c-badge--success,
body.theme-theme16 [data-overlay="account"] .c-badge--success { color: var(--ink-hi); background: var(--surface-raised); }
/* closed (the shelf card's ปิดรับ; the hub's ปิดรับแทง and the statement's ยกเลิก are dimmed above) */
body.theme-theme16 .c-card[data-card="lottery"] .c-badge--danger { color: var(--ink-mid); background: var(--surface-raised); }
/* a returned ticket (คืนโพย) is settled, not waiting */
body.theme-theme16 [data-lt-poy-list] [data-f="status"].c-badge--warning,
body.theme-theme16 [data-overlay="poy-detail"] [data-f="status"].c-badge--warning { color: var(--ink-hi); background: var(--surface-raised); }
/* a locked game is a restriction to wait out, not an error */
body.theme-theme16 [data-promo-v2-mode="guard"] .c-badge--danger { color: var(--ink-warning); background: var(--state-warning-tint); }
/* returning a ticket gives the stake back: a plain ghost action, not red */
body.theme-theme16 .c-btn--danger[data-lt-poy-return] { color: var(--ink-hi); background: transparent; border-color: var(--line-gold-strong); box-shadow: none; }
/* a success toast reports an action: the gold edge */
body.theme-theme16 .c-toast[data-kind="success"] { border-inline-start-color: var(--action-fill); }
/* the receipt's medallion: a purchase done, in gold */
body.theme-theme16 [data-overlay="buy-success"] .c-overlay__body::before {
  background: var(--action-soft);
  box-shadow: 0 0 0 1px var(--line-gold-strong), 0 0 0 7px var(--action-soft);
}
body.theme-theme16 [data-overlay="buy-success"] .c-overlay__body::after { border-color: var(--ink-accent); }

/* ── The hero only: a denser gold lattice — each diagonal a double line — and an inner gold fret line 8px inside the
      frame. Every line keeps the page lattice's 4 % gold and a pixel meets at most one line of each diagonal, so the
      worst point behind the text is the same 8 % crossing as before: text contrast unchanged. ── */
[data-region="hero"] .c-banner.c-card {
  background:
    repeating-linear-gradient(45deg, color-mix(in srgb, var(--seed-accent) 4%, transparent) 0 1px, transparent 1px 4px, color-mix(in srgb, var(--seed-accent) 4%, transparent) 4px 5px, transparent 5px 24px),
    repeating-linear-gradient(-45deg, color-mix(in srgb, var(--seed-accent) 4%, transparent) 0 1px, transparent 1px 4px, color-mix(in srgb, var(--seed-accent) 4%, transparent) 4px 5px, transparent 5px 24px),
    radial-gradient(640px 320px at 100% 0%, var(--glow-gold), transparent 70%),
    linear-gradient(150deg, var(--elev-3), var(--elev-0) 72%);
  outline: 1px solid var(--line-gold);
  outline-offset: -8px;
}

/* =============================================================================
   STATES
   ========================================================================== */

/* ── hover (pointer devices only, so a tap does not leave it stuck) ── */
@media (hover: hover) {
  .c-btn:hover                  { color: var(--ink-on-hover); background: var(--surface-hover); }
  .c-btn--primary:hover         { color: var(--ink-on-gold); background: var(--grad-accent-hover); box-shadow: var(--glow-accent); }
  .c-btn--soft:hover            { color: var(--ink-on-soft); background: var(--action-soft-hover); }
  .c-btn--ghost:hover           { color: inherit; background: color-mix(in srgb, currentColor 10%, transparent); }
  .c-btn--danger:hover          { color: var(--ink-on-danger); background: var(--fill-danger); box-shadow: 0 0 18px color-mix(in srgb, var(--fill-danger) 35%, transparent); }
  .c-btn--success:hover         { color: var(--ink-on-success); background: var(--fill-success); box-shadow: 0 0 18px color-mix(in srgb, var(--fill-success) 35%, transparent); }
  .c-chip:hover                 { color: var(--ink-on-hover); background: var(--surface-hover); }
  .c-tab:hover                  { color: var(--ink-hi); }
  a.c-list__item:hover,
  button.c-list__item:hover     { color: var(--ink-on-hover); background: var(--surface-hover); }
  .c-tile:hover                 { color: var(--ink-on-hover); background: var(--surface-hover); border-color: var(--line-strong); }
  .c-bottom-nav__item:hover     { color: var(--ink-on-chrome); }
  .c-numpad__key:hover          { color: var(--ink-on-hover); background: var(--surface-hover); }
  .c-numgrid__cell:hover        { color: var(--ink-on-hover); background: var(--surface-hover); }
  .c-overlay__close:hover       { color: var(--ink-hi); background: var(--surface-hover); }
  .c-input:hover,
  .c-select:hover               { border-color: var(--ink-dim); }
}
/* The filled buttons keep their fill on hover and when pressed (B3 §3): each
   ink is the server's black-or-white pick for that one fill, and a mix
   toward --seed-fg moves toward the ink in some palettes (the danger hover
   was 4.12:1 at gate preset 4, the primary 3.25:1 at preset 2). Hover shows
   a glow of the fill; pressed is the translate below. */

/* ── active (pressed) ── */
.c-btn:active,
.c-chip:active,
.c-tile:active,
.c-numpad__key:active,
.c-numgrid__cell:active {
  transform: translateY(1px);
}
.c-btn--primary:active {
  color: var(--ink-on-action);
  background: var(--action-fill);
}
.c-numpad__key:active {
  color: var(--ink-on-soft);
  background: var(--action-soft-hover);
}

/* ── focus-visible. The outline is the opaque accent, not --ring-focus.
      The doc formula for --ring-focus (accent 45 percent over transparent)
      measures 2.34 to 2.57:1 on the ladder at the example seeds, under 3:1.
      --action-fill measures 5.83 to 8.50:1 on the same surfaces. Fields also
      get the --focus-ring halo, which is built from --ring-focus. ── */
body.theme-theme16 :focus-visible {
  outline: 2px solid var(--action-fill);
  outline-offset: 2px;
}
/* A tab bar scrolls sideways (overflow-x), which clips an outline drawn
   outside a tab, so a tab draws it inside its own box (B3 §3; the
   specificity 0,3,0 beats the rule above). */
.c-tabs > .c-tab:focus-visible {
  outline-offset: -2px;
}
.c-input:focus-visible,
.c-select:focus-visible,
.c-input:focus,
.c-select:focus {
  outline: 2px solid var(--action-fill);
  outline-offset: 1px;
  border-color: var(--action-fill);
  box-shadow: var(--focus-ring);
}

/* ── selected / active / current ── */
.c-btn.is-active,
.c-btn[aria-current="page"],
.c-btn[aria-pressed="true"] {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
  border-color: var(--action-fill);
}
.c-tab.is-active,
.c-tab[aria-selected="true"] {
  color: var(--ink-accent);
  border-bottom-color: var(--action-fill);
}
.c-tabs--pills > .c-tab.is-active,
.c-tabs--pills > .c-tab[aria-selected="true"] {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
  border-color: var(--action-fill);
}
.c-chip.is-active,
.c-chip.is-selected,
.c-chip[aria-selected="true"],
.c-chip[aria-pressed="true"] {
  color: var(--ink-on-soft);
  background: var(--action-soft);
  border-color: var(--action-fill);
}
.c-bottom-nav__item.is-active,
.c-bottom-nav__item[aria-current="page"] {
  color: var(--ink-on-chrome);
}
.c-bottom-nav__item.is-active::before,
.c-bottom-nav__item[aria-current="page"]::before {
  background: var(--action-fill);
}
.c-list__item.is-active,
.c-list__item.is-selected,
.c-list__item[aria-selected="true"],
.c-tile.is-active,
.c-tile.is-selected,
.c-tile[aria-selected="true"] {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
}
/* The selected tint alone is 1.05:1 against a card, so the state is carried
   by an opaque accent edge (7.50:1 on a card). */
.c-list__item.is-active,
.c-list__item.is-selected,
.c-list__item[aria-selected="true"] {
  box-shadow: inset 3px 0 0 var(--action-fill);
}
.c-tile.is-active,
.c-tile.is-selected,
.c-tile[aria-selected="true"] {
  border-color: var(--action-fill);
}
.c-numgrid__cell.is-selected,
.c-numgrid__cell.is-active,
.c-numgrid__cell[aria-selected="true"],
.c-numgrid__cell[aria-pressed="true"] {
  color: var(--ink-on-action);
  background: var(--action-fill);
  border-color: var(--action-fill);
}

/* ── open. shell.js adds .is-open to every overlay it shows. The open
      panel carries the accent glow. ── */
.c-overlay.is-open .c-overlay__panel {
  box-shadow: var(--shadow-lg), var(--glow-accent);
}

/* ── disabled. Exempt from contrast targets by WCAG, but kept legible:
      --ink-on-disabled is --ink-dim. ── */
.c-btn:disabled,
.c-btn[aria-disabled="true"],
.c-btn.is-disabled,
.c-chip:disabled,
.c-chip[aria-disabled="true"],
.c-chip.is-disabled,
.c-tab:disabled,
.c-tab[aria-disabled="true"],
.c-tab.is-disabled,
.c-tile[aria-disabled="true"],
.c-tile.is-disabled,
.c-numpad__key:disabled,
.c-numpad__key[aria-disabled="true"],
.c-numgrid__cell:disabled,
.c-numgrid__cell[aria-disabled="true"],
.c-numgrid__cell.is-disabled,
.c-list__item[aria-disabled="true"],
.c-list__item.is-disabled {
  color: var(--ink-on-disabled);
  background: var(--surface-disabled);
  border-color: transparent;
  box-shadow: none;
  cursor: not-allowed;
  transform: none;
}
.c-input:disabled,
.c-select:disabled,
.c-input[aria-disabled="true"],
.c-select[aria-disabled="true"],
.c-input.is-disabled,
.c-select.is-disabled {
  color: var(--ink-on-disabled);
  background-color: var(--surface-disabled);
  border-color: var(--line);
  cursor: not-allowed;
}

/* ── loading / busy ── */
.is-loading,
[aria-busy="true"] {
  cursor: progress;
}
.c-btn.is-loading,
.c-btn[aria-busy="true"] {
  pointer-events: none;
}
.c-btn.is-loading::after,
.c-btn[aria-busy="true"]::after {
  content: "";
  flex: none;
  width: 1em;
  height: 1em;
  border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: c-spin 0.8s linear infinite;
}

/* ── Data-display states.
      Every stub that shows fetched data renders a .c-data with five .c-state
      slots, and shell or page code un-hides exactly one of them:
        loading  skeleton while the request is in flight (aria-busy)
        empty    ยังไม่มีรายการ (no items yet)
        error    โหลดข้อมูลไม่สำเร็จ (could not load) plus a retry button, role alert
        zero     the value exists and is zero, for example 0.00 balance
        ready    the real content
      Each display below renders these four states (plus ready) through
      .c-data and .c-state.

      docs/THEME_UI_INVENTORY.md section 5, data displays:
        [ยอดเงิน Balance readout]
        [ยอดเงิน / โบนัส / รอผลรางวัล / ชนะสะสม — wallet bento]   ABSENT, stub is TODO-ABSENT
        [Profile card]
        [รายงานการเงิน Statement ledger]
        [Provider grid]
        [Game lobby grid]
        [บัญชีธนาคาร Bank list]
        [แนะนำเพื่อน Affiliate KPIs + tables]
        [คืนยอดเสีย Cashback panel]
        [โปรโมชั่น Promotion list + detail]
        [ยอดฝากรอดำเนินการ Pending deposits]
        [Live winners / activity ticker]
        [Live player-count chips]
        [Notifications list]
        [รายงานเกม Game report]
        [Mini-game history]

      Lottery data displays (inventory section 7):
        [Lottery-type selector]                  market lists
        [Lottery-type selector + market region]
        [Market card + countdown ปิดรับใน]
        [เลือกหวย hub — 4 in-scope group accordions: กลุ่มหวยไทย / กลุ่มหวยต่างประเทศ / กลุ่มหวยหุ้น / กลุ่มหวยหุ้นต่างประเทศ VIP]
        [โพย cart / bet slip]                    cart lines and totals
        [Cart panel + quick-price chips]
        [Step 2 — bet slip / cart, totals, coupon, coupon wallet]
        [อัตราจ่าย / ใส่ราคา bet slip]
        [Coupon picker คูปองของฉัน]
        [Coupon picker]
        [เลือกคูปอง Select coupon]
        [อัตราจ่าย payout-rate table in กติกาหวย]
        [อัตราจ่าย & เงื่อนไขการแทง payout table]
        [In-sheet rates panel เงื่อนไขการแทง & อัตราจ่าย]
        [Rules modal + rates body]
        [เลขชุด number sets]
        [number sets (create + list)]
        [Number-set panel (pull / manage / create)]
        [ดึงจากโพยเก่า history re-import]
        [history re-import]
        [History panel]
        [ดึงโพย Pull ticket]
        [โพยหวย bet history — 3 scopes + 4-stat strip + status filter]
        [Bet history — scopes + date range + 4 stats]
        [tab โพยหวย — scopes, stats, sub-status, date range]
        [รายการแทงหวย bet history hub]
        [ประวัติโพย Ticket history]
        [Ticket detail + ยกเลิกและคืนโพย]
        [Poy detail + แจ้งคืนโพยนี้]
        [ผลหวย draw results — date picker + gov/simple templates]
        [Draw results + date filter]
        [tab ผลหวย + date picker]
        [ผลรางวัล draw results — fully SSR from `sections`, no fetch]
        [Homepage results strip]
        [หวยฟรี free lottery]
        [Free-lottery region]
        [Free-lottery modal]
        [เลขอั้น Limited numbers]
        [Quota gate]
   ── */
.c-state--loading {
  min-height: var(--tap-min);
}
.c-state--empty {
  padding: var(--space-5) var(--space-4);
  color: var(--ink-dim);
  text-align: center;
  font-size: var(--text-sm);
}
.c-state--error {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-4);
  color: var(--ink-danger);
  background: var(--soft-danger);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  text-align: center;
}
.c-state--zero {
  padding: var(--space-3) 0;
  color: var(--ink-mid);
  font-size: var(--text-xl);
}
.c-state--ready {
  display: block;
}
.c-data[aria-busy="true"] .c-state--ready {
  opacity: 0.6;
}

/* =============================================================================
   THEME16 · STATES (after the template's STATES, so these win at equal
   specificity): the gold hairline strengthens on hover and on selected,
   cards and tiles lift, the segmented and pill tabs fill soft gold.
   ========================================================================== */
@media (hover: hover) {
  .c-tile:hover { border-color: var(--line-gold-strong); transform: translateY(-2px); }
  .c-card[data-lt-card]:hover,
  .c-card[data-card]:hover { border-color: var(--line-gold-strong); }
  .c-btn--ghost:hover { border-color: var(--line-gold-strong); }
  .c-poy-card__btn:hover { border-color: var(--line-gold-strong); transform: translateY(-2px); }
  aside[data-region="sidebar"] .c-btn--ghost:hover,
  .c-drawer .c-list__item.c-btn:hover { color: var(--ink-hi); background: var(--surface-hover); }
  .c-tabs:is(.c-tabs--pills) > .c-tab:hover,
.c-tabs:is([data-lt-types]) > .c-tab:hover,
.c-tabs:is([data-lt-panel-ranges]) > .c-tab:hover,
.c-tabs:is([data-region="category-nav"] > .c-tabs) > .c-tab:hover { color: var(--ink-hi); border-color: var(--line-gold-strong); }
}
.c-tabs:is([data-lt-tabs]) > .c-tab.is-active,
.c-tabs:is([data-lt-macros]) > .c-tab.is-active,
[data-overlay="cashier"] .c-tabs:is([role="tablist"]) > .c-tab.is-active,
[data-overlay="auth"] .c-tabs:is([role="tablist"]) > .c-tab.is-active,
.c-tabs:is([data-lt-tabs]) > .c-tab[aria-selected="true"],
.c-tabs:is([data-lt-macros]) > .c-tab[aria-selected="true"],
[data-overlay="cashier"] .c-tabs:is([role="tablist"]) > .c-tab[aria-selected="true"],
[data-overlay="auth"] .c-tabs:is([role="tablist"]) > .c-tab[aria-selected="true"],
.c-tabs:is(.c-tabs--pills) > .c-tab.is-active,
.c-tabs:is([data-lt-types]) > .c-tab.is-active,
.c-tabs:is([data-lt-panel-ranges]) > .c-tab.is-active,
.c-tabs:is([data-region="category-nav"] > .c-tabs) > .c-tab.is-active,
.c-tabs:is(.c-tabs--pills) > .c-tab[aria-selected="true"],
.c-tabs:is([data-lt-types]) > .c-tab[aria-selected="true"],
.c-tabs:is([data-lt-panel-ranges]) > .c-tab[aria-selected="true"],
.c-tabs:is([data-region="category-nav"] > .c-tabs) > .c-tab[aria-selected="true"],
.c-tabs:is(.c-tabs--pills) > .c-tab[aria-current="page"],
.c-tabs:is([data-lt-types]) > .c-tab[aria-current="page"],
.c-tabs:is([data-lt-panel-ranges]) > .c-tab[aria-current="page"],
.c-tabs:is([data-region="category-nav"] > .c-tabs) > .c-tab[aria-current="page"] {
  color: var(--ink-on-soft);
  background: var(--action-soft);
  border-color: var(--action-fill);
}
.c-poy-card__btn:active { transform: translateY(1px); }
.c-poy-card__btn:focus-visible { outline: 2px solid var(--action-fill); outline-offset: 2px; }
aside[data-region="sidebar"] .c-btn.is-active,
aside[data-region="sidebar"] .c-btn[aria-current="page"] {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
  border-color: transparent;
  box-shadow: inset 3px 0 0 var(--action-fill);
}
.c-tile.is-active,
.c-tile.is-selected,
.c-tile[aria-selected="true"],
.c-tile[aria-checked="true"] { border-color: var(--action-fill); background: var(--surface-selected); color: var(--ink-on-selected); }
.c-bottom-nav__item.is-active,
.c-bottom-nav__item[aria-current="page"] { color: var(--ink-hi); }

/* =============================================================================
   MPA BOOTSTRAP MAPPING
   The MPA layout loads Bootstrap 5.3.3 CSS and its stubs use only the
   structural classes. These rules put Bootstrap's modal parts onto the
   tokens. Scoped under the body class so they outrank Bootstrap whatever the
   load order.
   ========================================================================== */

body.theme-theme16 .modal-backdrop {
  background-color: var(--surface-dark);
}
body.theme-theme16 .modal-content {
  color: var(--ink-on-modal);
  background-color: var(--surface-modal);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
body.theme-theme16 .modal-header {
  gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
}
body.theme-theme16 .modal-title {
  font-family: var(--font-bold);
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--ink-hi);
}
body.theme-theme16 .modal-body {
  padding: var(--space-4);
}
body.theme-theme16 .modal-footer {
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--line);
}
body.theme-theme16 .modal-footer > * {
  margin: 0;
}
/* Bootstrap draws the close cross as a dark image and flips it with a
   filter. That cannot follow the canvas, so the image is dropped and the
   cross is drawn in the modal's own ink. */
body.theme-theme16 .btn-close {
  position: relative;
  box-sizing: border-box;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  margin: 0 0 0 auto;
  color: var(--ink-mid);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  opacity: 1;
  filter: none;
  cursor: pointer;
}
body.theme-theme16 .btn-close::before,
body.theme-theme16 .btn-close::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
}
body.theme-theme16 .btn-close::before { transform: translate(-50%, -50%) rotate(45deg); }
body.theme-theme16 .btn-close::after  { transform: translate(-50%, -50%) rotate(-45deg); }
body.theme-theme16 .btn-close:hover {
  color: var(--ink-hi);
  background: var(--surface-hover);
  opacity: 1;
}
body.theme-theme16 .btn-close:focus {
  box-shadow: none;
}
body.theme-theme16 .btn-close:focus-visible {
  outline: 2px solid var(--action-fill);
  outline-offset: 2px;
  opacity: 1;
}

/* =============================================================================
   SWEETALERT2 (B3 §3 round 2)
   public/css/swal-theme.css, shared by every theme and not edited here,
   re-tones a stock popup's confirm and deny buttons to the accent and the
   danger seed, each with the ink (light or dark) picked for that fill,
   and sets SweetAlert2's --swal2-action-button-hover and -active to
   --seed-fg 14 and 26 percent, the doc's 86 and 74 percent rungs, the
   stops the primary button stopped painting in B3 §3. The light ink on the
   hovered confirm measured 4.04 to 1 at gate preset 2 (3.25 pressed), and
   the dark ink on the pressed deny 3.15 at gate preset 4. So those two
   buttons keep their fill on hover and when pressed, and hover shows a
   glow of the fill, as .c-btn--primary and .c-btn--danger do. The
   selector is swal-theme.css's own; its @supports test (relative colour)
   is not repeated: themeNewGate's mock scan counts a color( call as a
   literal, and without the test a browser that lacks relative colour just
   keeps stock fills with no hover shade. So this reaches the popups whose
   fills it re-tones. A feature popup
   that paints itself, and the cancel button (a neutral pair that holds on
   hover), keep SweetAlert2's hover shade.
   ========================================================================== */

/* no @supports test here (see above) */
body.theme-theme16 .swal2-container .swal2-popup:not([style*="background"]):not([style*="color"]):not(.swal-lotto):not(.hh-swal):not(.promotion-v2-choice-swal):not(.promo-v2-pending-swal):not(.dep-success-swal):not(.mh-swal-image-popup):not(:has(.swal2-html-container [style*="color"])):not(:has(.swal2-styled[style*="background"])) :is(.swal2-confirm, .swal2-deny) {
  --swal2-action-button-hover: transparent 0%;
  --swal2-action-button-active: transparent 0%;
}

@media (hover: hover) {

  body.theme-theme16 .swal2-container .swal2-popup:not([style*="background"]):not([style*="color"]):not(.swal-lotto):not(.hh-swal):not(.promotion-v2-choice-swal):not(.promo-v2-pending-swal):not(.dep-success-swal):not(.mh-swal-image-popup):not(:has(.swal2-html-container [style*="color"])):not(:has(.swal2-styled[style*="background"])) .swal2-confirm:hover {
    --swal2-confirm-button-box-shadow: 0 0 18px color-mix(in srgb, var(--action-fill) 35%, transparent);
  }
  body.theme-theme16 .swal2-container .swal2-popup:not([style*="background"]):not([style*="color"]):not(.swal-lotto):not(.hh-swal):not(.promotion-v2-choice-swal):not(.promo-v2-pending-swal):not(.dep-success-swal):not(.mh-swal-image-popup):not(:has(.swal2-html-container [style*="color"])):not(:has(.swal2-styled[style*="background"])) .swal2-deny:hover {
    --swal2-deny-button-box-shadow: 0 0 18px color-mix(in srgb, var(--fill-danger) 35%, transparent);
  }

}

/* =============================================================================
   KEYFRAMES
   ========================================================================== */

@keyframes c-fade-in      { from { opacity: 0; } to { opacity: 1; } }
@keyframes c-pop-in       { from { opacity: 0; transform: translateY(8px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes c-sheet-up     { from { transform: translateY(100%); } to { transform: none; } }
@keyframes c-side-in      { from { transform: translateX(-100%); } to { transform: none; } }
@keyframes c-side-in-end  { from { transform: translateX(100%); } to { transform: none; } }
@keyframes c-marquee      { from { transform: translateX(0); } to { transform: translateX(-100%); } }
@keyframes c-shimmer      { from { background-position: 150% 0; } to { background-position: -50% 0; } }
@keyframes c-spin         { to { transform: rotate(360deg); } }
@keyframes c-drawer-in    { from { transform: translateX(-100%); } to { transform: none; } }

/* =============================================================================
   REDUCED MOTION
   Animations and transitions stop. The marquee becomes a still line the user
   can scroll sideways.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  body.theme-theme16 *,
  body.theme-theme16 *::before,
  body.theme-theme16 *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .c-marquee {
    overflow-x: auto;
  }
  .c-marquee > * {
    padding-inline-start: 0;
    animation: none !important;
  }
  .c-skeleton__line,
  .c-skeleton__block {
    animation: none !important;
  }
  .c-btn.is-loading::after,
  .c-btn[aria-busy="true"]::after {
    animation: c-spin 1.6s linear infinite !important;
  }
}

/* ── Web Chat window (TASK TH-1024-D, plan §3): below 768px the shared client (public/js/web-chat.js) sets the sheet's
      bottom to the bottom nav's top (--wc-bottom); the centre coin rises above that line, so the sheet sits half a coin
      higher and leaves the coin and the nav uncovered. Tokens only; a class rule beats the client's :where(). ── */
@media (max-width: 767.98px) {
  body.theme-theme16 .mhwc { bottom: calc(var(--wc-bottom, calc(var(--bottomnav-h) + env(safe-area-inset-bottom, 0px))) + var(--fab-size) / 2); }
}

/* ── Casino player: full screen (TASK AP-1027-B). Only this overlay (data-overlay="casino-player"): no backdrop gutter,
      no 480/520 px panel, no radius or border; one thin head row (game name · ฝาก · ถอน · แจ้งปัญหา · ×, safe-area
      insets); the game frame takes the rest: the chain body → [data-display] → ready state → iframe is a flex column, so
      the iframe gets the height (it had none: an iframe's own default is 150 px). The balance display stays in the
      markup (a HOOKS.json hook) and is hidden here. While the player is open the shared chat window sits above it
      (web-chat.js reads --wc-z; a class rule beats its :where()). ── */
.c-overlay[data-overlay="casino-player"] {
  padding: 0;
  align-items: stretch;
  justify-content: stretch;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.c-overlay[data-overlay="casino-player"] > .c-overlay__panel {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.c-overlay[data-overlay="casino-player"] .c-overlay__head {
  flex: none;
  box-sizing: border-box;
  height: calc(48px + env(safe-area-inset-top, 0px));
  min-height: 0;
  gap: var(--space-2);
  padding: env(safe-area-inset-top, 0px) calc(var(--space-2) + env(safe-area-inset-right, 0px)) 0 calc(var(--space-3) + env(safe-area-inset-left, 0px));
}
.c-overlay[data-overlay="casino-player"] .c-overlay__head > h2 {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-size: var(--text-sm);
  white-space: nowrap;
  text-overflow: ellipsis;
}
.c-overlay[data-overlay="casino-player"] [data-display="casino-player-balance"] { display: none; }
.c-overlay[data-overlay="casino-player"] .c-overlay__close { width: 40px; height: 40px; }
.c-player-acts { display: flex; flex: none; align-items: center; gap: var(--space-1); }
.c-player-act.c-player-act { min-height: 40px; min-width: 40px; gap: var(--space-1); padding-inline: var(--space-2); }
.c-player-act > .c-icon { flex: none; width: 18px; height: 18px; }
.c-overlay[data-overlay="casino-player"] .c-overlay__body {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  overflow: hidden;
}
.c-overlay[data-overlay="casino-player"] [data-display="casino-player"] {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
.c-overlay[data-overlay="casino-player"] [data-display="casino-player"] > .c-state--ready {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
}
.c-overlay[data-overlay="casino-player"] [data-player-frame] {
  display: block;
  flex: 1 1 auto;
  width: 100%;
  height: auto;
  min-height: 0;
  background: var(--elev-1);
  border: 0;
}
/* below 768 px: the head shows the game name only (the h2 keeps its whole text for aria-labelledby) */
@media (max-width: 767.98px) {
  .c-overlay[data-overlay="casino-player"] .c-overlay__head > h2,
  .c-overlay[data-overlay="casino-player"] .c-overlay__head > h2 > .u-text-dim { font-size: 0; }
  .c-overlay[data-overlay="casino-player"] .c-overlay__head > h2 > [data-player-game-name] { font-size: var(--text-sm); }
}
/* below 768 px: icon over a small label, as the bottom nav items */
@media (max-width: 767.98px) {
  .c-player-act.c-player-act { flex-direction: column; justify-content: center; gap: 2px; min-width: 44px; height: 44px; padding: 0 var(--space-1); font-size: var(--text-xs); line-height: 1; }
}
/* below 360 px: icons only (each keeps its aria-label) */
@media (max-width: 359.98px) {
  .c-player-act.c-player-act { width: 40px; min-width: 40px; height: 40px; padding: 0; }
  .c-player-act > .c-player-act__label { display: none; }
}
/* the shared chat window above the player while the player is open (web-chat.js reads --wc-z); below 768 px its sheet
   sits on the screen's bottom edge, since the player covers the bottom nav it normally stands on */
body.theme-theme16:has(> .c-overlay[data-overlay="casino-player"]:not([hidden])) { --wc-z: calc(var(--z-modal) + 1); }
@media (max-width: 767.98px) {
  body.theme-theme16:has(> .c-overlay[data-overlay="casino-player"]:not([hidden])) .mhwc { bottom: env(safe-area-inset-bottom, 0px); }
}
