/* =============================================================================
   theme12 · 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-theme12)
     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-theme12 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-theme12 {
  /* ── 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));
  --ink-success:         color-mix(in srgb, var(--seed-success) 55%, var(--seed-fg));
  --ink-danger:          color-mix(in srgb, var(--seed-danger) 59%, 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. */
  --surface-chrome:      var(--surface-dark);    --ink-on-chrome:    var(--ink-on-dark);
  --ink-on-chrome-dim:   var(--ink-on-dark-dim);
  /* 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) 55%, var(--ink-on-chrome));
  --ink-on-chrome-danger:  color-mix(in srgb, var(--seed-danger) 59%, 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(1100px 520px at 88% -12%, color-mix(in srgb, var(--seed-accent) 12%, transparent), transparent 62%), radial-gradient(800px 460px at -12% 6%, color-mix(in srgb, var(--seed-accent) 6%, transparent), transparent 58%);
  --shadow-sm:           0 1px 2px color-mix(in srgb, var(--seed-ink) 30%, transparent), 0 2px 8px color-mix(in srgb, var(--seed-ink) 22%, transparent);
  --shadow:              0 4px 16px color-mix(in srgb, var(--seed-ink) 40%, transparent);
  --shadow-lg:           0 16px 48px color-mix(in srgb, var(--seed-ink) 55%, transparent);
  --glow-accent:         0 0 18px color-mix(in srgb, var(--seed-accent) 35%, 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: Mitr for display, Noto Sans Thai for body text and
        for every figure (its digits are tabular by default). ── */
  --font-thai:           "Noto Sans Thai","Sarabun",system-ui,sans-serif;
  --font-bold:           "Mitr","Noto Sans Thai",system-ui,sans-serif;
  --font-num:            "Noto Sans Thai",system-ui,sans-serif;

  /* ── Radii ── */
  --radius-xs: 6px;  --radius-sm: 10px;  --radius: 14px;
  --radius-lg: 20px; --radius-pill: 999px;

  /* ── Layout metrics (carried from theme9). --tap-min is the smallest hit
        area of any interactive element. ── */
  --hdr-h:               60px;
  --mbbrand-h:           52px;
  --tabbar-h:            56px;
  --bottomnav-h:         64px;
  --ticker-h:            40px;
  --sidebar-w:           240px;
  --feed-max:            1200px;
  --gutter:              16px;
  --gutter-lg:           24px;
  --tap-min:             44px;

  /* ── Z-index ladder ── */
  --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(.16,1,.3,1);
  --dur-fast:            140ms;  --dur: 240ms;  --dur-slow: 400ms;

  /* ── 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.7;

  /* ── Type scale ── */
  --text-xs:             0.75rem;
  --text-sm:             0.8125rem;
  --text-base:           0.9375rem;
  --text-lg:             1.0625rem;
  --text-xl:             1.25rem;
  --text-2xl:            1.5rem;

  /* ── Space scale ── */
  --space-1:             4px;
  --space-2:             8px;
  --space-3:             12px;
  --space-4:             16px;
  --space-5:             24px;
  --space-6:             32px;
  /* ── theme12 · the VIP look tokens (DESIGN.md §4, §5, §10). Sizes, motion,
        and the gold edge, lip and ramp (the ramp only where no text sits), bevels, lit edges, obsidian card, gold line,
        scrim and bronze fade, each a mix of seeds, ladder rungs or the poles. ── */
  --text-3xl:            1.875rem;
  --leading-display:     1.4;
  --leading-num:         1.2;
  --ease-spring:         cubic-bezier(.34,1.56,.64,1);
  --dur-press:           90ms;
  --field-h:             48px;
  --tile-h:              76px;
  --key-h:               64px;
  --cell-h:              48px;
  --fab-size:            60px;
  --fab-lift:            20px;
  --gold-edge:           color-mix(in srgb, var(--seed-accent) 62%, var(--seed-ink));
  --grad-gold:           linear-gradient(90deg, var(--gold-edge) 0%, var(--seed-accent) 26%, var(--seed-accent) 74%, var(--gold-edge) 100%);
  --gold-lip:            inset 0 1px 0 color-mix(in srgb, var(--ink-bright) 40%, transparent), inset 0 6px 8px -6px color-mix(in srgb, var(--ink-bright) 30%, transparent), inset 0 -2px 0 color-mix(in srgb, var(--seed-ink) 22%, transparent), 0 3px 0 var(--gold-edge), 0 6px 14px color-mix(in srgb, var(--seed-ink) 35%, transparent);
  --gold-lip-sm:         inset 0 1px 0 color-mix(in srgb, var(--ink-bright) 40%, transparent), inset 0 -2px 0 color-mix(in srgb, var(--seed-ink) 22%, transparent), 0 2px 0 var(--gold-edge);
  --gold-pressed:        inset 0 2px 4px color-mix(in srgb, var(--seed-ink) 30%, transparent);
  --bevel:               inset 0 1px 0 color-mix(in srgb, var(--ink-bright) 30%, transparent), inset 0 -2px 0 color-mix(in srgb, var(--seed-ink) 28%, transparent);
  --bevel-key:           inset 0 1px 0 color-mix(in srgb, var(--ink-bright) 8%, transparent), 0 2px 0 color-mix(in srgb, var(--seed-ink) 40%, transparent);
  --edge-lit:            inset 0 1px 0 color-mix(in srgb, var(--ink-bright) 6%, transparent);
  --obsidian:            linear-gradient(180deg, color-mix(in srgb, var(--elev-2) 86%, var(--ink-bright)) 0%, var(--elev-2) 62%);
  --tile-lit:            linear-gradient(180deg, color-mix(in srgb, var(--elev-3) 88%, var(--ink-bright)), var(--elev-2));
  --key-lit:             linear-gradient(180deg, color-mix(in srgb, var(--elev-4) 88%, var(--ink-bright)), var(--elev-4));
  --fab-core:            linear-gradient(180deg, color-mix(in srgb, var(--surface-chrome) 88%, var(--ink-bright)), var(--surface-chrome));
  --line-gold:           color-mix(in srgb, var(--seed-accent) 45%, transparent);
  --scrim:               color-mix(in srgb, var(--seed-ink) 72%, transparent);
  --fade-bronze:         linear-gradient(90deg, color-mix(in srgb, var(--seed-accent) 24%, transparent), transparent 88%);
  /* Scrollbars: the thumb moves from the accent toward fg, so it stands off a dark canvas as champagne and
     off a light one as dark bronze (>= 3:1 against every rung at the gate's four presets); the track is the
     surface itself. */
  --scroll-thumb:        color-mix(in srgb, var(--seed-accent) 65%, var(--seed-fg));
  /* The field's identifying edge: fg 55 % into the canvas holds 3:1 on every rung a field sits on at
     the light preset too (--line-input, 42 %, fell to 2.74 on a light card). */
  --line-field:          color-mix(in srgb, var(--seed-fg) 55%, var(--seed-canvas));
  --scroll-track:        transparent;
  /* BUILD Stage 5 (P1-CSS C1, C2): the split market card's banner column (LOTTO_CARD_STYLE 2), and the
     smallest split card or result card a wide track holds before it wraps. */
  --lt-banner-w:         112px;
  --market-min:          320px;
}

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

body.theme-theme12 {
  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-theme12 *,
body.theme-theme12 *::before,
body.theme-theme12 *::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-theme12:lang(th),
body.theme-theme12 [lang|="th"] {
  line-height: var(--leading-thai);
}
body.theme-theme12 [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-theme12) h1,
html :where(body.theme-theme12) h2,
html :where(body.theme-theme12) h3,
html :where(body.theme-theme12) h4 {
  margin: 0;
  font-family: var(--font-bold);
  font-weight: 600;
  line-height: inherit;
  color: inherit;
}
html :where(body.theme-theme12) h1 { font-size: var(--text-2xl); }
html :where(body.theme-theme12) h2 { font-size: var(--text-xl); }
html :where(body.theme-theme12) h3 { font-size: var(--text-lg); }
html :where(body.theme-theme12) h4 { font-size: var(--text-base); }

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

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

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

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

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

/* Open overlays lock the page behind them. */
body.theme-theme12: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);
}

.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);
  }
}

/* ── 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);
}

.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);
}

/* =============================================================================
   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-theme12 :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-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;
}
/* The header's error state (TASK TH-1009-C, docs/themelog/_p-org/theme12-live-fixes.md M3): after the stale-session fix
   only a network failure at boot shows it. One line under the header bar, the bar keeping its height, the logo and its
   buttons, in the header's own chrome tokens; the balance box it sits in collapses meanwhile. */
.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-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-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;
}

