/* =============================================================================
   bust.fun — DRAGON TOWER styles  (games/dragontower/dragontower.css)
   -----------------------------------------------------------------------------
   Game-SPECIFIC styles for this module. Linked from index.html. SYNTHWAVE TOKENS
   ONLY (tokens.css) — no hard-coded palette. A vertical tower climbed from the
   bottom: neutral tiles until the server resolves, then a bottom-up egg/dragon
   reveal. Mirrors mines.css (look, motion, a11y bars).

   HARD bars honored here:
     • 44px touch targets (tiles + difficulty buttons).
     • 0 horizontal overflow at 360/390px (rows are fluid; gap shrinks at small w).
     • reduced-motion: ALL keyframe motion is no-op'd (the @media block at the bottom);
       reveals still land via the static end-state classes (the core paints all at once).
     • synthwave tokens only; the win/loss/cashout/bust feel comes from ctx.fx.
   ========================================================================== */

/* Difficulty selector: now gv-controls segmented() TWO-LINE pills (.dt-difficulty .gv-seg / .gv-seg-btn) —
   each pill shows the difficulty NAME over its per-step multiplier, so the risk/reward of every difficulty is
   visible at a glance (no dropdown to open). The shared .seg button.active / .gv-seg chrome lives in
   styles.css + gv-controls.css; this only tunes the per-step sub-label tint + the locked (mid-round) state.
   No new tokens, no hard-coded palette. */
.panel[data-panel="dragontower"] .dt-difficulty,
#gameControlsSlot .dt-difficulty { margin: 0; }
/* CLIP FIX (audit 6/10): five EQUAL flex pills (the shared .gv-seg-btn { flex:1 1 0 }) can't hold five two-line
   labels in the ~250px left rail — "Medium"/"Expert"/"Master" truncated and the active green fill cut through
   the text. In the narrow rail we let the pills WRAP to two rows: the base .seg already carries flex-wrap:wrap,
   but the shared flex:1 1 0 was forcing all five onto one line, so we override the BASIS to ~30% here. That
   lays them out 3-over-2 (Easy/Medium/Hard on row 1, Expert/Master stretched across row 2) so every NAME + its
   per-step × is fully readable and each pill stays a comfortable >=44px tap target. min-width:0 keeps a long
   label from forcing horizontal overflow at the tightest rail. This is PURELY a layout change on dragontower's
   own pills — the segmented() DOM, the #dtDiffSelect id, the data-value hooks, the active state, and the
   sub-label --brand-ink color fix are all untouched. (The wider min-width:520px split gives the rail more room;
   the pills simply flow back onto fewer rows there — same rule, no separate breakpoint needed.)
   HARD: 44px touch target on every pill (the two-line layout is shorter than the shared select). */
#gameControlsSlot .dt-difficulty .gv-seg { flex-wrap: wrap; align-items: stretch; }
#gameControlsSlot .dt-difficulty .gv-seg-btn { min-height: 44px; flex: 1 1 30%; min-width: 0; }
/* the per-step "×" sub-label reads as the win/payout figure (still muted enough not to fight the name) */
#gameControlsSlot .dt-difficulty .gv-seg-sub {
  color: var(--win);
  opacity: .82;
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-semi);
}
/* keep the chosen pill's sub-label bright AND legible: on the ACTIVE pill the background flips to --brand
   (green), so the --win (green) multiplier tint above would collide green-on-green and vanish (contrast ~1.03).
   Flip it to the on-accent ink (matches the active pill's main label) so the committed multiplier stays readable. */
#gameControlsSlot .dt-difficulty .gv-seg-btn.active .gv-seg-sub { opacity: 1; color: var(--brand-ink); }
/* LOCKED mid-round: gv-controls disables the pills (segmented .disable → button:disabled); recede them so the
   selection reads as committed (not clickable) without a layout/size change (CLS-stable: same box, same height). */
#gameControlsSlot .dt-difficulty .gv-seg-btn:disabled {
  opacity: .55;
  cursor: default;
}
#gameControlsSlot .dt-difficulty .gv-seg-btn.active:disabled { opacity: .9; } /* the locked-in choice stays legible */

/* the plain-language per-difficulty odds note under the selector */
.panel[data-panel="dragontower"] .dt-odds {
  margin: calc(var(--sp-1) * -1) 0 0;
  font-size: var(--fs-xs); line-height: 1.4; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* =============================================================================
   The tower — a vertical stack of rows, fluid, never overflows
   ========================================================================== */
/* FILL the display: the wrap grows to take the whole board area, centered + wider, and lays the banner +
   tower in a column so the tower can flex to the remaining height (rows then scale up — no scroll). The
   max-width is bumped 520->560 so the tower breathes into the wide desktop board column (it was floating in
   a wide gutter at 520; 560 fills more of the column while still centring on narrower widths). */
.dt-tower-wrap {
  position: relative; width: 100%; max-width: min(560px, 100%); margin: 0 auto;
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
}

/* =============================================================================
   BOARD EMPTY-STATE — the "press Bet to climb" CTA over the inactive tower.
   A first-time player faced a grid of dead, disabled tiles with no cue to start. This overlay sits ABSOLUTELY
   over the SAME box the live tower occupies (inset to the tower area, below the banner), so showing/clearing
   it adds NO layout box and never shifts the board — idle→play→result stays shift-free. It's a hint, not a
   control: pointer-events:none so clicks fall through, aria-hidden so the #dtLive status owns the real
   announcement. Tokens only. Visible only when .on (toggled off while a round is ACTIVE).
   ========================================================================== */
.dt-empty {
  position: absolute;
  /* span the tower's box: the banner is auto-height at the top of the column, the tower fills the rest with a
     var(--sp-2) top/bottom margin — inset to that region so the copy centers over the tower, not the banner. */
  left: 0; right: 0; bottom: 0; top: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-3);
  text-align: center;
  pointer-events: none;        /* a hint, never intercepts the (disabled) tiles below */
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-2) var(--ease-out), visibility var(--dur-2);
  z-index: 2;                  /* above the tower's tiles + ambient layers, below nothing interactive */
}
.dt-empty.on { opacity: 1; visibility: visible; }
.dt-empty-glyph {
  font-size: clamp(28px, 8vw, 40px);
  line-height: 1;
  filter: drop-shadow(0 0 12px var(--win-glow));
  opacity: .9;
}
.dt-empty-title {
  font-size: var(--fs-md);
  font-weight: var(--fw-bold, 700);
  color: var(--fg);
  max-width: 22ch;
  line-height: 1.3;
}
.dt-empty-sub {
  font-size: var(--fs-xs);
  color: var(--muted);
  max-width: 28ch;
  line-height: 1.4;
}

/* live climb-multiplier HUD banner above the tower (decorative; a11y is via #dtLive).
   Hidden until a climb begins resolving (.on), then it fades in and ticks up per egg. */
.dt-climb-banner {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--sp-2);
  margin: var(--sp-2) 0 0;
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: linear-gradient(180deg, var(--surface-3), var(--surface-2));
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--dur-2), transform var(--dur-2), box-shadow var(--dur-2), border-color var(--dur-2);
  pointer-events: none;
}
.dt-climb-banner.on { opacity: 1; transform: none; }
.dt-climb-label { font-size: clamp(11px, 3vw, 13px); color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.dt-climb-mult {
  font-size: clamp(20px, 6vw, 28px);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--win);
  text-shadow: 0 0 14px var(--win-glow);
  transition: color var(--dur-2), text-shadow var(--dur-2);
}
.dt-climb-banner.win { border-color: var(--win); box-shadow: 0 0 18px var(--win-glow); }
.dt-climb-banner.win .dt-climb-mult { color: var(--gold, var(--win)); text-shadow: 0 0 18px var(--win-glow); }
.dt-climb-banner.bust { border-color: var(--lose); box-shadow: 0 0 18px var(--lose-glow); }
.dt-climb-banner.bust .dt-climb-mult { color: var(--lose); text-shadow: 0 0 18px var(--lose-glow); }
/* the × pops each time it ticks up (motion-gated below) */
.dt-climb-banner.on .dt-climb-mult { animation: dt-tick var(--dur-2) var(--ease-out); }

.dt-tower {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  width: 100%;
  max-width: none;
  flex: 1 1 auto;   /* fill the wrap's remaining height so the rows can grow large */
  min-height: 0;
  margin: var(--sp-2) 0;
  padding: var(--sp-3) var(--sp-2) var(--sp-2);
  border-radius: var(--r-lg);
  position: relative;
  isolation: isolate;   /* contain the ::before/::after ambient layers behind the rows */
  overflow: hidden;     /* clip the summit glow + base fade to the rounded well */
  /* AT-REST DEPTH (audit: BORING → an inviting tower-shaft). The well now reads as a vertical SHAFT you
     climb out of: a deep, slightly cool base that lifts toward a warm, glowing SUMMIT at the top (the
     goal). Layers, front→back: a gold summit crown (the prize you're climbing to), a brand aura just
     below it, then the bottom-to-top depth gradient over the base surface. Tokens only. */
  background:
    radial-gradient(90% 42% at 50% -6%, var(--gold-glow), transparent 70%),
    radial-gradient(120% 70% at 50% 2%, var(--brand-soft), transparent 64%),
    linear-gradient(180deg, var(--surface-3) 0%, var(--surface-2) 34%, var(--bg-1) 100%),
    var(--surface-2);
  border: 1px solid var(--line-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), inset 0 -24px 40px -24px rgba(0, 0, 0, .55);
}
/* SUMMIT CROWN — a thin neon ridge + glow at the very top of the shaft: the visual "goal" of the climb,
   so a logged-out player reads the board as a tower-with-a-prize-at-the-top, not a flat grid. The egg
   theme is hinted with a faint floating egg glyph when a skin is active (collapsed otherwise). */