/* =============================================================================
   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-theme12 .modal-backdrop {
  background-color: var(--surface-dark);
}
body.theme-theme12 .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-theme12 .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-theme12 .modal-title {
  font-family: var(--font-bold);
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--ink-hi);
}
body.theme-theme12 .modal-body {
  padding: var(--space-4);
}
body.theme-theme12 .modal-footer {
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--line);
}
body.theme-theme12 .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-theme12 .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-theme12 .btn-close::before,
body.theme-theme12 .btn-close::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
}
body.theme-theme12 .btn-close::before { transform: translate(-50%, -50%) rotate(45deg); }
body.theme-theme12 .btn-close::after  { transform: translate(-50%, -50%) rotate(-45deg); }
body.theme-theme12 .btn-close:hover {
  color: var(--ink-hi);
  background: var(--surface-hover);
  opacity: 1;
}
body.theme-theme12 .btn-close:focus {
  box-shadow: none;
}
body.theme-theme12 .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-theme12 .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-theme12 .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-theme12 .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-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); } }

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

@media (prefers-reduced-motion: reduce) {
  body.theme-theme12 *,
  body.theme-theme12 *::before,
  body.theme-theme12 *::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;
  }
}

/* =============================================================================
   THEME12 · LOOK — the VIP mobile-game layer (docs/design/theme12/DESIGN.md).
   Everything above this banner is the template's, restyled only where a token
   value changed (type, radii, motion, the page wash) or a token was added at
   the end of the token block (the gold ramp, sheen, bevels, obsidian card,
   gold line, scrim, bronze fade, sizes). This layer only writes rules.
   Every colour is a token or a color-mix() of tokens; no literal colour, no
   seed and no layout token is declared here.
   The CONTRAST tables at the top of this file are the template's, measured at
   its example seeds. theme12's own figures: DESIGN.md §2 (the gate's table)
   and docs/design/theme12/renders/audit-mock.json (every text node, every
   preset). Solid fills keep the token pairs the tools score as their
   background-color, and every gold face that carries a label is the flat
   accent (its ink is bestInk of exactly that colour, in every palette): the
   metal is drawn at the edges only — a top glint, a bronze lip under the
   face and the accent glow — never as a gradient behind text. The obsidian
   card gradient lightens toward the light pole and stays under 4.5:1 of no
   ink rung that sits on it.
   ========================================================================== */

/* ── Type: Mitr for display (--font-bold), Noto Sans Thai for body and every
      figure (--font-num, tabular by default). Headings take the display
      leading; running Thai copy keeps --leading-thai. ── */
html :where(body.theme-theme12) :is(h1, h2, h3, h4) {
  line-height: var(--leading-display);
}
.u-num.u-num {
  font-variant-numeric: tabular-nums lining-nums;
}

/* ── Buttons ── */
.c-btn {
  border-radius: var(--radius-pill);
  font-weight: 500;
  line-height: var(--leading-display);
}
.c-btn--sm {
  border-radius: var(--radius-pill);
}
.c-btn--primary {
  color: var(--ink-on-gold);
  background-color: var(--surface-gold);
  border-color: transparent;
  box-shadow: var(--gold-lip);
  font-weight: 600;
}
.c-btn--primary:active {
  color: var(--ink-on-gold);
  background-color: var(--surface-gold);
  box-shadow: var(--gold-pressed);
  transform: translateY(3px);
}
.c-btn--danger,
.c-btn--success {
  box-shadow: var(--bevel);
}
.c-btn:active {
  transition-duration: var(--dur-press);
}

/* ── Chips: the selected (held) state is the selected rung, an accent edge and
      a gold check; one-tap money chips are the gold ramp ── */
.c-chip {
  font-family: var(--font-bold);
  font-weight: 500;
}
.c-chip:is(.is-selected, [aria-pressed="true"], [aria-selected="true"]) {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
  border-color: var(--action-fill);
}
.c-chip:is(.is-selected, [aria-pressed="true"])::before {
  content: "✓";
  color: var(--ink-accent);
  font-weight: 700;
}
.c-chip:is([data-amount], [data-lt-stake]) {
  min-width: 0;
  color: var(--ink-on-gold);
  background-color: var(--surface-gold);
  border-color: transparent;
  box-shadow: var(--gold-lip-sm);
  font-family: var(--font-num);
  font-weight: 700;
}
.u-row:has(> .c-chip[data-amount]),
.u-row:has(> .c-chip[data-lt-stake]) {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-2);
}
:is([data-lt-quick], [data-lt-three-sc]) > .c-chip {
  color: var(--ink-on-soft);
  background: var(--action-soft);
  border-color: var(--action-soft-border);
  font-size: var(--text-sm);
}
.c-chip[data-lt-quick-digit] {
  width: var(--tap-min);
  padding: 0;
  border-radius: 50%;
}

/* ── Tabs: underline by default (3 px gold bar); the lottery tabs, the macro
      row and the cashier tabs are a segmented pill track ── */
.c-tab {
  border-bottom-width: 3px;
  font-weight: 500;
}
.c-tab.is-active,
.c-tab[aria-selected="true"] {
  color: var(--ink-hi);
}
:is(.c-tabs[data-lt-tabs], .c-tabs[data-lt-macros], [data-overlay="cashier"] .c-overlay__body > .c-tabs) {
  gap: var(--space-1);
  padding: var(--space-1);
  background: var(--elev-1);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
}
:is(.c-tabs[data-lt-tabs], .c-tabs[data-lt-macros], [data-overlay="cashier"] .c-overlay__body > .c-tabs) > .c-tab {
  flex: 1 0 auto;
  margin: 0;
  padding: 0 var(--space-2);
  border: 0;
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  white-space: nowrap;
}
@media (max-width: 479.98px) {
  [data-overlay="cashier"] .c-overlay__body > .c-tabs > .c-tab [lang="en"] {
    display: none;
  }
}
:is(.c-tabs[data-lt-tabs], .c-tabs[data-lt-macros], [data-overlay="cashier"] .c-overlay__body > .c-tabs) > .c-tab:is(.is-active, [aria-selected="true"]) {
  color: var(--ink-on-gold);
  background-color: var(--surface-gold);
  box-shadow: var(--gold-lip-sm);
}
:is(.c-tabs[data-lt-tabs], .c-tabs[data-lt-macros], [data-overlay="cashier"] .c-overlay__body > .c-tabs) > .c-tab:is(.is-active, [aria-selected="true"]) :is(.u-text-dim, .u-text-mid) {
  color: inherit;
}
/* The chip rails: the market types and the number ranges. */
:is(.c-tabs[data-lt-types], .c-tabs[data-lt-panel-ranges]) {
  gap: var(--space-2);
  border-bottom: 0;
  -webkit-mask-image: linear-gradient(90deg, var(--seed-ink) 86%, transparent);
  mask-image: linear-gradient(90deg, var(--seed-ink) 86%, transparent);
}
:is(.c-tabs[data-lt-types], .c-tabs[data-lt-panel-ranges]) > .c-tab {
  margin: 0;
  padding: 0 var(--space-4);
  color: var(--ink-mid);
  background: var(--elev-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
}
:is(.c-tabs[data-lt-types], .c-tabs[data-lt-panel-ranges]) > .c-tab:is(.is-active, [aria-selected="true"]) {
  color: var(--ink-on-soft);
  background: var(--action-soft);
  border-color: var(--action-fill);
}
.c-tabs[data-lt-panel-ranges] > .c-tab {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
}

/* ── Badges ── */
.c-badge {
  font-family: var(--font-bold);
  font-weight: 500;
  line-height: var(--leading-display);
}
[data-profile-rank] {
  color: var(--ink-on-soft);
  background: var(--action-soft);
  box-shadow: inset 0 0 0 1px var(--action-soft-border);
}

/* ── Surfaces: the obsidian card with a gold hairline and a lit top edge ── */
.c-card {
  background-color: var(--surface-card);
  background-image: var(--obsidian);
  border-color: var(--line-accent);
  box-shadow: var(--edge-lit), var(--shadow-sm);
}
.c-stat {
  border-color: var(--line-accent);
}
.c-stat__value {
  font-weight: 700;
}
.c-panel {
  border-color: var(--line);
}
.c-tile {
  border-color: var(--line-accent);
  box-shadow: var(--edge-lit);
}
.c-avatar {
  color: var(--ink-accent);
  box-shadow: 0 0 0 2px var(--line-gold);
}

/* ── Section heads: a short gold bar before the title ── */
.c-section__head :is(h1, h2, h3),
.c-group-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.c-section__head :is(h1, h2, h3)::before,
.c-group-head::before {
  content: "";
  flex: none;
  width: 4px;
  height: 1.1em;
  border-radius: 2px;
  background-color: var(--action-fill);
  background-image: linear-gradient(180deg, var(--seed-accent), var(--gold-edge));
}
.c-group-head {
  padding-block-end: 0;
  color: var(--ink-hi);
  border-bottom: 0;
  font-size: var(--text-lg);
  font-weight: 600;
}

/* ── Fields: the 2 px bottom edge identifies the field (3:1); the other
      three sides are a decorative hairline ── */
.c-input,
.c-select {
  min-height: var(--field-h);
  border-color: var(--line-strong);
  border-bottom: 2px solid var(--line-field);
}
.c-input[aria-invalid="true"],
.c-select[aria-invalid="true"] {
  border-color: var(--state-danger);
}
.c-label {
  font-family: var(--font-bold);
}
.c-field .u-row > .c-btn {
  flex: none;
  white-space: nowrap;
}
@media (max-width: 767.98px) {
  .u-row.u-wrap > .c-field.u-grow {
    flex: 1 1 calc(50% - var(--space-2));
    min-width: 0;
  }
  .u-row.u-wrap:has(> .c-field.u-grow) > .c-btn {
    flex: 1 1 100%;
  }
  #promo-v2-strip .u-grid-2:has(> .c-btn) {
    grid-template-columns: minmax(0, 1fr);
  }
}
.c-help {
  line-height: var(--leading-thai);
}