.dt-tower::before {
  content: "";
  position: absolute;
  top: 0; left: 8%; right: 8%; height: 3px;
  border-radius: 0 0 var(--r-pill) var(--r-pill);
  background: linear-gradient(90deg, transparent, var(--gold) 22%, var(--win) 50%, var(--gold) 78%, transparent);
  box-shadow: 0 0 14px var(--gold-glow), 0 0 26px var(--win-glow);
  opacity: .7;
  z-index: -1;
  pointer-events: none;
}
/* AMBIENT VIGNETTE — a soft inner edge-darkening that frames the rows and adds depth without touching
   any tile contrast. Sits behind the rows; never intercepts clicks. */
.dt-tower::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(130% 100% at 50% 38%, transparent 56%, rgba(7, 7, 18, .42) 100%);
  z-index: -1;
  pointer-events: none;
}

/* =============================================================================
   AT-REST REWARD LADDER — "higher row = bigger reward".
   DOM order is TOP row first (row 8, the summit / biggest payout) → BOTTOM row last (row 0, the start),
   so :nth-child(1) is the summit and :nth-child(9) is the base. Each row carries a resting tier tint that
   HEATS UP toward the top: cool/neutral at the base, brand-violet through the middle, gold at the summit.
   This is the core "tower to climb" cue at rest — purely presentational (no per-row data, no leaked
   outcome). Resolved (.egg/.dragon) + the .active row keep their own brighter rules (higher specificity /
   later in the cascade), so this only colors UN-resolved, inactive rows. Tokens only.
   ========================================================================== */
.dt-row {
  /* a faint left-edge "rung" marker so the eye reads the stack as a climbable ladder of rungs */
  border-left: 2px solid var(--dt-rung, var(--line));
  border-radius: var(--r-sm);
  padding-left: var(--sp-1);
  /* the resting tier glow (set per-tier below); soft, never competes with the .active ready-glow */
  box-shadow: -6px 0 14px -10px var(--dt-rung-glow, transparent);
}
/* summit (top ~3 rows) — the jackpot: warm gold */
#dtTower .dt-row:nth-child(-n+3)  { --dt-rung: var(--gold);   --dt-rung-glow: var(--gold-glow); }
/* upper-middle — neon win/teal */
#dtTower .dt-row:nth-child(4),
#dtTower .dt-row:nth-child(5)     { --dt-rung: var(--win);    --dt-rung-glow: var(--win-glow); }
/* lower-middle — brand violet */
#dtTower .dt-row:nth-child(6),
#dtTower .dt-row:nth-child(7)     { --dt-rung: var(--violet); --dt-rung-glow: var(--violet-glow); }
/* base (bottom ~2 rows, where you start) — cool/quiet so the eye climbs UP toward the hot summit */
#dtTower .dt-row:nth-child(n+8)   { --dt-rung: var(--line-3); --dt-rung-glow: transparent; }
/* Once a round is live, the resting ladder recedes so the ACTIVE/CLIMBED state owns the eye: the active
   row gets a brand rung + glow (reinforcing "pick here"), and climbed rows go quiet. */
#dtTower .dt-row.active   { --dt-rung: var(--brand); --dt-rung-glow: var(--brand-glow); }
#dtTower .dt-row.climbed  { --dt-rung: var(--line-2); --dt-rung-glow: transparent; }
.dt-row {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);   /* minmax(0,..) lets tiles shrink below content @360px */
  gap: var(--sp-1);
  /* COLLAPSE FIX (the BORING/dead-space root cause): rows were `flex: 1 1 0; min-height: 0`, which lets a
     row shrink to ZERO height when the tower isn't handed a definite height to distribute. On MOBILE the
     gameview panel is content-sized (flex:0 0 auto), so the tower got no height to share -> all 9 rows
     collapsed to 0px and only the first row's 44px tile min-height showed: a single squashed row floating
     in ~180px of empty dark box (the whole 9-row tower — the entire game — was invisible at rest). With a
     REAL per-row min-height the tower is always >= 9 rows tall and lays out as the full tower regardless of
     the parent flex distribution; `flex: 1 1 auto` still lets the rows grow to FILL the column on tall
     viewports (tablet/desktop unchanged: they were already handing the tower a definite height). The
     clamp tracks viewport height so each row is a comfortable, legible cell at every size. */
  flex: 1 1 auto;
  min-height: clamp(38px, 7.4vh, 72px);
  /* future (not-yet-reachable) rows read as dimmed BUT still legible as a tower-you'll-climb preview
     (was .5, which combined with the low tile contrast made the pre-round board read as a blank box). */
  opacity: .82;
  transition: opacity var(--dur-2);
}
.dt-row.active { opacity: 1; }          /* the row you can pick right now */
.dt-row.climbed { opacity: 1; }         /* a resolved/picked row stays lit */

.dt-tile {
  min-height: 44px;            /* HARD: 44px touch target */
  min-width: 0;
  background: linear-gradient(180deg, var(--surface-3), var(--surface-2));
  /* A brighter resting border + a subtle inner stroke so each tile reads as a discrete, clickable cell
     against the dark well even pre-round (the audit: the old --line-2 border ≈ the fill made the tower a
     blank box). --line-3 is the strongest control border in EITHER theme (synthwave purple / slate), and the
     theme-agnostic white inner stroke + top sheen lift the resting edge well above 3:1, so a logged-out /
     pre-round player sees the 9-row tower they're about to climb. */
  border: 1px solid var(--line-3);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .06), inset 0 1px 0 rgba(255, 255, 255, .05);
  border-radius: var(--r-md);
  font-size: clamp(16px, 4.5vw, 20px);
  line-height: 1;
  color: var(--fg);
  cursor: pointer;
  padding: 0;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  -webkit-tap-highlight-color: transparent;
  transition:
    transform var(--dur-1) var(--ease-out),
    border-color var(--dur-2),
    background var(--dur-2),
    box-shadow var(--dur-2),
    opacity var(--dur-2);
}
/* sheen sweep on hover (motion-gated below) */
.dt-tile::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(120deg, transparent 35%, rgba(255, 255, 255, .06) 50%, transparent 65%);
  transform: translateX(-120%);
  transition: transform var(--dur-3) var(--ease-out);
  pointer-events: none;
}
.dt-tile:not(:disabled):hover { border-color: var(--brand); transform: translateY(-2px); box-shadow: var(--glow-brand); }
.dt-tile:not(:disabled):hover::before { transform: translateX(120%); }
.dt-tile:not(:disabled):active { transform: scale(.95); }
.dt-tile:focus-visible { outline: none; box-shadow: var(--ring); border-color: var(--brand); z-index: 1; }
/* CRITICAL (audit #2 root cause): EVERY pre-round / future tile is `disabled`, and the GLOBAL
   `button:disabled` reset (03-layout-forms.css) strips box-shadow + drops opacity to .45 — which is what
   made the tower read as a blank dark box. This rule (specificity 0,2,0 beats the global 0,1,1) keeps the
   NEUTRAL (un-resolved) tiles fully opaque with their resting inner-stroke so the 9-row tower you'll climb
   is legible at rest. Resolved egg/dragon tiles keep their own brighter `:disabled` rules below. */
.dt-tile:disabled {
  cursor: default;
  opacity: 1;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .06), inset 0 1px 0 rgba(255, 255, 255, .05);
}
/* the pickable (active) row's tiles get a steady brand-tinted ready glow so the eye lands on where to click
   (only the enabled tiles of the active row, so future/resolved rows stay quiet). */
.dt-row.active .dt-tile:not(:disabled) {
  border-color: var(--brand);
  box-shadow: inset 0 0 0 1px var(--brand-soft), 0 0 12px var(--brand-soft);
}

/* ---- REVEALED: EGG (safe) — the premium payoff look ---- */
.dt-tile.egg {
  background:
    radial-gradient(80% 80% at 50% 30%, var(--win), var(--win-2)),
    linear-gradient(180deg, var(--win), var(--win-2));
  color: var(--win-ink);
  border-color: transparent;
  box-shadow: 0 0 22px var(--win-glow), inset 0 0 0 1px rgba(255, 255, 255, .25);
}
.dt-tile.egg::after {
  content: "🥚";
  font-size: 1em;
  filter: drop-shadow(0 0 6px var(--win-glow));
}

/* ---- REVEALED: DRAGON — the bust look ---- */
.dt-tile.dragon {
  background:
    radial-gradient(70% 70% at 50% 40%, var(--lose), var(--lose-2)),
    linear-gradient(180deg, var(--lose), var(--lose-2));
  color: var(--brand-ink);
  border-color: transparent;
  box-shadow: 0 0 26px var(--lose-glow), inset 0 0 0 1px rgba(255, 255, 255, .15);
}
.dt-tile.dragon::after {
  content: "🐉";
  font-size: 1.05em;
  text-shadow: 0 0 10px rgba(0, 0, 0, .5);
}

/* ---- CHARGE: the picked tile's held-breath beat BEFORE it flips (the suspense). Never reveals
   the outcome — it's a neutral, pulsing "about to turn over" state on the player's OWN pick. ---- */
.dt-tile.charge {
  border-color: var(--info);
  box-shadow: inset 0 0 0 1px var(--info), 0 0 16px var(--cyan-glow);
  animation: dt-charge var(--dur-3) var(--ease-out) infinite alternate;
}
@keyframes dt-charge {
  0%   { transform: scale(1);    box-shadow: inset 0 0 0 1px var(--info), 0 0 8px var(--cyan-glow); }
  100% { transform: scale(1.04); box-shadow: inset 0 0 0 1px var(--info), 0 0 22px var(--cyan-glow); }
}