/* ── The header (chrome) ── */
.c-header {
  background-color: var(--surface-chrome);
  background-image: linear-gradient(180deg, color-mix(in srgb, var(--ink-bright) 6%, transparent), transparent);
  border-bottom-color: var(--line-accent);
}
.c-header .u-row-between > .u-row:first-child {
  flex: 1 1 auto;
  min-width: 0;
}
.c-header a.u-row {
  min-width: 0;
  font-family: var(--font-bold);
  font-size: var(--text-lg);
  font-weight: 600;
  text-decoration: none;
}
.c-header a.u-row img {
  display: block;
  max-height: 36px;
  width: auto;
}
.c-header .c-btn--sm {
  flex: none;
  padding: 0 var(--space-3);
  white-space: nowrap;
}
.c-header .u-row-between > .u-row:first-child > .c-btn .c-icon {
  width: 24px;
  height: 24px;
}
.c-header .c-btn--ghost .c-icon {
  color: var(--ink-on-chrome-accent);
}
.c-header .c-btn--ghost {
  border-color: var(--line-gold);
}
.c-header [data-overlay-open="drawer"] {
  border-color: transparent;
}
.c-header .c-btn--soft:has(> .c-avatar) {
  padding: 0;
  background: transparent;
  border-color: transparent;
  border-radius: 50%;
}
.c-header .c-stat {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: var(--space-1) var(--space-3);
  background-color: var(--surface-card);
  background-image: var(--obsidian);
  border-radius: var(--radius-sm);
}
.c-header .c-stat__value {
  font-size: var(--text-base);
}
@media (max-width: 479.98px) {
  .c-header .u-row {
    gap: 6px;
  }
  .c-header a.u-row {
    font-size: var(--text-base);
  }
  .c-header .c-stat {
    padding-inline: 10px;
  }
}
@media (max-width: 767.98px) {
  .c-header .c-stat__label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* ── The announcement marquee: an inset pill strip ── */
.c-marquee {
  min-height: 36px;
  margin: var(--space-2) var(--gutter) 0;
  background: var(--elev-1);
  border: 1px solid var(--line-accent);
  border-radius: var(--radius-pill);
  position: relative;
}
.c-marquee::before,
.c-marquee::after {
  content: "";
  position: absolute;
  z-index: 1;
  top: 0;
  bottom: 0;
  width: 28px;
  pointer-events: none;
}
.c-marquee::before {
  left: 0;
  border-radius: var(--radius-pill) 0 0 var(--radius-pill);
  background: linear-gradient(90deg, var(--elev-1), transparent);
}
.c-marquee::after {
  right: 0;
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
  background: linear-gradient(270deg, var(--elev-1), transparent);
}
.c-marquee .c-icon {
  color: var(--ink-accent);
}
.c-marquee > * {
  animation-duration: 32s;
  animation-delay: -4s; /* the line is already in view at load */
}
/* The strip is never a scroll area: it clips. The line pauses under a pointer or focus (WCAG 2.2.2). */
.c-marquee:hover > *,
.c-marquee:focus-within > * {
  animation-play-state: paused;
}

/* ── The category grid (mobile): eight tiles, then search as a full row;
      a pill rail from 768 px ── */
[data-region="category-nav"] {
  padding-block: var(--space-3) var(--space-1);
}
[data-region="category-nav"] .c-tabs.u-scroll-x {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  overflow: visible;
  border-bottom: 0;
}
[data-region="category-nav"] .c-tab {
  flex-direction: column;
  gap: 6px;
  min-height: var(--tile-h);
  margin: 0;
  padding: 10px var(--space-1) var(--space-2);
  color: var(--ink-hi);
  background-color: var(--elev-3);
  background-image: var(--tile-lit);
  border: 1px solid var(--line-accent);
  border-radius: var(--radius);
  box-shadow: var(--bevel-key);
  font-size: var(--text-sm);
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
[data-region="category-nav"] .c-tab .c-icon {
  width: 28px;
  height: 28px;
  color: var(--ink-accent);
}
[data-region="category-nav"] .c-tab:is(.is-active, [aria-current="page"]) {
  color: var(--ink-on-gold);
  background-color: var(--surface-gold);
  background-image: none;
  border-color: transparent;
  box-shadow: var(--gold-lip-sm), var(--glow-accent);
}
[data-region="category-nav"] .c-tab:is(.is-active, [aria-current="page"]) .c-icon {
  color: inherit;
}
[data-region="category-nav"] .c-tab[data-overlay-open="search"] {
  grid-column: 1 / -1;
  flex-direction: row;
  justify-content: flex-start;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-4);
  color: var(--ink-dim);
  background: var(--surface-input);
  border-color: var(--line);
  border-radius: var(--radius-pill);
  box-shadow: none;
  font-family: var(--font-thai);
  font-weight: 400;
}
[data-region="category-nav"] .c-tab[data-overlay-open="search"] .c-icon {
  width: 20px;
  height: 20px;
}
[data-region="category-nav"] .c-tab:active {
  transform: translateY(2px);
  box-shadow: none;
}
@media (min-width: 768px) {
  [data-region="category-nav"] .c-tabs.u-scroll-x {
    display: flex;
    gap: var(--space-2);
    overflow-x: auto;
  }
  [data-region="category-nav"] .c-tab {
    flex-direction: row;
    min-height: var(--tap-min);
    padding: 0 var(--space-4);
    border-radius: var(--radius-pill);
  }
  [data-region="category-nav"] .c-tab .c-icon {
    width: 20px;
    height: 20px;
  }
  [data-region="category-nav"] .c-tab[data-overlay-open="search"] {
    grid-column: auto;
    margin-inline-start: auto;
    min-width: 220px;
  }
}

/* ── The activity ticker (sample data, labelled as such) ── */
.c-ticker {
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-2) var(--gutter);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.c-ticker > .c-data {
  flex-basis: 100%;
}
.c-ticker h2 {
  font-size: var(--text-sm);
  font-weight: 500;
  white-space: nowrap;
}
.c-ticker [data-ticker-item] {
  min-height: 30px;
  color: var(--ink-mid);
  background: var(--elev-2);
  border-color: var(--line);
  cursor: default;
  font-family: var(--font-thai);
  font-weight: 400;
}
.c-ticker [data-ticker-item] .u-num {
  color: var(--ink-hi);
  font-weight: 600;
}

/* ── Chrome rows above the feed keep the feed's ceiling on a wide screen ── */
:is([data-region="category-nav"], [data-region="activity-ticker"], #promo-v2-strip, [data-region="marquee"]) {
  max-width: var(--feed-max);
  margin-inline: auto;
}
@media (min-width: 1248px) {
  :is([data-region="activity-ticker"], [data-region="marquee"]) {
    margin-inline: auto;
  }
}
#promo-v2-strip {
  margin-inline: var(--gutter);
}
@media (min-width: 1248px) {
  #promo-v2-strip {
    margin-inline: auto;
  }
}

/* ── The hero ── */
.c-banner {
  border: 1px solid var(--line-accent);
}
.c-banner.c-card {
  background-color: var(--surface-card);
  background-image: radial-gradient(420px 220px at 100% 0%, color-mix(in srgb, var(--seed-accent) 22%, transparent), transparent 70%), var(--obsidian);
  border-color: var(--line-gold);
}
.c-banner.c-card .u-stack-tight > span {
  font-family: var(--font-bold);
  font-size: var(--text-xl);
  font-weight: 600;
}
.c-banner.c-card .u-stack-tight > strong {
  color: var(--ink-accent);
  font-family: var(--font-bold);
  font-size: var(--text-2xl);
  font-weight: 600;
}
.c-banner.c-card > span.c-chip {
  align-self: flex-start;
  min-height: 32px;
  color: var(--ink-on-soft);
  background: var(--action-soft);
  border-color: var(--action-soft-border);
  cursor: default;
}
.c-chip[data-banner-dot] {
  position: relative;
  min-width: 32px;
  padding: 0;
  background: transparent;
  border: 0;
}
.c-chip[data-banner-dot]::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--line-strong);
  transition: width var(--dur) var(--ease-out);
}
.c-chip[data-banner-dot]:is(.is-active, [aria-current="true"])::after {
  width: 22px;
  background-color: var(--action-fill);
  background-image: var(--grad-gold);
}