/* the row mid-resolve glows so the eye tracks the climb up the tower */
.dt-row.resolving { box-shadow: 0 0 0 1px var(--brand) inset; border-radius: var(--r-md); }

/* the bust tile (the player's dragon pick) gets the strongest emphasis */
.dt-tile.dragon.bust-tile { box-shadow: 0 0 30px var(--lose-glow), inset 0 0 0 2px rgba(255, 255, 255, .25); z-index: 1; }

/* ---- DIM: the non-picked tiles in a revealed row fade so the player's pick pops ---- */
.dt-tile.dim { opacity: .4; filter: saturate(.55); }

/* ---- WON CASHOUT: a cashout returns NO layout (no-leak), so the un-picked cells of climbed rows + the
   un-reached rows above must NOT read as a big empty box. We make the WON PATH the hero: the non-picked
   tiles of cleared rows get a quiet "climbed past" marker (a faint check, no outcome leaked), and the rows
   above the cash-out point recede so the eye stays on the lit egg column the player actually climbed. ---- */
.dt-tower.dt-won .dt-tile.past {
  background: linear-gradient(180deg, var(--surface-3), var(--surface-2));
  border-color: var(--line-3);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07);
  opacity: .7;
}
.dt-tower.dt-won .dt-tile.past::after {
  content: "✓"; font-size: .8em; color: var(--win); opacity: .55;
  text-shadow: 0 0 8px var(--win-glow);
}
.dt-tower.dt-won .dt-row:not(.climbed) { opacity: .28; filter: saturate(.5); }

/* keep revealed fills at full strength when disabled (override UA disabled-opacity) */
.dt-tile.egg:disabled { box-shadow: 0 0 22px var(--win-glow), inset 0 0 0 1px rgba(255, 255, 255, .25); }
.dt-tile.dragon:disabled { box-shadow: 0 0 26px var(--lose-glow), inset 0 0 0 1px rgba(255, 255, 255, .15); }
.dt-tile:disabled::before { display: none; }
.dt-tile.dim:disabled { opacity: .4; }

/* the reveal flip + per-state pop (motion-gated below) */
.dt-tile.flip { animation: dt-flip var(--dur-3) var(--ease-spring) both; }
.dt-tile.egg.flip { animation: dt-flip var(--dur-3) var(--ease-spring) both, dt-egg-pop var(--dur-4) var(--ease-out) both; }
.dt-tile.dragon.flip { animation: dt-flip var(--dur-2) var(--ease-out) both, dt-dragon-pop var(--dur-3) var(--ease-out) both; }

@keyframes dt-flip {
  0%   { transform: rotateX(90deg) scale(.85); opacity: .3; }
  100% { transform: none; opacity: 1; }
}
@keyframes dt-egg-pop {
  0%   { box-shadow: 0 0 0 0 var(--win-glow), inset 0 0 0 1px rgba(255, 255, 255, .25); }
  60%  { box-shadow: 0 0 30px 6px var(--win-glow), inset 0 0 0 1px rgba(255, 255, 255, .4); }
  100% { box-shadow: 0 0 22px var(--win-glow), inset 0 0 0 1px rgba(255, 255, 255, .25); }
}
@keyframes dt-dragon-pop {
  0%   { transform: scale(1); box-shadow: 0 0 0 0 var(--lose-glow); }
  30%  { transform: scale(1.08); box-shadow: 0 0 34px 8px var(--lose-glow); }
  100% { transform: scale(1); box-shadow: 0 0 26px var(--lose-glow); }
}
/* the climb-multiplier × kicks each time it ticks up to the next row's payout */
@keyframes dt-tick {
  0%   { transform: scale(1.18); }
  100% { transform: scale(1); }
}

/* =============================================================================
   The "Next row / Cash out" readout + cleared counter
   ========================================================================== */
.panel[data-panel="dragontower"] .preview-inline { font-variant-numeric: tabular-nums; }
.panel[data-panel="dragontower"] #dtNextMult { color: var(--win); }
.panel[data-panel="dragontower"] #dtPlanned { color: var(--info); font-variant-numeric: tabular-nums; }

/* =============================================================================
   Interactive controls — the Cash Out button in the LEFT action rail (#gameActionSlot, below the Bet
   button), shown during an ACTIVE round. Module-owned (created in dragontower.js).
   NOTE: the Cash Out button lives in #gameActionSlot (the controls rail), NOT inside the dragontower
   display .panel — so these rules are scoped to #gameActionSlot, not .panel[data-panel="dragontower"]
   (which never matched the button; the prior panel-scoped rules were dead and the button just inherited
   the default brand-button gradient, making it indistinguishable from Bet). #gameActionSlot .dt-cashout
   (0,2,0) beats the global `button` rule (0,0,1) so the win-green / inert states actually apply.
   ========================================================================== */
#gameActionSlot .dt-cashout {
  background: linear-gradient(180deg, var(--win), var(--win-grad-end));
  color: #00120a;
  font-weight: 800;
  min-height: 44px;
  border: 1px solid transparent;
  transition: background var(--dur-2), color var(--dur-2), border-color var(--dur-2), opacity var(--dur-2);
}
#gameActionSlot .dt-cashout:hover { box-shadow: 0 0 16px var(--win-glow); }
/* INERT pre-round: when disabled (no round open / in-flight) the Cash Out drops its win-green so it doesn't
   compete with the primary Bet CTA or invite dead clicks — it reads as a quiet placeholder until a round is
   live, then renderControls() re-enables it and the green returns. (Audit #12.) */
#gameActionSlot .dt-cashout:disabled {
  background: var(--surface-3);
  /* inert label on the elevated --surface-3 chip — --muted (3.86:1) fails AA; --muted-2 lifts it ≥4.5:1
     (contrast audit, systemic --muted-on-elevated-surface fix). */
  color: var(--muted-2);
  border-color: var(--line-2);
  box-shadow: none;
  opacity: 1;        /* override the global button:disabled opacity:.45 so it reads as a clean inert chip */
}
/* CLS-STABLE: Cash Out + Pick-random are PRESENT-BUT-DISABLED pre-round (kept in flow, never display:none) so
   starting/ending a round never collapses↔expands the action rail. renderControls only enables/disables them. */
#gameActionSlot .dt-cashout.loading { pointer-events: none; opacity: .7; }

/* =============================================================================
   Reduced motion — HARD gate: no-op ALL keyframe/transition motion. The end-state
   color classes (.egg/.dragon/.dim) still apply, and the core collapses the
   cascade to a single frame, so every reveal still lands — just without animation.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .dt-tile,
  .dt-tile.flip,
  .dt-tile.egg.flip,
  .dt-tile.dragon.flip,
  .dt-tile.charge { animation: none !important; transition: none !important; }
  .dt-tile::before { transition: none !important; }
  .dt-tile:hover { transform: none; }
  .dt-tile:active { transform: none; }
  .dt-row { transition: none !important; }
  /* the climb banner still SHOWS (legible) but never animates/ticks under reduced motion */
  .dt-climb-banner { transition: none !important; }
  .dt-climb-banner.on .dt-climb-mult { animation: none !important; }
  /* the board empty-state still SHOWS/HIDES (the CTA is legible) but without the fade under reduced motion */
  .dt-empty { transition: none !important; }
  /* skin reveal choreography: art just appears, no drop/wiggle/burst (flipIn already skips `.flip`
     under reduced motion; this is belt-and-suspenders so a lingering `.flip` can never animate) */
  .dt-skinned .dt-tile.egg.flip::after,
  .dt-skinned .dt-tile.dragon.flip::after { animation: none !important; }
}

/* =============================================================================
   PHONE FIT (cluster-C in-game): the FULL 9-row tower must fit the display without
   clipping the BASE row (where the climb STARTS).
   -----------------------------------------------------------------------------
   At rest the rows carried `min-height: clamp(38px, 7.4vh, 72px)` ≈ 62px on an 844px
   phone, so 9 rows + gaps ≈ 733px — but the shared .gameview-display caps at ~388px on
   phone (max-height clamp in 12-gameview.css) and overflow:auto, which scrolled rows
   4–8 (incl. the base/start row) OUT of view: a player could not see or tap the first
   tile to begin climbing. On phone we floor each row to the 44px touch target (NOT
   7.4vh) so all 9 rows fit in ~430px, and 12-gameview.css raises the dragontower
   display allowance to hold that full tower. Desktop/tablet keep the taller 7.4vh rows
   (this is a max-width:720px override only). HARD: the tile stays a 44px tap target. */
@media (max-width: 720px) {
  .dt-row { min-height: 44px; }
  /* reclaim the tower's vertical chrome so all 9 44px rows + the pre-round banner clear the display without
     an internal scroll pushing the base row under the fold: trim the well's top/bottom padding + margins. */
  .dt-tower { margin: var(--sp-1) 0; padding: var(--sp-2) var(--sp-2) var(--sp-1); gap: 5px; }
  /* PRE-ROUND, collapse the climb-HUD banner's reserved ~45px (it's opacity:0 but display:flex for CLS
     stability on desktop). On a phone that 45px pushed the whole tower — and the Bet CTA below it — down
     past the fold. The banner is DECORATIVE (a11y is via #dtLive), so pre-round we take it fully out of flow;
     when a round begins (.on) it re-enters + fades in above the tower. The small one-time shift happens at
     bet time, when the player's eye is already on the board reveal — an acceptable phone-only trade for a
     fold-reachable Bet. Desktop keeps the reserved slot (this is a max-width:720px override). */
  .dt-climb-banner:not(.on) { display: none; }
}