/* ── Tiles: providers (logo stage + name) and games (art + caption) ── */
.c-tiles {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}
.c-tiles[data-games-list] {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 768px) {
  .c-tiles {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
  .c-tiles[data-games-list] {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
.c-tile[data-card="provider"] {
  align-items: stretch;
  gap: var(--space-2);
  padding: var(--space-2);
  border-top-color: var(--line-gold);
  font-family: var(--font-bold);
}
.c-tile[data-card="provider"] img {
  aspect-ratio: 16 / 9;
  object-fit: contain;
  padding: var(--space-2);
  background: var(--elev-1);
}
.u-scroll-x > .c-tile[data-card="provider"] {
  width: 156px;
}
.c-tile[data-card="game"] {
  justify-content: flex-start;
  gap: var(--space-1);
  padding: 0 0 var(--space-2);
  overflow: hidden;
  border-radius: 12px;
}
.c-tile[data-card="game"] img {
  aspect-ratio: 5 / 6;
  border-radius: 0;
}
.c-tile[data-card="game"] .c-badge {
  padding: 0 var(--space-2);
  color: var(--ink-dim);
  background: transparent;
  font-size: var(--text-xs);
}
.c-tile[data-card="game"] [data-f="name"] {
  max-width: 100%;
  padding: 0 var(--space-2);
  color: var(--ink-hi);
  font-family: var(--font-bold);
}
@media (hover: hover) {
  .c-tile:hover {
    border-color: var(--line-gold);
    transform: translateY(-2px);
  }
}

/* ── Lottery: the three tabs and the market cards ── */
.c-tabs[data-lt-tabs] > .c-tab .c-icon {
  width: 16px;
  height: 16px;
}
[data-lt-quickmenu] > .c-tile {
  align-items: flex-start;
  justify-content: flex-start;
  min-height: 112px;
  padding: var(--space-3);
  text-align: start;
  background-color: var(--elev-3);
  background-image: var(--tile-lit);
}
[data-lt-quickmenu] > .c-tile strong {
  font-family: var(--font-bold);
  font-weight: 600;
  line-height: var(--leading-display);
}
[data-lt-quickmenu] > .c-tile .u-text-dim {
  font-size: var(--text-xs);
}
:is([data-card="lottery"], [data-lt-card]) {
  gap: 10px;
  padding: var(--space-3);
}
.u-scroll-x > [data-card="lottery"] {
  width: 260px;
}
:is([data-card="lottery"], [data-lt-card]) h3 {
  font-size: var(--text-base);
}
:is([data-card="lottery"] [data-f="flag"], [data-lt-card] [data-f="flag"]) {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  color: var(--ink-accent);
  background: var(--elev-4);
  box-shadow: 0 0 0 2px var(--line-gold);
  font-family: var(--font-bold);
  font-size: var(--text-xs);
}
[data-lt-card] > .c-btn {
  width: 100%;
}
.u-grid-auto.u-grid-auto:is([data-lt-all-list], [data-lt-list]) {
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
@media (min-width: 768px) {
  .u-grid-auto.u-grid-auto:is([data-lt-all-list], [data-lt-list]) {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  }
}
[data-lt-card] > .u-row:first-child > h3 {
  flex-basis: 0;
}
[data-lt-card] > .u-row:first-child {
  flex-wrap: wrap;
}
[data-lt-card] [data-f="live"] {
  order: 3;
  flex-basis: 100%;
  justify-content: flex-start;
  white-space: nowrap;
  min-height: 24px;
  padding: 0 var(--space-2);
  color: var(--ink-dim);
  background: var(--elev-4);
  border-color: transparent;
  cursor: default;
  font-family: var(--font-thai);
  font-size: var(--text-xs);
  font-weight: 400;
}
[data-lt-card]:is([data-lt-state="closed"], [data-lt-state="maint"]) {
  background-image: none;
  filter: grayscale(1);
}
.c-countdown {
  padding: 2px var(--space-2);
  background: var(--elev-4);
  border: 1px solid var(--line-accent);
  border-radius: var(--radius-xs);
  font-size: var(--text-base);
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: var(--leading-num);
}

/* ── The buy sheet ── */
:is([data-lt-buffer], [data-lt-win-digits]) {
  min-height: 64px;
  color: var(--ink-hi);
  background: var(--surface-input);
  border: 1px solid var(--line);
  border-bottom: 2px solid var(--line-input);
  border-radius: var(--radius-sm);
  font-size: var(--text-3xl);
  font-weight: 700;
  letter-spacing: 0.3em;
}
[data-lt-win-digits] {
  font-size: var(--text-xl);
  letter-spacing: 0.08em;
}
.c-numpad {
  gap: 10px;
}
.c-numpad__key {
  min-height: var(--key-h);
  background-color: var(--surface-raised);
  background-image: var(--key-lit);
  border-color: var(--line);
  box-shadow: var(--bevel-key);
  font-size: var(--text-2xl);
  font-weight: 700;
}
.c-numpad__key:is([data-lt-key-del], [data-lt-key-clear], [data-lt-win-del], [data-lt-win-clear]) {
  color: var(--ink-mid);
  background-color: var(--elev-2);
  background-image: none;
  font-family: var(--font-bold);
  font-size: var(--text-base);
  font-weight: 500;
}
.c-numpad__key[aria-pressed="true"] {
  color: var(--ink-on-gold);
  background-color: var(--surface-gold);
  background-image: none;
  border-color: transparent;
  box-shadow: var(--gold-lip-sm);
}
.c-numpad__key:active {
  box-shadow: none;
  transform: translateY(2px);
}
.c-numgrid {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-2);
}
@media (min-width: 768px) {
  .c-numgrid {
    grid-template-columns: repeat(10, minmax(0, 1fr));
  }
}
.c-numgrid__cell {
  min-height: var(--cell-h);
  color: var(--ink-mid);
  background: var(--elev-3);
  border-color: var(--line);
}
.c-numgrid__cell:is(.is-selected, .is-active, [aria-selected="true"], [aria-pressed="true"]) {
  color: var(--ink-on-gold);
  background-color: var(--surface-gold);
  border-color: transparent;
  box-shadow: var(--gold-lip-sm);
}
.c-numgrid__cell:is(:disabled, [aria-disabled="true"], .is-disabled) {
  background-color: var(--surface-disabled);
  background-image: repeating-linear-gradient(135deg, var(--line-strong) 0 1px, transparent 1px 7px);
}
[data-lt-win-filters] > span.u-row {
  min-height: var(--tap-min);
  padding: 0 var(--space-4) 0 var(--space-3);
  background: var(--elev-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
}
[data-lt-win-filters] > span:has(> input:checked) {
  color: var(--ink-on-soft);
  background: var(--action-soft);
  border-color: var(--action-fill);
}
[data-lt-win-filters] input {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--action-fill);
}
[data-lt-win-filters] label {
  font-family: var(--font-bold);
  font-size: var(--text-sm);
  cursor: pointer;
}
details.c-panel > summary {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  color: var(--ink-accent);
  font-family: var(--font-bold);
  cursor: pointer;
}
.c-table thead th {
  font-family: var(--font-bold);
  font-size: var(--text-xs);
  font-weight: 500;
}
.c-table tbody tr:nth-child(even) {
  background: var(--elev-1);
}
.c-table [data-f="rate"] {
  color: var(--ink-accent);
  font-weight: 700;
}

/* ── Overlays ── */
.c-overlay {
  background: var(--scrim);
}
.c-overlay__panel {
  border-color: var(--line-accent);
}
.c-overlay__close {
  border-radius: 50%;
  background: var(--elev-4);
}
.c-overlay__foot {
  border-top-color: var(--line-accent);
}
.c-overlay__foot.u-sticky-bottom {
  background: var(--elev-4);
  box-shadow: 0 -12px 24px color-mix(in srgb, var(--seed-ink) 35%, transparent);
}
@media (max-width: 767.98px) {
  .c-overlay--sheet:not(:has(> .c-drawer)) .c-overlay__panel {
    position: relative;
  }
  .c-overlay--sheet:not(:has(> .c-drawer)) .c-overlay__panel::before {
    content: "";
    position: absolute;
    top: 6px;
    left: 50%;
    width: 36px;
    height: 4px;
    margin-left: -18px;
    border-radius: 2px;
    background: var(--line-strong);
  }
}

/* ── The drawer: a left slide-in panel on the darkest panel rung ── */
.c-overlay--sheet:has(> .c-drawer) {
  align-items: stretch;
  justify-content: flex-start;
  padding: 0;
}
.c-overlay--sheet > .c-overlay__panel.c-drawer {
  width: 86%;
  max-width: 360px;
  height: 100%;
  max-height: none;
  background: var(--elev-1);
  border: 0;
  border-inline-end: 1px solid var(--line-accent);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  animation: t12-drawer-in var(--dur) var(--ease-out);
}
.c-drawer .c-overlay__close {
  color: var(--ink-accent);
  background: transparent;
  box-shadow: inset 0 0 0 2px var(--line-gold);
}
.c-drawer .c-list {
  gap: 6px;
}
.c-drawer .c-list__item.c-btn {
  justify-content: flex-start;
  gap: var(--space-3);
  min-height: 52px;
  padding: 0 14px;
  color: var(--ink-hi);
  background-color: transparent;
  background-image: var(--fade-bronze);
  border: 0;
  border-radius: var(--radius-sm);
}
.c-drawer .c-list__item .c-icon {
  width: 22px;
  height: 22px;
  color: var(--ink-accent);
}
/* The desktop sidebar takes the drawer's rows. */
[data-region="sidebar"] .c-btn--ghost.c-btn--block {
  justify-content: flex-start;
  gap: var(--space-3);
  padding: 0 var(--space-3);
  color: var(--ink-hi);
  background-color: transparent;
  background-image: var(--fade-bronze);
  border: 0;
  border-radius: var(--radius-sm);
}
[data-region="sidebar"] .c-btn--ghost .c-icon {
  color: var(--ink-accent);
}
[data-region="sidebar"] .c-btn--ghost.c-btn--block:is(.is-active, [aria-current="page"]) {
  color: var(--ink-on-selected);
  background-color: var(--surface-selected);
  box-shadow: inset 3px 0 0 var(--action-fill);
}
.c-drawer .c-btn--danger {
  color: var(--ink-danger);
  background: transparent;
  border: 1px solid var(--state-danger);
  box-shadow: none;
}

/* ── The bottom tab bar and its raised centre action (the home cell) ── */
.c-bottom-nav {
  min-height: var(--bottomnav-h);
  background-color: var(--surface-chrome);
  background-image: linear-gradient(180deg, color-mix(in srgb, var(--seed-accent) 10%, transparent), transparent 70%);
  border-top-color: var(--line-accent);
  box-shadow: 0 -10px 28px color-mix(in srgb, var(--seed-ink) 50%, transparent);
}
.c-bottom-nav__item {
  gap: 4px;
  font-family: var(--font-bold);
  font-weight: 500;
}
.c-bottom-nav__item .c-icon {
  width: 24px;
  height: 24px;
  color: var(--ink-on-chrome-accent);
}
.c-bottom-nav__item[data-view-target="home"] {
  justify-content: flex-end;
  padding-bottom: 6px;
}
.c-bottom-nav__item[data-view-target="home"]::before {
  display: none;
}
.c-bottom-nav__item[data-view-target="home"]::after {
  content: "";
  position: absolute;
  z-index: -1;
  top: calc(var(--fab-lift) * -1);
  left: 50%;
  width: var(--fab-size);
  height: var(--fab-size);
  margin-left: calc(var(--fab-size) / -2);
  border: 3px solid transparent;
  border-radius: 50%;
  background: var(--fab-core) padding-box, var(--grad-gold) border-box;
  box-shadow: var(--glow-accent), 0 6px 16px color-mix(in srgb, var(--seed-ink) 60%, transparent);
  transition: transform var(--dur-press) var(--ease-spring);
}
.c-bottom-nav__item[data-view-target="home"] .c-icon {
  position: absolute;
  top: calc(var(--fab-lift) * -1 + (var(--fab-size) - 28px) / 2);
  left: 50%;
  width: 28px;
  height: 28px;
  margin-left: -14px;
}
.c-bottom-nav__item[data-view-target="home"]:active::after {
  transform: scale(0.94);
}

/* ── The footer: chrome bento tiles, stacked on a phone ── */
.c-footer {
  border-top: 1px solid var(--line-accent);
}
.c-footer .u-grid-3 > * {
  padding: var(--space-4);
  background: color-mix(in srgb, var(--ink-on-chrome) 4%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink-on-chrome) 10%, transparent);
  border-radius: var(--radius);
}
.c-footer h2 {
  color: var(--ink-on-chrome);
  font-size: var(--text-base);
}
@media (max-width: 767.98px) {
  .c-footer .u-grid-3.u-grid-3 {
    grid-template-columns: minmax(0, 1fr);
  }
}
/* The provider chips (data-tpl="footer-provider", filled by the data layer): a long provider name
   ends in an ellipsis inside its chip and never widens the page (P1-CSS C4, the footer's half). */
.c-footer .c-chip[data-card="provider"] {
  max-width: 100%;
  min-width: 0;
}
.c-footer .c-chip[data-card="provider"] > [data-f="name"] {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── The slip ── */
.c-slip {
  margin: 0;
  padding: 0;
  list-style: none;
  border-color: var(--line-accent);
}
.c-slip__line {
  min-height: 56px;
}
.c-slip__line > .u-num:first-child {
  flex: none;
  min-width: 52px;
  padding: 2px var(--space-2);
  color: var(--ink-hi);
  border: 2px solid var(--line-gold);
  border-radius: var(--radius-pill);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-align: center;
}
.c-slip__line .c-field {
  flex: none;
}
.c-slip__line .c-input {
  width: 84px;
  text-align: end;
}
.c-slip__line > .u-num:not(:first-child) {
  color: var(--ink-mid);
  font-size: var(--text-sm);
}
.c-slip__line [data-lt-line-del] {
  color: var(--ink-danger);
  border-color: transparent;
  border-radius: 50%;
  padding: 0;
  font-size: 24px;
}
@media (max-width: 767.98px) {
  .c-slip__line.u-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "num info del" "num price win";
    align-items: center;
    column-gap: var(--space-3);
    row-gap: var(--space-1);
    padding-block: var(--space-2);
  }
  .c-slip__line.u-row > .u-num:first-child { grid-area: num; }
  .c-slip__line.u-row > .u-stack-tight { grid-area: info; }
  .c-slip__line.u-row > .c-field { grid-area: price; }
  .c-slip__line.u-row > .u-num:not(:first-child) { grid-area: win; justify-self: end; }
  .c-slip__line.u-row > [data-lt-line-del] { grid-area: del; justify-self: end; }
  .c-slip__line.u-row .c-input { width: 120px; }
}
dl.c-slip__total {
  align-items: stretch;
}
.c-slip__total {
  border-top-color: var(--line-accent);
}
.c-slip__total dd {
  margin: 0;
}
.c-slip__total .u-num {
  color: var(--ink-hi);
}
.c-slip__total [data-lt-cart-discount-row] .u-num {
  color: var(--ink-success);
}
.c-slip__total > .u-row-between:last-child .u-num {
  font-size: var(--text-xl);
  text-decoration: underline 2px var(--line-gold);
  text-underline-offset: 6px;
}
[data-lt-cart-error]:not(:empty) {
  color: var(--ink-danger);
}

/* ── The cashier ── */
[data-overlay="cashier"] .c-overlay__head .c-chip {
  min-height: 32px;
  color: var(--ink-accent);
  background: var(--elev-4);
  border-color: var(--line-accent);
  cursor: default;
  font-family: var(--font-num);
  font-weight: 700;
}
[data-deposit-methods] {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
[data-deposit-methods] > .c-tile {
  min-height: 64px;
  font-family: var(--font-bold);
}
[data-deposit-methods] > .c-tile[aria-checked="true"] {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
  border-color: var(--action-fill);
}
[data-form="deposit"] input[name="amount"] {
  min-height: 56px;
  font-family: var(--font-num);
  font-size: var(--text-xl);
  font-weight: 700;
  text-align: center;
}

/* ── The account panel ── */
[data-overlay="account"] [data-display="account"] .c-avatar {
  width: 56px;
  height: 56px;
  font-size: var(--text-xl);
}
[data-profile-username] {
  font-family: var(--font-bold);
  font-size: var(--text-lg);
  font-weight: 600;
}
[data-overlay="account"] .u-grid-3 > .c-btn--soft {
  min-height: 56px;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius);
  font-size: var(--text-sm);
}

/* ── ส่งโพยสำเร็จ: the success emblem and the notched receipt ── */
[data-overlay="buy-success"] .c-overlay__body > .c-badge--success {
  align-self: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: var(--space-2) 0 0;
  background: transparent;
  font-size: var(--text-lg);
}
[data-overlay="buy-success"] .c-overlay__body > .c-badge--success::before {
  content: "✓";
  display: grid;
  place-items: center;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  color: var(--ink-on-success);
  background: var(--fill-success);
  box-shadow: 0 0 0 3px var(--line-gold), var(--glow-accent);
  font-family: var(--font-num);
  font-size: 36px;
  font-weight: 700;
  animation: t12-pop var(--dur-slow) var(--ease-spring);
}
[data-overlay="buy-success"] .c-slip {
  -webkit-mask: radial-gradient(circle 8px at 0 50%, transparent 98%, var(--seed-ink)) left / 51% 100% no-repeat, radial-gradient(circle 8px at 100% 50%, transparent 98%, var(--seed-ink)) right / 51% 100% no-repeat;
  mask: radial-gradient(circle 8px at 0 50%, transparent 98%, var(--seed-ink)) left / 51% 100% no-repeat, radial-gradient(circle 8px at 100% 50%, transparent 98%, var(--seed-ink)) right / 51% 100% no-repeat;
}
[data-overlay="buy-success"] .c-slip__total .u-num {
  color: var(--ink-hi);
  font-size: var(--text-2xl);
}

/* ── Data states ── */
.c-state--empty .u-center.u-pad {
  gap: 10px;
  padding-block: var(--space-6);
}
.c-state--empty .u-center > .c-icon {
  box-sizing: content-box;
  width: 40px;
  height: 40px;
  padding: 24px;
  border-radius: 50%;
  color: var(--ink-accent);
  background: var(--elev-4);
  box-shadow: 0 0 0 2px var(--line-gold), var(--glow-accent);
}
.c-state--empty .u-center > .u-text-mid {
  color: var(--ink-hi);
  font-family: var(--font-bold);
  font-size: var(--text-lg);
}
.c-state--empty .u-center > .c-btn {
  margin-top: var(--space-2);
}

/* ── Hover (pointer devices only) — kept after the base rules above ── */
@media (hover: hover) {
  .c-btn--primary:hover {
    color: var(--ink-on-gold);
    background-color: var(--surface-gold);
    box-shadow: var(--gold-lip), var(--glow-accent);
    transform: translateY(-1px);
  }
  .c-chip:is([data-amount], [data-lt-stake]):hover,
  .c-numgrid__cell:is(.is-selected, [aria-pressed="true"]):hover,
  .c-numpad__key[aria-pressed="true"]:hover,
  [data-region="category-nav"] .c-tab:is(.is-active, [aria-current="page"]):hover {
    color: var(--ink-on-gold);
    background-color: var(--surface-gold);
    background-image: none;
    box-shadow: var(--gold-lip-sm), var(--glow-accent);
  }
  :is([data-lt-quick], [data-lt-three-sc]) > .c-chip:hover {
    color: var(--ink-on-soft);
    background: var(--action-soft-hover);
  }
  [data-region="category-nav"] .c-tab:not(.is-active):not([aria-current="page"]):not([data-overlay-open="search"]):hover {
    color: var(--ink-hi);
    background-color: var(--elev-hover);
    border-color: var(--line-gold);
  }
  [data-region="sidebar"] .c-btn--ghost.c-btn--block:not(.is-active):not([aria-current="page"]):hover {
    color: var(--ink-hi);
    background-color: var(--elev-hover);
    background-image: var(--fade-bronze);
  }
  .c-drawer .c-list__item.c-btn:hover {
    color: var(--ink-hi);
    background-color: var(--elev-hover);
    background-image: var(--fade-bronze);
  }
  .c-numpad__key:not([aria-pressed="true"]):hover {
    background-image: none;
  }
}

/* ── Disabled always wins over the gold and the hover lift ── */
.c-btn:is(:disabled, [aria-disabled="true"], .is-disabled),
.c-chip:is(:disabled, [aria-disabled="true"], .is-disabled),
.c-tab:is(:disabled, [aria-disabled="true"], .is-disabled),
.c-tile:is([aria-disabled="true"], .is-disabled),
.c-numpad__key:is(:disabled, [aria-disabled="true"]) {
  background-image: none;
  box-shadow: none;
  transform: none;
}
.c-chip:is([data-amount], [data-lt-stake]):is(:disabled, [aria-disabled="true"], .is-disabled) {
  color: var(--ink-on-disabled);
  background-color: var(--surface-disabled);
}
.c-tab:is(:disabled, [aria-disabled="true"], .is-disabled) {
  background: transparent;
}

/* ── Focus. The outline takes the accent INK, not the raw accent: it moves toward fg, so it holds 3:1 on
      every canvas rung in every palette (the raw blue accent of preset 2 was 2.79 on a raised foot). On the
      chrome it takes the chrome accent ink; on a gold face, where a tab draws it inside (-2 px), the gold
      face's own ink. Gold buttons draw it outside the bevel. ── */
body.theme-theme12 :focus-visible {
  outline-color: var(--ink-accent);
}
body.theme-theme12 :is(.c-header, .c-footer, .c-bottom-nav) :focus-visible {
  outline-color: var(--ink-on-chrome-accent);
}
.c-input:is(:focus, :focus-visible),
.c-select:is(:focus, :focus-visible) {
  outline-color: var(--ink-accent);
}
:is(.c-tabs[data-lt-tabs], .c-tabs[data-lt-macros], [data-overlay="cashier"] .c-overlay__body > .c-tabs) > .c-tab:is(.is-active, [aria-selected="true"]):focus-visible,
[data-region="category-nav"] .c-tab:is(.is-active, [aria-current="page"]):focus-visible {
  outline-color: var(--ink-on-gold);
}
.c-btn--primary:focus-visible,
.c-chip:is([data-amount], [data-lt-stake]):focus-visible {
  outline-offset: 3px;
}

/* ── Scrollbars: every scroll area that shows one gets a thin bar coloured from the seeds
      (scrollbar-color inherits from body; scrollbar-width does not, so the areas name it). The
      navigation rails and the activity ticker keep the template's hidden bar; the marquee clips. ── */
body.theme-theme12 {
  scrollbar-color: var(--scroll-thumb) var(--scroll-track);
}
:is(.c-overlay__panel, .c-overlay__body, .c-drawer, .c-panel, [data-shelf-list], .u-scroll-x:has(> .c-table)) {
  scrollbar-width: thin;
}
[data-shelf-list].u-scroll-x.u-scroll-x,
.u-scroll-x.u-scroll-x:has(> .c-table) {
  scrollbar-width: thin;
  padding-bottom: var(--space-1);
}
.c-overlay__panel::-webkit-scrollbar,
.c-overlay__body::-webkit-scrollbar,
.c-drawer::-webkit-scrollbar,
.c-panel::-webkit-scrollbar,
[data-shelf-list]::-webkit-scrollbar,
.u-scroll-x:has(> .c-table)::-webkit-scrollbar {
  display: block;
  width: 8px;
  height: 8px;
}
.c-overlay__panel::-webkit-scrollbar-thumb,
.c-overlay__body::-webkit-scrollbar-thumb,
.c-drawer::-webkit-scrollbar-thumb,
.c-panel::-webkit-scrollbar-thumb,
[data-shelf-list]::-webkit-scrollbar-thumb,
.u-scroll-x:has(> .c-table)::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border-radius: var(--radius-pill);
}
.c-overlay__panel::-webkit-scrollbar-track,
.c-overlay__body::-webkit-scrollbar-track,
.c-drawer::-webkit-scrollbar-track,
.c-panel::-webkit-scrollbar-track,
[data-shelf-list]::-webkit-scrollbar-track,
.u-scroll-x:has(> .c-table)::-webkit-scrollbar-track {
  background: var(--scroll-track);
}

/* ── Motion ── */
@keyframes t12-drawer-in { from { transform: translateX(-100%); } to { transform: none; } }
@keyframes t12-pop       { from { opacity: 0; transform: scale(0.6); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .c-btn--primary:hover,
  .c-tile:hover {
    transform: none;
  }
  /* The template turns the still marquee into a horizontal scroll strip here (overflow-x: auto), which
     shows the browser's default scrollbar. theme12 keeps it a clipped line: still, ellipsised, no bar. */
  .c-marquee {
    overflow: hidden;
  }
  .c-marquee > p {
    flex: 1 1 auto;
    min-width: 0;
  }
  .c-marquee > p > span:first-of-type {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .c-marquee > p > span[aria-hidden="true"] {
    display: none;
  }
  [data-overlay="buy-success"] .c-overlay__body > .c-badge--success::before {
    animation: none !important;
  }
}

/* =============================================================================
   THEME12 · BUILD — the template markup the approved mock did not carry (TPL-LIFT's
   P1-CSS in docs/themelog/_theme-templates.md, and the parts of the real partials the
   mock simplified), in theme12's tokens. One part per BUILD stage; the LOG rows of
   docs/themelog/theme12.md give each rule's reason.
   ========================================================================== */

/* ── Stage 4 · home ── */
/* The shelf heads: a phone swipes the shelf, so the carousel arrows give their room to the title (the
   approved mock's head: title, LIVE, ดูทั้งหมด). From 768 px they are back. The buttons stay in the
   markup for games.js. */
@media (max-width: 767.98px) {
  .c-section__head > .u-row-end > :is([data-carousel-prev], [data-carousel-next]) {
    display: none;
  }
}
/* A provider or game name never widens its tile or the page (P1-CSS C4). */
.c-tile > [data-f="name"] {
  max-width: 100%;
}
/* "No market open" while closed cards still show (data-shelf-none on the home shelf, data-lt-none-note
   on the hub): a quiet notice line above the cards. */
:is([data-shelf-none], [data-lt-none-note]) {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  color: var(--ink-mid);
  background: var(--elev-1);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  text-align: center;
}
/* Empty states with an emblem (DESIGN.md "Per data surface": the market list and the game or provider
   grid; A29): a gold-ringed disc with a gold line glyph above the template's one-line text, drawn here
   so the template's markup stays as it is. The home shelves keep their one line. */
:is([data-display^="lottery-markets-"], [data-display="games"]) > .c-state--empty {
  position: relative;
  padding-top: calc(var(--space-5) + 88px + var(--space-3));
  color: var(--ink-mid);
  font-size: var(--text-base);
}
:is([data-display^="lottery-markets-"], [data-display="games"]) > .c-state--empty::before,
:is([data-display^="lottery-markets-"], [data-display="games"]) > .c-state--empty::after {
  content: "";
  position: absolute;
  left: 50%;
  border-radius: 50%;
  transform: translateX(-50%);
}
:is([data-display^="lottery-markets-"], [data-display="games"]) > .c-state--empty::before {
  top: var(--space-5);
  width: 88px;
  height: 88px;
  background: var(--elev-4);
  box-shadow: 0 0 0 2px var(--line-gold), var(--glow-accent);
}
:is([data-display^="lottery-markets-"], [data-display="games"]) > .c-state--empty::after {
  top: calc(var(--space-5) + 24px);
  width: 40px;
  height: 40px;
  border-radius: 0;
  background-color: var(--ink-accent);
}
[data-display^="lottery-markets-"] > .c-state--empty::after {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9V6.5A1.5 1.5 0 0 1 4.5 5h15A1.5 1.5 0 0 1 21 6.5V9a2.5 2.5 0 0 0 0 5v2.5a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 16.5V14a2.5 2.5 0 0 0 0-5Z'/%3E%3Cpath d='M14 5v14' stroke-dasharray='2 2.6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 9V6.5A1.5 1.5 0 0 1 4.5 5h15A1.5 1.5 0 0 1 21 6.5V9a2.5 2.5 0 0 0 0 5v2.5a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 16.5V14a2.5 2.5 0 0 0 0-5Z'/%3E%3Cpath d='M14 5v14' stroke-dasharray='2 2.6'/%3E%3C/svg%3E") center / contain no-repeat;
}
[data-display="games"] > .c-state--empty::after {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='11' height='15' rx='1.8'/%3E%3Cpath d='M16.5 4.5 20 5.6c.8.25 1.25 1.1 1 1.9L18 18'/%3E%3Cpath d='M8.5 10c.9-1.2 2.6-.6 2.6.8 0 1.2-1.5 2.1-2.6 3.2-1.1-1.1-2.6-2-2.6-3.2 0-1.4 1.7-2 2.6-.8Z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='11' height='15' rx='1.8'/%3E%3Cpath d='M16.5 4.5 20 5.6c.8.25 1.25 1.1 1 1.9L18 18'/%3E%3Cpath d='M8.5 10c.9-1.2 2.6-.6 2.6.8 0 1.2-1.5 2.1-2.6 3.2-1.1-1.1-2.6-2-2.6-3.2 0-1.4 1.7-2 2.6-.8Z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* ── Stage 5 · lottery ── */
/* The flag slot (kit.js flag(): the lottery's flag picture as an <img>, else the ticket glyph) is a disc:
   the picture is cut to the 36 px circle inside its gold ring (DESIGN.md 9.2), on market cards, shelf cards
   and result cards alike. */
.c-icon[data-f="flag"] {
  overflow: hidden;
}
.c-icon[data-f="flag"] > img {
  object-fit: cover;
  border-radius: 50%;
}
/* LOTTO_CARD_STYLE 2 — the split market card (lottery.js clones lt-market-split; P1-CSS C1): the banner
   column at the start, beside the card's three rows (name, state, แทง). The banner picture covers its
   column; with none, or a file that fails, the column shows the placeholder ticket on a dark well. */
.c-card[data-lt-card-split] {
  display: grid;
  grid-template-columns: var(--lt-banner-w) minmax(0, 1fr);
  grid-template-rows: auto auto 1fr;
  column-gap: var(--space-3);
  row-gap: var(--space-2);
  align-items: start;
}
.c-card[data-lt-card-split] > [data-lt-banner] {
  grid-column: 1;
  grid-row: 1 / span 3;
  align-self: stretch;
  position: relative;
  display: grid;
  place-items: center;
  min-height: var(--lt-banner-w);
  overflow: hidden;
  color: var(--ink-accent);
  background: var(--elev-1);
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px var(--line-accent);
}
.c-card[data-lt-card-split] > [data-lt-banner] > .c-icon {
  width: 32px;
  height: 32px;
}
.c-card[data-lt-card-split] > [data-lt-banner] > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.c-card[data-lt-card-split] > :not([data-lt-banner]) {
  grid-column: 2;
  min-width: 0;
}
.c-card[data-lt-card-split] > .u-row:last-child {
  align-self: end;
}
.c-card[data-lt-card-split] > .u-row:last-child > .c-btn {
  flex: 1 1 auto;
}
/* The split hub's market track (lottery.js marks the hub data-lt-card-style="split"; P1-CSS C2): one card
   per row on a phone, then as many --market-min cards as fit. */
[data-lt-card-style="split"] .u-grid-auto.u-grid-auto:is([data-lt-list], [data-lt-all-list]) {
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 768px) {
  [data-lt-card-style="split"] .u-grid-auto.u-grid-auto:is([data-lt-list], [data-lt-all-list]) {
    grid-template-columns: repeat(auto-fill, minmax(var(--market-min), 1fr));
  }
}

/* โพยหวย ticket rows (DESIGN.md 9.12): the blueprint's ghost button becomes the ticket row, elev-2, r 14,
   72 px, the market name and its time on the start side, the total and the status badge on the end side,
   a chevron last. The status classes are the data layer's (lottery.js badge()). */
[data-lt-poy-list] > .c-list__item {
  padding: 0;
  background: none;
  border: 0;
}
[data-lt-poy-list] > .c-list__item + .c-list__item {
  margin-top: var(--space-2);
}
[data-lt-poy-list] .c-btn[data-poy-trx] {
  gap: var(--space-3);
  min-height: 72px;
  padding: var(--space-3) var(--space-4);
  color: var(--ink-hi);
  background: var(--elev-2);
  border: 1px solid var(--line-accent);
  border-radius: var(--radius);
  box-shadow: var(--edge-lit);
  font-family: var(--font-thai);
  font-weight: 400;
  text-align: start;
  white-space: normal;
}
[data-lt-poy-list] .c-btn[data-poy-trx]::after {
  content: "";
  flex: none;
  width: 16px;
  height: 16px;
  background-color: var(--ink-dim);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 5 7 7-7 7'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 5 7 7-7 7'/%3E%3C/svg%3E") center / contain no-repeat;
}
@media (hover: hover) {
  [data-lt-poy-list] .c-btn[data-poy-trx]:hover {
    color: var(--ink-on-hover);
    background: var(--surface-hover);
    border-color: var(--line-gold);
  }
}
[data-lt-poy-list] .c-btn[data-poy-trx] > .u-stack-tight {
  min-width: 0;
}
[data-lt-poy-list] .c-btn[data-poy-trx] > .u-text-end {
  flex: none;
  align-items: flex-end;
}
[data-lt-poy-list] [data-poy-trx] [data-f="title"] {
  font-family: var(--font-bold);
  font-size: var(--text-base);
  font-weight: 500;
}
[data-lt-poy-list] [data-poy-trx] [data-f="meta"] {
  font-size: var(--text-sm);
}
[data-lt-poy-list] [data-poy-trx] [data-f="total"] {
  font-weight: 700;
}
/* The empty ticket list, a first-class screen (9.12): a 96 px emblem, the hint in ink-mid, ไปแทงหวย. */
[data-lt-poy-empty] > .c-icon {
  padding: 28px;
}
[data-lt-poy-empty] > .u-text-dim {
  color: var(--ink-mid);
}

/* Ticket detail (9.13): the head block's status badge keeps its own width; each line's number is the
   slip capsule; the line's status badge sits at the end; the outcome figure takes the data layer's tone
   (P1-CSS C3: data-tone win / loss on [data-f="outcome"]); the totals rows span the box. */
[data-display="poy-detail"] [data-lt-poy-detail-market] {
  color: var(--ink-hi);
  font-family: var(--font-bold);
  font-size: var(--text-lg);
}
[data-display="poy-detail"] .c-badge {
  align-self: flex-start;
}
[data-lt-poy-detail-lines] > .c-slip__line {
  align-items: flex-start;
  padding-block: var(--space-3);
}
[data-lt-poy-detail-lines] [data-f="number"] {
  align-self: flex-start;
  min-width: 52px;
  padding: 2px var(--space-2);
  color: var(--ink-hi);
  border: 2px solid var(--line-gold);
  border-radius: var(--radius-pill);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-align: center;
}
[data-lt-poy-detail-lines] .u-text-end {
  align-items: flex-end;
}
[data-lt-poy-detail-lines] .u-text-end > .c-badge {
  align-self: flex-end;
}
[data-lt-poy-detail-lines] .u-text-end > .u-num {
  color: var(--ink-hi);
  font-weight: 600;
}
[data-lt-poy-detail-lines] [data-f="outcome"][data-tone="win"] {
  color: var(--ink-success);
  font-weight: 600;
}
[data-lt-poy-detail-lines] [data-f="outcome"][data-tone="loss"] {
  color: var(--ink-danger);
  font-weight: 600;
}
.c-slip__total.u-stack-tight {
  align-items: stretch;
}

/* ผลหวย (9.14): one result card per row on a phone, --market-min cards from 768 px. The card is the
   obsidian card with a soft gold head band (flag, name); รางวัลที่ 1 is the wide capsule; the smaller
   figures sit in capsules under their labels. The figures are the backend's, as given. */
.u-grid-auto.u-grid-auto[data-lt-result-grid] {
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 768px) {
  .u-grid-auto.u-grid-auto[data-lt-result-grid] {
    grid-template-columns: repeat(auto-fill, minmax(var(--market-min), 1fr));
  }
}
[data-lt-result-grid] > .c-result {
  align-items: stretch;
  gap: var(--space-3);
  padding: 0 var(--space-4) var(--space-4);
  overflow: hidden;
  color: var(--ink-on-card);
  background: var(--surface-card);
  background-image: var(--obsidian);
  border-color: var(--line-accent);
  box-shadow: var(--edge-lit), var(--shadow-sm);
  text-align: start;
}
[data-lt-result-grid] > .c-result > .u-row:first-child {
  gap: var(--space-2);
  margin-inline: calc(var(--space-4) * -1);
  padding: var(--space-3) var(--space-4);
  background: var(--action-soft);
  border-bottom: 1px solid var(--line-accent);
}
[data-lt-result-grid] > .c-result > .u-row:first-child > h3 {
  min-width: 0;
  color: var(--ink-on-soft);
  font-size: var(--text-lg);
}
[data-lt-result-grid] .c-stat {
  align-items: center;
  gap: var(--space-1);
  min-width: 0;
  padding: var(--space-2);
  background: var(--elev-1);
  border-color: var(--line);
  text-align: center;
}
[data-lt-result-grid] .c-stat__value.u-num {
  color: var(--ink-hi);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: 0.1em;
  overflow-wrap: anywhere;
}
[data-lt-result-grid] > .c-result > .c-stat {
  border: 2px solid var(--line-gold);
  border-radius: var(--radius-pill);
}
[data-lt-result-grid] > .c-result > .c-stat .c-stat__value.u-num {
  font-size: var(--text-3xl);
  letter-spacing: 0.2em;
}
[data-lt-result-grid] .u-grid-3 {
  gap: var(--space-2);
}
/* The results day note (data-lt-result-note) reads like the hub's no-market note. */
[data-lt-result-note] {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  color: var(--ink-mid);
  background: var(--elev-1);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  text-align: center;
}
/* shell.js showView marks every data-view-target of the visible view is-active with aria-current, so the
   ไปแทงหวย button inside the lottery view would wear the selected rung; it keeps its gold face. */
[data-lt-poy-empty] > .c-btn--primary:is(.is-active, [aria-current="page"]) {
  color: var(--ink-on-gold);
  background-color: var(--surface-gold);
  border-color: transparent;
  box-shadow: var(--gold-lip);
}
@media (hover: hover) {
  [data-lt-poy-empty] > .c-btn--primary:is(.is-active, [aria-current="page"]):hover {
    box-shadow: var(--gold-lip), var(--glow-accent);
  }
}
[data-lt-poy-empty] > .c-btn--primary:is(.is-active, [aria-current="page"]):active {
  box-shadow: var(--gold-pressed);
}
/* The coupon picker row (lt-coupon-row): ใช้คูปองนี้ keeps one line; the coupon text beside it wraps. */
[data-lt-coupon] > .u-stack-tight {
  min-width: 0;
}
[data-lt-coupon] > [data-lt-coupon-pick] {
  flex: none;
  white-space: nowrap;
}

/* ── Stage 6 · wallet, account, overlays ── */
/* The rank badge's icon (account.js paints /api/rank/badge's badge_icon as img[data-rank-icon] before the rank
   name; P1-CSS C5): a 1em square beside the name, whatever the picture's own size. */
[data-profile-rank] > img[data-rank-icon] {
  flex: none;
  width: 1em;
  height: 1em;
  object-fit: contain;
}
/* A promotion's picture in its card (promo-card data-f="imgWrap", shown by the data layer when the row has
   promotion_image) and in a promo-v2 choice card: a flat picture at the card's width, small radius, no
   hero frame or shadow inside a card. */
.c-card > .c-banner[data-f="imgWrap"] {
  background: var(--elev-1);
  border-color: var(--line);
  border-radius: var(--radius-sm);
  box-shadow: none;
}
[data-promotion-id] > img[data-f="img"] {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}
/* The withdraw bank rows (cashier blueprint withdraw-bank; cashier.js marks the checked row is-selected): the
   whole row is the hit area and the radio takes the gold; a checked row reads selected even before the
   class arrives. */
[data-withdraw-bank] {
  cursor: pointer;
}
[data-withdraw-bank] > input[type="radio"] {
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--action-fill);
}
[data-withdraw-bank]:has(> input:checked):not(.is-selected) {
  color: var(--ink-on-selected);
  background: var(--surface-selected);
}

/* ── Stage 7 · everything else ── */
/* The game lobby (game-lobby.ejs lobby-tile: games.js puts the game's picture in the [data-f="logo"] slot, a
   .c-icon): DESIGN A11's art tile, the 5:6 picture edge to edge at the top, the name under it in Mitr. The
   approved mock drew the lobby with the game-card blueprint; the real partial has its own. */
[data-lobby-grid] > .c-tile[data-game-id] {
  justify-content: flex-start;
  gap: var(--space-2);
  padding: 0 0 var(--space-2);
  overflow: hidden;
  border-radius: 12px;
}
[data-lobby-grid] > .c-tile[data-game-id] > [data-f="logo"] {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 5 / 6;
  background: var(--elev-1);
}
[data-lobby-grid] > .c-tile[data-game-id] > [data-f="logo"] > img {
  object-fit: cover;
}
[data-lobby-grid] > .c-tile[data-game-id] > [data-f="name"] {
  max-width: 100%;
  padding: 0 var(--space-2);
  color: var(--ink-hi);
  font-family: var(--font-bold);
}
/* The in-page game player (casino-player.ejs, a full overlay): the game frame fills the body under the head,
   with no border; nothing scrolls around it. The frame only shows in the display's ready state. */
[data-overlay="casino-player"] .c-overlay__body {
  display: flex;
  flex-direction: column;
  padding: 0;
  overflow: hidden;
}
[data-overlay="casino-player"] [data-display="casino-player"] {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}
[data-overlay="casino-player"] [data-display="casino-player"] > [data-state="ready"] {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
}
[data-overlay="casino-player"] iframe[data-player-frame] {
  display: block;
  flex: 1 1 auto;
  width: 100%;
  height: auto;
  min-height: 0;
  background: var(--elev-1);
  border: 0;
}
[data-overlay="casino-player"] [data-display="casino-player"] > :is([data-state="loading"], [data-state="error"], [data-state="empty"]) {
  margin: var(--space-4);
}