/* =============================================================================
   SKIN ANIMATIONS — one-shot, NON-looping flourishes played by games/_shared/anim.js for the
   active skin (the 'embergate' skin's signatures). anim.js already gates reduced-motion (it never
   spawns the overlay), so no @media guard is needed. Display only; the overlay is pointer-events:none
   and self-removes on animationend. Phase 4 can swap these CSS placeholders for richer Higgsfield art
   (type:'sprite'/'video') with zero game-code change.
   ========================================================================== */
.skin-anim { will-change: opacity, transform; }

/* egg landing-dust — fires on a SAFE pick. The egg ART itself drops in (CSS on the tile art, below);
   this overlay is the DUST kicked up at the moment the egg lands (invisible through the fall, then it
   puffs at impact near the base and settles). */
.skin-anim.dt-egg-poof {
  background: radial-gradient(58% 34% at 50% 90%, rgba(255,255,255,.5), rgba(200,225,255,.2) 52%, transparent 76%);
  animation: dt-egg-poof 820ms var(--ease-out, ease-out) forwards;
}
.skin-anim.dt-egg-poof::after {
  content: ""; position: absolute; left: 6%; right: 6%; bottom: 5%; height: 42%;
  background-image:
    radial-gradient(3px 3px at 20% 70%, rgba(255,255,255,.9), transparent 60%),
    radial-gradient(2.5px 2.5px at 40% 82%, rgba(220,235,255,.85), transparent 60%),
    radial-gradient(2.5px 2.5px at 60% 74%, rgba(255,255,255,.8), transparent 60%),
    radial-gradient(3px 3px at 80% 84%, rgba(210,230,255,.8), transparent 60%),
    radial-gradient(2px 2px at 50% 62%, rgba(255,255,255,.75), transparent 60%);
  animation: dt-dust-kick 820ms var(--ease-out, ease-out) forwards;
}
@keyframes dt-egg-poof {
  0%, 40% { opacity: 0; transform: scale(.6); }
  49%     { opacity: .92; transform: scale(1.06); }   /* the puff bursts as the egg lands */
  100%    { opacity: 0; transform: scale(1.55); }
}
@keyframes dt-dust-kick {
  0%, 40% { opacity: 0; transform: translateY(6px) scale(.7); }
  50%     { opacity: 1; }
  100%    { opacity: 0; transform: translateY(-11px) scale(1.5); }
}

/* dragon HATCH — fires on a DRAGON pick (the per-tile bust). The egg drops in just like a safe reveal
   (the fake-out, on ::before), then at the bottom it CRACKS / flashes white and vanishes while a flame
   billows up — synchronized with the feisty dragon bursting in on the tile art (CSS below). It's ONE
   continuous overlay (both layers animate the full duration) so anim.js's single animationend cleanup
   only fires at the very end, never mid-show. */
.skin-anim.dt-dragon-hatch { animation: dt-hatch-hold 1400ms linear forwards; }  /* visually inert; drives anim.js cleanup timing */
.skin-anim.dt-dragon-hatch::before {   /* the fake-out egg dropping in (independent opacity from the flame) */
  content: ""; position: absolute; inset: 12%;
  background: var(--dt-egg-art) center / contain no-repeat;
  transform-origin: 50% 100%;
  animation: dt-hatch-egg 1400ms var(--ease-out, ease-out) forwards;
}
.skin-anim.dt-dragon-hatch::after {    /* the flame that billows once the egg cracks (sits above the egg) */
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(122% 112% at 50% 120%, rgba(255,150,40,.92), rgba(225,45,12,.55) 38%, rgba(120,16,0,.3) 66%, transparent 82%);
  animation: dt-hatch-flame 1400ms var(--ease-out, ease-out) forwards;
}
@keyframes dt-hatch-hold { 0%, 100% { opacity: 1; } }
@keyframes dt-hatch-egg {
  0%   { transform: translateY(-188%) scale(.9); opacity: 0; animation-timing-function: cubic-bezier(.45,0,.75,.5); }  /* accelerating fall */
  8%   { opacity: 1; }
  20%  { transform: translateY(0) scale(1); opacity: 1; animation-timing-function: ease-out; }   /* lands (~280ms) */
  24%  { transform: translateY(0) scale(1.16); opacity: 1; filter: brightness(2.6); }            /* crack flash */
  29%  { transform: translateY(0) scale(1.36); opacity: 0; filter: brightness(3); }              /* gone — cut to dragon */
  100% { opacity: 0; }
}
@keyframes dt-hatch-flame {
  0%, 18% { opacity: 0; transform: translateY(20%) scaleY(.66); }
  27%     { opacity: 1; }                                            /* ignites as the egg cracks (~380ms) */
  62%     { opacity: .95; transform: translateY(0) scaleY(1.08); }
  100%    { opacity: 0; transform: translateY(-9%) scaleY(1.16); }
}

/* dragon flame — full-display wash on a LOSS (anchor = the tower; overlay covers the game display) */
.skin-anim.dt-dragon-flame {
  /* a reliable direct orange wash (no blend) so it always reads over lit tiles */
  background: radial-gradient(150% 130% at 50% 112%, rgba(255,120,28,.86), rgba(220,40,12,.62) 32%, rgba(120,16,0,.36) 62%, transparent 86%);
  animation: dt-flame 1200ms var(--ease-out, ease-out) forwards;
}
.skin-anim.dt-dragon-flame::before {
  /* a screen-blend bright core for the flicker glow on top of the wash */
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(120% 80% at 45% 110%, rgba(255,215,90,.72), rgba(255,130,30,.32) 45%, transparent 68%);
  mix-blend-mode: screen;
  animation: dt-flame-flicker 1200ms steps(9) forwards;
}
@keyframes dt-flame {
  0%   { opacity: 0; transform: translateY(22%) scaleY(.6); }
  18%  { opacity: 1; }
  52%  { transform: translateY(0) scaleY(1.06); }
  100% { opacity: 0; transform: translateY(-7%) scaleY(1.12); }
}
@keyframes dt-flame-flicker {
  0%,100% { opacity: 0; }
  20% { opacity: .85; } 35% { opacity: .42; } 50% { opacity: .92; }
  65% { opacity: .5; } 80% { opacity: .72; }
}

/* SKIN ART: when the active skin places egg/dragon art (the core adds .dt-skinned + the --dt-*-art vars
   on the tower), paint the art on revealed tiles instead of the built-in emoji. Falls back to the emoji
   automatically when the skin is classic/disabled (no .dt-skinned, higher specificity than the emoji rule). */
.dt-skinned .dt-tile.egg::after,
.dt-skinned .dt-tile.dragon::after {
  content: ""; position: absolute; inset: 8%;
  background-position: center; background-repeat: no-repeat; background-size: contain;
  font-size: 0;  /* collapse the emoji glyph box */
}
.dt-skinned .dt-tile.egg::after { background-image: var(--dt-egg-art); filter: drop-shadow(0 0 8px var(--win-glow)); }
/* DRAGON art fills MORE of the tile (smaller inset) + a stronger drop-shadow so it reads as a dragon at the
   small mobile tile size with clear figure/ground against the red bust fill (audit #6: it was a tiny imp). */
.dt-skinned .dt-tile.dragon::after {
  inset: 3%;
  background-image: var(--dt-dragon-art);
  filter: drop-shadow(0 0 10px var(--lose-glow)) drop-shadow(0 1px 2px rgba(0, 0, 0, .6));
}

/* REVEAL CHOREOGRAPHY (the owner's spec). `.flip` is added to a just-revealed tile by ui.flipIn and
   never stripped (and only when motion is allowed — under reduced-motion no `.flip`, so the art simply
   appears). The tile's overflow:hidden clips the art above the frame, so it reads as dropping IN. */

/* SAFE: the egg drops from above the frame, lands with a squash, wiggles left↔right and settles. */
.dt-skinned .dt-tile.egg.flip::after {
  transform-origin: 50% 100%;
  animation: dt-egg-drop 820ms var(--ease-out, ease-out) both;
}
@keyframes dt-egg-drop {
  0%   { transform: translateY(-185%) scale(.92); opacity: 0; animation-timing-function: cubic-bezier(.45,0,.75,.5); }  /* accelerating fall */
  12%  { opacity: 1; }
  44%  { transform: translateY(0) scaleY(1) scaleX(1); animation-timing-function: ease-out; }   /* lands (~360ms) */
  52%  { transform: translateY(0) scaleY(.8) scaleX(1.16); }                   /* squash on impact */
  60%  { transform: translateY(-9%) scaleY(1.07) scaleX(.95) rotate(7deg); }   /* bounce + wiggle right */
  72%  { transform: translateY(0) scaleY(1) scaleX(1) rotate(-6deg); }         /* wiggle left */
  83%  { transform: translateY(0) rotate(4deg); }
  92%  { transform: translateY(0) rotate(-2deg); }
  100% { transform: translateY(0) rotate(0); }
}

/* BUST: the dragon stays hidden behind the hatch overlay's dropping fake-out egg, then bursts out at
   the crack (~360ms) with an angry shake (the "feisty" beat) before settling. */
.dt-skinned .dt-tile.dragon.flip::after {
  transform-origin: 50% 82%;
  animation: dt-dragon-appear 1200ms var(--ease-out, ease-out) both;
}
@keyframes dt-dragon-appear {
  0%, 22% { opacity: 0; transform: scale(.45); }                 /* hidden behind the dropping egg */
  30%     { opacity: 1; transform: scale(1.2) rotate(-6deg); }   /* BAM — bursts out feisty (~360ms) */
  40%     { transform: scale(.97) rotate(6deg); }
  52%     { transform: scale(1.07) rotate(-5deg); }
  64%     { transform: scale(1) rotate(4deg); }
  76%     { transform: scale(1.03) rotate(-3deg); }
  88%     { transform: scale(1) rotate(2deg); }
  100%    { transform: scale(1) rotate(0); }
}
