/* ---- Fonts ----
   'Ndot 47' here is our extended build (ndot-47-cyrillic.otf): the original
   Latin glyphs untouched + Cyrillic (ru/uk/be incl. Ё Ї Є Ґ Ў), Greek, and
   Hebrew drawn on the SAME 5x7 dot grid (see scripts/gen-ndot-cyrillic.py), so
   every block script is pixel-consistent with Latin — no font switch.
   Scripts that can't live on a 5x7 round-dot grid use a per-locale PIXEL
   fallback, loaded only when that locale is active via the [lang=…] blocks
   below (the browser fetches the file only when its glyphs actually render):
     • CJK — Fusion Pixel 12px (OFL), one subset per ja/ko/zh-Hans/zh-Hant so
       Simplified vs Traditional vs Japanese glyph variants stay correct.
     • Arabic/Thai/Devanagari — Seven Fifteen Mono Rounded (CC-BY 4.0, Douglas
       Vautour) which ships GSUB/GPOS shaping so Arabic joins, Thai marks stack,
       and Devanagari conjuncts form. A non-shaping pixel font would break these.
   Each pixel face sits AFTER 'Ndot 47' in the chain, so Ndot still serves
   Latin/Cyrillic/Greek/Hebrew and the pixel font only fills its own script.
   'Handjet' remains a generic dot-matrix safety net. 'LED Dot-Matrix' stays
   Latin-only (card ranks), scoped via unicode-range so non-Latin skips it. */
@font-face {
  font-family: 'LED Dot-Matrix';
  src: url('./fonts/LED-Dot-Matrix.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-036F, U+1E00-1EFF, U+2000-206F, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Ndot 47';
  src: url('./fonts/ndot-47-cyrillic.otf') format('opentype');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}
/* Handjet — dot-matrix safety-net fallback, self-hosted (Latin subset, variable
   weight) so the app makes NO network call to Google Fonts at launch. The old
   cross-origin <link> was render-blocking on boot, un-cacheable by our SW, and
   an external request the store reviewers/privacy nutrition label would flag.
   Ndot 47 covers the primary UI scripts; Handjet only fills the rare gap. */
@font-face {
  font-family: 'Handjet';
  src: url('./fonts/handjet-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range:
    U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2074, U+20AC, U+2122, U+2212, U+2215;
}

/* Per-locale pixel fallbacks for scripts that can't sit on the 5x7 dot grid.
   Each is a tiny subset (only the glyphs our locale catalog uses) and is only
   referenced from its [lang=…] block below, so it downloads lazily per locale. */
@font-face {
  font-family: 'Pixel JA';
  src: url('./fonts/pixel-ja.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Pixel KO';
  src: url('./fonts/pixel-ko.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Pixel Hans';
  src: url('./fonts/pixel-zh-Hans.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Pixel Hant';
  src: url('./fonts/pixel-zh-Hant.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Pixel Arabic';
  src: url('./fonts/pixel-arabic.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Pixel Thai';
  src: url('./fonts/pixel-thai.woff2') format('woff2');
  font-display: swap;
}
@font-face {
  font-family: 'Pixel Deva';
  src: url('./fonts/pixel-devanagari.woff2') format('woff2');
  font-display: swap;
}

/* Insert the right pixel face right after 'Ndot 47' for the active locale.
   html[lang=…] (0,1,1) outranks :root / [data-theme] (0,1,0), so this wins in
   either theme. Ndot keeps Latin; the pixel face only fills its own script. */
html[lang='ja'] {
  --font-mono: 'Ndot 47', 'Pixel JA', 'Handjet', 'Space Mono', 'SF Mono', monospace;
  --font-sans: 'Ndot 47', 'Pixel JA', 'Handjet', 'Space Grotesk', system-ui, sans-serif;
}
html[lang='ko'] {
  --font-mono: 'Ndot 47', 'Pixel KO', 'Handjet', 'Space Mono', 'SF Mono', monospace;
  --font-sans: 'Ndot 47', 'Pixel KO', 'Handjet', 'Space Grotesk', system-ui, sans-serif;
}
html[lang='zh-Hans'] {
  --font-mono: 'Ndot 47', 'Pixel Hans', 'Handjet', 'Space Mono', 'SF Mono', monospace;
  --font-sans: 'Ndot 47', 'Pixel Hans', 'Handjet', 'Space Grotesk', system-ui, sans-serif;
}
html[lang='zh-Hant'] {
  --font-mono: 'Ndot 47', 'Pixel Hant', 'Handjet', 'Space Mono', 'SF Mono', monospace;
  --font-sans: 'Ndot 47', 'Pixel Hant', 'Handjet', 'Space Grotesk', system-ui, sans-serif;
}
html[lang='ar'],
html[lang='fa'] {
  --font-mono: 'Ndot 47', 'Pixel Arabic', 'Handjet', 'Space Mono', 'SF Mono', monospace;
  --font-sans: 'Ndot 47', 'Pixel Arabic', 'Handjet', 'Space Grotesk', system-ui, sans-serif;
}
html[lang='th'] {
  --font-mono: 'Ndot 47', 'Pixel Thai', 'Handjet', 'Space Mono', 'SF Mono', monospace;
  --font-sans: 'Ndot 47', 'Pixel Thai', 'Handjet', 'Space Grotesk', system-ui, sans-serif;
}
html[lang='hi'] {
  --font-mono: 'Ndot 47', 'Pixel Deva', 'Handjet', 'Space Mono', 'SF Mono', monospace;
  --font-sans: 'Ndot 47', 'Pixel Deva', 'Handjet', 'Space Grotesk', system-ui, sans-serif;
}

/* ---- Dark theme (default) ---- */
:root,
[data-theme='dark'] {
  --bg: #000;
  --surface: #141414;
  --surface-2: #1e1e1e;
  --border: #333;
  --text: #fff;
  --text-dim: #b0b0b0;
  --text-muted: #707070;
  --dot-color: #555;
  --bg-pattern: #555;

  --hearts: #d71921;
  --hearts-bg: rgba(253, 87, 140, 0.75);
  --diamonds: #4488ff;
  --diamonds-bg: rgba(253, 87, 140, 0.75);
  --spades: #d0d0d0;
  --spades-bg: rgba(253, 87, 140, 0.75);
  --clubs: #33cc66;
  --clubs-bg: rgba(253, 87, 140, 0.75);

  --green: #33cc66;
  --blue: #4488ff;
  --yellow: #ffcc33;
  --red: #d71921;

  /* Ndot 47 covers Latin + Cyrillic + Greek + Hebrew (our dot grid). Handjet
     slots in after as a generic dot-matrix safety net; per-locale pixel faces
     (CJK / Arabic / Thai / Devanagari) are layered in by the html[lang=…]
     blocks above. LED Dot-Matrix stays Latin-only for card ranks. */
  --font-mono: 'Ndot 47', 'Handjet', 'Space Mono', 'SF Mono', monospace;
  --font-sans: 'Ndot 47', 'Handjet', 'Space Grotesk', system-ui, sans-serif;
  --font-rank: 'Ndot 47', 'LED Dot-Matrix', 'Handjet', monospace;
  /* Card FACE type — deliberately NOT --font-rank, which ~30 other rules use
     for dot-matrix chrome. A playing card's value has to be read instantly at
     ~9px on a 7x7 board, which is the one job the dot-matrix face is worst at,
     so the card face is a grotesque: the A2 mock's approved `grotesk` variant.
     Inter is not bundled (no CDN allowed, see the header above), so the stack
     resolves to Helvetica Neue on iOS and Roboto via system-ui on Android —
     which is what the approved mock actually rendered on a Mac. */
  --card-rank-font: 'Inter', 'Helvetica Neue', 'Segoe UI', system-ui, sans-serif;
  --card-rank-weight: 600;
  --card-rank-track: 0.05em;
  --radius: 6px;

  --glass-bg: rgba(255, 255, 255, 0.75);
  --glass-blur: 10px;
  --card-text: #000;
  --card-suit: #d71921;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  /* App, not web page: nothing is selectable/copyable anywhere — on web this
     stops text/image selection, and on native (Capacitor WKWebView/Android)
     it suppresses the long-press selection + callout menus. Inputs opt back
     in below so login/forms still work. */
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Images: not draggable (the ghost-drag affordance on web). Selection is
   already off via the universal rule above; pointer-events are left intact so
   icon clicks and the board canvas keep working. */
img {
  -webkit-user-drag: none;
  user-drag: none;
}

/* Re-enable selection + interaction where the user genuinely edits/selects text. */
input,
textarea,
[contenteditable='true'] {
  -webkit-user-select: text;
  -moz-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

html,
body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #000;
  color: var(--text);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  /* Prevent iOS rubber-band scroll */
  overscroll-behavior: none;
}

/* In the game scene, paint ONLY the Safari bottom-toolbar zone gray. Height
   is the lvh/dvh delta — zero when the toolbar is hidden (lvh == dvh) and
   grows to the toolbar's height when it appears. Anchored bottom:0 so it
   can never touch the top safe area. */
body:has(#scene .game-root)::after {
  content: '';
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(100lvh - 100dvh);
  background: rgb(50, 50, 50);
  z-index: -1;
  pointer-events: none;
}

#app {
  width: 100%;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  padding-top: env(safe-area-inset-top, 0px);
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
  box-sizing: border-box;
  background: #000;
  position: relative;
}
#scene {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
  padding: 8px;
  overflow: hidden;
}
#scene:has(.game-root) {
  padding: 0;
  overflow: visible;
}
#scene:has(.menu-wrap) {
  padding: 0;
  align-items: stretch;
}
#scene:has(.auth-root) {
  padding: 0;
  align-items: stretch;
}
#scene:has(.profile-root) {
  padding: 0;
  align-items: stretch;
  justify-content: center;
}

/* ---- Buttons ---- */
.btn {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 10px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: all 0.12s;
  user-select: none;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.btn:active {
  transform: scale(0.96);
}
.btn-primary {
  background: var(--red);
  border-color: var(--red);
}
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
}

.mono {
  font-family: var(--font-mono);
}
.text-dim {
  color: var(--text-dim);
}
.text-muted {
  color: var(--text-muted);
}
.text-center {
  text-align: center;
}
.uppercase {
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

/* ---- Suit colors ---- */
.suit-hearts {
  --suit: var(--hearts);
  --suit-bg: var(--hearts-bg);
}
.suit-diamonds {
  --suit: var(--diamonds);
  --suit-bg: var(--diamonds-bg);
}
.suit-spades {
  --suit: var(--spades);
  --suit-bg: var(--spades-bg);
}
.suit-clubs {
  --suit: var(--clubs);
  --suit-bg: var(--clubs-bg);
}

/* Media-free compatibility anchors for HUD effects that historically targeted
   full-body portrait images. They sit over the two 32px identity discs and
   paint only while an ability, item, thinking, or Arcanium state owns them. */
.hud-portrait-anchor {
  position: absolute;
  top: 8px;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 4;
}
.hud-portrait-anchor::before {
  content: '';
  position: absolute;
  inset: 3px;
  border: 1px solid rgba(176, 205, 255, 0.55);
  border-radius: inherit;
  background: radial-gradient(circle, rgba(145, 108, 225, 0.2), transparent 68%);
  box-shadow:
    0 0 12px rgba(145, 108, 225, 0.45),
    inset 0 0 8px rgba(176, 205, 255, 0.24);
  opacity: 0;
}
.hud-portrait-left {
  left: 8px;
  transition: transform 220ms var(--cm-ease);
}
.hud-portrait-left.arcanum-open {
  transform: translate3d(8px, -6px, 0);
}
.hud-portrait-right {
  right: 8px;
}
.hud-portrait-anchor.ability-flash::before {
  opacity: 1;
}
.hud-portrait-anchor.is-thinking::before {
  opacity: 0.52;
}
.hud-panel {
  position: absolute;
  z-index: 2;
  padding: 6px 10px;
  background: rgba(30, 30, 30, 0.82);
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-width: 52%;
}
.hud-player {
  top: 6px;
  left: 6px;
}
.hud-opponent {
  bottom: 6px;
  right: 6px;
}
.hud-name-row {
  display: flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: clamp(10px, 2.5vw, 14px);
  color: #fff;
}
.hud-name-wrap {
  min-width: 0;
}
.hud-meta-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px;
  width: 100%;
  min-width: 0;
  font-family: var(--font-mono);
}
.hud-opponent .hud-meta-row {
  flex-direction: row-reverse;
}
.hud-hp-readout {
  --hp-color: #33cc66;
  display: inline-flex;
  flex: none;
  align-items: baseline;
  gap: 1px;
  color: rgba(255, 255, 255, 0.62);
  font-family: var(--font-mono);
  font-size: clamp(8px, 2vw, 10px);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  white-space: nowrap;
}
.hud-hp-glyph {
  align-self: center;
  margin-inline-end: 1px;
  color: var(--hp-color);
  font-size: 0.78em;
}
.hud-hp-current {
  color: var(--hp-color);
  font-size: 1.2em;
  font-weight: 800;
}
.hud-hp-separator,
.hud-hp-max {
  color: rgba(255, 255, 255, 0.58);
  font-weight: 600;
}
.hud-icon {
  font-size: clamp(10px, 2.5vw, 14px);
  color: var(--red);
}
/* The hero disc. Framed head-and-shoulders, so the crop sits on the face rather
   than in the middle of it; `top` keeps hair in and chins out when the disc is
   smaller than the source. */
.hud-face {
  object-fit: cover;
  object-position: 50% 12%;
  background: radial-gradient(circle at 50% 30%, #2b3340, #141920);
}
.hud-name {
  font-weight: 700;
  letter-spacing: 0.04em;
}
.hud-mana-text {
  font-size: clamp(8px, 2vw, 11px);
  color: rgba(255, 255, 255, 0.7);
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.hud-mana-icon {
  width: clamp(8px, 2vw, 11px);
  height: clamp(8px, 2vw, 11px);
  object-fit: contain;
  flex-shrink: 0;
}
/* Extra-action indicator — only shown when the player holds a bonus action
   (Time Fracture / Enchanting Dance). Sits just right of the mana readout. */
.hud-actions {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  margin-left: 4px;
  padding: 1px 4px;
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: clamp(8px, 2vw, 11px);
  font-weight: 700;
  line-height: 1;
  color: var(--accent, #f4c542);
  background: rgba(244, 197, 66, 0.14);
  border: 1px solid rgba(244, 197, 66, 0.55);
  box-shadow: 0 0 6px rgba(244, 197, 66, 0.45);
  white-space: nowrap;
}
.hud-actions-hidden {
  display: none;
}
.hud-actions-bolt {
  font-size: clamp(9px, 2.2vw, 12px);
}
.hud-actions.granted {
  animation: hud-action-grant 0.9s ease-out;
}
@keyframes hud-action-grant {
  0% {
    transform: scale(0.4);
    opacity: 0;
    box-shadow: 0 0 0 rgba(244, 197, 66, 0);
  }
  35% {
    transform: scale(1.35);
    opacity: 1;
    box-shadow: 0 0 14px rgba(244, 197, 66, 0.9);
  }
  100% {
    transform: scale(1);
    box-shadow: 0 0 6px rgba(244, 197, 66, 0.45);
  }
}
.hud-dots-row {
  display: flex;
  gap: 2px;
  align-items: center;
}
@property --fill {
  syntax: '<number>';
  inherits: false;
  initial-value: 1;
}
.hud-dot {
  width: clamp(5px, 1.2vw, 7px);
  height: clamp(5px, 1.2vw, 7px);
  border-radius: 50%;
  --fill: 1;
  /* Binary bullets: --fill is 0 or 1, so this resolves to solid white or solid
     red. Flips are instant (no transition) — never a partial bullet. */
  background: linear-gradient(
    90deg,
    #fff calc(var(--fill) * 100%),
    #d71921 calc(var(--fill) * 100%)
  );
}
.hud-dot-green,
.hud-dot-red {
  background: linear-gradient(
    90deg,
    #fff calc(var(--fill) * 100%),
    #d71921 calc(var(--fill) * 100%)
  );
}
.hud-bar-track {
  display: none;
}
.hud-bar {
  display: none;
}

/* Dot damage animations — pulse scale/glow only; gradient stays owned by --fill */
.hud-dot.burst {
  animation: dot-burst 0.35s ease-out forwards;
}
@keyframes dot-burst {
  0% {
    transform: scale(1);
  }
  30% {
    transform: scale(2.4);
    box-shadow: 0 0 6px 2px rgba(215, 25, 33, 0.8);
  }
  100% {
    transform: scale(1);
    box-shadow: none;
  }
}

/* Center turn countdown */
.hud-timer {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  font-family: 'LED Dot-Matrix', var(--font-mono);
  font-size: clamp(28px, 8vw, 48px);
  line-height: 1;
  letter-spacing: 0.06em;
  color: #f4f4f4;
  text-shadow:
    0 0 6px rgba(255, 255, 255, 0.55),
    0 0 14px rgba(255, 255, 255, 0.25);
  pointer-events: none;
  user-select: none;
  transition: color 0.15s ease;
}
.hud-timer-hidden {
  display: none;
}
.hud-timer-low {
  color: var(--red);
  text-shadow:
    0 0 6px rgba(255, 68, 102, 0.7),
    0 0 14px rgba(255, 68, 102, 0.35);
  animation: hud-timer-pulse 1s ease-in-out infinite;
}
@keyframes hud-timer-pulse {
  0%,
  100% {
    transform: translate(-50%, -50%) scale(1);
  }
  50% {
    transform: translate(-50%, -50%) scale(1.08);
  }
}

/* Damage projectile */
.hud-projectile {
  z-index: 9999;
  pointer-events: none;
  /* Promote to its own compositor layer so the translate flight stays smooth
     on Android WebView even while the AI turn churns the main thread. */
  will-change: transform;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #d71921;
  box-shadow:
    0 0 8px 2px #d71921,
    0 0 16px 4px rgba(255, 68, 102, 0.5);
}

/* ---- Board ---- */
.board-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  position: relative;
}
.board-inner {
  position: relative;
  width: var(--board-w, 100%);
  height: var(--board-h, 100%);
}
.board-bg {
  display: none;
}
.board-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
}
.board-grid {
  position: absolute;
  inset: 0;
  z-index: 1;
}
.board-cell {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
}
.board-cell:focus-visible {
  outline: 2px solid #f6df91;
  outline-offset: 2px;
  border-radius: 4px;
  z-index: 8;
}
.board-cell.highlight {
  background: rgba(255, 255, 255, 0.12);
  border-radius: 4px;
}
.board-cell.king-target {
  background: rgba(215, 25, 33, 0.3);
  border-radius: 4px;
  cursor: pointer;
}
.board-cell.king-target .board-card {
  box-shadow: 0 0 8px 2px rgba(215, 25, 33, 0.6);
}
.board-cell.is-inscribed {
  --inscription-color: #e8c36a;
}
.board-cell.is-inscribed-opponent:not(.is-inscribed-mine) {
  --inscription-color: #a98bdd;
}
.board-cell.is-inscribed-mine.is-inscribed-opponent {
  --inscription-color: #f0cc78;
}
.board-cell.is-inscribed-row::before,
.board-cell.is-inscribed-col::after {
  content: '';
  position: absolute;
  pointer-events: none;
  z-index: 2;
  background: var(--inscription-color);
  box-shadow: 0 0 5px color-mix(in srgb, var(--inscription-color) 72%, transparent);
  opacity: 0.82;
}
.board-cell.is-inscribed-row::before {
  left: -2px;
  right: -2px;
  top: 50%;
  height: 2px;
  transform: translateY(-50%);
}
.board-cell.is-inscribed-col::after {
  top: -2px;
  bottom: -2px;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
}
.board-inscription-markers {
  position: absolute;
  top: 2px;
  right: 2px;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  pointer-events: none;
}
.board-inscription-marker {
  min-width: 12px;
  height: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding-inline: 2px;
  border: 1px solid currentColor;
  border-radius: 2px;
  background: rgba(9, 12, 15, 0.88);
  font-family: var(--font-mono);
  font-size: 7px;
  font-weight: 900;
  line-height: 1;
  color: #e8c36a;
}
.board-inscription-marker--opponent {
  color: #bca3ed;
}
.board-inscription-tooltip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 5px);
  z-index: 30;
  width: max-content;
  max-width: min(220px, 78vw);
  padding: 5px 7px;
  border: 1px solid rgba(232, 195, 106, 0.7);
  border-radius: 3px;
  background: rgba(7, 9, 12, 0.96);
  color: #f6f0df;
  font-family: var(--font-mono);
  font-size: 9px;
  line-height: 1.35;
  text-align: left;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 3px);
  transition:
    opacity 0.12s ease,
    transform 0.12s ease;
}
.board-cell:hover .board-inscription-tooltip,
.board-cell:focus .board-inscription-tooltip,
.board-cell:focus-visible .board-inscription-tooltip {
  opacity: 1;
  transform: translate(-50%, 0);
}
.board-card.king-pending {
  opacity: 0.5;
  transform: scale(0.92);
}

/* ---- Board card ---- */
.board-card {
  width: 85%;
  aspect-ratio: 0.714;
  max-height: 92%;
  border-radius: 5px;
  background-color: #fff;
  background-size: cover;
  background-position: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  position: relative;
  overflow: hidden;
  contain: layout style paint;
  transform: translateZ(0);
}
.board-card-rank {
  font-family: var(--card-rank-font);
  font-size: var(--fs-board-rank, 12px);
  font-weight: var(--card-rank-weight);
  letter-spacing: var(--card-rank-track);
  /* Trailing letter-spacing pushes a 2-glyph rank ("10") off-centre — pull it
     back so the number optically sits where a single glyph does. */
  padding-right: var(--card-rank-track);
  color: var(--card-text);
  line-height: 1;
  position: absolute;
  /* Percentages, not px: the rank has to keep the same clearance from the pip
     on a 7x7 cell as on a 5x5 one. Fixed 3px/4px crowded the small card, which
     is what made the rank "almost touch the suit". */
  top: 6%;
  left: 8%;
  z-index: 1;
}
.board-card-suit {
  width: var(--fs-board-suit, 24px);
  height: var(--fs-board-suit, 24px);
  object-fit: contain;
  position: relative;
  /* Drops the pip clear of the inset rank. Transform, so it composites. */
  transform: translateY(9%);
  z-index: 1;
}
.board-card.ai-card {
  background-color: #000;
  opacity: 0.85;
  border: none;
}
.board-card.ai-card .board-card-rank {
  color: #fff;
  text-shadow: none;
}
.board-card.ai-card .board-card-suit {
  filter: brightness(0) invert(1);
}

/* ---- Gambit: face-down card-back ----
   Every concealed card uses one anonymous back. card-back--mine adds a compact
   identity tab with the real rank/suit — safe ONLY because the caller derives
   ownership from the local player's board, never from a redacted card value. */
.board-card.card-back {
  background-color: #121011;
  background-image: url('/src/assets/cards/gambit-card-back-v1.webp');
  background-position: center;
  background-size: 100% 100%;
  background-repeat: no-repeat;
  border: 1px solid rgba(201, 170, 113, 0.5);
}
.board-card.card-back--mine {
  border-color: #d7ad5d;
  box-shadow: 0 0 0 1px rgba(215, 173, 93, 0.3);
}
.card-back-peek {
  position: absolute;
  inset-inline: 1px;
  bottom: 2px;
  min-height: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1px;
  padding: 1px;
  border: 1px solid rgba(54, 39, 33, 0.48);
  border-radius: 2px;
  background: #f0e7d8;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
  z-index: 1;
}
.card-back-peek-rank {
  font-family: var(--card-rank-font);
  font-size: clamp(9px, calc(var(--fs-board-rank, 10px) * 1.08), 13px);
  font-weight: 750;
  color: #181417;
  line-height: 1;
}
.card-back-peek[data-suit='hearts'] .card-back-peek-rank,
.card-back-peek[data-suit='diamonds'] .card-back-peek-rank {
  color: #bd1620;
}
.card-back-peek-suit {
  width: clamp(7px, calc(var(--fs-board-suit, 14px) * 0.55), 11px);
  height: clamp(7px, calc(var(--fs-board-suit, 14px) * 0.55), 11px);
  object-fit: contain;
}
/* ---- Hand card ---- */
.hand-card {
  width: var(--hand-card-w, 36px);
  height: var(--hand-card-h, 50px);
  border-radius: 5px;
  background-color: #fff;
  overflow: visible;
  position: relative;
  cursor: grab;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  flex-shrink: 0;
  touch-action: none;
  padding: 3px 1px;
  transition: transform 0.1s;
}
.hand-card:focus-visible,
.hand-card.keyboard-selected {
  outline: 2px solid #f6df91;
  outline-offset: 3px;
  transform: translateY(-5px);
}
.hand-card.keyboard-selected {
  box-shadow: 0 0 14px rgba(246, 223, 145, 0.58);
}
.hand-card.unaffordable {
  opacity: 0.35;
  cursor: not-allowed;
}
/* Cards locked while the Architect wildcard is the only legal play. */
.hand-card.locked {
  opacity: 0.3;
  filter: grayscale(0.7);
  cursor: not-allowed;
  pointer-events: none;
}
/* The Architect wildcard — rainbow, pulsing, demands placement. */
.hand-card.wildcard,
.ghost-card.wildcard {
  background: linear-gradient(135deg, #ff5f6d, #ffc371, #47e891, #4facfe, #c471f5);
  background-size: 300% 300%;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 10px rgba(196, 113, 245, 0.7);
  animation: wildcard-shimmer 2.4s ease infinite;
}
.hand-card.wildcard {
  animation:
    wildcard-shimmer 2.4s ease infinite,
    wildcard-pulse 1.1s ease-in-out infinite;
}
.hand-card.wildcard .hand-card-rank,
.ghost-card.wildcard .ghost-rank {
  font-size: 1.5em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
@keyframes wildcard-shimmer {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}
@keyframes wildcard-pulse {
  0%,
  100% {
    transform: scale(1);
    box-shadow: 0 0 8px rgba(196, 113, 245, 0.6);
  }
  50% {
    transform: scale(1.08);
    box-shadow: 0 0 18px rgba(196, 113, 245, 0.95);
  }
}
/* Architect wildcard rendered on the board — gold joker star, no suit. */
.board-card.wildcard {
  background: linear-gradient(135deg, #fff6d8, #ffd966);
  color: #8a6400;
  display: flex;
  align-items: center;
  justify-content: center;
}
.board-card.wildcard .board-card-rank {
  font-size: 1.4em;
  line-height: 1;
}
.hand-card-rank {
  font-family: var(--card-rank-font);
  /* --fs-rank, not --fs-board-rank: the hand card has its own width, and the
     tray rescales it independently (dom-hand-renderer `_applyFit`). Reading the
     board metric here sized the hand rank off the wrong card. */
  font-size: var(--fs-rank, 10px);
  font-weight: var(--card-rank-weight);
  letter-spacing: var(--card-rank-track);
  padding-right: var(--card-rank-track);
  color: var(--card-text, #fff);
  line-height: 1;
  position: absolute;
  top: 5%;
  left: 7%;
  z-index: 1;
}
.hand-card-suit {
  width: var(--fs-suit, 16px);
  height: var(--fs-suit, 16px);
  object-fit: contain;
  position: relative;
  transform: translateY(9%);
  z-index: 1;
}
.hand-card-cost {
  font-family: var(--card-rank-font);
  font-size: var(--fs-cost, 8px);
  font-weight: 700;
  color: #fff;
  line-height: 1;
  position: absolute;
  bottom: -5px;
  right: -5px;
  z-index: 10;
  width: clamp(14px, 35%, 18px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: #d71921;
  display: flex;
  align-items: center;
  justify-content: center;
}
.game-hand-tray {
  isolation: auto;
}
.game-hand-tray .hand-card:hover {
  z-index: 20;
}

/* ---- Ghost card (drag) ---- */
.ghost-card {
  position: fixed;
  z-index: 100;
  pointer-events: none;
  width: var(--hand-card-w, 36px);
  height: var(--hand-card-h, 50px);
  border-radius: 5px;
  background-color: #fff;
  overflow: visible;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  opacity: 0.85;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  padding: 3px 1px;
  transform: scale(1.04);
}
.ghost-card.visible {
  display: flex;
}
.ghost-rank {
  font-family: var(--card-rank-font);
  /* The drag ghost is the hand card you just picked up — same metric. */
  font-size: var(--fs-rank, 10px);
  font-weight: var(--card-rank-weight);
  letter-spacing: var(--card-rank-track);
  padding-right: var(--card-rank-track);
  color: var(--card-text, #fff);
  line-height: 1;
  position: absolute;
  top: 5%;
  left: 7%;
  z-index: 1;
}
.ghost-suit {
  width: var(--fs-suit, 16px);
  height: var(--fs-suit, 16px);
  object-fit: contain;
  position: relative;
  z-index: 1;
}

/* ---- Focus Medallion: one-card damage mark ----
   A static sigil follows the physical card from hand to drag ghost to its
   face-up board slot. Concealed cards intentionally get no marker at all, so
   Gambit never reveals which anonymous card carries the Focus investment. */
.hand-card.loadout-marked,
.ghost-card.loadout-marked,
.board-card.loadout-marked {
  box-shadow:
    inset 0 0 0 1px rgba(235, 125, 255, 0.74),
    0 0 9px rgba(112, 218, 255, 0.34);
}
.card-loadout-mark {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  width: 88%;
  height: 88%;
  object-fit: contain;
  opacity: 0.78;
  pointer-events: none;
  transform: translate(-50%, -50%);
  filter: drop-shadow(0 0 3px rgba(232, 118, 255, 0.72));
}
.loadout-marked .hand-card-rank,
.loadout-marked .hand-card-suit,
.loadout-marked .ghost-rank,
.loadout-marked .ghost-suit,
.loadout-marked .board-card-rank,
.loadout-marked .board-card-suit {
  z-index: 3;
}
.card-loadout-mark-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---- Game layout ---- */
.game-root {
  width: 100%;
  height: 100%;
  max-width: 480px;
  margin: 0 auto;
  position: relative;
  background: #000;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  overflow: hidden;
  /* Safe-area insets applied at #app level (dynamic — notch/Dynamic Island/SE status bar).
     Bottom handled inside .game-bottom-area so gray panel reaches the home-indicator. */
  box-sizing: border-box;
}

/* NB: the 480px column is deliberate on tablets too, and was re-tested here.
   Widening it to 600 on large portrait screens does buy bigger cards, but the
   board's height is 1.4x its width, so every pixel of extra width costs 1.4 of
   scene: on a 768x1024 iPad the band fell from 315px to 186px and the fighters
   ended up squeezed under the HUD. The scene is what makes the screen feel like
   a battlefield, and it now runs full-bleed behind the column (see .bf-stage),
   so a tablet reads as a wide scene with a comfortable board rather than as a
   stretched phone. Cards also stay the same size across every device. */

/* Pure black background — all zones float on black */
.game-bg {
  display: none;
}

/* ---- HUD zone (top) ---- */
.hud-wrapper {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--hud-zone-h, 14vh);
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 3%;
  overflow: visible;
  pointer-events: none;
}
.hud-banner {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 12px;
  overflow: hidden;
  clip-path: inset(0 round 12px);
  contain: paint;
  isolation: isolate;
  /* No backdrop-filter: the banner floats over the live PIXI battlefield canvas
     which repaints every frame, so a blur here would re-run continuously while
     the banner is up. The background is already 85% opaque — the blur behind it
     was barely visible, so dropping it is effectively lossless. */
  background: rgba(26, 40, 64, 0.88);
  border: 1px solid rgba(255, 255, 255, 0.08);
  pointer-events: auto;
}

/* Toasts disabled — animated over-board text will replace later */
.game-feedback {
  display: none;
}
.gambit-outcome-banner:empty {
  display: none;
}
.gambit-outcome-banner {
  position: absolute;
  z-index: 12;
  top: max(88px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  max-width: min(92vw, 560px);
  padding: 8px 12px;
  border: 1px solid color-mix(in srgb, var(--yellow) 70%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, #111 86%, transparent);
  color: var(--yellow);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-align: center;
}
.placement-intent-preview[hidden] {
  display: none;
}
.placement-intent-preview {
  position: absolute;
  z-index: 11;
  top: calc(var(--hud-zone-h, 14vh) + 8px);
  left: 50%;
  width: min(calc(100% - 20px), 430px);
  padding: 7px 9px 6px;
  transform: translateX(-50%);
  border: 1px solid oklch(0.84 0.09 83 / 0.62);
  border-radius: 10px;
  background: oklch(0.16 0.018 72 / 0.94);
  box-shadow: 0 5px 18px oklch(0.04 0.01 72 / 0.48);
  color: oklch(0.93 0.012 82);
  font-family: var(--font-mono);
  pointer-events: none;
}
.placement-intent-preview__headline {
  display: flex;
  align-items: baseline;
  gap: 7px;
  min-width: 0;
}
.placement-intent-preview__eyebrow,
.placement-intent-preview__note {
  color: oklch(0.72 0.025 75);
  font-size: 8px;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.placement-intent-preview__base {
  color: oklch(0.88 0.13 88);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.placement-intent-preview__mode {
  margin-inline-start: auto;
  color: oklch(0.83 0.11 154);
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.045em;
  text-align: end;
}
.placement-intent-preview[data-resolution='word'] .placement-intent-preview__mode {
  color: oklch(0.86 0.12 83);
}
.placement-intent-preview__chain {
  margin-top: 2px;
  color: oklch(0.82 0.07 247);
  font-size: 9px;
  font-variant-numeric: tabular-nums;
  font-weight: 750;
}
.placement-intent-preview__axes {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 8px;
  margin-top: 2px;
}
.placement-intent-preview__axis {
  color: oklch(0.9 0.018 75);
  font-size: 9px;
  font-variant-numeric: tabular-nums;
  line-height: 1.25;
}
.placement-intent-preview__note {
  display: block;
  margin-top: 3px;
  line-height: 1.15;
}
.game-root.has-battle-stage .placement-intent-preview {
  top: calc(var(--band-h) - 6px);
  transform: translate(-50%, -100%);
}
@media (max-width: 340px) {
  .placement-intent-preview {
    width: calc(100% - 12px);
    padding-inline: 7px;
  }
  .placement-intent-preview__headline {
    gap: 5px;
  }
  .placement-intent-preview__eyebrow {
    display: none;
  }
  .placement-intent-preview__base {
    font-size: 11px;
  }
  .placement-intent-preview__mode,
  .placement-intent-preview__axis,
  .placement-intent-preview__chain {
    font-size: 8px;
  }
  .placement-intent-preview__note {
    font-size: 7px;
  }
}
.hud-board-spacer {
  display: none;
}

/* ---- Board zone — gap from HUD, larger gap before bottom panel ---- */
.board-wrap {
  position: absolute;
  top: calc(var(--hud-zone-h, 14vh) + var(--zone-gap, 1.5vh));
  left: 0;
  right: 0;
  /* Shorter by --board-bottom-gap so there's a visible strip between the
     board's bottom edge and the top of the arcanium/hand gray panel —
     requested in the UX pass so the two surfaces don't read as one. */
  height: calc(var(--board-zone-h, 62vh) - var(--board-bottom-gap, 12px));
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  transition:
    height 0.3s ease,
    top 0.3s ease;
}

/* ---- Action row (ARCANIUM + Pause) — bisects bottom panel top edge ---- */
.game-action-row {
  position: absolute;
  bottom: calc(var(--bottom-zone-h, 22vh) - 22px);
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  padding: 0 16px;
  z-index: 5;
  transition: bottom 0.3s ease;
}

/* ---- Bottom area — full-width gray panel flush to screen bottom. Top edge bisects arcanum button. ---- */
.game-bottom-area {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--bottom-zone-h, 22vh);
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  background: rgb(50, 50, 50);
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  /* top padding = arcanum btn inner-half (22px) + tight breathing */
  padding: 28px 10px calc(16px + env(safe-area-inset-bottom));
  z-index: 3;
  overflow: hidden;
  transition: height 0.3s ease;
}
.arcanium-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 24px;
  border: none;
  border-radius: 125px;
  background: #d71921;
  cursor: pointer;
  font-family: var(--font-mono);
  transition: all 0.12s;
  min-height: 44px;
  justify-content: center;
}
.arcanium-btn:active {
  transform: scale(0.95);
}
.arcanium-icon {
  width: 14px;
  height: 14px;
  object-fit: contain;
}
.arcanium-label {
  font-family: var(--font-rank);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: #fff;
}
.gambit-btn {
  position: relative;
  min-width: 48px;
  min-height: 48px;
  padding: 10px 18px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 125px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: #f4f0e8;
  color: var(--red);
  cursor: pointer;
  transition: transform 0.12s;
  white-space: nowrap;
}
.gambit-btn:active {
  transform: scale(0.95);
}
.gambit-btn:focus-visible,
.gambit-rule-toggle:focus-visible,
.gambit-action-row:focus-visible,
.gambit-back-btn:focus-visible,
.gambit-raise-btn:focus-visible,
.gambit-declare-btn:focus-visible,
.gambit-stake-step:focus-visible,
.gambit-stake-range:focus-visible {
  outline: 3px solid #fff;
  outline-offset: 3px;
  box-shadow: 0 0 0 5px var(--red);
}
.gambit-btn--armed {
  border-color: #df3c45;
  background: #fff7ef;
}
.gambit-btn--targeting {
  outline: 2px solid var(--red);
  outline-offset: 2px;
}
.gambit-targeting .gambit-icon::after {
  content: '◎';
  margin-inline-start: 3px;
  font-size: 10px;
  vertical-align: middle;
}
.gambit-icon {
  font-size: 18px;
  line-height: 1;
}
.gambit-label {
  font-family: var(--font-rank);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.gambit-armed-indicator {
  position: static;
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  min-height: 14px;
  padding: 2px 4px;
  border: 1px solid rgba(119, 13, 20, 0.42);
  border-radius: 999px;
  background: #b91620;
  color: #fff8f0;
  font-family: var(--font-mono);
  font-size: 7px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.06em;
}
.gambit-armed-indicator[hidden] {
  display: none;
}
.pause-btn {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: #fff;
  border: none;
  cursor: pointer;
}
/* 40px visual, ~48px hit area — the leave/pause control during a live match. */
.pause-btn::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
}
.pause-btn:active {
  transform: scale(0.95);
}
.pause-bar {
  width: 3px;
  height: 14px;
  background: var(--text);
  border-radius: 1px;
}
.pause-icon {
  width: 14px;
  height: 14px;
  object-fit: contain;
}
/* ---- Hand tray (inside bottom area) ---- */
.game-hand-tray-wrap {
  flex: 0 0 auto;
  padding: 2px 8px 0;
  transition:
    transform 0.3s ease,
    opacity 0.3s ease;
}
.game-hand-info {
  font-family: var(--font-mono);
  font-size: 9px;
  text-align: center;
  color: var(--text-muted);
  padding: 1px 0;
}
.game-hand {
  display: flex;
  justify-content: center;
  overflow: visible;
  align-items: flex-end;
  padding: 2px 0;
}
.game-hand-tray {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: var(--hand-card-gap, 4px);
  padding: 2px 8px;
}

/* ---- Gambit: temporary sheet decisions ----
   Incoming Call/Fold and optional Raise/Play Normally replace the ordinary
   Gambit panel children. No body-level overlay or second sheet is mounted. */
.gambit-raise-panel {
  display: flex;
  min-height: 0;
  flex: 0 1 auto;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  gap: 8px;
  width: min(100%, 760px);
  margin-inline: auto;
  padding: 8px 0;
}
.gambit-raise-title {
  font-family: var(--font-mono);
  font-size: clamp(12px, 2svh, 14px);
  font-weight: 900;
  letter-spacing: 0.08em;
  color: #fff;
  text-align: left;
}
.gambit-raise-claim {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  text-align: left;
}
.gambit-raise-contract {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 3px;
  background: rgba(8, 11, 14, 0.54);
}
.gambit-raise-contract__row {
  min-width: 0;
  padding: 7px 9px;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: 2px 8px;
  border-inline-end: 1px solid rgba(255, 255, 255, 0.12);
}
.gambit-raise-contract__row:last-child {
  border-inline-end: 0;
}
.gambit-raise-contract__row.is-fold {
  box-shadow: inset 3px 0 0 #c83e48;
}
.gambit-raise-contract__row.is-call {
  box-shadow: inset 3px 0 0 #55ad69;
}
.gambit-raise-contract__action {
  color: rgba(245, 239, 230, 0.56);
  font-family: var(--font-mono);
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.1em;
}
.gambit-raise-contract__value {
  color: #fff8ee;
  font-family: var(--font-mono);
  font-size: 10px;
  text-align: end;
  white-space: nowrap;
}
.gambit-raise-contract__note {
  grid-column: 1 / -1;
  overflow: hidden;
  color: rgba(245, 239, 230, 0.55);
  font-size: 8px;
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.word-window-axes {
  display: grid;
  gap: 5px;
}
.word-window-axis {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 5px 7px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-inline-start: 3px solid #e8c36a;
  border-radius: 3px;
  background: rgba(8, 11, 14, 0.48);
}
.word-window-axis--append {
  border-inline-start-color: #8f71d2;
}
.word-window-axis__identity {
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-family: var(--font-mono);
}
.word-window-axis__line {
  flex: 0 0 auto;
  color: var(--text-muted);
  font-size: 8px;
  letter-spacing: 0.09em;
}
.word-window-axis__combo {
  min-width: 0;
  color: #fff;
  font-size: 10px;
  font-weight: 900;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.word-window-axis__kind,
.word-window-chain {
  padding: 3px 5px;
  border: 1px solid rgba(232, 195, 106, 0.5);
  border-radius: 2px;
  color: #f2d98b;
  font-family: var(--font-mono);
  font-size: 8px;
  font-weight: 900;
  letter-spacing: 0.06em;
}
.word-window-axis--append .word-window-axis__kind {
  border-color: rgba(169, 139, 221, 0.7);
  color: #d6c4f5;
}
.word-window-axis__values {
  color: #d9d4cb;
  font-family: var(--font-mono);
  font-size: 8px;
  white-space: nowrap;
}
.word-window-summary {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  color: #fff;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.04em;
}
.word-window-chain-formula {
  color: #f2d98b;
  font-variant-numeric: tabular-nums;
  font-weight: 900;
}
.word-window-base-hint {
  margin-top: -4px;
  color: var(--text-muted);
  font-size: 9px;
  line-height: 1.3;
}
.word-window-panel .gambit-timer-ring {
  flex: 0 0 auto;
}
.gambit-raise-buttons {
  display: flex;
  gap: 8px;
  width: 100%;
  margin-top: 2px;
}
.gambit-raise-btn {
  flex: 1;
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 3px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: none;
  transition:
    transform 0.12s,
    filter 0.12s,
    box-shadow 0.12s;
}
.gambit-raise-btn:active {
  transform: translateY(1px);
  box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.18);
}
.gambit-raise-btn--call {
  background: rgba(57, 148, 84, 0.2);
  border-color: #55ad69;
  color: #e5f5e8;
}
.gambit-raise-btn--fold {
  background: rgba(255, 255, 255, 0.04);
  color: #f2efec;
  border-color: rgba(255, 255, 255, 0.2);
}
.gambit-decision-icon {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  border-radius: 2px;
  font-family: var(--font-mono);
  font-size: 13px;
  font-weight: 900;
  line-height: 1;
}
.gambit-decision-icon::before {
  display: block;
}
.gambit-decision-icon[data-gambit-action='call'] {
  background: #2c8742;
  color: #f7fbf7;
}
.gambit-decision-icon[data-gambit-action='call']::before {
  content: '\2713';
}
.gambit-decision-icon[data-gambit-action='fold'] {
  background: rgba(255, 255, 255, 0.14);
  color: #f2efec;
}
.gambit-decision-icon[data-gambit-action='fold']::before {
  content: '\00d7';
}
.gambit-decision-icon[data-word-action='proclaim'] {
  background: #2c8742;
  color: #f7fbf7;
}
.gambit-decision-icon[data-word-action='proclaim']::before {
  content: '\2726';
}
.gambit-decision-icon[data-word-action='inscribe'] {
  background: rgba(143, 113, 210, 0.4);
  color: #eee7fb;
}
.gambit-decision-icon[data-word-action='inscribe']::before {
  content: '\25c7';
}
.gambit-decision-label {
  min-width: 0;
}

/* Ring timer — a static decorative conic-gradient track (painted ONCE, never
   re-painted per frame) plus a sweep arm whose only animated property is
   `transform: rotate()`, driven by a plain CSS animation so it stays on the
   compositor. NEVER animate stroke-dashoffset on an SVG ring here — that's a
   paint cost per frame on Android WebView (see CLAUDE.md). The numeric
   readout is the authoritative, accessible countdown. */
.gambit-timer-ring {
  position: relative;
  width: 34px;
  height: 34px;
  border-radius: 3px;
  background: repeating-conic-gradient(rgba(255, 255, 255, 0.16) 0deg 6deg, transparent 6deg 18deg);
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.gambit-timer-ring__arm {
  position: absolute;
  inset: 0;
  transform-origin: 50% 50%;
  animation-name: gambit-ring-sweep;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}
.gambit-timer-ring__arm::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 50%;
  width: 3px;
  height: 9px;
  margin-left: -1.5px;
  border-radius: 2px;
  background: var(--red);
}
.gambit-timer-ring__value {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: #fff;
}
@keyframes gambit-ring-sweep {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .gambit-timer-ring__arm {
    animation: none;
  }
}

/* ---- Deterministic intent + authoritative combat ledger (Core v2) ---- */
.combo-reference-btn {
  min-width: 48px;
  min-height: 48px;
  padding: 8px 14px;
  border: 1px solid rgba(214, 184, 128, 0.48);
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  background: #171b1f;
  color: #f5ead7;
  cursor: pointer;
  white-space: nowrap;
  transition:
    transform 120ms ease,
    border-color 120ms ease;
}
.combo-reference-btn:active {
  transform: scale(0.95);
}
.combo-reference-btn:focus-visible,
.combo-reference-back-btn:focus-visible,
.gambit-claim-option:focus-visible {
  outline: 3px solid #f4ead8;
  outline-offset: 3px;
  box-shadow: 0 0 0 5px #8f1922;
}
.combo-reference-icon {
  min-width: 24px;
  padding: 3px 4px;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 3px;
  background: #f4efe6;
  color: #8f1922;
  font-family: var(--font-rank);
  font-size: 10px;
  font-weight: 800;
  line-height: 1;
  transform: rotate(-3deg);
}
.combo-reference-label {
  font-family: var(--font-rank);
  font-size: 10px;
  font-weight: 750;
  letter-spacing: 0.1em;
}

.combo-reference-panel {
  display: none;
  flex: 1 1 auto;
  min-height: 0;
  flex-direction: column;
  gap: 10px;
  padding: 0 12px max(10px, env(safe-area-inset-bottom));
  color: #f5efe6;
  overflow: hidden;
  contain: layout style;
}
.combo-reference-content {
  width: min(100%, 780px);
  flex: 1 1 0;
  min-height: 0;
  margin-inline: auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  -webkit-overflow-scrolling: touch;
}
.combo-reference-ledger {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-bottom: 6px;
}
.combat-reference-intro {
  padding: 2px 0 10px;
  border-bottom: 1px solid rgba(214, 184, 128, 0.35);
}
.combat-reference-eyebrow {
  display: block;
  margin-bottom: 4px;
  color: #d6b880;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.16em;
}
.combat-reference-title {
  margin: 0;
  color: #fff8ee;
  font-family: var(--font-rank);
  font-size: clamp(20px, 5vw, 30px);
  line-height: 1;
  letter-spacing: 0.04em;
}
.combat-reference-subtitle {
  max-width: 62ch;
  margin: 7px 0 0;
  color: rgba(245, 239, 230, 0.68);
  font-size: 11px;
  line-height: 1.45;
}
.combat-reference-modifiers {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 4px;
  background: #11161a;
}
.combat-reference-modifier {
  min-width: 0;
  padding: 9px 10px;
  display: grid;
  gap: 2px;
  border-inline-end: 1px solid rgba(255, 255, 255, 0.12);
}
.combat-reference-modifier:last-child {
  border-inline-end: 0;
}
.combat-reference-modifier__label,
.combat-reference-modifier__note {
  overflow: hidden;
  color: rgba(245, 239, 230, 0.58);
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.08em;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.combat-reference-modifier__value {
  color: #f2c66e;
  font-family: var(--font-rank);
  font-size: 18px;
  line-height: 1;
}
.combat-reference-formula {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 4px 12px;
  padding: 10px 12px;
  border-inline-start: 3px solid #9d2730;
  background: #211619;
  color: rgba(255, 247, 236, 0.74);
  font-size: 10px;
  line-height: 1.35;
}
.combat-reference-formula strong {
  grid-row: 1 / 4;
  color: #f2c66e;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
}
.combat-reference-formula code {
  color: #fff8ee;
  font-family: var(--font-mono);
  white-space: normal;
}
.combat-reference-causality {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid rgba(214, 184, 128, 0.3);
  border-radius: 3px;
  background: #15191c;
}
.combat-reference-causality__rule {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 4px;
}
.combat-reference-causality__label {
  color: #d6b880;
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.1em;
}
.combat-reference-causality__formula {
  color: #fff8ee;
  font-family: var(--font-mono);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  white-space: normal;
}
.combat-reference-causality__text {
  color: rgba(245, 239, 230, 0.68);
  font-size: 9px;
  line-height: 1.4;
}
.combat-reference-rows {
  border-top: 1px solid rgba(255, 255, 255, 0.14);
}
.combat-reference-row {
  display: grid;
  grid-template-columns: 30px minmax(0, 1.3fr) minmax(176px, 0.8fr);
  align-items: center;
  gap: 10px;
  min-height: 86px;
  padding: 9px 6px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(11, 15, 18, 0.48);
}
.combat-reference-row.is-disabled {
  opacity: 0.46;
}
.combat-reference-rank {
  align-self: stretch;
  display: grid;
  place-items: center;
  border-inline-end: 1px solid rgba(214, 184, 128, 0.28);
  color: #d6b880;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 800;
}
.combat-reference-identity {
  min-width: 0;
}
.combat-reference-name {
  margin: 0;
  color: #fff8ee;
  font-family: var(--font-rank);
  font-size: 14px;
  letter-spacing: 0.035em;
}
.combat-reference-recipe {
  margin: 3px 0 6px;
  color: rgba(245, 239, 230, 0.58);
  font-size: 9px;
  line-height: 1.25;
}
.combat-reference-pattern {
  height: 30px;
  display: flex;
  align-items: center;
  padding-inline-start: 2px;
}
.combat-reference-card {
  width: 23px;
  height: 29px;
  margin-inline-start: -3px;
  border: 1px solid rgba(20, 24, 27, 0.34);
  border-radius: 3px;
  display: grid;
  place-items: center;
  background: #f3eee5;
  color: #171b1f;
  box-shadow: 2px 1px 0 rgba(0, 0, 0, 0.26);
  font-family: var(--font-rank);
  font-size: 8px;
  font-weight: 800;
}
.combat-reference-card.is-red {
  color: #a51e28;
}
.combat-reference-numbers {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 4px;
  margin: 0;
}
.combat-reference-stat {
  min-width: 0;
  padding: 5px 6px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: #171c20;
}
.combat-reference-stat dt {
  color: rgba(245, 239, 230, 0.52);
  font-family: var(--font-mono);
  font-size: 7px;
  letter-spacing: 0.08em;
}
.combat-reference-stat dd {
  margin: 2px 0 0;
  color: #f7efe2;
  font-family: var(--font-mono);
  font-size: 10px;
  font-variant-numeric: tabular-nums;
  font-weight: 750;
}
.combat-reference-stat.is-primary {
  border-color: rgba(184, 50, 61, 0.48);
  background: #25171a;
}
.combat-reference-stat.is-primary dd {
  color: #ffb0a9;
}
.combat-reference-disabled {
  display: inline-block;
  margin-top: 4px;
  color: #d6b880;
  font-family: var(--font-mono);
  font-size: 7px;
  letter-spacing: 0.08em;
}
.combat-reference-note {
  padding: 11px 12px;
  border: 1px solid rgba(214, 184, 128, 0.28);
  background: #171a1c;
  color: rgba(245, 239, 230, 0.68);
  font-size: 10px;
  line-height: 1.45;
}
.combat-reference-note strong {
  color: #f2c66e;
}
.combo-reference-back-btn {
  align-self: center;
  min-width: 150px;
  min-height: 48px;
  flex: 0 0 auto;
  padding: 8px 28px;
  border-color: rgba(214, 184, 128, 0.42);
  border-radius: 3px;
  background: #161a1d;
  color: #f5ead7;
}

.gambit-claim-status {
  color: #d6b880;
  font-family: var(--font-mono);
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.11em;
}
.gambit-claim-chooser {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
  min-height: 48px;
  flex: 0 0 auto;
  max-height: 132px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.gambit-claim-option {
  min-width: 0;
  padding: 7px 8px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 3px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 8px;
  text-align: start;
  background: #171b1f;
  color: #f4efe7;
  cursor: pointer;
}
.gambit-claim-option.is-selected {
  border-color: #df525c;
  box-shadow: inset 3px 0 0 #b91e29;
  background: #26171a;
}
.gambit-claim-option__combo {
  overflow: hidden;
  font-family: var(--font-rank);
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gambit-claim-option__axis {
  color: #d6b880;
  font-family: var(--font-mono);
  font-size: 8px;
}
.gambit-claim-option__meta {
  grid-column: 1 / -1;
  color: rgba(244, 239, 231, 0.58);
  font-family: var(--font-mono);
  font-size: 7px;
  letter-spacing: 0.04em;
}
.gambit-claim-lock {
  padding: 8px 10px;
  border: 1px solid rgba(214, 184, 128, 0.28);
  border-inline-start: 3px solid #b91e29;
  background: #1b1718;
}
.gambit-claim-lock__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.gambit-claim-lock__eyebrow,
.gambit-claim-lock__cells,
.gambit-claim-lock__result {
  color: rgba(244, 239, 231, 0.62);
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.08em;
}
.gambit-claim-lock__combo {
  color: #f2c66e;
  font-family: var(--font-rank);
  font-size: 13px;
}
.gambit-claim-lock__cells,
.gambit-claim-lock__result {
  margin-top: 4px;
}
.gambit-claim-lock__result {
  color: #ffaaa5;
  font-weight: 750;
}
.gambit-claim-lock__rule {
  margin: 6px 0 0;
  color: rgba(244, 239, 231, 0.54);
  font-size: 9px;
  line-height: 1.35;
}
.gambit-claim-lock__eligibility {
  margin: 4px 0 0;
  color: #d6b880;
  font-size: 9px;
  line-height: 1.35;
}
.word-window-axis__cells {
  display: block;
  margin-top: 3px;
  color: #d6b880;
  font-family: var(--font-mono);
  font-size: 7px;
  letter-spacing: 0.06em;
}

@media (max-width: 520px) {
  .combat-reference-causality {
    grid-template-columns: 1fr;
    gap: 8px;
  }
  .combat-reference-row {
    grid-template-columns: 26px minmax(0, 1fr);
  }
  .combat-reference-numbers {
    grid-column: 2;
  }
}
@media (max-width: 420px) {
  .combo-reference-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }
  .combo-reference-btn {
    padding-inline: 11px;
  }
  .combat-reference-modifier {
    padding-inline: 7px;
  }
  .gambit-claim-chooser {
    grid-template-columns: 1fr;
  }
  .gambit-raise-contract {
    grid-template-columns: 1fr;
  }
  .gambit-raise-contract__row {
    border-inline-end: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  }
  .gambit-raise-contract__row:last-child {
    border-bottom: 0;
  }
  .gambit-raise-contract__note {
    overflow: visible;
    text-overflow: initial;
    white-space: normal;
  }
}
@media (max-width: 420px) {
  .arcanium-btn {
    min-width: 48px;
    padding-inline: 12px;
  }
  .arcanium-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }
  .game-action-row {
    gap: 7px;
    padding-inline: 8px;
  }
  .combat-reference-formula {
    grid-template-columns: 1fr;
  }
  .combat-reference-formula strong {
    grid-row: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  .combo-reference-btn,
  .combo-reference-panel,
  .gambit-claim-option {
    transition: none !important;
  }
}

.gambit-declare-decision .gambit-timer-ring {
  flex: 0 0 auto;
  align-self: flex-start;
}
.gambit-declare-decision {
  display: flex;
  min-height: 0;
  flex: 0 1 auto;
  flex-direction: column;
  justify-content: flex-start;
  gap: 8px;
  width: min(100%, 760px);
  margin-inline: auto;
  padding: 8px 0;
}
.gambit-declare-title {
  font-family: var(--font-mono);
  font-size: clamp(12px, 2svh, 14px);
  font-weight: 900;
  letter-spacing: 0.08em;
  color: #fff;
  text-align: left;
}
.gambit-stake-control {
  display: grid;
  gap: 4px;
  padding: 7px 8px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.035);
}
.gambit-stake-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--font-mono);
  text-transform: uppercase;
}
.gambit-stake-label {
  color: #f4f0e8;
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.08em;
}
.gambit-stake-range-hint {
  color: rgba(244, 240, 232, 0.66);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
}
.gambit-stake-adjuster {
  display: grid;
  grid-template-columns: 48px minmax(80px, 1fr) 48px minmax(62px, auto);
  align-items: center;
  gap: 8px;
}
.gambit-stake-step {
  min-width: 48px;
  min-height: 48px;
  padding: 0;
  border: 1px solid rgba(239, 89, 96, 0.62);
  border-radius: 3px;
  background: rgba(215, 25, 33, 0.12);
  color: #fff8f5;
  font-family: var(--font-mono);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  transition:
    transform 0.12s,
    background-color 0.12s;
}
.gambit-stake-step:active:not(:disabled) {
  transform: translateY(1px);
  background: rgba(215, 25, 33, 0.24);
}
.gambit-stake-step:disabled {
  border-color: rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.025);
  color: rgba(244, 240, 232, 0.3);
  cursor: default;
}
.gambit-stake-range {
  width: 100%;
  min-width: 0;
  height: 48px;
  margin: 0;
  accent-color: var(--red);
  cursor: pointer;
}
.gambit-stake-number-wrap {
  display: flex;
  min-width: 72px;
  align-items: baseline;
  gap: 3px;
  padding-inline: 6px 4px;
  border: 1px solid rgba(239, 89, 96, 0.62);
  border-radius: 3px;
  background: rgba(12, 17, 18, 0.72);
  color: #fff8f5;
  font-family: var(--font-mono);
  white-space: nowrap;
}
.gambit-stake-number {
  width: 100%;
  min-width: 0;
  height: 46px;
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: inherit;
  font-family: inherit;
  font-size: 20px;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  text-align: right;
}
.gambit-stake-number-wrap:focus-within {
  outline: 3px solid #fff;
  outline-offset: 3px;
  box-shadow: 0 0 0 5px var(--red);
}
.gambit-stake-number__unit {
  color: rgba(244, 240, 232, 0.7);
  font-size: 10px;
  font-weight: 900;
  letter-spacing: 0.06em;
}
.gambit-declare-buttons {
  display: flex;
  flex-direction: row;
  gap: 8px;
  width: 100%;
}
.gambit-declare-btn {
  min-height: 48px;
  width: auto;
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 8px 12px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  box-shadow: none;
  transition:
    transform 0.12s,
    filter 0.12s,
    box-shadow 0.12s;
}
.gambit-declare-btn:active {
  transform: translateY(1px);
  box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.2);
}
.gambit-declare-btn--raise {
  background: rgba(215, 25, 33, 0.2);
  color: #fff8f5;
  border-color: #ef5960;
}
.gambit-declare-btn--place {
  background: rgba(255, 255, 255, 0.04);
  color: #f2efec;
  border-color: rgba(255, 255, 255, 0.2);
}
.gambit-decision-icon[data-gambit-action='raise'] {
  background: rgba(255, 255, 255, 0.18);
  color: #fff8f5;
}
.gambit-decision-icon[data-gambit-action='raise']::before {
  content: '\2191';
}
.gambit-decision-icon[data-gambit-action='continue'] {
  background: rgba(255, 255, 255, 0.14);
  color: #f2efec;
}
.gambit-decision-icon[data-gambit-action='continue']::before {
  content: '\2192';
}

/* ---- Arcanium panel (abilities view, inside bottom area) ----
   Layout strategy: when arcanum-open the bottom area grows so the panel never
   scrolls on a phone. The redistribution lives below under .arcanum-open.
   Internal sizing uses clamp(small, viewport-relative, comfortable) so a
   320×568 iPhone SE fits without scroll while a tall device still breathes. */
.arcanium-panel {
  display: none;
  flex-direction: column;
  gap: clamp(6px, 1.4svh, 12px);
  padding: 2px 12px 0;
  contain: layout style;
  transform: translateZ(0);
  flex: 1 1 auto;
  /* No vertical scroll — content must fit. If a future addition forces overflow,
     the right answer is to shrink content via clamp(), not enable scrolling. */
  overflow: hidden;
  justify-content: flex-start;
}
/* Arcanum mode: redistribute the three zones so the panel owns most of the
   screen. The action row + hand tray are hidden in this mode, so their share
   of the bottom area is free to absorb. Board shrinks to ~30svh — still
   recognizable, doesn't dominate. svh keeps things stable under iOS chrome. */
.game-root.arcanum-open .game-bottom-area {
  padding-top: clamp(8px, 1.6svh, 16px);
  padding-bottom: calc(clamp(6px, 1.2svh, 12px) + env(safe-area-inset-bottom));
}

/* ---- Modifier rail (will hold equipped Arcana once wired) ---- */
.arcanium-modifier-rail {
  display: flex;
  justify-content: space-between;
  gap: clamp(6px, 1.4svh, 10px);
  padding: 0;
  flex: 0 0 auto;
}
.modifier-circle {
  flex: 1 1 0;
  aspect-ratio: 1;
  /* clamp by svh so very short phones get smaller slots instead of overflow. */
  max-width: clamp(34px, 7svh, 56px);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modifier-circle.filled {
  background: #f2f2f2;
}
.modifier-circle.empty {
  background: var(--surface-2);
}
.modifier-icon {
  font-size: clamp(13px, 2.4svh, 18px);
  line-height: 1;
  color: #555;
}

.arcanium-content {
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1.2svh, 12px);
  flex: 1 1 auto;
  min-height: 0;
  /* Safety net: if abilities + the Architect button still exceed the panel on a
     short phone, scroll the list rather than overlap the BACK button below. */
  overflow-y: auto;
  padding-right: 2px;
}
.ability-btn {
  display: flex;
  align-items: center;
  gap: clamp(8px, 1.6svh, 12px);
  padding: clamp(4px, 0.9svh, 8px) clamp(10px, 2.4vw, 16px);
  border: none;
  border-radius: 20px;
  background: #fff;
  cursor: pointer;
  font-family: var(--font-mono);
  transition: transform 0.12s;
  min-height: clamp(38px, 7svh, 48px);
  width: 100%;
  text-align: left;
}
.ability-btn:active {
  transform: scale(0.98);
}
.ability-btn.disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
/* theArchitect's once-per-match wildcard button — gold to read as "legendary". */
.ability-btn.architect-btn {
  background: linear-gradient(135deg, #fff6d8, #ffe49b);
  box-shadow: 0 0 0 1px #d4a829 inset;
}
.ability-btn.architect-btn .ability-icon {
  background: linear-gradient(135deg, #d4a829, #b8860b);
}
.ability-btn.architect-btn .ability-name {
  color: #b8860b;
}
.ability-icon {
  flex: 0 0 auto;
  width: clamp(28px, 5svh, 34px);
  height: clamp(28px, 5svh, 34px);
  border-radius: 50%;
  background: #d71921;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ability-icon-glyph {
  color: #fff;
  font-size: clamp(14px, 2.4svh, 18px);
  line-height: 1;
}
.ability-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}
.ability-name {
  font-family: var(--font-rank);
  font-size: clamp(10px, 1.8svh, 12px);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #d71921;
}
.ability-desc {
  font-size: clamp(9px, 1.5svh, 10px);
  line-height: 1.2;
  color: #6b6b6b;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ability-meta {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  font-size: clamp(9px, 1.5svh, 10px);
  color: #6b6b6b;
}
.ability-cd-sep {
  color: #c4c4c4;
}
.arcanium-back-btn {
  align-self: center;
  margin-top: auto;
  width: auto;
  min-width: 140px;
  background: #d71921;
  border-color: #d71921;
  color: #fff;
  font-size: clamp(10px, 1.8svh, 12px);
  padding: clamp(6px, 1.2svh, 8px) 28px;
  border-radius: 20px;
  min-height: clamp(32px, 5.5svh, 36px);
  flex: 0 0 auto;
}

/* ---- Gambit action sheet ---- */
.gambit-panel {
  display: none;
  flex: 0 1 auto;
  min-height: 0;
  flex-direction: column;
  gap: 10px;
  padding: 10px 14px max(10px, env(safe-area-inset-bottom));
  contain: layout style;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.gambit-content,
.gambit-actions {
  display: flex;
  min-height: 0;
  flex: 0 1 auto;
  flex-direction: column;
  gap: 0;
}
.gambit-content,
.gambit-actions {
  width: min(100%, 760px);
  margin-inline: auto;
}
.gambit-actions {
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 4px;
  background: rgba(9, 12, 15, 0.54);
}
.gambit-command-dock-header {
  min-height: 34px;
  display: flex;
  align-items: center;
  padding: 0 10px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 900;
  letter-spacing: 0.14em;
  color: var(--text-muted);
}
.gambit-action-btn,
.gambit-action-row {
  --gambit-action-accent: #8f71d2;
  width: 100%;
  min-height: 60px;
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 0;
  border-inline-start: 3px solid var(--gambit-action-accent);
  background: transparent;
  color: #f4f0e8;
  text-align: left;
  cursor: pointer;
  box-shadow: none;
  transition:
    transform 0.12s,
    filter 0.12s,
    box-shadow 0.12s;
}
.gambit-conceal-action {
  --gambit-action-accent: #8f71d2;
}
.gambit-reveal-action {
  --gambit-action-accent: #5bb7cc;
}
.gambit-allin-action {
  --gambit-action-accent: #e66267;
}
.gambit-action-btn:active:not(:disabled) {
  transform: translateY(1px);
  box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.22);
}
.gambit-action-btn:disabled {
  filter: none;
  opacity: 0.54;
  cursor: not-allowed;
  box-shadow: none;
}
.gambit-action-btn:disabled .gambit-action-meta {
  color: #b8b2aa;
  font-weight: 800;
}
.gambit-action-btn[aria-pressed='true'] {
  border-color: var(--gambit-action-accent);
  background: color-mix(in srgb, var(--gambit-action-accent) 18%, transparent);
  color: #fff;
  box-shadow:
    inset 0 0 0 1px var(--gambit-action-accent),
    0 0 0 1px color-mix(in srgb, var(--gambit-action-accent) 45%, transparent);
}
.gambit-action-icon {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 2px;
  border: 1px solid color-mix(in srgb, var(--gambit-action-accent) 70%, transparent);
  background: color-mix(in srgb, var(--gambit-action-accent) 18%, transparent);
  color: #fffaf7;
  font-family: var(--font-mono);
  font-size: 16px;
  font-weight: 900;
  line-height: 1;
  box-shadow: none;
}
.gambit-action-icon::before {
  display: block;
}
.gambit-action-icon[data-gambit-action='conceal']::before {
  content: '\25b0';
  transform: rotate(-4deg);
}
.gambit-action-icon[data-gambit-action='reveal']::before {
  content: '\25c9';
}
.gambit-action-icon[data-gambit-action='allin']::before {
  content: '\2665';
}
.gambit-action-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.gambit-action-title {
  font-family: var(--font-mono);
  font-size: clamp(10px, 1.7svh, 12px);
  font-weight: 900;
  letter-spacing: 0.06em;
  color: #f4f0e8;
  overflow-wrap: anywhere;
}
.gambit-action-status,
.gambit-action-meta {
  font-family: var(--font-mono);
  font-size: clamp(8px, 1.35svh, 9px);
  font-weight: 700;
  letter-spacing: 0.05em;
  color: #b8b2aa;
  overflow-wrap: anywhere;
}
.gambit-action-btn[aria-pressed='true'] .gambit-action-status {
  color: #ede5ff;
}
@media (min-width: 720px) {
  .gambit-actions {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .gambit-command-dock-header {
    grid-column: 1 / -1;
  }
  .gambit-action-row {
    min-height: 76px;
    align-content: center;
    border-bottom: 0;
    border-inline-start: 0;
    border-top: 3px solid var(--gambit-action-accent);
  }
  .gambit-action-row + .gambit-action-row {
    border-inline-start: 1px solid rgba(255, 255, 255, 0.12);
  }
}
@media (max-width: 379px) {
  .gambit-declare-buttons {
    flex-direction: column;
  }
  .word-window-panel .gambit-raise-buttons {
    flex-direction: column;
  }
  .word-window-panel .gambit-raise-btn {
    width: 100%;
  }
  .word-window-axis {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .word-window-axis__values {
    grid-column: 1 / -1;
    text-align: right;
  }
  .gambit-stake-adjuster {
    grid-template-columns: 48px minmax(54px, 1fr) 48px 64px;
    gap: 6px;
  }
  .gambit-stake-number-wrap {
    min-width: 64px;
    padding-inline: 4px 3px;
  }
}
@media (hover: hover) {
  .gambit-action-btn:hover:not(:disabled),
  .gambit-raise-btn:hover,
  .gambit-declare-btn:hover,
  .gambit-stake-step:hover:not(:disabled) {
    filter: brightness(1.035);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
  }
}
.gambit-back-btn {
  align-self: center;
  min-width: 140px;
  min-height: 48px;
  flex: 0 0 auto;
  padding: 8px 28px;
  border-color: rgba(255, 255, 255, 0.28);
  border-radius: 3px;
  background: transparent;
  color: #f4f0e8;
  font-size: clamp(10px, 1.8svh, 12px);
}
.board-cell.gambit-reveal-target {
  z-index: 2;
  outline: 3px solid var(--red);
  outline-offset: -3px;
  border-radius: 8px;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9);
}

@media (max-width: 420px) {
  .gambit-btn {
    padding-inline: 12px;
  }
  .gambit-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}

@media (max-height: 600px) {
  .gambit-panel {
    gap: 8px;
    padding-top: 8px;
  }
  .gambit-content {
    gap: 7px;
  }
  .gambit-actions {
    gap: 0;
  }
  .gambit-action-row {
    padding-block: 7px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gambit-btn,
  .gambit-panel,
  .gambit-action-row,
  .gambit-back-btn,
  .gambit-raise-btn,
  .gambit-declare-btn,
  .gambit-stake-step {
    animation: none !important;
    transition: none !important;
  }
}

/* ---- Circle splash transition ----
   Transform/opacity only. Previously animated `clip-path: circle()`, which is
   NOT compositor-accelerated on Android WebView (re-rasters the full-screen
   layer every frame → visible jank; smooth on desktop/iOS). Now a viewport-
   sized layer (cheap texture) holds a radial-gradient centered on the tap point
   and is SCALED from its origin, giving the same expanding-ripple feel on the
   compositor. */
.arcanium-splash {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  transform-origin: var(--splash-x, 50%) var(--splash-y, 80%);
  background: radial-gradient(
    circle at var(--splash-x, 50%) var(--splash-y, 80%),
    rgba(215, 25, 33, 0.5),
    rgba(215, 25, 33, 0.28) 32%,
    transparent 62%
  );
  transform: scale(0.2);
  opacity: 0;
  will-change: transform, opacity;
  animation: arcanium-expand 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes arcanium-expand {
  0% {
    transform: scale(0.2);
    opacity: 0.5;
  }
  60% {
    opacity: 0.3;
  }
  100% {
    transform: scale(2.6);
    opacity: 0;
  }
}
.arcanium-splash.reverse {
  animation: arcanium-collapse 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes arcanium-collapse {
  0% {
    transform: scale(2.6);
    opacity: 0;
  }
  40% {
    opacity: 0.28;
  }
  100% {
    transform: scale(0.2);
    opacity: 0;
  }
}

/* ---- Game bottom (result screen) ---- */
.game-bottom {
  display: flex;
  justify-content: center;
  padding: 2px 8px 4px;
  flex: 0 0 auto;
}

/* ---- HUD transitions ---- */
.hud-wrapper {
  transition: all 0.35s ease;
}
.hud-banner {
  transition: height 0.35s ease;
  overflow: hidden;
}

/* ---- HUD collapsed state ---- */
/* Anchor banner to top so it "rolls up from the bottom" instead of shrinking both edges */
.hud-wrapper.hud-collapsed {
  align-items: flex-start;
}
.hud-collapsed .hud-banner {
  height: 40px;
  border-radius: 10px;
  overflow: hidden;
}
/* Keep the effect anchors aligned with the compact identity discs. */
.hud-collapsed .hud-portrait-anchor {
  top: 1px;
  width: 38px;
  height: 38px;
}
.hud-collapsed .hud-portrait-left {
  left: 1px;
}
.hud-collapsed .hud-portrait-right {
  right: 1px;
}
/* Info panels: at the outer edges */
.hud-collapsed .hud-panel {
  padding: 2px 6px;
  border-radius: 4px;
  max-width: 38%;
  z-index: 2;
}
.hud-collapsed .hud-player {
  top: 3px;
  bottom: auto;
  left: 2px;
  right: auto;
}
.hud-collapsed .hud-opponent {
  top: 3px;
  bottom: auto;
  right: 2px;
  left: auto;
}
.hud-collapsed .hud-name-row {
  font-size: clamp(7px, 1.8vw, 9px);
}
.hud-collapsed .hud-mana-text {
  font-size: clamp(6px, 1.4vw, 8px);
}
.hud-collapsed .hud-dots-row {
  gap: 1px;
}
.hud-collapsed .hud-dot {
  width: 3px;
  height: 3px;
}

/* ---- Menu ----
 * Single-screen layout. No vertical scroll on phones — every selector
 * is a row of compact chips so 4 selectors fit above the PLAY button
 * inside ~600px of vertical budget. */
.menu-wrap {
  max-width: 480px;
  width: calc(100% - 24px);
  height: 100%;
  margin: 0 auto;
  padding: calc(env(safe-area-inset-top) + 8px) 0 calc(env(safe-area-inset-bottom) + 12px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1.4vh, 14px);
  overflow: hidden;
}

/* Top bar — language toggle + auth pill */
.menu-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.menu-auth-pill {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 6px 12px;
  min-height: 28px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition:
    color 0.12s,
    border-color 0.12s,
    background 0.12s;
}
.menu-auth-pill:hover {
  color: var(--text);
  border-color: var(--text-muted);
}
.menu-auth-dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--green);
  box-shadow: 0 0 6px var(--green);
}
.menu-auth-name {
  letter-spacing: 0.12em;
}

/* Logo */
.menu-logo {
  text-align: center;
}
.menu-title {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(28px, 7vw, 42px);
  letter-spacing: 0.22em;
  line-height: 1;
}
.menu-subtitle {
  font-family: var(--font-mono);
  font-size: clamp(8px, 2vw, 11px);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 6px;
}

/* Hero row — 3 cards, suit-tinted, primary visual choice */
.hero-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
}
.hero-card {
  position: relative;
  overflow: hidden;
  cursor: pointer;
  text-align: center;
  padding: clamp(10px, 1.6vh, 14px) 6px clamp(8px, 1.4vh, 12px);
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  transition:
    border-color 0.15s,
    background 0.15s,
    transform 0.12s;
}
.hero-card:active {
  transform: scale(0.98);
}
.hero-glyph {
  font-family: var(--font-rank);
  font-size: clamp(28px, 7vw, 38px);
  line-height: 1;
  color: var(--text-muted);
  opacity: 0.55;
  transition:
    color 0.15s,
    opacity 0.15s,
    transform 0.2s;
}
.hero-name {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(13px, 3vw, 17px);
  letter-spacing: 0.12em;
  color: var(--text);
  margin-top: 2px;
}
.hero-role {
  font-family: var(--font-mono);
  font-size: clamp(8px, 1.8vw, 10px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.hero-card.selected {
  background: var(--surface-2);
}
.hero-diamonds.selected {
  border-color: var(--diamonds);
}
.hero-diamonds.selected .hero-glyph {
  color: var(--diamonds);
  opacity: 1;
}
.hero-hearts.selected {
  border-color: var(--hearts);
}
.hero-hearts.selected .hero-glyph {
  color: var(--hearts);
  opacity: 1;
}
.hero-spades.selected {
  border-color: var(--spades);
}
.hero-spades.selected .hero-glyph {
  color: var(--spades);
  opacity: 1;
}
.hero-clubs.selected {
  border-color: var(--clubs);
}
.hero-clubs.selected .hero-glyph {
  color: var(--clubs);
  opacity: 1;
}

/* Segmented control — used for board / mode / lang */
.seg {
  display: grid;
  grid-template-columns: repeat(var(--seg-cols, 3), 1fr);
  gap: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--surface);
}
.seg-2 {
  --seg-cols: 2;
}
.seg-3 {
  --seg-cols: 3;
}
.seg-compact {
  --seg-cols: 2;
  width: auto;
  align-self: start;
  border-radius: 999px;
}
.seg-chip {
  font-family: var(--font-mono);
  background: transparent;
  color: var(--text-dim);
  border: 0;
  cursor: pointer;
  padding: 8px 6px;
  min-height: 38px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  transition:
    background 0.12s,
    color 0.12s;
  position: relative;
}
.seg-chip + .seg-chip {
  box-shadow: -1px 0 0 var(--border);
}
.seg-chip:hover {
  color: var(--text);
}
.seg-chip.selected {
  background: var(--surface-2);
  color: var(--text);
}
.seg-chip-label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.seg-chip-name {
  font-size: 9px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.seg-chip.selected .seg-chip-name {
  color: var(--text-dim);
}
.seg-compact .seg-chip {
  min-height: 28px;
  padding: 4px 14px;
  font-size: 10px;
  letter-spacing: 0.14em;
}
.seg-chip-lock {
  position: absolute;
  top: 4px;
  right: 6px;
  font-size: 9px;
  color: var(--text-muted);
  letter-spacing: 0;
}

/* Mode-specific accent — selected mode glows red */
.seg-2 .seg-chip.selected {
  background: rgba(255, 68, 102, 0.1);
  color: var(--text);
}
.seg-3 .seg-chip.selected {
  background: rgba(255, 68, 102, 0.1);
  color: var(--text);
}

/* Difficulty slot — wraps tier bar (AI) and PvP fallback note */
.tier-slot {
  position: relative;
  min-height: 44px;
}
.tier-slot .tier-pvp-note {
  display: none;
}
.tier-slot.is-pvp .tier-bar {
  opacity: 0.18;
  pointer-events: none;
  filter: grayscale(1);
}
.tier-slot.is-pvp .tier-pvp-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  position: absolute;
  inset: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-dim);
  pointer-events: none;
}
.tier-pvp-icon {
  color: var(--red);
  font-size: 12px;
}

/* Difficulty tier bar — 4 chips, stars escalate left→right */
.tier-bar {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
  transition:
    opacity 0.18s,
    filter 0.18s;
}
.tier-chip {
  font-family: var(--font-mono);
  background: var(--surface);
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  padding: 6px 2px;
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  transition:
    border-color 0.12s,
    color 0.12s,
    background 0.12s;
}
.tier-chip:hover {
  color: var(--text);
}
.tier-stars {
  color: var(--text-muted);
  font-size: 11px;
  letter-spacing: 1px;
  line-height: 1;
}
.tier-name {
  font-size: 8px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  line-height: 1.15;
  text-align: center;
  padding: 0 2px;
}
.tier-chip.selected {
  border-color: var(--red);
  background: var(--surface-2);
  color: var(--text);
}
.tier-chip.selected .tier-stars {
  color: var(--red);
}
.tier-chip.selected .tier-name {
  color: var(--text);
}

/* Auth notice (reserves stable height) */
.menu-auth-notice {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-align: center;
  color: var(--text-muted);
  min-height: 14px;
  padding: 0;
  opacity: 0;
  transition: opacity 0.15s;
}
.menu-auth-notice.visible {
  opacity: 1;
}

/* Spacer — ambient void between selectors and PLAY, lets PLAY anchor low for thumb reach */
.menu-spacer {
  flex: 1;
  min-height: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}
.menu-ornament {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 90%;
  max-width: 320px;
  opacity: 0.55;
}
.menu-ornament-line {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent 0%, var(--border) 50%, var(--border) 100%);
}
.menu-ornament-line:last-of-type {
  background: linear-gradient(90deg, var(--border) 0%, var(--border) 50%, transparent 100%);
}
.menu-ornament-suit {
  font-family: var(--font-rank);
  font-size: 10px;
  line-height: 1;
  color: var(--text-muted);
  letter-spacing: 0;
}
.menu-ornament-suit.suit-d {
  color: var(--diamonds);
}
.menu-ornament-suit.suit-h {
  color: var(--hearts);
}
.menu-ornament-suit.suit-s {
  color: var(--spades);
}
.menu-ornament-suit.suit-c {
  color: var(--clubs);
}

/* PLAY + Tutorial */
.menu-play {
  width: 100%;
  font-size: clamp(14px, 3.2vw, 16px);
  letter-spacing: 0.32em;
  padding: clamp(11px, 1.8vh, 16px);
  border-radius: 8px;
  box-shadow: 0 0 0 0 rgba(255, 68, 102, 0.5);
  transition:
    transform 0.12s,
    box-shadow 0.2s,
    background 0.12s;
}
.menu-play:hover {
  box-shadow: 0 0 24px -2px rgba(255, 68, 102, 0.6);
}
.menu-tutorial-link {
  background: transparent;
  border: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 8px;
  min-height: 32px;
  cursor: pointer;
  text-decoration: none;
  transition: color 0.12s;
  align-self: center;
}
.menu-tutorial-link:hover {
  color: var(--text-dim);
}

/* ---- Profile ---- */
.profile-root {
  width: min(100% - 24px, 1040px);
  height: 100%;
  margin: 0 auto;
  padding: calc(env(safe-area-inset-top) + 10px) 0 calc(env(safe-area-inset-bottom) + 16px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
  overflow-x: hidden;
}
.profile-topbar {
  min-height: 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.profile-back-link,
.profile-logout,
.profile-history-sentinel {
  min-height: 32px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 6px 12px;
  transition:
    background 0.16s ease-out,
    border-color 0.16s ease-out,
    color 0.16s ease-out;
}
.profile-back-link:hover,
.profile-history-sentinel:hover {
  color: var(--text);
  border-color: var(--text-muted);
  background: var(--surface);
}
.profile-logout {
  color: var(--red);
  border-color: color-mix(in oklch, var(--red) 42%, var(--border));
}
.profile-logout:hover {
  color: var(--text);
  background: color-mix(in oklch, var(--red) 14%, transparent);
}
.profile-logout:disabled,
.profile-history-sentinel:disabled {
  cursor: wait;
  opacity: 0.65;
}
.profile-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 18px;
  align-items: end;
  padding: 10px 0 4px;
}
.profile-kicker,
.profile-subline,
.profile-status,
.profile-section-heading span,
.profile-history-meta,
.profile-history-detail,
.profile-ranking-place,
.profile-ranking-games,
.profile-ranking-record,
.profile-stat-label {
  font-family: var(--font-mono);
  text-transform: uppercase;
}
.profile-kicker {
  color: var(--text-muted);
  font-size: 10px;
  letter-spacing: 0.22em;
  margin-bottom: 8px;
}
.profile-name {
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 28px;
  font-weight: 700;
  letter-spacing: 0.12em;
  line-height: 1.05;
  overflow-wrap: anywhere;
}
.profile-subline {
  color: var(--text-dim);
  font-size: 11px;
  letter-spacing: 0.14em;
  margin-top: 8px;
}
.profile-status {
  min-height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  color: var(--text-dim);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 10px;
  letter-spacing: 0.16em;
  white-space: nowrap;
}
.profile-status-dot {
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--green);
  box-shadow: 0 0 10px color-mix(in oklch, var(--green) 74%, transparent);
}
.profile-stat-rail {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--surface);
}
.profile-stat-cell {
  min-width: 0;
  padding: 11px 10px 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.profile-stat-cell + .profile-stat-cell {
  box-shadow: -1px 0 0 var(--border);
}
.profile-stat-label {
  color: var(--text-muted);
  font-size: 9px;
  letter-spacing: 0.18em;
}
.profile-stat-value {
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 18px;
  letter-spacing: 0.08em;
  line-height: 1;
}
.profile-body {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(280px, 0.75fr);
  gap: 14px;
  align-items: start;
  padding-bottom: 6px;
}
.profile-section {
  border: 1px solid var(--border);
  border-radius: 8px;
  background: oklch(17% 0.008 28);
  overflow: hidden;
}
.profile-section-heading {
  min-height: 42px;
  padding: 11px 12px 9px;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  border-bottom: 1px solid var(--border);
}
.profile-section-heading h2 {
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text);
}
.profile-section-heading span {
  color: var(--text-muted);
  font-size: 9px;
  letter-spacing: 0.16em;
  white-space: nowrap;
}
.profile-history-list,
.profile-ranking-list {
  display: flex;
  flex-direction: column;
}
.profile-history-row {
  display: grid;
  grid-template-columns: 78px minmax(0, 1fr) minmax(132px, auto);
  gap: 12px;
  align-items: center;
  min-height: 68px;
  padding: 12px;
  border-bottom: 1px solid var(--border);
  background: transparent;
  transition: background 0.16s ease-out;
}
.profile-history-row:hover {
  background: color-mix(in oklch, var(--surface-2) 70%, transparent);
}
.profile-result-badge {
  min-height: 30px;
  border-radius: 999px;
  border: 1px solid var(--border);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.result-win .profile-result-badge {
  color: var(--green);
  border-color: color-mix(in oklch, var(--green) 48%, var(--border));
  background: color-mix(in oklch, var(--green) 12%, transparent);
}
.result-loss .profile-result-badge {
  color: var(--red);
  border-color: color-mix(in oklch, var(--red) 48%, var(--border));
  background: color-mix(in oklch, var(--red) 12%, transparent);
}
.result-draw .profile-result-badge,
.result-abandoned .profile-result-badge {
  color: var(--yellow);
  border-color: color-mix(in oklch, var(--yellow) 38%, var(--border));
  background: color-mix(in oklch, var(--yellow) 10%, transparent);
}
.result-active .profile-result-badge {
  color: var(--blue);
  border-color: color-mix(in oklch, var(--blue) 45%, var(--border));
  background: color-mix(in oklch, var(--blue) 12%, transparent);
}
.profile-history-main {
  min-width: 0;
}
.profile-history-title {
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: 0.08em;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.profile-history-meta {
  color: var(--text-muted);
  font-size: 9px;
  letter-spacing: 0.12em;
  margin-top: 6px;
  overflow-wrap: anywhere;
}
.profile-history-detail {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 5px;
  color: var(--text-dim);
  font-size: 9px;
  letter-spacing: 0.12em;
}
.profile-history-detail span {
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 7px;
  white-space: nowrap;
}
.profile-history-sentinel {
  width: calc(100% - 24px);
  margin: 12px;
}
.profile-empty {
  min-height: 128px;
  padding: 24px 18px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 9px;
  color: var(--text-muted);
  font-size: 12px;
  letter-spacing: 0.06em;
}
.profile-empty strong {
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.profile-empty-compact {
  min-height: 80px;
}
.profile-ranking-row {
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr) 54px 70px;
  gap: 8px;
  align-items: center;
  min-height: 46px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
.profile-ranking-row.is-current {
  background: color-mix(in oklch, var(--blue) 10%, transparent);
}
.profile-ranking-place {
  color: var(--text-muted);
  font-size: 10px;
  letter-spacing: 0.1em;
}
.profile-ranking-name {
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.profile-ranking-games,
.profile-ranking-record {
  color: var(--text-dim);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-align: right;
}
.profile-skeleton {
  position: relative;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface-2);
}
.profile-skeleton::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, oklch(78% 0.01 28 / 0.16), transparent);
  transform: translateX(-100%);
  animation: profile-skeleton-sweep 1.4s ease-out infinite;
}
.profile-skeleton-badge {
  width: 62px;
  height: 28px;
}
.profile-skeleton-line {
  width: min(220px, 100%);
  margin-bottom: 8px;
}
.profile-skeleton-stat {
  width: 112px;
}
@keyframes profile-skeleton-sweep {
  to {
    transform: translateX(100%);
  }
}

@media (max-width: 760px) {
  .profile-root {
    width: calc(100% - 18px);
    gap: 10px;
  }
  .profile-header {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .profile-status {
    justify-self: start;
  }
  .profile-stat-rail {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .profile-stat-cell + .profile-stat-cell {
    box-shadow: none;
  }
  .profile-stat-cell {
    border-bottom: 1px solid var(--border);
  }
  .profile-stat-cell:nth-child(odd) {
    box-shadow: 1px 0 0 var(--border);
  }
  .profile-body {
    grid-template-columns: 1fr;
  }
  .profile-history-row {
    grid-template-columns: 70px minmax(0, 1fr);
    gap: 10px;
  }
  .profile-history-detail {
    grid-column: 1 / -1;
    justify-content: flex-start;
    padding-left: 82px;
  }
}

/* ---- Auth ---- */
.auth-root {
  width: calc(100% - 24px);
  max-width: 380px;
  margin: 0 auto;
  height: 100%;
  padding: calc(env(safe-area-inset-top) + 8px) 0 calc(env(safe-area-inset-bottom) + 16px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  overflow-x: hidden;
}
.auth-topbar {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-height: 28px;
}
.auth-back-link {
  background: transparent;
  border: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 4px 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: color 0.12s;
}
.auth-back-link:hover {
  color: var(--text);
}
.auth-back-arrow {
  font-size: 14px;
  line-height: 1;
}
.auth-brand {
  text-align: center;
  margin-top: clamp(6px, 2vh, 22px);
}
.auth-logo {
  font-size: clamp(26px, 7vw, 38px);
  font-weight: 700;
  letter-spacing: 0.22em;
  color: var(--text);
  line-height: 1;
}
.auth-tagline {
  font-size: clamp(8px, 2vw, 11px);
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 6px;
}
.auth-step-title {
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-align: center;
  margin-top: 4px;
}
.auth-tabs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border: 1px solid var(--border);
  border-radius: 999px;
  overflow: hidden;
  background: var(--surface);
  margin-top: 4px;
}
.auth-tab {
  background: transparent;
  border: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 9px 6px;
  min-height: 36px;
  transition:
    color 0.12s,
    background 0.12s;
}
.auth-tab.selected {
  background: var(--surface-2);
  color: var(--text);
}

.auth-feedback {
  font-size: 11px;
  text-align: center;
  min-height: 14px;
  letter-spacing: 0.06em;
}
.auth-google {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 0;
  min-height: 44px;
  border-radius: 6px;
  background: #fff;
  border: 1px solid #dadce0;
  cursor: pointer;
  transition: background 0.12s;
}
.auth-google:hover {
  background: #f7f8f8;
}
.auth-google:active {
  background: #eee;
}
.auth-google-icon {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.auth-google-text {
  font-family: 'Roboto', system-ui, sans-serif;
  font-size: 14px;
  font-weight: 500;
  color: #3c4043;
  letter-spacing: 0.01em;
}
/* Sign in with Apple — HIG black button, same geometry as the Google one. */
.auth-apple {
  margin-top: 8px;
  background: #000;
  border-color: #000;
}
.auth-apple:hover {
  background: #111;
}
.auth-apple:active {
  background: #222;
}
.auth-apple .auth-google-text {
  color: #fff;
}
.auth-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 2px 0;
}
.auth-divider-line {
  flex: 1;
  height: 1px;
  background: var(--border);
}
.auth-divider-text {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.auth-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.auth-input {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: 0.04em;
  outline: none;
  transition:
    border-color 0.15s,
    background 0.15s;
}
.auth-input:focus {
  border-color: var(--red);
  background: var(--surface-2);
}
.auth-input::placeholder {
  color: var(--text-muted);
  letter-spacing: 0.08em;
}
/* iOS WebKit paints autofilled inputs with a yellow background. Mask it with
   an inset shadow that matches our surface, and re-assert the text colour. */
.auth-input:-webkit-autofill,
.auth-input:-webkit-autofill:hover,
.auth-input:-webkit-autofill:focus,
.auth-input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 1000px var(--surface) inset;
  -webkit-text-fill-color: var(--text);
  caret-color: var(--text);
  transition: background-color 9999s ease-out 0s;
}
.auth-input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px var(--surface-2) inset;
}
.auth-input-otp {
  text-align: center;
  letter-spacing: 0.45em;
  font-size: 18px;
  font-weight: 700;
}
.auth-submit {
  width: 100%;
  letter-spacing: 0.32em;
  border-radius: 8px;
  margin-top: 2px;
  transition: box-shadow 0.2s;
}
.auth-submit:hover {
  box-shadow: 0 0 24px -2px rgba(255, 68, 102, 0.6);
}
.auth-links {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  justify-content: center;
}
.auth-link {
  background: transparent;
  border: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  padding: 6px 4px;
  min-height: 32px;
  text-decoration: none;
  transition: color 0.12s;
}
.auth-link:hover {
  color: var(--text);
}

/* ---- HUD status rail (active ability tokens) ---- */
.hud-status-rail {
  position: absolute;
  top: calc(var(--hud-zone-h, 14vh) + 2px);
  left: 3%;
  right: 3%;
  height: var(--effect-rail-h, 24px);
  z-index: 5;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 12px;
  pointer-events: none;
}
.hud-status-row {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}
.hud-status-player {
  justify-content: flex-start;
}
.hud-status-opponent {
  justify-content: flex-end;
}
.hud-status-token {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: min(22px, var(--effect-rail-h, 24px));
  min-width: 22px;
  max-width: 68px;
  padding: 0 6px;
  border-radius: 999px;
  border: 1px solid rgba(160, 178, 194, 0.32);
  background: rgba(15, 18, 22, 0.82);
  color: rgb(214, 224, 234);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.26);
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1;
  white-space: nowrap;
  cursor: default;
  pointer-events: auto;
}
.hud-status-token::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  z-index: 20;
  max-width: 190px;
  padding: 6px 8px;
  border: 1px solid rgba(220, 225, 230, 0.18);
  border-radius: 6px;
  background: rgba(14, 15, 17, 0.96);
  color: rgb(232, 235, 238);
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.35);
  font-size: 10px;
  line-height: 1.35;
  text-align: center;
  white-space: pre-line;
  opacity: 0;
  transform: translate(-50%, -2px);
  transition:
    opacity 0.14s ease-out,
    transform 0.14s ease-out;
  pointer-events: none;
}
.hud-status-opponent .hud-status-token::after {
  left: auto;
  right: 0;
  transform: translate(0, -2px);
}
.hud-status-token:hover::after,
.hud-status-token:focus-visible::after {
  opacity: 1;
  transform: translate(-50%, 0);
}
.hud-status-opponent .hud-status-token:hover::after,
.hud-status-opponent .hud-status-token:focus-visible::after {
  transform: translate(0, 0);
}
.hud-status-icon {
  font-size: 11px;
  line-height: 1;
}
.hud-status-value {
  font-weight: 700;
  color: rgb(250, 250, 250);
}
.hud-status-turns {
  opacity: 0.72;
}
/* Per-effect tints — debuffs go red, charges/buffs purple, regen green. */
.hud-status-token.hud-status-cursedHealing,
.hud-status-token.hud-status-abilitiesBlocked {
  background: rgba(58, 18, 22, 0.86);
  border-color: rgba(238, 100, 112, 0.5);
  color: rgb(255, 188, 192);
}
.hud-status-token.hud-status-comboCharge {
  background: rgba(68, 30, 18, 0.86);
  border-color: rgba(238, 130, 94, 0.5);
  color: rgb(255, 210, 188);
}
.hud-status-token.hud-status-resonanceBoost {
  background: rgba(18, 42, 58, 0.86);
  border-color: rgba(112, 190, 238, 0.52);
  color: rgb(210, 236, 252);
}
.hud-status-token.hud-status-shield {
  background: rgba(58, 48, 18, 0.86);
  border-color: rgba(232, 198, 94, 0.55);
  color: rgb(255, 230, 176);
}
.hud-status-overflow {
  background: rgba(36, 38, 42, 0.86);
  color: rgb(218, 222, 226);
}
@media (max-width: 520px) {
  .hud-status-rail {
    left: 4%;
    right: 4%;
    gap: 8px;
  }
  .hud-status-row {
    gap: 4px;
  }
  .hud-status-token {
    min-width: 20px;
    height: min(20px, var(--effect-rail-h, 22px));
    padding: 0 5px;
    font-size: 9px;
  }
}

/* ---- Ability fired — portrait pulse + purple halo ---- */
.ability-flash {
  /* `both` keeps the final-frame state until JS removes the class so a long
     drop-shadow tail doesn't get clipped; the actual values are 100% normal
     so it's a visual no-op once the animation ends. */
  animation: ability-pulse 0.7s ease both;
}
@keyframes ability-pulse {
  0% {
    filter: brightness(1) drop-shadow(0 0 0 rgba(170, 136, 255, 0));
  }
  35% {
    filter: brightness(1.6) drop-shadow(0 0 12px rgba(170, 136, 255, 0.95))
      drop-shadow(0 0 28px rgba(140, 70, 220, 0.8));
  }
  70% {
    filter: brightness(1.3) drop-shadow(0 0 8px rgba(170, 136, 255, 0.7))
      drop-shadow(0 0 18px rgba(140, 70, 220, 0.5));
  }
  100% {
    filter: brightness(1) drop-shadow(0 0 0 rgba(170, 136, 255, 0));
  }
}

/* ---- Ability fired banner (both modes) ---- */
.ability-banner {
  position: absolute;
  top: 38%;
  left: 50%;
  box-sizing: border-box;
  width: min(320px, calc(100vw - 32px));
  transform: translate(-50%, -10px) scale(0.92);
  padding: 12px 22px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  background: rgba(20, 12, 32, 0.94);
  color: #fff;
  font-family: Impact, 'Arial Black', sans-serif;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  transition:
    opacity 0.18s ease,
    transform 0.22s cubic-bezier(0.2, 0.9, 0.3, 1);
  z-index: 9999;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  transform-origin: center center;
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.7);
}
.ability-banner-hero {
  flex: 0 1 auto;
  font-size: 16px;
  opacity: 0.85;
  min-width: 0;
}
.ability-banner-name {
  flex: 0 1 auto;
  font-size: 18px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ability-banner-icon {
  aspect-ratio: 230 / 110;
  height: 44px;
  width: auto;
  max-width: min(170px, 48vw);
  flex: 0 0 auto;
  display: block;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
}
.ability-banner-me {
  border-color: rgba(110, 195, 255, 0.45);
  box-shadow:
    0 0 0 1px rgba(110, 195, 255, 0.12),
    0 14px 32px rgba(0, 0, 0, 0.7);
}
.ability-banner-opponent {
  border-color: rgba(255, 122, 153, 0.45);
  box-shadow:
    0 0 0 1px rgba(255, 122, 153, 0.12),
    0 14px 32px rgba(0, 0, 0, 0.7);
}
.ability-banner-show {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
}

/* When the Arcanium panel is open the bottom ~40% of the screen is taken by
   the panel + close button, shifting the visual center of the remaining
   playable area upward. Without this override the banner reads as bottom-
   biased and partially covers the panel itself. The 22% top puts it at the
   midpoint of the visible board+HUD region instead. */
.game-root.arcanum-open .ability-banner {
  top: 22%;
}
.game-root.arcanum-open .ability-banner-show {
  /* Same target transform as the default; the rule exists so the override
     above keeps the banner centered horizontally without re-stating the
     translate. */
  transform: translate(-50%, 0) scale(1);
}

/* ---- Shadow Foresight reveal overlay ---- */
.shadow-foresight-overlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  background: rgba(0, 0, 0, 0.75);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  pointer-events: none;
  animation: sf-fade-in 0.4s ease forwards;
}
.shadow-foresight-overlay.fade-out {
  animation: sf-fade-out 0.5s ease forwards;
}
.shadow-foresight-label {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: #a8f;
  text-shadow: 0 0 8px #a8f;
}
.shadow-foresight-cards {
  display: flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  padding: 0 16px;
}
.shadow-foresight-card {
  width: 44px;
  height: 62px;
  border-radius: 5px;
  background: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--font-rank);
  font-size: 14px;
  font-weight: 800;
  color: var(--card-text);
  border: 1px solid rgba(168, 136, 255, 0.5);
  box-shadow: 0 0 8px rgba(168, 136, 255, 0.4);
  position: relative;
  overflow: hidden;
}
.shadow-foresight-card-rank {
  position: absolute;
  top: 2px;
  left: 3px;
  font-size: 10px;
  color: var(--card-text);
  font-family: var(--font-rank);
  font-weight: 800;
}
.shadow-foresight-card-suit {
  width: 20px;
  height: 20px;
  object-fit: contain;
}
@keyframes sf-fade-in {
  0% {
    opacity: 0;
    transform: scale(0.95);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}
@keyframes sf-fade-out {
  0% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(0.95);
  }
}

/* ---- Effects ---- */
.fx-float {
  position: absolute;
  font-family: var(--font-mono);
  font-size: 20px;
  font-weight: 700;
  pointer-events: none;
  z-index: 20;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
  animation: float-up 0.8s ease-out forwards;
}
@keyframes float-up {
  0% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  70% {
    opacity: 1;
    transform: translateY(-30px) scale(1.1);
  }
  100% {
    opacity: 0;
    transform: translateY(-50px) scale(0.9);
  }
}

/* The banner used to run a 1s `combo-pop` and OWN the combat timeline — it was
   the bottleneck that gated every combo. Now it stamps in fast and holds while
   the cascade plays underneath, then `is-out` fades it. */
.fx-combo-banner {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  pointer-events: none;
  z-index: 20;
  animation: combo-stamp 380ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
  will-change: transform, opacity;
}
@keyframes combo-stamp {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.55);
  }
  55% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.12);
  }
  100% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
}
.fx-combo-banner.is-out {
  animation: combo-out 220ms ease-out forwards;
}
@keyframes combo-out {
  0% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -62%) scale(0.94);
  }
}
/* Impact frame — the stamp kicks once when the damage lands. */
.fx-combo-banner.is-impact .fx-combo-dmg {
  animation: combo-dmg-impact 240ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes combo-dmg-impact {
  0% {
    transform: scale3d(1.45, 1.45, 1);
  }
  100% {
    transform: scale3d(1, 1, 1);
  }
}
/* Rarest combos earn a visible payoff instead of one extra note. */
.fx-combo-banner.is-legendary .fx-combo-img {
  filter: drop-shadow(0 0 10px rgba(255, 210, 90, 0.9)) drop-shadow(0 2px 8px rgba(0, 0, 0, 0.8));
}
.fx-combo-chain {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--yellow);
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.9);
}
.fx-combo-name {
  font-family: var(--font-mono);
  font-size: clamp(16px, 4vw, 22px);
  font-weight: 700;
  color: var(--yellow);
  letter-spacing: 0.15em;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.8);
}
.fx-combo-img {
  height: clamp(40px, 10vw, 60px);
  width: auto;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.8));
}
/* The running damage total. Was a static "{n} DMG" string; it now counts up
   across the cascade, so it's sized to be readable as a number in motion. */
.fx-combo-dmg {
  font-family: var(--font-mono);
  font-size: clamp(15px, 4.2vw, 22px);
  font-weight: 700;
  color: var(--red);
  letter-spacing: 0.06em;
  line-height: 1;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.85);
  min-height: 1em;
  will-change: transform;
}
@keyframes combo-pop {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.5);
  }
  15% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.15);
  }
  30% {
    transform: translate(-50%, -50%) scale(1);
  }
  75% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -55%) scale(0.95);
  }
}

.board-cell.fx-flash {
  animation: cell-flash 0.5s ease-out forwards;
}
@keyframes cell-flash {
  0% {
    background: rgba(255, 204, 51, 0.35);
  }
  40% {
    background: rgba(255, 204, 51, 0.2);
  }
  100% {
    background: transparent;
  }
}

/* One beat of the combo cascade: this cell's card pops and a glow washes over
   it. Opacity + transform only (the older `cell-flash` animates `background`,
   which is a paint property — kept for the legacy path but not used here). */
.board-cell.fx-step {
  z-index: 3;
}
.board-cell.fx-step::after {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 6px;
  pointer-events: none;
  background: radial-gradient(circle, rgba(255, 214, 92, 0.85) 0%, rgba(255, 214, 92, 0) 70%);
  animation: cell-step-glow 380ms ease-out forwards;
}
.board-cell.fx-step > .board-card {
  animation: cell-step-pop 320ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
  will-change: transform;
}
@keyframes cell-step-glow {
  0% {
    opacity: 0;
    transform: scale(0.7);
  }
  25% {
    opacity: 1;
    transform: scale(1.08);
  }
  100% {
    opacity: 0;
    transform: scale(1.25);
  }
}
@keyframes cell-step-pop {
  0% {
    transform: scale3d(1, 1, 1);
  }
  35% {
    transform: scale3d(1.18, 1.18, 1);
  }
  100% {
    transform: scale3d(1, 1, 1);
  }
}

/* (`.fx-clear` / `cell-clear` removed — it had no call site anywhere in the
   client. The cascade's `.fx-step` is the live per-cell beat now.) */

.board-cell.fx-vanish > .board-card {
  animation: card-vanish 0.4s ease-in forwards;
}
@keyframes card-vanish {
  0% {
    opacity: 1;
    transform: scale(1);
  }
  60% {
    opacity: 0.5;
    transform: scale(1.1);
  }
  100% {
    opacity: 0;
    transform: scale(0.3);
  }
}

.fx-shake-light {
  animation: shake-light 0.3s ease-out;
}
.fx-shake-heavy {
  animation: shake-heavy 0.4s ease-out;
}
@keyframes shake-light {
  0%,
  100% {
    transform: translateX(0);
  }
  20% {
    transform: translateX(-3px);
  }
  40% {
    transform: translateX(3px);
  }
  60% {
    transform: translateX(-2px);
  }
  80% {
    transform: translateX(2px);
  }
}
@keyframes shake-heavy {
  0%,
  100% {
    transform: translateX(0);
  }
  15% {
    transform: translateX(-5px) rotate(-0.5deg);
  }
  30% {
    transform: translateX(5px) rotate(0.5deg);
  }
  45% {
    transform: translateX(-4px);
  }
  60% {
    transform: translateX(4px);
  }
  75% {
    transform: translateX(-2px);
  }
}

/* ---- Juice toolkit (ui/juice/*) ----
   ONE shake whose magnitude is continuous: JS writes --cm-shake-amp/-rot/-ms
   and the keyframes below read them, so a 19-damage pair and a 38-damage
   straight-flush no longer feel byte-identical. translate3d + a sub-degree
   rotate only — no layout/paint properties, per the WebView rules. */
.cm-shake {
  animation: cm-shake var(--cm-shake-ms, 300ms) cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
  will-change: transform;
}
@keyframes cm-shake {
  0%,
  100% {
    transform: translate3d(0, 0, 0);
  }
  12% {
    transform: translate3d(calc(var(--cm-shake-amp, 3px) * -1), 0, 0)
      rotate(calc(var(--cm-shake-rot, 0deg) * -1));
  }
  28% {
    transform: translate3d(var(--cm-shake-amp, 3px), 0, 0) rotate(var(--cm-shake-rot, 0deg));
  }
  44% {
    transform: translate3d(calc(var(--cm-shake-amp, 3px) * -0.7), 0, 0)
      rotate(calc(var(--cm-shake-rot, 0deg) * 0.5));
  }
  60% {
    transform: translate3d(calc(var(--cm-shake-amp, 3px) * 0.55), 0, 0);
  }
  78% {
    transform: translate3d(calc(var(--cm-shake-amp, 3px) * -0.3), 0, 0);
  }
}

/* Card lands on the board — the beat that was missing on literally every move. */
.cm-squash-land {
  animation: cm-squash-land 220ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
  will-change: transform;
}

.cm-card-reveal {
  animation: cm-card-reveal 200ms cubic-bezier(0.16, 1, 0.3, 1) both;
  will-change: transform;
}
@keyframes cm-card-reveal {
  from {
    transform: scaleX(0.08);
  }
  to {
    transform: scaleX(1);
  }
}

.cm-card-fold {
  animation: cm-card-fold 120ms cubic-bezier(0.4, 0, 1, 1) both;
  will-change: transform;
}
@keyframes cm-card-fold {
  from {
    transform: scaleX(1);
  }
  to {
    transform: scaleX(0);
  }
}

@keyframes cm-squash-land {
  0% {
    transform: scale3d(1.14, 0.86, 1);
  }
  45% {
    transform: scale3d(0.95, 1.07, 1);
  }
  100% {
    transform: scale3d(1, 1, 1);
  }
}

/* A card landing in a slot whose aspect is not its own (none shipped since the
   token shapes were removed — kept because the keyframes are shape-agnostic): it
   arrives at the hand card's proportions — same width, `--squeeze-sy` times the
   height — and is compressed into the slot, overshooting once so it reads as
   being pressed into a hole rather than resized.

   Transform-only on purpose: this is the one moment the shape change is
   visible, and animating width/height (or the octagon's clip-path) would
   re-layout or repaint the card every frame on Android WebView. The card is
   briefly taller than its cell, so it lifts above its neighbours for the
   duration. */
.cm-squeeze-land {
  animation: cm-squeeze-land 300ms cubic-bezier(0.22, 1.2, 0.36, 1) both;
  will-change: transform;
  z-index: 4;
}
@keyframes cm-squeeze-land {
  0% {
    transform: scale3d(0.96, calc(var(--squeeze-sy, 1.4) * 1.02), 1);
  }
  8% {
    transform: scale3d(0.97, var(--squeeze-sy, 1.4), 1);
  }
  62% {
    /* Past the target: the squeeze bottoms out flatter and wider than the slot. */
    transform: scale3d(1.07, 0.9, 1);
  }
  82% {
    transform: scale3d(0.98, 1.04, 1);
  }
  100% {
    transform: scale3d(1, 1, 1);
  }
}

/* A card landing in a SEATED slot. Nothing about it changes shape, so there is
   nothing to squeeze: it starts a little shallower in the socket and is pushed
   down past its resting depth before settling, the way a card meets the bottom
   of a holder. The cell's own `overflow: hidden` clips the head while it is
   high, which is what sells the push rather than a drop.

   Percentages on translate resolve against the CARD's own box, so the travel
   scales with the cell and needs no per-size tuning. */
.cm-seat-land {
  animation: cm-seat-land 260ms cubic-bezier(0.3, 1.1, 0.4, 1) both;
  will-change: transform;
}
@keyframes cm-seat-land {
  0% {
    transform: translate3d(0, -9%, 0) scale3d(1.03, 1.03, 1);
  }
  60% {
    /* Bottomed out: a hair deeper than it rests, and squatter for the impact. */
    transform: translate3d(0, 2%, 0) scale3d(1.01, 0.97, 1);
  }
  100% {
    transform: translate3d(0, 0, 0) scale3d(1, 1, 1);
  }
}

.cm-press-squash {
  animation: cm-press-squash 160ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes cm-press-squash {
  0% {
    transform: scale3d(0.93, 0.93, 1);
  }
  100% {
    transform: scale3d(1, 1, 1);
  }
}

/* Drag ghost lift — the card gets "picked up" instead of just sliding. */
.ghost-card.cm-lift {
  animation: cm-ghost-lift 150ms ease-out both;
}
@keyframes cm-ghost-lift {
  0% {
    transform: translate3d(0, 0, 0) scale(1);
  }
  100% {
    transform: translate3d(0, -12px, 0) scale(1.05);
  }
}

/* Defender flinch — a one-frame wash when the LOCAL hero takes the hit, so
   getting hit stops feeling identical to dealing it. */
.cm-hit-flash::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 60;
  background: rgba(255, 40, 40, 0.22);
  animation: cm-hit-flash 260ms ease-out forwards;
}
@keyframes cm-hit-flash {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

/* HUD panel flinch — the side that just lost HP recoils. */
.hud-panel.is-hit {
  animation: cm-panel-flinch 400ms cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
  will-change: transform;
}
@keyframes cm-panel-flinch {
  0% {
    transform: translate3d(0, 0, 0);
  }
  14% {
    transform: translate3d(-4px, 1px, 0);
  }
  30% {
    transform: translate3d(3px, -1px, 0);
  }
  48% {
    transform: translate3d(-2px, 0, 0);
  }
  70% {
    transform: translate3d(1px, 0, 0);
  }
  100% {
    transform: translate3d(0, 0, 0);
  }
}

/* Opponent "thinking" presence during the AI pause — a slow breath on the
   portrait plus marching dots, so the wait reads as tension, not a freeze. */
.hud-portrait-right.is-thinking {
  animation: cm-thinking-breath 1.6s ease-in-out infinite;
  will-change: transform, opacity;
}
@keyframes cm-thinking-breath {
  0%,
  100% {
    transform: translate3d(0, 0, 0) scale(1);
    opacity: 0.86;
  }
  50% {
    transform: translate3d(0, -3px, 0) scale(1.025);
    opacity: 1;
  }
}
.game-thinking-dots {
  display: inline-block;
  margin-left: 2px;
}
.game-thinking-dot {
  display: inline-block;
  animation: cm-thinking-dot 1.1s ease-in-out infinite;
}
.game-thinking-dot:nth-child(2) {
  animation-delay: 0.16s;
}
.game-thinking-dot:nth-child(3) {
  animation-delay: 0.32s;
}
@keyframes cm-thinking-dot {
  0%,
  70%,
  100% {
    opacity: 0.25;
    transform: translate3d(0, 0, 0);
  }
  35% {
    opacity: 1;
    transform: translate3d(0, -2px, 0);
  }
}

/* Scene enter transition — replaces the hard `replaceChildren` cut between
   menu ↔ game ↔ chest. Deliberately enter-only: a true cross-dissolve would
   have to keep the outgoing scene mounted past its cleanup(), which is exactly
   where this codebase has had teardown races before. */
.cm-scene-enter {
  animation: cm-scene-enter 220ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@keyframes cm-scene-enter {
  0% {
    opacity: 0;
    transform: translate3d(0, 8px, 0);
  }
  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

/* Every control acknowledges the press. Transform-only, so it composites. */
.btn:active,
.cm-pill:active,
.cm-quick:active,
.cm-currency:active,
.arcanium-btn:active,
.pause-btn:active,
.ability-btn:active,
.hud-status-token:active {
  transform: scale3d(0.94, 0.94, 1);
}
.btn,
.cm-pill,
.cm-quick,
.cm-currency,
.arcanium-btn,
.pause-btn,
.ability-btn,
.hud-status-token {
  transition: transform 110ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Balance gained — the chip kicks as the number ticks up. */
.cm-currency.is-gain {
  animation: cm-currency-gain 700ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes cm-currency-gain {
  0% {
    transform: scale3d(1, 1, 1);
  }
  18% {
    transform: scale3d(1.12, 1.12, 1);
  }
  100% {
    transform: scale3d(1, 1, 1);
  }
}

/* ---- Game result ---- */
.game-result {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.game-result-title {
  font-family: var(--font-mono);
  font-size: clamp(20px, 5vw, 28px);
  font-weight: 700;
  letter-spacing: 0.2em;
  animation: combo-pop 0.6s ease-out;
}
.game-result-title.win {
  color: var(--green);
}
.game-result-title.lose {
  color: var(--red);
}
.game-result-stats {
  display: flex;
  gap: 12px;
  font-size: 11px;
  color: var(--text-dim);
}

/* ---- Matchmaking ---- */
.matchmaking-root {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 400px;
}
.matchmaking-spinner {
  display: flex;
  gap: 8px;
  justify-content: center;
}
.spinner-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--red);
  animation: spinner-bounce 1.2s ease-in-out infinite;
}
.spinner-dot:nth-child(2) {
  animation-delay: 0.2s;
}
.spinner-dot:nth-child(3) {
  animation-delay: 0.4s;
}
@keyframes spinner-bounce {
  0%,
  80%,
  100% {
    transform: scale(0.6);
    opacity: 0.4;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

/* ---- Card placement animation (removed — cleaner without) ---- */

/* ---- Skin selector ---- */
.menu-skin-btn {
  width: 100%;
  margin-bottom: 6px;
  font-size: 11px;
}
.skin-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0, 0, 0, 0.7);
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.skin-modal.open {
  display: flex;
}
.skin-modal-inner {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px;
  max-width: 400px;
  width: 100%;
  max-height: 80vh;
  overflow-y: auto;
}
.skin-modal-title {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-align: center;
  margin-bottom: 12px;
  color: var(--text);
}
.skin-modal-close {
  width: 100%;
  margin-top: 8px;
}
.skin-selector {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
}
.skin-option {
  cursor: pointer;
  text-align: center;
  padding: 8px 4px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  transition: all 0.12s;
}
.skin-option.selected {
  border-color: #d71921;
  background: var(--surface-2);
}
.skin-option-name {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--text-dim);
}
.skin-preview-card {
  width: 36px;
  height: 50px;
  border-radius: 5px;
  background: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  background-size: cover;
  background-position: center;
}
/* The skin preview is a fourth card face: it reuses .board-card-* class names
   but hard-overrides family/size/inset, so it does NOT pick up the card-face
   type automatically. It has to be kept in step by hand or the shop shows a
   different card from the one you play with. */
.skin-preview-card .board-card-rank {
  position: absolute;
  top: 6%;
  left: 8%;
  font-size: 8px;
  color: var(--card-text);
  font-family: var(--card-rank-font);
  font-weight: var(--card-rank-weight);
  letter-spacing: var(--card-rank-track);
  padding-right: var(--card-rank-track);
}
.skin-preview-card .board-card-suit {
  width: 16px;
  height: 16px;
  object-fit: contain;
}

/* ---- BG preview swatch ---- */
.bg-preview {
  width: 48px;
  height: 32px;
  border-radius: 4px;
  background-size: cover;
  background-position: center;
  border: 1px solid rgba(255, 255, 255, 0.1);
}

/* Small screen overrides — only tweak spacing */
@media (max-width: 380px) {
  .game-hand-tray {
    padding: 4px 6px;
    gap: 3px;
  }
}

/* Short viewports (iPhone SE, older iPhones, landscape phones) — tighten so
 * everything still fits one screen without scrolling. */
@media (max-height: 700px) {
  .menu-wrap {
    gap: 8px;
  }
  .menu-title {
    font-size: 26px;
    letter-spacing: 0.18em;
  }
  .menu-subtitle {
    font-size: 9px;
    letter-spacing: 0.3em;
    margin-top: 4px;
  }
  .hero-card {
    padding: 8px 4px;
  }
  .hero-glyph {
    font-size: 26px;
  }
  .hero-name {
    font-size: 13px;
  }
  .hero-role {
    font-size: 8px;
  }
  .seg-chip {
    min-height: 34px;
    padding: 6px 4px;
  }
  .seg-chip-label {
    font-size: 12px;
  }
  .seg-chip-name {
    font-size: 8px;
    letter-spacing: 0.18em;
  }
  .tier-chip {
    min-height: 38px;
    padding: 4px 2px;
  }
  .tier-stars {
    font-size: 10px;
  }
  .tier-name {
    font-size: 8px;
  }
  .menu-play {
    padding: 11px;
    font-size: 14px;
    letter-spacing: 0.28em;
  }
  .menu-tutorial-link {
    padding: 4px;
    font-size: 10px;
    min-height: 26px;
  }
}

@media (max-height: 600px) {
  .menu-wrap {
    gap: 6px;
  }
  .menu-logo {
    margin-top: -2px;
  }
  .menu-title {
    font-size: 22px;
  }
  .menu-subtitle {
    display: none;
  }
  .hero-card {
    padding: 6px 3px;
    gap: 1px;
  }
  .hero-glyph {
    font-size: 22px;
  }
}

/* Tall viewports — center the column, collapse the spacer so PLAY isn't pinned at the absolute bottom. */
@media (min-height: 760px) {
  .menu-wrap {
    justify-content: center;
    gap: clamp(14px, 2vh, 22px);
  }
  .menu-spacer {
    display: none;
  }
  .auth-root {
    justify-content: center;
    gap: 14px;
  }
  /* Reposition the back link absolutely so centering ignores it */
  .auth-topbar {
    position: absolute;
    top: calc(env(safe-area-inset-top) + 12px);
    left: 50%;
    transform: translateX(-50%);
    width: calc(100% - 24px);
    max-width: 380px;
  }
}

/* ---------------------------------------------------------------------------
 * Tutorial — dim/spotlight overlay + dialog bubble + drop-target marker
 * ------------------------------------------------------------------------- */

.tutorial-word-host {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--bottom-zone-h, 200px) + 28px + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  background: rgba(12, 10, 18, 0.94);
  pointer-events: auto;
  max-height: max(
    120px,
    calc(100% - var(--bottom-zone-h, 200px) - var(--tutorial-coach-bottom, 180px) - 40px)
  );
  overflow-y: auto;
  overscroll-behavior: contain;
}
.tutorial-word-host:empty,
.tutorial-word-host[hidden] {
  display: none;
}
.tutorial-word-host .gambit-timer-ring {
  /* Academy decisions wait for the learner; there is no running deadline. */
  display: none;
}

/* Focus stays on cards; never darken the heroes or the whole arena column. */

/* Spotlit elements sit above their siblings. Board
 * cells are already absolutely positioned by the layout pass, so we only set
 * z-index and never override position (doing so would re-flow them out of
 * the grid). Hand cards are static, so they need the position:relative hop
 * for z-index to apply. */
.tutorial-root.tutorial-dim .tutorial-spotlight {
  z-index: 31;
}
.tutorial-root.tutorial-dim .hand-card.tutorial-spotlight {
  position: relative;
}
.tutorial-root.tutorial-dim .board-cell.tutorial-drop-target {
  z-index: 31;
}
/* Keep the live game's zone order: raising the entire hand panel above the
 * arena would cover its floating action buttons. Dim only nearby cards. */
.tutorial-root.tutorial-dim
  .game-bottom-area:has(.hand-card.tutorial-spotlight)
  .hand-card:not(.tutorial-spotlight) {
  filter: brightness(0.65);
}
.tutorial-root.tutorial-dim
  .board-wrap:has(.tutorial-spotlight)
  .board-cell:not(.tutorial-spotlight):not(.tutorial-drop-target),
.tutorial-root.tutorial-dim
  .board-wrap:has(.tutorial-drop-target)
  .board-cell:not(.tutorial-spotlight):not(.tutorial-drop-target) {
  filter: brightness(0.65);
}
/* Spotlit HUD elements (mana text, HP readout) get a tiffany-neon halo so the
 * player's eye lands on the focal stat. Same colour for both so the visual
 * vocabulary is consistent across intro-mana and intro-hp steps. The
 * lightning glyph is rendered as an <img> by dom-hud-renderer (same asset as
 * the arcanium button) — no CSS pseudo-element needed. */
.tutorial-root .hud-mana-text.tutorial-spotlight {
  color: #b6fff4;
  font-weight: 800;
  font-size: 18px;
  letter-spacing: 0.04em;
  text-shadow:
    0 0 6px rgba(92, 255, 233, 0.95),
    0 0 14px rgba(92, 255, 233, 0.7),
    0 0 28px rgba(0, 220, 200, 0.45);
  animation: tutorial-mana-pulse 1.4s ease-in-out infinite;
}
.tutorial-root .hud-mana-text.tutorial-spotlight .hud-mana-icon {
  width: 18px;
  height: 18px;
  /* Same tiffany halo as the mana text — stacked drop-shadows mirror the
   * text-shadow chain so the icon and digits read as one glowing unit. */
  filter: drop-shadow(0 0 4px rgba(92, 255, 233, 0.95))
    drop-shadow(0 0 10px rgba(92, 255, 233, 0.7)) drop-shadow(0 0 18px rgba(0, 220, 200, 0.5));
}
.tutorial-root .hud-hp-readout.tutorial-spotlight {
  position: relative;
  border-radius: 6px;
  padding: 2px 4px;
  background: rgba(92, 255, 233, 0.12);
  box-shadow:
    0 0 10px rgba(92, 255, 233, 0.7),
    0 0 24px rgba(92, 255, 233, 0.45),
    inset 0 0 6px rgba(92, 255, 233, 0.3);
  animation: tutorial-mana-pulse 1.4s ease-in-out infinite;
}
@keyframes tutorial-mana-pulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.06);
  }
}

/* Gray + dim the non-focal HUD half so attention lands 100% on the spotlit
 * stat. intro-hp gates greyed mana on both sides; intro-mana greys the HP
 * rows. Uses :has() to scope only to the active step. */
.tutorial-root:has(.tutorial-dialog[data-step-id='intro-hp']) .hud-mana-text {
  filter: grayscale(1) opacity(0.42);
}
.tutorial-root:has(.tutorial-dialog[data-step-id='intro-mana'])
  .hud-hp-readout:not(.tutorial-spotlight) {
  filter: grayscale(1) opacity(0.42);
}
/* Hand + board spotlight share the same red neon glow so the player reads
 * both kinds of focus point as one visual vocabulary ("this is what you
 * interact with next"). Sizes differ because hand cards are smaller. */
.tutorial-root .hand-card.tutorial-spotlight {
  box-shadow: 0 0 14px 3px rgba(255, 68, 102, 0.75);
  border-radius: 6px;
}
.tutorial-root .board-cell.tutorial-spotlight {
  box-shadow: 0 0 24px 5px rgba(255, 68, 102, 0.65);
  border-radius: 8px;
}

/* Drop-target cell: the empty cell the player must drag onto. Pulsing red
 * neon glow only — no solid border, so the cell still reads as an empty
 * board slot rather than a card-shaped outline. */
.tutorial-root .board-cell.tutorial-drop-target {
  box-shadow: 0 0 28px 8px rgba(255, 68, 102, 0.75);
  border-radius: 10px;
  animation: tutorial-target-pulse 1.2s ease-in-out infinite;
  filter: none !important;
}
@keyframes tutorial-target-pulse {
  0%,
  100% {
    box-shadow: 0 0 22px 6px rgba(255, 68, 102, 0.55);
  }
  50% {
    box-shadow: 0 0 36px 12px rgba(255, 68, 102, 0.9);
  }
}

/* Drag-hint pantomime — body-appended translucent clone of the source hand
 * card with a finger glyph below-right. Fixed-positioned so getBoundingClient
 * coords from the overlay map 1:1. Above everything (dim layer, dialog),
 * pointer-events: none so it never intercepts the player's own drag. The
 * WAAPI animation in createTutorialOverlay drives transform + opacity;
 * styles here just establish baseline appearance and stacking. */
.tutorial-drag-hint {
  position: fixed;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.85;
  filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.55));
  will-change: transform, opacity;
}
.tutorial-drag-hint .tutorial-drag-hint-card {
  width: 100%;
  height: 100%;
  opacity: 0.78;
  box-shadow:
    0 0 14px 3px rgba(255, 68, 102, 0.55),
    0 4px 12px rgba(0, 0, 0, 0.4);
  pointer-events: none;
}
.tutorial-drag-hint-hand {
  position: absolute;
  right: -16px;
  bottom: -22px;
  width: 38px;
  height: 38px;
  opacity: 0.95;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.7));
  transform: rotate(-12deg);
}
.tutorial-drag-hint-hand svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Academy guidance owns a row above the live arena on narrow windows and a
 * sidebar on desktop. It never covers either 3D hero, the HUD or the board. */
.tutorial-dialog {
  position: absolute;
  top: calc(var(--hud-zone-h, 72px) + 8px);
  left: 12px;
  right: 12px;
  z-index: 50;
  pointer-events: none;
  color: var(--text);
}
.tutorial-dialog[hidden] {
  display: none;
}
.tutorial-root .bf-stage,
.tutorial-root .hud-wrapper {
  top: var(--tutorial-guidance-h, 0px);
}
.game-root.tutorial-root.has-battle-stage .board-wrap {
  top: calc(var(--tutorial-guidance-h, 0px) + var(--hud-zone-h) + var(--zone-gap));
}
@media (max-width: 1179px) {
  .tutorial-root .tutorial-dialog {
    top: 6px;
  }
  .tutorial-root .tutorial-bubble {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 4px 12px;
    padding: 10px 12px;
  }
  .tutorial-root .tutorial-speaker-row {
    grid-column: 1 / -1;
  }
  .tutorial-root .tutorial-text {
    margin: 0;
  }
  .tutorial-root .tutorial-nav {
    align-self: end;
    margin: 0;
  }
  .tutorial-root .tutorial-bubble:has(.tutorial-nav[hidden]) .tutorial-text {
    grid-column: 1 / -1;
  }
}
.tutorial-bubble {
  pointer-events: auto;
  padding: 12px 14px;
  border: 1px solid oklch(42% 0.018 290);
  border-radius: 12px;
  background: oklch(20% 0.018 290);
  box-shadow: 0 8px 24px oklch(10% 0.01 290 / 0.24);
}
.tutorial-speaker-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.tutorial-speaker-name {
  color: oklch(82% 0.11 75);
  font: 700 12px/1.3 var(--font-mono);
}
.tutorial-counter {
  color: oklch(75% 0.015 290);
  font-size: 11px;
}
.tutorial-text {
  margin: 7px 0 0;
  color: oklch(94% 0.009 290);
  font: 400 13px/1.45 var(--font-mono);
  max-width: 65ch;
}
.tutorial-nav {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 10px;
}
.tutorial-nav[hidden] {
  display: none;
}
.tutorial-prev,
.tutorial-next {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid oklch(50% 0.02 290);
  border-radius: 50%;
  cursor: pointer;
  transition:
    transform 150ms ease-out,
    opacity 150ms ease-out;
}
.tutorial-prev {
  background: oklch(92% 0.01 80);
  color: oklch(20% 0.015 290);
}
.tutorial-next {
  background: var(--red);
  color: oklch(97% 0.008 80);
  border-color: transparent;
}
.tutorial-prev:hover,
.tutorial-next:hover {
  transform: translateY(-2px);
}
.tutorial-prev:active,
.tutorial-next:active {
  transform: translateY(0);
}
.tutorial-prev:focus-visible,
.tutorial-next:focus-visible {
  outline: 2px solid oklch(82% 0.11 75);
  outline-offset: 3px;
}
.tutorial-arrow-icon {
  width: 22px;
  height: 22px;
  pointer-events: none;
}
.tutorial-hidden {
  visibility: hidden;
  pointer-events: none;
}
.tutorial-next.finish {
  width: auto;
  padding: 0 18px;
  border-radius: 24px;
  font: 700 12px/1 var(--font-mono);
}
.tutorial-shake {
  animation: tutorial-nudge 200ms ease-out;
}
@keyframes tutorial-nudge {
  50% {
    border-color: oklch(82% 0.11 75);
  }
}
@media (min-width: 1180px) {
  .tutorial-word-host {
    max-height: calc(100% - var(--bottom-zone-h, 200px) - var(--hud-zone-h, 72px) - 52px);
  }
  .tutorial-root .tutorial-dialog {
    left: auto;
    right: calc(100% + 24px);
    width: 292px;
  }
  .tutorial-bubble {
    padding: 18px;
  }
  .tutorial-text {
    font-size: 14px;
    line-height: 1.55;
  }
}
.tut-coach-dialog {
  position: fixed;
  top: auto;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  right: auto;
  width: min(420px, calc(100vw - 24px));
  transform: translateX(-50%);
}
.tut-coach-dialog[data-position='top'] {
  top: calc(12px + env(safe-area-inset-top, 0px));
  bottom: auto;
}
@media (max-height: 700px) {
  .tutorial-bubble {
    padding: 8px 10px;
  }
  .tutorial-text {
    font-size: 12px;
    line-height: 1.35;
    margin-top: 4px;
  }
  .tutorial-nav {
    margin-top: 6px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .tutorial-root .tutorial-spotlight,
  .tutorial-root .tutorial-drop-target,
  .tut-coach-spot,
  .tutorial-shake {
    animation: none !important;
  }
  .tutorial-prev,
  .tutorial-next {
    transition: none;
  }
}

/* Modal overlay — used by every modal in the app (marketplace buy form,
 * any future confirm dialogs). modal-shell.ts wires Escape + focus-trap;
 * this is the visual layer. */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: env(safe-area-inset-top) 16px env(safe-area-inset-bottom);
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  overflow: hidden;
}

/* ────────────────────────────────────────────────────────────────────
 * Marketplace — card-table treatment.
 * Reuses the menu's dot-matrix vocabulary (Ndot 47 + LED Dot-Matrix),
 * wide tracking, --surface layered on --bg, --hearts/--diamonds/--spades/
 * --clubs as semantic accents per product type. The shop reads as a row
 * of laid-out playing cards; orders read as a betting-table ledger.
 * ──────────────────────────────────────────────────────────────────── */
.mp-wrap {
  display: flex;
  flex-direction: column;
  width: min(100% - 24px, 1080px);
  /* 100svh keeps the wrap stable as iOS chrome shows/hides. The inner content
     area is the only thing that scrolls — tabs + topbar stay fixed so the
     user always sees which section they're in. */
  height: 100svh;
  max-height: 100svh;
  margin: 0 auto;
  padding: calc(env(safe-area-inset-top) + 10px) 0 calc(env(safe-area-inset-bottom) + 16px);
  box-sizing: border-box;
  gap: 14px;
  color: var(--text);
  /* Opaque background so the gap between tabs and content doesn't let
     scrolling items leak through underneath the tabs. */
  background: var(--bg, #000);
  overflow: hidden;
  min-height: 0;
  /* New stacking context so child z-index rules apply within the wrap only. */
  isolation: isolate;
}

/* Top bar + tabs are fixed — they must NEVER shrink (was the "jump" bug:
   when the items list grew, the parent's gap budget pushed the tabs to a
   smaller height, momentarily hiding the count chip). */
.mp-topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  flex: 0 0 auto;
}
.mp-back {
  font-family: var(--font-mono);
  background: transparent;
  border: 0;
  color: var(--text-muted);
  padding: 8px 4px;
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 0.12s;
}
.mp-back:hover {
  color: var(--text-dim);
}
.mp-title {
  flex: 1;
  text-align: center;
  font-family: var(--font-mono);
  font-size: clamp(18px, 4.5vw, 22px);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--text);
  /* Suit ornaments flank the title on wider viewports — pure decoration,
   * keeps the ace-of-spades feel without crowding the chrome. */
  position: relative;
}
.mp-title::before,
.mp-title::after {
  content: '◆';
  font-family: var(--font-rank);
  font-size: 9px;
  color: var(--diamonds);
  letter-spacing: 0;
  margin: 0 10px;
  vertical-align: middle;
  opacity: 0.5;
}
.mp-back-spacer {
  width: 64px;
}

/* Tabs — reuse the menu's segmented-chip pattern verbatim. They live INSIDE
   the scroll container as `position: sticky` so:
   1. A hovered card (z-index 10) can paint over them (z-index 2) — both
      are stacking siblings inside the scroll area.
   2. The tab bar stays pinned at the top of the visible scroll viewport
      as items scroll past underneath. */
.mp-tabs {
  display: grid;
  grid-template-columns: repeat(var(--mp-tab-count, 3), 1fr);
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  overflow: hidden;
  min-height: 52px;
  position: sticky;
  top: 0;
  z-index: 2;
  margin-bottom: 14px;
}
.mp-tab {
  font-family: var(--font-mono);
  background: transparent;
  border: 0;
  color: var(--text-dim);
  cursor: pointer;
  padding: 8px 6px;
  /* Full row height of the bar — no per-tab shrink. */
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  transition:
    background 0.12s,
    color 0.12s;
  position: relative;
}
.mp-tab + .mp-tab {
  box-shadow: -1px 0 0 var(--border);
}
.mp-tab:hover {
  color: var(--text);
}
.mp-tab.selected {
  background: var(--surface-2);
  color: var(--text);
}
.mp-tab-label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* Stop the label from shifting up when the count label appears — the count
     row below always reserves its slot, so position is identical empty or not. */
  line-height: 1.2;
}
.mp-tab-count {
  font-size: 9px;
  letter-spacing: 0.2em;
  color: var(--text-muted);
  /* Reserve a line even when textContent is empty so the tab doesn't reflow
     between "inventory loading" and "12 items" — was the visible jump. */
  min-height: 11px;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  /* "·" placeholder rendered transparently keeps the box's height stable
     across font fallbacks. */
}
.mp-tab-count:empty::before {
  content: '·';
  visibility: hidden;
}
.mp-tab.selected .mp-tab-count {
  color: var(--text-dim);
}

/* Single scroll surface wrapping the sticky tabs + grid. Owning the scroll
   here (not on .mp-content) lets the tabs stick to the top while a hovered
   card can paint over them via z-index in the same stacking context. */
.mp-scroll {
  /* flex-basis 0 forces height to compute purely from `grow`, so the
     scroll container always equals (wrap - topbar - gaps) instead of
     inheriting `auto` size from its content (which would skip the
     overflow scroll entirely). */
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding-right: 2px;
  position: relative;
  /* Hide the scrollbar — Firefox + IE/old Edge. WebKit handled below. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
/* WebKit (Safari/Chrome): zero-width bar = fully hidden. */
.mp-scroll::-webkit-scrollbar {
  width: 0;
  height: 0;
}

.mp-content {
  /* Nothing else — the parent .mp-scroll handles scroll, isolation is
     intentionally absent so child z-index can compete with sticky tabs. */
}

/* Cards stay below the sticky tab bar (z-index 2) at ALL times — including on
   hover. So as you scroll a hovered card slides UNDER the tabs and never paints
   over its neighbours. Hover feedback is colour-only (no lift), so a hovered
   card never intrudes into the cards around it. */
.mp-card {
  z-index: 1;
}

/* Empty state — frame it with a suit ornament so it reads as "the table is
 * still being dealt", not "broken". */
.mp-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
  color: var(--text-dim);
  font-family: var(--font-mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-size: 12px;
  padding: 80px 20px;
}
.mp-empty::before {
  content: '◆ ♥ ♠ ♣';
  display: block;
  font-family: var(--font-rank);
  font-size: 18px;
  letter-spacing: 0.6em;
  color: var(--text-muted);
  opacity: 0.4;
  margin-bottom: 14px;
}
.mp-empty-detail {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  text-transform: none;
  max-width: 36ch;
  line-height: 1.6;
}
.mp-retry-btn {
  margin-top: 8px;
  font-family: var(--font-mono);
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 22px;
  border-radius: 999px;
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  cursor: pointer;
  transition:
    border-color 0.12s,
    color 0.12s;
}
.mp-retry-btn:hover {
  border-color: var(--red);
  color: var(--red);
}

/* ── Shop grid ────────────────────────────────────────────────────── */
.mp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
}
@media (max-width: 480px) {
  .mp-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
  }
}
.mp-card {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  cursor: default;
  transition:
    border-color 0.15s,
    background 0.15s;
  /* Faint felt grain — pure CSS, free, sells the "card on a table" feel. */
  background-image:
    radial-gradient(rgba(255, 255, 255, 0.012) 1px, transparent 1px),
    radial-gradient(rgba(255, 255, 255, 0.008) 1px, transparent 1px);
  background-size:
    7px 7px,
    13px 13px;
  background-position:
    0 0,
    3px 4px;
}
.mp-card:hover {
  background-color: var(--surface-2);
}

/* Suit watermark — giant glyph in the corner, low-opacity, color per type.
 * Lifts the card from "another tile in a grid" to "an actual playing card
 * with a suit identity". */
.mp-card-watermark {
  position: absolute;
  right: -14px;
  bottom: -28px;
  font-family: var(--font-rank);
  font-size: 130px;
  line-height: 1;
  pointer-events: none;
  opacity: 0.07;
  color: var(--text);
  transition:
    opacity 0.18s,
    transform 0.3s ease-out;
}
.mp-card:hover .mp-card-watermark {
  opacity: 0.14;
  transform: translate(-2px, -2px);
}

.mp-card-hero .mp-card-watermark,
.mp-card-hero .mp-card-suit {
  color: var(--hearts);
}
.mp-card-hero:hover {
  border-color: var(--hearts);
}

.mp-card-deck .mp-card-watermark,
.mp-card-deck .mp-card-suit {
  color: var(--spades);
}
.mp-card-deck:hover {
  border-color: var(--spades);
}

.mp-card-cosmetic .mp-card-watermark,
.mp-card-cosmetic .mp-card-suit {
  color: var(--diamonds);
}
.mp-card-cosmetic:hover {
  border-color: var(--diamonds);
}

.mp-card-currency .mp-card-watermark,
.mp-card-currency .mp-card-suit {
  color: var(--clubs);
}
.mp-card-currency:hover {
  border-color: var(--clubs);
}

.mp-card-battle_pass .mp-card-watermark,
.mp-card-battle_pass .mp-card-suit {
  color: var(--yellow);
}
.mp-card-battle_pass:hover {
  border-color: var(--yellow);
}

/* Top row — suit + type label on the left, price chip on the right.
 * Mirrors a card's rank/suit corner. */
.mp-card-corner {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  position: relative;
  z-index: 1;
}
.mp-card-suit {
  font-family: var(--font-rank);
  font-size: 22px;
  line-height: 1;
}
.mp-card-type {
  display: block;
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 4px;
}
.mp-card-price {
  font-family: var(--font-rank);
  font-size: 14px;
  letter-spacing: 0.04em;
  color: var(--text);
  border: 1px solid var(--border);
  padding: 3px 8px;
  border-radius: 4px;
  background: var(--bg);
}
.mp-card-price-currency {
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.18em;
  color: var(--text-muted);
  margin-left: 4px;
}

.mp-card-name {
  position: relative;
  z-index: 1;
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text);
  line-height: 1.3;
  margin-top: 2px;
}
.mp-card-desc {
  position: relative;
  z-index: 1;
  font-family: var(--font-sans);
  font-size: 11px;
  color: var(--text-dim);
  line-height: 1.5;
  flex: 1;
  /* Pin the description to 2 lines so the grid stays even — clip with
   * an ellipsis since we control the seed copy length. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.mp-card-footer {
  position: relative;
  z-index: 1;
  margin-top: 6px;
  padding-top: 10px;
  border-top: 1px dashed var(--border);
}
.mp-card-buy {
  width: 100%;
  font-family: var(--font-mono);
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  cursor: pointer;
  transition:
    background 0.12s,
    border-color 0.12s,
    color 0.12s;
}
.mp-card-buy:hover {
  background: var(--red);
  border-color: var(--red);
  color: var(--text);
  box-shadow: 0 0 18px -4px rgba(255, 68, 102, 0.55);
}
.mp-card-buy[disabled] {
  background: transparent;
  border-color: var(--border);
  color: var(--text-muted);
  cursor: default;
  box-shadow: none;
}

/* OWNED stamp — a stamped/distressed mark across the corner. */
.mp-card-owned-stamp {
  position: absolute;
  top: 12px;
  right: -32px;
  z-index: 2;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.32em;
  padding: 3px 32px;
  background: var(--green);
  color: var(--bg);
  transform: rotate(35deg);
  text-transform: uppercase;
  pointer-events: none;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
}
.mp-card-owned {
  opacity: 0.66;
}
.mp-card-owned:hover {
  transform: none;
  background-color: var(--surface);
}

/* ── Owned tab — tighter list, suit-coded. ───────────────────────── */
.mp-owned-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 8px;
}
.mp-owned-tile {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  position: relative;
}
.mp-owned-suit {
  font-family: var(--font-rank);
  font-size: 22px;
  line-height: 1;
  flex-shrink: 0;
  width: 22px;
  text-align: center;
}
.mp-owned-tile.suit-hero .mp-owned-suit {
  color: var(--hearts);
}
.mp-owned-tile.suit-deck .mp-owned-suit {
  color: var(--spades);
}
.mp-owned-tile.suit-cosmetic .mp-owned-suit {
  color: var(--diamonds);
}
.mp-owned-tile.suit-currency .mp-owned-suit {
  color: var(--clubs);
}
.mp-owned-tile.suit-battle_pass .mp-owned-suit {
  color: var(--yellow);
}
.mp-owned-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.mp-owned-name {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mp-owned-meta {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ── Orders tab — ledger row treatment. ──────────────────────────── */
.mp-orders-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.mp-order-row {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-family: var(--font-mono);
}
.mp-order-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}
.mp-order-name {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mp-order-detail {
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--text-muted);
  text-transform: uppercase;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.mp-order-detail-money {
  color: var(--text-dim);
  font-family: var(--font-rank);
  letter-spacing: 0.04em;
}
.mp-order-detail-refund {
  color: var(--diamonds);
}

/* Status — LED-dot vocabulary, not a colored pill. Mirrors menu-auth-dot. */
.mp-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 3px 8px 3px 6px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg);
}
.mp-status::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--text-muted);
  box-shadow: 0 0 6px transparent;
}
.mp-status-pending::before {
  background: var(--yellow);
  box-shadow: 0 0 6px var(--yellow);
  animation: mpPulse 1.4s ease-in-out infinite;
}
.mp-status-pending {
  color: var(--yellow);
  border-color: rgba(255, 204, 51, 0.35);
}
.mp-status-success::before {
  background: var(--green);
  box-shadow: 0 0 6px var(--green);
}
.mp-status-success {
  color: var(--green);
  border-color: rgba(51, 204, 102, 0.35);
}
.mp-status-failed::before {
  background: var(--red);
  box-shadow: 0 0 6px var(--red);
}
.mp-status-failed {
  color: var(--red);
  border-color: rgba(255, 68, 102, 0.35);
}
.mp-status-refunded::before {
  background: var(--diamonds);
  box-shadow: 0 0 6px var(--diamonds);
}
.mp-status-refunded {
  color: var(--diamonds);
  border-color: rgba(68, 136, 255, 0.35);
}
.mp-status-disputed::before {
  background: var(--yellow);
  box-shadow: 0 0 6px var(--yellow);
  animation: mpPulse 0.9s ease-in-out infinite;
}
.mp-status-disputed {
  color: var(--yellow);
  border-color: rgba(255, 204, 51, 0.45);
}

@keyframes mpPulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}

/* Test panel — a quiet, dashed footer band. Don't compete with the row. */
.mp-test-panel {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 4px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
  flex-wrap: wrap;
}
.mp-test-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.28em;
  color: var(--text-muted);
  margin-right: 4px;
}
.mp-test-btn {
  font-family: var(--font-mono);
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
  transition:
    color 0.12s,
    border-color 0.12s,
    background 0.12s;
}
.mp-test-btn:hover:not([disabled]) {
  color: var(--text);
  border-color: var(--text-muted);
}
.mp-test-btn[disabled] {
  opacity: 0.3;
  cursor: not-allowed;
}

/* ── Buy modal — playing-card-shaped panel. ──────────────────────── */
.mp-buy-panel {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 16px 18px 14px;
  width: min(400px, 94vw);
  /* No internal scroll — modal sizes to its content and the content is
   * tightened to fit common phone heights (≥640px). Felt grain + red
   * trim sell the "table where the deal happens" framing. */
  display: flex;
  flex-direction: column;
  gap: 10px;
  background-image:
    radial-gradient(rgba(255, 255, 255, 0.014) 1px, transparent 1px),
    radial-gradient(rgba(255, 255, 255, 0.009) 1px, transparent 1px);
  background-size:
    7px 7px,
    13px 13px;
  background-position:
    0 0,
    3px 4px;
  box-shadow:
    0 0 0 1px rgba(255, 68, 102, 0.12),
    0 24px 48px -16px rgba(0, 0, 0, 0.7);
}
.mp-buy-corner {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
}
.mp-buy-suit {
  font-family: var(--font-rank);
  font-size: 22px;
  line-height: 1;
}
.mp-buy-eyebrow {
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 2px;
}
.mp-buy-title {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
  line-height: 1.2;
}
.mp-buy-price {
  text-align: right;
  font-family: var(--font-rank);
  font-size: 18px;
  letter-spacing: 0.04em;
  color: var(--text);
  white-space: nowrap;
}
.mp-buy-price-currency {
  display: block;
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.28em;
  color: var(--text-muted);
}

/* Divider used between header and test-cards — kept tiny so it doesn't
 * eat vertical space. Removed visually when the test-cards block is hidden
 * (real-provider mode). */
.mp-buy-divider {
  display: none; /* Compressed layout: the test-cards block has its own
                  * dashed border; no separate divider needed. */
}

/* Test-cards strip — three compact buttons in a row. Numbers themselves
 * live in the form input; clicking a button fills the input. */
.mp-card-hint {
  background: var(--bg);
  border: 1px dashed var(--border);
  border-radius: 6px;
  padding: 6px 8px;
  font-family: var(--font-mono);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 4px;
}
.mp-card-hint-title {
  display: none;
}
.mp-card-hint-row {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 4px 6px;
  text-align: center;
  cursor: pointer;
  font-family: inherit;
  font-size: 9px;
  color: var(--text-dim);
  letter-spacing: 0.06em;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  line-height: 1.15;
  transition:
    border-color 0.12s,
    color 0.12s;
}
.mp-card-hint-row:hover {
  border-color: var(--border);
  color: var(--text);
}
.mp-card-hint-row .mp-card-hint-num {
  display: none;
}
.mp-card-hint-row .mp-card-hint-mark {
  font-family: var(--font-rank);
  font-size: 14px;
  line-height: 1;
}
.mp-card-hint-row.outcome-succeed .mp-card-hint-mark {
  color: var(--green);
}
.mp-card-hint-row.outcome-decline .mp-card-hint-mark {
  color: var(--red);
}
.mp-card-hint-row.outcome-dispute .mp-card-hint-mark {
  color: var(--yellow);
}

.mp-card-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.mp-form-row {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.mp-form-half-wrap {
  display: flex;
  gap: 8px;
}
/* min-width: 0 lets flex children shrink past their default content width.
 * Without it, the input's intrinsic width pushes its parent past the panel
 * edge — visible on the CVC column at narrow viewports. */
.mp-form-half {
  flex: 1;
  min-width: 0;
}
.mp-input {
  width: 100%;
  box-sizing: border-box;
}
.mp-label {
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.mp-input {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  color: var(--text);
  font-family: var(--font-rank);
  font-size: 14px;
  letter-spacing: 0.06em;
  outline: none;
  transition:
    border-color 0.12s,
    box-shadow 0.12s;
  min-height: 36px;
}
.mp-input:focus {
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(255, 68, 102, 0.18);
}
.mp-input::placeholder {
  color: var(--text-muted);
  letter-spacing: 0.04em;
}

.mp-buy-status {
  min-height: 14px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--text-dim);
}
.mp-buy-status.is-error {
  color: var(--red);
}
.mp-buy-status.is-success {
  color: var(--green);
}

.mp-buy-actions {
  display: flex;
  gap: 8px;
  margin-top: 2px;
}
.mp-buy-cancel {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  padding: 9px 14px;
  border-radius: 8px;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  cursor: pointer;
  transition:
    color 0.12s,
    border-color 0.12s;
}
.mp-buy-cancel:hover {
  color: var(--text);
  border-color: var(--text-muted);
}
.mp-buy-pay {
  flex: 1;
  font-family: var(--font-mono);
  background: var(--red);
  border: 1px solid var(--red);
  color: var(--text);
  padding: 9px 14px;
  border-radius: 8px;
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  font-weight: 700;
  cursor: pointer;
  transition:
    box-shadow 0.18s,
    background 0.12s;
}
.mp-buy-pay:hover {
  box-shadow: 0 0 24px -2px rgba(255, 68, 102, 0.6);
}
.mp-buy-pay[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
}

/* Marketplace shop button — sits in the menu topbar between language toggle
 * and auth pill. Same pill aesthetic as auth, but with a diamond glyph so
 * it scans as "store" without needing translated text. */
.menu-shop-btn {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 6px 12px;
  min-height: 28px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition:
    color 0.12s,
    border-color 0.12s,
    background 0.12s;
}
.menu-shop-btn:hover {
  color: var(--text);
  border-color: var(--accent, #d4a574);
}
.menu-shop-glyph {
  font-size: 11px;
  color: var(--accent, #d4a574);
}
.menu-shop-label {
  /* On very narrow viewports, hide the label and keep just the glyph. */
}
@media (max-width: 360px) {
  .menu-shop-label {
    display: none;
  }
}

/* Menu — language pill (replaces the legacy two-chip EN/UK toggle). Same
   pill aesthetic as the shop button so the topbar reads as a row of glyph
   pills + the auth chip. The selector modal carries all 30 locales. */
.menu-lang-btn {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 6px 12px;
  min-height: 28px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition:
    color 0.12s,
    border-color 0.12s,
    background 0.12s;
}
.menu-lang-btn:hover {
  color: var(--text);
  border-color: var(--accent, #d4a574);
}
.menu-lang-glyph {
  font-size: 13px;
  line-height: 1;
}
.menu-lang-code {
  font-variant-numeric: tabular-nums;
}
@media (max-width: 360px) {
  .menu-lang-code {
    display: none;
  }
}

/* Language selector modal — list-style with each row showing native name +
   English name. Native column reads in the row's own script (dir attr is
   set per-row), so RTL languages display correctly inside the LTR shell. */
.lang-panel {
  background: var(--surface, #141414);
  color: var(--text, #fff);
  border: 1px solid var(--border);
  border-radius: 14px;
  width: min(420px, calc(100vw - 32px));
  max-height: calc(100svh - 80px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.lang-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  flex: 0 0 auto;
}
.lang-title {
  font-family: var(--font-rank);
  font-size: 14px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.lang-close {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.lang-list {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 6px 0;
}
.lang-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 18px;
  border: 0;
  background: transparent;
  color: var(--text);
  cursor: pointer;
  text-align: left;
  font-family: var(--font-mono);
}
.lang-item:hover {
  background: rgba(255, 255, 255, 0.04);
}
.lang-item.selected {
  background: rgba(212, 165, 116, 0.08);
}
.lang-item.loading {
  opacity: 0.6;
  pointer-events: none;
}
.lang-native {
  flex: 1 1 auto;
  font-size: 14px;
  /* Each row's `lang` attr triggers correct font selection for CJK/Arabic. */
}
.lang-english {
  flex: 0 0 auto;
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.lang-check {
  flex: 0 0 16px;
  color: var(--accent, #d4a574);
  font-size: 13px;
  text-align: right;
}
.lang-check-spacer {
  flex: 0 0 16px;
}
[dir='rtl'] .lang-item {
  text-align: right;
}

/* =================================================================== */
/* Equip scene — pre-match Arcana loadout                              */
/* =================================================================== */
.equip-wrap {
  position: fixed;
  inset: 0;
  background: var(--bg, #000);
  color: var(--text, #fff);
  font-family: var(--font-mono);
  display: flex;
  flex-direction: column;
  padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);
  overflow: hidden;
}
.equip-topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 18px 4px;
  flex: 0 0 auto;
}
.equip-back {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.equip-title-block {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.equip-eyebrow {
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.equip-title {
  font-family: var(--font-rank);
  font-size: 18px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.equip-subtitle {
  padding: 0 18px 8px;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  flex: 0 0 auto;
}

/* Slot rail — 5 large equipped-arcana chips at the top of the scene. */
.equip-rail {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px;
  padding: 4px 18px 12px;
  flex: 0 0 auto;
}
.equip-slot {
  aspect-ratio: 3 / 4;
  border-radius: 10px;
  border: 1px dashed var(--border);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 6px 4px;
  font-family: var(--font-mono);
}
.equip-slot.empty {
  color: var(--text-muted);
}
/* Slot disabled by a slot-cap arcana (e.g. Reckless Draw drops the 5th). */
.equip-slot.locked {
  opacity: 0.3;
  pointer-events: none;
  filter: grayscale(1);
}
.equip-slot.filled {
  border-style: solid;
  border-color: var(--accent, #d4a574);
  background: rgba(212, 165, 116, 0.08);
}
.equip-slot-glyph {
  font-size: clamp(18px, 4svh, 26px);
  line-height: 1;
}
.equip-slot-name {
  font-size: 9px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.15;
  /* clamp to 2 lines, ellipsize the rest */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* School chips — show set-bonus progress (1/3, 2/3, etc.). */
.equip-sets {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  padding: 0 18px 10px;
  flex: 0 0 auto;
}
.equip-set-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 6px 4px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: 10px;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}
.equip-set-chip.active-2 {
  color: var(--text);
  border-color: var(--accent, #d4a574);
}
.equip-set-chip.active-3 {
  color: #fff;
  border-color: var(--accent, #d4a574);
  background: rgba(212, 165, 116, 0.18);
}
.equip-set-glyph {
  font-size: 12px;
  line-height: 1;
}
.equip-set-count {
  font-variant-numeric: tabular-nums;
}

/* Catalog — scrolling list of every arcana, grouped by rarity then school. */
.equip-list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px;
  padding: 4px 18px 12px;
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
@media (min-width: 520px) {
  .equip-list {
    grid-template-columns: 1fr 1fr;
  }
}
.equip-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  text-align: left;
  font-family: var(--font-mono);
  position: relative;
}
.equip-card.selected {
  border-color: var(--accent, #d4a574);
  background: rgba(212, 165, 116, 0.08);
}
.equip-card.locked {
  opacity: 0.55;
}
.equip-card.locked::before {
  content: '🔒';
  position: absolute;
  right: 10px;
  top: 8px;
  font-size: 12px;
  opacity: 0.7;
}

/* Marketplace Owned tile — pack entitlements get an inline Open button */
.mp-owned-open {
  margin-top: 8px;
  padding: 8px 14px;
  border-radius: 999px;
  border: 0;
  background: var(--red, #d71921);
  color: #fff;
  font-family: var(--font-rank);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
  align-self: flex-start;
}
.mp-owned-open:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Pack-grant banner — shown over the chest reveal when the unwrap is from
   an arcana pack. Lists exactly what the server granted so the player can
   confirm the inventory landed. */
.pack-grant-banner {
  position: fixed;
  left: 50%;
  bottom: calc(env(safe-area-inset-bottom) + 32px);
  transform: translateX(-50%);
  z-index: 60;
  width: min(420px, calc(100vw - 32px));
  background: rgba(20, 20, 20, 0.92);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 14px 18px;
  font-family: var(--font-mono);
  color: var(--text);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pack-grant-title {
  font-family: var(--font-rank);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent, #d4a574);
}
.pack-grant-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.pack-grant-row {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12px;
}
.pack-grant-glyph {
  font-size: 14px;
  color: var(--accent, #d4a574);
}
.pack-grant-name {
  flex: 1;
}
.pack-grant-meta {
  color: var(--text-muted);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.equip-card.selected::after {
  content: '✓';
  position: absolute;
  right: 10px;
  top: 8px;
  color: var(--accent, #d4a574);
  font-size: 14px;
}
.equip-card-glyph {
  font-size: 18px;
  line-height: 1;
  flex: 0 0 auto;
}
.equip-card-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
}
.equip-card-name {
  font-family: var(--font-rank);
  font-size: 13px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.equip-card-meta {
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  text-transform: uppercase;
}
/* Rarity tint — left border doubles as a quick scan key. */
.equip-rarity-common {
  border-left: 3px solid #707070;
}
.equip-rarity-uncommon {
  border-left: 3px solid #4ec76b;
}
.equip-rarity-rare {
  border-left: 3px solid #4488ff;
}
.equip-rarity-epic {
  border-left: 3px solid #b264e0;
}
.equip-rarity-legendary {
  border-left: 3px solid #f5b94a;
}

.equip-footer {
  display: flex;
  gap: 10px;
  padding: 10px 18px 16px;
  flex: 0 0 auto;
  border-top: 1px solid var(--border);
  background: var(--surface);
}
.equip-skip {
  flex: 0 0 auto;
  padding: 12px 18px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
}
.equip-ready {
  flex: 1 1 auto;
  padding: 14px 18px;
  border-radius: 999px;
  border: 0;
  background: var(--red, #d71921);
  color: #fff;
  font-family: var(--font-rank);
  font-size: 13px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
}

/* Arcana detail modal — opens on equip-card tap so the player can read the
   effect before committing a slot. Inherits the rarity left-border class
   from .equip-rarity-* so visual signal stays consistent. */
.arcana-detail-panel {
  background: var(--surface, #141414);
  color: var(--text, #fff);
  border: 1px solid var(--border);
  border-radius: 14px;
  width: min(420px, calc(100vw - 32px));
  max-height: calc(100svh - 80px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: var(--font-mono);
}
.arcana-detail-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 18px 10px;
  border-bottom: 1px solid var(--border);
}
.arcana-detail-glyph {
  font-size: 28px;
  line-height: 1;
  flex: 0 0 auto;
}
.arcana-detail-title-block {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.arcana-detail-name {
  font-family: var(--font-rank);
  font-size: 16px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.arcana-detail-meta {
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.arcana-detail-desc {
  padding: 12px 18px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text);
}
.arcana-detail-trigger,
.arcana-detail-tax {
  display: flex;
  gap: 8px;
  padding: 4px 18px;
  font-size: 11px;
  letter-spacing: 0.06em;
}
.arcana-detail-trigger-label {
  color: var(--text-muted);
  text-transform: uppercase;
  font-size: 10px;
  letter-spacing: 0.16em;
  min-width: 44px;
}
.arcana-detail-trigger-text {
  color: var(--text);
}
.arcana-detail-tax-text {
  color: var(--red, #d71921);
}
.arcana-detail-set {
  margin: 12px 18px 0;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.arcana-detail-set-title {
  font-family: var(--font-rank);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent, #d4a574);
}
.arcana-detail-set-line {
  font-size: 11px;
  color: var(--text-dim);
}
.arcana-detail-actions {
  display: flex;
  gap: 8px;
  padding: 14px 18px calc(env(safe-area-inset-bottom) + 14px);
  margin-top: auto;
  border-top: 1px solid var(--border);
}
.arcana-detail-cancel {
  flex: 0 0 auto;
  padding: 10px 18px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
}
.arcana-detail-equip {
  flex: 1 1 auto;
  padding: 12px 18px;
  border-radius: 999px;
  border: 0;
  background: var(--red, #d71921);
  color: #fff;
  font-family: var(--font-rank);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
}
.arcana-detail-equip.is-unequip {
  background: var(--surface-2, #1e1e1e);
  color: var(--text);
  border: 1px solid var(--border);
}

/* ---- Full-screen main loader (boot/preload) ---- */
.main-loader {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: #000;
  color: #fff;
  transition: opacity 200ms ease;
  opacity: 1;
}
.main-loader.is-leaving {
  opacity: 0;
  pointer-events: none;
}
.main-loader-video {
  width: min(40vw, 260px);
  height: auto;
  max-height: 40vh;
  object-fit: contain;
  filter: drop-shadow(0 6px 20px rgba(0, 0, 0, 0.6));
}
.main-loader-status {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.7);
}
.main-loader-bar {
  width: min(60vw, 280px);
  height: 3px;
  background: rgba(255, 255, 255, 0.12);
  border-radius: 2px;
  overflow: hidden;
}
.main-loader-bar-fill {
  height: 100%;
  width: 0%;
  background: var(--red, #d71921);
  transition: width 140ms ease-out;
}

/* ---- Inline loader (matchmaking, uploads, in-flight buttons) ---- */
.inline-loader {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  vertical-align: middle;
}
.inline-loader video {
  display: block;
}

/* Override the legacy CSS-dot matchmaking spinner sizing so the video
   loader sits in the same vertical rhythm as before. */
.matchmaking-spinner {
  margin: 4px auto 0;
}

/* Marketplace buy modal — status line with optional inline loader. */
.mp-buy-status {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 24px;
}
.mp-buy-status-loader {
  flex-shrink: 0;
}

/* Menu — music mute/unmute chip in topbar. */
.menu-music-btn {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--text, #fff);
  border-radius: 10px;
  padding: 4px 10px;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  transition:
    border-color 120ms ease,
    background 120ms ease;
}
.menu-music-btn:hover,
.menu-music-btn:focus {
  border-color: var(--accent, #d4a574);
  background: rgba(255, 255, 255, 0.04);
  outline: none;
}

/* ===== Pack opening (chest button) ===== */
.menu-chest-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.04);
  color: #f4e9c8;
  cursor: pointer;
}
.menu-chest-glyph {
  font-size: 14px;
}
.menu-chest-label {
  font-size: 11px;
  letter-spacing: 0.08em;
}

.chest-stage {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  perspective: 1100px;
  /* Pure-black like the rest of the app, with a faint rarity-tinted bloom
     behind the action (set by the card's rarity glow var when revealed). */
  background:
    radial-gradient(90% 70% at 50% 44%, var(--stage-glow, transparent), transparent 70%),
    radial-gradient(130% 130% at 50% 40%, #161616, #000 72%);
  overflow: hidden;
  z-index: 50;
}
.chest-hint {
  position: fixed;
  bottom: 8%;
  left: 0;
  right: 0;
  text-align: center;
  font-family: var(--font-mono);
  color: var(--text-dim, #b0b0b0);
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  z-index: 51;
  pointer-events: none;
}
/* Matches the app's pill/button language: transparent, thin border, mono. */
.chest-close {
  position: fixed;
  top: 14px;
  right: 14px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--border, #333);
  background: transparent;
  color: var(--text-dim, #b0b0b0);
  font-size: 13px;
  cursor: pointer;
  z-index: 52;
  transition:
    color 0.12s,
    border-color 0.12s;
}
.chest-close:hover {
  color: var(--text, #fff);
  border-color: var(--text-muted, #707070);
}

/* --- Shared holographic shine (driven by --mx / --my) --- */
.pack-holo,
.chest-card-holo {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  mix-blend-mode: soft-light;
  opacity: 0.6;
  /* Metallic foil glint — a single cool→warm sheen sweep, not a rainbow. */
  background: linear-gradient(
    115deg,
    transparent 30%,
    rgba(190, 205, 230, 0.25) 44%,
    rgba(255, 255, 255, 0.4) 50%,
    rgba(255, 226, 170, 0.28) 56%,
    transparent 70%
  );
  background-size: 250% 250%;
  background-position: calc(var(--mx, 0) * 120% + 50%) calc(var(--my, 0) * 60% + 50%);
  pointer-events: none;
}
/* --- Moving specular glare (foil + card) --- */
.pack-specular,
.chest-card-specular {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    115deg,
    transparent 38%,
    rgba(255, 255, 255, 0.45) 50%,
    transparent 62%
  );
  background-size: 250% 250%;
  background-position: calc(var(--mx, 0) * 110% + 50%) center;
  mix-blend-mode: overlay;
  pointer-events: none;
}

/* --- Pack --- */
.chest-pack {
  position: relative;
  width: 216px;
  height: 304px;
  --tear: 0;
  --seam: 52px; /* y of the seal/pouch crimp line */
}
.chest-pack.entrance {
  animation: pack-enter 0.6s cubic-bezier(0.18, 0.9, 0.3, 1.15) both;
}
@keyframes pack-enter {
  0% {
    transform: translateY(60vh) scale(0.7);
    opacity: 0;
  }
  60% {
    opacity: 1;
  }
  72% {
    transform: translateY(-12px) scale(1.04);
  }
  100% {
    transform: translateY(0) scale(1);
  }
}
.chest-pack.focus {
  transform: scale(1.06);
  transition: transform 0.4s ease;
}
.chest-pack.idle {
  animation: pack-float 3.4s ease-in-out infinite;
}
@keyframes pack-float {
  0%,
  100% {
    transform: scale(1.06) translateY(0);
  }
  50% {
    transform: scale(1.06) translateY(-9px);
  }
}

/* Only the tear canvas takes input; every other pack layer is visual. */
.pack-inner,
.pack-body,
.pack-rip-light {
  pointer-events: none;
}

/* The cloth seal lives on a canvas overlaying the whole pack so the torn strip
   can flop and fall past the pack's lower edge. It sits above the body and
   handles only the tear gesture (the stage handles dismissal). */
.pack-tear-canvas {
  position: absolute;
  left: -20%;
  right: -20%;
  top: -10%;
  width: 140%;
  height: 150%;
  z-index: 4;
  touch-action: none;
  cursor: grab;
}
.pack-tear-canvas:active {
  cursor: grabbing;
}
.chest-pack.torn .pack-tear-canvas {
  pointer-events: none;
}

/* Glowing interior — bleeds out of the opening as the seal tears off. White-hot
   core fading to the pull's rarity tint, so the leaking light hints what's
   inside (cool app-palette colours, not generic warm "loot" gold). */
.pack-inner {
  position: absolute;
  left: 9%;
  right: 9%;
  top: 5%;
  height: 34%;
  border-radius: 8px;
  background: radial-gradient(
    72% 92% at 50% 26%,
    #fff,
    var(--pull-tint, var(--yellow, #ffcc33)) 46%,
    transparent 76%
  );
  opacity: calc(var(--tear, 0) * 0.9);
  filter: blur(3px);
  /* Parallax: the interior glow sits "behind", drifting opposite the tilt. */
  transform: translate(calc(var(--mx, 0) * -7px), calc(var(--my, 0) * -6px));
  transition:
    opacity 0.1s linear,
    transform 0.12s ease-out;
}

/* The pouch — everything below the crimp. Holds the branding; stays after the
   seal is torn off, like a real opened booster. */
.pack-body {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--seam);
  bottom: 0;
  border-radius: 3px 3px 10px 10px;
  overflow: hidden;
}
.pack-foil {
  position: absolute;
  inset: 0;
  /* Flat app-surface foil carrying the game's dot-matrix texture — the dot
     grid is Cardomancer's core motif. Thin border, not glossy. */
  background-image:
    radial-gradient(var(--dot-color, #555) 1px, transparent 1.4px),
    linear-gradient(155deg, var(--surface-2, #1e1e1e), var(--surface, #141414) 60%, #101010);
  background-size:
    12px 12px,
    cover;
  box-shadow:
    inset 0 0 0 1px var(--border, #333),
    inset 0 0 26px rgba(0, 0, 0, 0.55),
    0 14px 30px rgba(0, 0, 0, 0.55);
}

/* Hot light flaring along the rip — intensifies with the tear. */
.pack-rip-light {
  position: absolute;
  top: calc(var(--seam) - 16px);
  left: 4%;
  right: 4%;
  height: 30px;
  z-index: 2;
  background: radial-gradient(
    60% 100% at 50% 50%,
    #fff,
    var(--pull-tint, var(--yellow, #ffcc33)) 48%,
    transparent 78%
  );
  filter: blur(3px);
  opacity: var(--tear, 0);
}
.pack-label {
  position: absolute;
  top: 42%;
  left: 0;
  right: 0;
  text-align: center;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  color: var(--red, #d71921);
  text-shadow: 0 0 12px rgba(255, 68, 102, 0.35);
  font-weight: 700;
  letter-spacing: 0.18em;
  font-size: 15px;
  /* Parallax: branding sits "in front", drifting with the tilt. */
  transform: translateY(-50%) translate(calc(var(--mx, 0) * 5px), calc(var(--my, 0) * 4px));
  transition: transform 0.12s ease-out;
}
.pack-sub {
  position: absolute;
  top: 52%;
  left: 0;
  right: 0;
  text-align: center;
  color: var(--text-muted, #707070);
  font-size: 9px;
  letter-spacing: 0.4em;
  transform: translate(calc(var(--mx, 0) * 5px), calc(var(--my, 0) * 4px));
  transition: transform 0.12s ease-out;
}

/* Spent pack drops out of frame once the card is clear. */
.chest-pack.spent {
  animation: pack-drop 0.5s ease-in forwards;
  pointer-events: none;
}
@keyframes pack-drop {
  to {
    transform: translateY(120px) scale(0.92) rotateZ(-4deg);
    opacity: 0;
  }
}

/* Invisible swipe hitbox over the top third */
.pack-tear-zone {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 36%;
  z-index: 6;
  cursor: grab;
  touch-action: none;
}

/* Swipe tutorial hint — finger + arrow sweeping right→left */
.swipe-hint {
  position: absolute;
  top: 5%;
  left: 0;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 4px;
  z-index: 5;
  pointer-events: none;
  animation: swipe-hint-move 1.5s ease-in-out infinite;
}
.swipe-hint-finger {
  font-size: 24px;
}
.swipe-hint-arrow {
  color: var(--yellow, #ffcc33);
  font-weight: 700;
  text-shadow: 0 0 8px rgba(255, 204, 51, 0.8);
}
@keyframes swipe-hint-move {
  0% {
    transform: translateX(34%);
    opacity: 0.25;
  }
  50% {
    opacity: 1;
  }
  100% {
    transform: translateX(-34%);
    opacity: 0.25;
  }
}

/* Tear particle burst */
.pack-sparks {
  position: absolute;
  top: 16%;
  left: 50%;
  width: 0;
  height: 0;
  z-index: 6;
  pointer-events: none;
}
.pack-spark {
  position: absolute;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #ffe9a8;
  box-shadow: 0 0 6px 1px #ffd86e;
  animation: spark-fly var(--d, 0.6s) ease-out forwards;
}
@keyframes spark-fly {
  to {
    transform: translate(var(--tx, 0), var(--ty, -60px));
    opacity: 0;
  }
}

/* --- Card --- */
.chest-card {
  position: relative;
  width: 232px;
  height: 324px;
  border-radius: 10px;
  transform-style: preserve-3d;
  transform: rotateX(calc(var(--my, 0) * -13deg)) rotateY(calc(var(--mx, 0) * 13deg));
  transition: transform 0.12s ease-out;
  border: 2px solid var(--rarity-color, #aaa);
  box-shadow:
    0 0 28px var(--rarity-glow, rgba(255, 255, 255, 0.3)),
    0 18px 40px rgba(0, 0, 0, 0.6);
}
/* Rising out of the torn pack: starts low + small (hidden behind the pack
   body), grows up into frame as the spent pack drops away. */
.chest-card.emerge {
  animation: card-emerge 0.72s cubic-bezier(0.18, 0.9, 0.28, 1.08) both;
}
@keyframes card-emerge {
  0% {
    transform: translateY(66px) scale(0.58);
    opacity: 0.2;
  }
  55% {
    opacity: 1;
  }
  82% {
    transform: translateY(-8px) scale(1.03);
  }
  100% {
    transform: translateY(0) scale(1);
  }
}
.chest-card.dismiss {
  animation: card-dismiss 0.3s forwards;
}
@keyframes card-dismiss {
  to {
    transform: rotateY(90deg);
    opacity: 0;
  }
}

.chest-card-inner {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
  /* Flat app surface (matches the menu hero-cards). */
  background: var(--surface, #141414);
}
.chest-card-bg {
  position: absolute;
  inset: -8%;
  /* Rarity bloom + the app's signature dot grid (same 12px cell as the pack). */
  background:
    radial-gradient(
      70% 55% at 50% 34%,
      var(--rarity-glow, rgba(255, 255, 255, 0.25)),
      transparent 70%
    ),
    radial-gradient(var(--dot-color, #555) 1px, transparent 1.4px);
  background-size:
    cover,
    12px 12px;
  opacity: 0.9;
  transform: translate(calc(var(--mx, 0) * -7px), calc(var(--my, 0) * -7px));
}
/* Big suit emblem — the real suit SVG used as a mask so it tints cleanly. */
.chest-card-emblem {
  position: absolute;
  top: 36%;
  left: 50%;
  width: 116px;
  height: 116px;
  transform: translate(-50%, -50%) translate(calc(var(--mx, 0) * 14px), calc(var(--my, 0) * 14px));
  background: var(--suit-color, #d0d0d0);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  filter: drop-shadow(0 0 16px var(--rarity-glow, rgba(255, 255, 255, 0.4)));
}
.chest-card-name {
  position: absolute;
  bottom: 56px;
  left: 0;
  right: 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--text, #fff);
  transform: translate(calc(var(--mx, 0) * 9px), calc(var(--my, 0) * 9px));
}
.chest-card-rarity {
  position: absolute;
  bottom: 34px;
  left: 0;
  right: 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--rarity-color, #aaa);
  transform: translate(calc(var(--mx, 0) * 9px), calc(var(--my, 0) * 9px));
}
.chest-card-frame {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.08),
    inset 0 0 18px var(--rarity-glow, rgba(255, 255, 255, 0.2));
  pointer-events: none;
}

/* Hero suit tints — reuse the app's suit palette */
.chest-card.hero-diamonds {
  --suit-color: var(--diamonds, #4488ff);
}
.chest-card.hero-hearts {
  --suit-color: var(--hearts, #d71921);
}
.chest-card.hero-spades {
  --suit-color: var(--spades, #d0d0d0);
}

/* Rarity frames + holo intensity — keyed to the app palette */
.chest-card.rarity-common {
  --rarity-color: var(--spades, #d0d0d0);
  --rarity-glow: rgba(208, 208, 208, 0.28);
}
.chest-card.rarity-rare {
  --rarity-color: var(--diamonds, #4488ff);
  --rarity-glow: rgba(68, 136, 255, 0.45);
}
.chest-card.rarity-epic {
  --rarity-color: var(--clubs, #33cc66);
  --rarity-glow: rgba(51, 204, 102, 0.45);
}
.chest-card.rarity-legendary {
  --rarity-color: var(--yellow, #ffcc33);
  --rarity-glow: rgba(255, 204, 51, 0.6);
}
.chest-card.rarity-common .chest-card-holo {
  opacity: 0.2;
}
.chest-card.rarity-rare .chest-card-holo {
  opacity: 0.36;
}
.chest-card.rarity-epic .chest-card-holo {
  opacity: 0.5;
}
.chest-card.rarity-legendary .chest-card-holo {
  opacity: 0.7;
}
.chest-card.rarity-legendary .chest-card-specular {
  background-image: linear-gradient(
    115deg,
    transparent 30%,
    rgba(255, 255, 255, 0.6) 50%,
    transparent 70%
  );
}

@media (prefers-reduced-motion: reduce) {
  .chest-pack.entrance,
  .chest-pack.idle,
  .chest-pack.idle .pack-zip-tab,
  .chest-pack.spent,
  .swipe-hint,
  .pack-spark,
  .chest-card.emerge {
    animation: none;
  }
}

/* In-app "Reduced motion" toggle (Settings). gameplay-prefs.ts toggles
   `.cm-reduce-motion` on <html> so users can kill animations without touching an
   OS setting. Previously NO rule keyed off this class, so the switch was inert;
   this is the standard a11y reset — collapse every animation/transition to
   ~instant. animationend still fires and `forwards` fills still apply, so game
   logic that awaits combo/shake/vanish animations still resolves; only the motion
   is gone. (PIXI/GSAP effects aren't CSS — gated separately in code.) */
.cm-reduce-motion *,
.cm-reduce-motion *::before,
.cm-reduce-motion *::after {
  animation-duration: 0.01ms !important;
  animation-delay: 0ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  transition-delay: 0ms !important;
  scroll-behavior: auto !important;
}

/* ===== Pack opening: style picker ===== */
.chest-picker {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 20px;
  z-index: 51;
  animation: picker-in 0.4s ease both;
}
@keyframes picker-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
.chest-picker-title {
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text, #fff);
}
.chest-picker-sub {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-dim, #9a9a9a);
  margin-bottom: 18px;
}
.chest-picker-grid {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}
.chest-opt {
  position: relative;
  width: 168px;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  padding: 14px;
  border-radius: 14px;
  border: 1px solid var(--border, #2c2c2c);
  background: var(--surface, #141414);
  color: var(--text, #fff);
  cursor: pointer;
  text-align: center;
  transition:
    transform 0.16s ease,
    border-color 0.16s ease,
    box-shadow 0.16s ease;
}
.chest-opt:hover {
  transform: translateY(-3px);
  border-color: var(--text-muted, #6a6a6a);
}
.chest-opt.selected {
  border-color: var(--yellow, #ffcc33);
  box-shadow:
    0 0 0 1px var(--yellow, #ffcc33),
    0 0 26px rgba(255, 204, 51, 0.35);
}
.chest-opt-preview {
  position: relative;
  height: 96px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  overflow: hidden;
  background:
    radial-gradient(80% 70% at 50% 40%, rgba(255, 255, 255, 0.06), transparent 70%),
    radial-gradient(var(--dot-color, #2a2a2a) 1px, transparent 1.4px);
  background-size:
    cover,
    11px 11px;
}
.chest-opt-anim {
  font-size: 38px;
  filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.25));
}
.chest-opt-anim-basic {
  animation: opt-rock 3.2s ease-in-out infinite;
}
.chest-opt-anim-advanced {
  animation: opt-pulse 1.8s ease-in-out infinite;
  color: var(--yellow, #ffcc33);
  filter: drop-shadow(0 0 14px rgba(255, 204, 51, 0.6));
}
@keyframes opt-rock {
  0%,
  100% {
    transform: rotate(-6deg) translateY(0);
  }
  50% {
    transform: rotate(6deg) translateY(-4px);
  }
}
@keyframes opt-pulse {
  0%,
  100% {
    transform: scale(1);
    opacity: 0.85;
  }
  50% {
    transform: scale(1.22);
    opacity: 1;
  }
}
.chest-opt-badge {
  position: absolute;
  top: 6px;
  right: 6px;
  font-size: 8px;
  letter-spacing: 0.16em;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--yellow, #ffcc33);
  color: #1a1400;
  font-weight: 700;
}
.chest-opt-name {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.1em;
}
.chest-opt-desc {
  font-size: 10px;
  line-height: 1.4;
  letter-spacing: 0.06em;
  color: var(--text-dim, #9a9a9a);
}

/* ===== Pack opening: cinematic (advanced) ===== */
.chest-adv {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  --charge: 0;
  --adv-tint: var(--yellow, #ffcc33);
  --adv-rgb: 255, 204, 51;
}
.chest-adv.shake {
  animation: adv-screen-shake 0.42s ease-out;
}
@keyframes adv-screen-shake {
  0%,
  100% {
    transform: translate(0, 0);
  }
  20% {
    transform: translate(-8px, 4px);
  }
  40% {
    transform: translate(7px, -5px);
  }
  60% {
    transform: translate(-5px, 6px);
  }
  80% {
    transform: translate(4px, -3px);
  }
}
.adv-particles {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 3;
  pointer-events: none;
}
.adv-rays {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 170vmax;
  height: 170vmax;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  background: repeating-conic-gradient(
    from 0deg,
    rgba(var(--adv-rgb), 0) 0deg,
    rgba(var(--adv-rgb), 0.16) 3deg,
    rgba(var(--adv-rgb), 0) 7deg
  );
  -webkit-mask: radial-gradient(closest-side, transparent 16%, #000 58%, transparent 80%);
  mask: radial-gradient(closest-side, transparent 16%, #000 58%, transparent 80%);
  animation: adv-spin 22s linear infinite;
}
.adv-rays.lit {
  opacity: 0.9;
  transition: opacity 0.7s ease;
}
@keyframes adv-spin {
  from {
    transform: translate(-50%, -50%) rotate(0);
  }
  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}
.adv-aura {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 460px;
  height: 460px;
  z-index: 0;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle, var(--adv-tint) 0%, transparent 62%);
  opacity: calc(0.16 + var(--charge) * 0.5);
  filter: blur(26px);
  transition: opacity 0.18s linear;
}
.adv-aura.lit {
  opacity: 0.8;
  transition: opacity 0.7s ease;
}
.adv-pack {
  position: relative;
  width: 216px;
  height: 300px;
  z-index: 2;
  border-radius: 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: linear-gradient(160deg, #1b1b21, #0b0b0f);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow:
    0 22px 54px rgba(0, 0, 0, 0.6),
    inset 0 0 34px rgba(0, 0, 0, 0.5);
  filter: brightness(calc(1 + var(--charge) * 0.7));
}
.adv-pack.enter {
  animation: adv-pack-enter 0.6s cubic-bezier(0.18, 0.9, 0.3, 1.15) both;
}
@keyframes adv-pack-enter {
  0% {
    transform: translateY(56vh) scale(0.7);
    opacity: 0;
  }
  60% {
    opacity: 1;
  }
  100% {
    transform: translateY(0) scale(1);
  }
}
.adv-pack.float {
  animation: adv-float 3.4s ease-in-out infinite;
}
@keyframes adv-float {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-9px);
  }
}
.adv-pack.charging {
  animation: adv-shake 0.09s linear infinite;
}
@keyframes adv-shake {
  0% {
    transform: translate(0, 0);
  }
  25% {
    transform: translate(-2px, 1px);
  }
  50% {
    transform: translate(2px, -1px);
  }
  75% {
    transform: translate(-1px, -2px);
  }
  100% {
    transform: translate(1px, 2px);
  }
}
.adv-pack.burst {
  animation: adv-pack-burst 0.36s ease-in forwards;
}
@keyframes adv-pack-burst {
  0% {
    transform: scale(1);
    filter: brightness(1.7);
  }
  100% {
    transform: scale(1.5);
    opacity: 0;
    filter: brightness(3) blur(14px);
  }
}
.adv-pack-foil {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(var(--dot-color, #333) 1px, transparent 1.4px);
  background-size: 11px 11px;
  opacity: 0.45;
}
.adv-pack-label {
  position: relative;
  font-family: var(--font-mono);
  font-weight: 700;
  letter-spacing: 0.14em;
  font-size: 15px;
  color: var(--text, #fff);
}
.adv-pack-sub {
  position: relative;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.3em;
  color: var(--text-dim, #999);
}
.adv-pack-shine {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(
    115deg,
    transparent 40%,
    rgba(255, 255, 255, 0.18) 50%,
    transparent 60%
  );
  background-size: 250% 250%;
  animation: adv-shine 3s linear infinite;
  pointer-events: none;
}
@keyframes adv-shine {
  to {
    background-position: -200% 0;
  }
}
.adv-flash {
  position: absolute;
  inset: 0;
  z-index: 6;
  background: #fff;
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: screen;
}
.adv-flash.fire {
  animation: adv-flash 0.5s ease-out;
}
@keyframes adv-flash {
  0% {
    opacity: 0;
  }
  8% {
    opacity: 0.95;
  }
  100% {
    opacity: 0;
  }
}
.adv-shock {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 46px;
  height: 46px;
  z-index: 5;
  border-radius: 50%;
  border: 2px solid rgba(var(--adv-rgb), 0.9);
  transform: translate(-50%, -50%) scale(0.2);
  opacity: 0;
  pointer-events: none;
}
.adv-shock.fire {
  animation: adv-shock 0.6s ease-out;
}
@keyframes adv-shock {
  0% {
    opacity: 0.9;
    transform: translate(-50%, -50%) scale(0.2);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(9);
  }
}
.adv-card {
  z-index: 4;
}
.adv-card.flip {
  animation: adv-card-flip 0.6s cubic-bezier(0.2, 0.8, 0.25, 1.05) both;
}
@keyframes adv-card-flip {
  0% {
    transform: translateY(44px) rotateY(180deg) scale(0.6);
    opacity: 0;
  }
  55% {
    opacity: 1;
  }
  100% {
    transform: translateY(0) rotateY(0) scale(1);
    opacity: 1;
  }
}
.adv-card.adv-dismiss {
  animation: card-dismiss 0.3s forwards;
}

@media (prefers-reduced-motion: reduce) {
  .chest-adv.reduced .adv-pack.enter,
  .chest-adv.reduced .adv-pack.float,
  .chest-adv.reduced .adv-pack.charging,
  .chest-adv.reduced .adv-pack-shine,
  .chest-adv.reduced .adv-rays,
  .chest-adv.reduced .adv-card.flip,
  .chest-opt-anim-basic,
  .chest-opt-anim-advanced {
    animation: none;
  }
  .chest-adv.shake {
    animation: none;
  }
}

/* =====================================================================
   ПРОТОТИП А — Design system (Nothing-OS dot-matrix)
   Additive layer applied to all NON-battlefield scenes. Never targets the
   game / hud / board / hand selectors. Brand red (#d71921) + Ndot 47 already
   match the base theme; this adds the red-field/ink canvases, pill radii,
   picker-wheel, FAB, status-bar header, currency + nav primitives.
   ===================================================================== */
:root,
[data-theme='dark'] {
  /* brand red ramp */
  --cm-red: #d71921;
  --cm-red-hot: #ff0000;
  --cm-red-deep: #a81219;
  --cm-red-pill-hover: #e51f28;
  --cm-red-grad-top: #f0353f;
  --cm-red-grad-bot: #c2161e;
  --cm-ink: #140a0b; /* dark "depth/content" canvas */

  /* Hub ambient background ("red field"). Calm, slowly-drifting aurora over a
     near-black oxblood base — the loud flat red was too eye-catching. Exposed as
     tokens so a future "background preset" just overrides these on .cm-screen--red.
     Aurora colors are raw R,G,B triples so they can be used inside rgba(). */
  --cm-hub-bg-top: #2a0b12; /* soft warm glow at the very top (status-bar zone) */
  --cm-hub-bg-mid: #1a0609;
  --cm-hub-bg-bot: #100406; /* darkest, outer */
  --cm-hub-shell: #160508; /* flat color for safe-areas / status bar tint */
  --cm-hub-aurora-1: 215, 25, 33; /* crimson  */
  --cm-hub-aurora-2: 168, 18, 25; /* deep red */
  --cm-hub-aurora-3: 232, 78, 96; /* ember rose */

  /* neutral spine (keep base --bg/--surface as-is) */
  --cm-shell: #0b0b0d;
  --cm-surface-1: #1a1d1c;
  --cm-surface-2b: #262626;
  --cm-surface-3: #323232;

  /* white-alpha ladder (prototype's exact alphas) */
  --cm-hair-08: rgba(255, 255, 255, 0.08);
  --cm-hair-10: rgba(255, 255, 255, 0.1);
  --cm-hair-12: rgba(255, 255, 255, 0.12);
  --cm-hair-16: rgba(255, 255, 255, 0.16);
  --cm-hair-18: rgba(255, 255, 255, 0.18);
  --cm-hair-22: rgba(255, 255, 255, 0.22);
  --cm-hair-30: rgba(255, 255, 255, 0.3);
  --cm-fg-mute: rgba(255, 255, 255, 0.75);
  --cm-fg-dim: rgba(255, 255, 255, 0.45);
  --cm-glass: rgba(0, 0, 0, 0.5);
  --cm-scrim: rgba(9, 9, 11, 0.94);

  /* accents — FINALLY define --accent (used ~30x as var(--accent,#d4a574)) */
  --accent: #d4a574;
  --cm-gold: #e8c36b; /* coins / done-ring */
  --cm-blue: #6fa8ff; /* gems */
  --cm-rose: #e85a7a; /* lira / featured */
  --cm-violet: #8e5cff; /* taren */
  --cm-mana: #7bd389;

  /* canonical card-suit palette (separate from UI --diamonds etc) */
  --cm-suit-hearts: var(--cm-red-hot);
  --cm-suit-diamonds: var(--cm-red);
  --cm-suit-spades: #000;
  --cm-suit-clubs: #000;

  /* type scale */
  --cm-h1: 56px;
  --cm-h2: 40px;
  --cm-h3: 28px;
  --cm-h4: 20px;
  --cm-body: 16px;
  --cm-small: 13px;
  --cm-micro: 11px;

  /* spacing scale */
  --cm-s-1: 4px;
  --cm-s-2: 8px;
  --cm-s-3: 12px;
  --cm-s-4: 16px;
  --cm-s-5: 24px;
  --cm-s-6: 32px;
  --cm-s-8: 48px;

  /* radii (extends the lone --radius:6px) */
  --cm-r-card: 5px;
  --cm-r-chip: 7px;
  --cm-r-banner: 12px;
  --cm-r-tile: 16px;
  --cm-r-tray: 20px;
  --cm-r-screen: 22px;
  --cm-r-pill: 125px;

  /* shadows / glows */
  --cm-shadow-card: 0 4.8px 4.8px rgba(3, 3, 3, 0.25);
  --cm-shadow-pill: 0 4px 4px rgba(3, 3, 3, 0.25);
  --cm-glow-red: 0 8px 20px rgba(215, 25, 33, 0.45);
  --cm-glow-red-fab: 0 6px 16px rgba(215, 25, 33, 0.45);
  --cm-glow-red-strong: 0 0 30px rgba(215, 25, 33, 0.55), 0 8px 22px rgba(215, 25, 33, 0.4);
  --cm-shadow-fab-white: 0 8px 22px rgba(0, 0, 0, 0.3);
  --cm-shadow-popup: 0 24px 60px rgba(0, 0, 0, 0.6);

  /* signature easing */
  --cm-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* ---- keyframes (scoped to .cm-* usage; names clear of existing set) ---- */
@keyframes cm-fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
@keyframes cm-slide {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes cm-pop {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}
@keyframes cm-reveal {
  from {
    clip-path: circle(0% at 50% 92%);
  }
  to {
    clip-path: circle(145% at 50% 92%);
  }
}
/* prototype `fillKf` — bars/loaders fill from 0 to their set width.
   (No !important: it is ignored inside @keyframes, which silently disabled
   the from-0 and killed the fill animation.) */
@keyframes cm-fill {
  from {
    width: 0;
  }
}
@keyframes cm-spin {
  to {
    transform: rotate(360deg);
  }
}
/* Sonar ping on a static ring — compositor-only (transform/opacity). The old
   variant animated box-shadow spread, repainting the region every frame for
   the whole matchmaking wait. The pinged layer is small and unblurred, so
   scale is safe per the animation rules. */
@keyframes cm-pulse-ring {
  from {
    transform: scale(1);
    opacity: 0.55;
  }
  to {
    transform: scale(1.12);
    opacity: 0;
  }
}

/* =========================== B.8 CANVAS =========================== */
.cm-screen {
  position: relative;
  /* The scene background fills the WHOLE window (no phone-frame surround, no
     black bars) — mobile-first but edge-to-edge on every size. The *content*
     stays in a centered phone-width column via .cm-stack (and the per-scene
     footers), so it reads the same on phone and desktop. */
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: cm-fade 0.36s var(--cm-ease) both;
}
/* Full-bleed: the scene paints edge-to-edge UNDER the iOS safe areas (no black
   borders). #app's safe-area padding and #scene's 8px gutter are zeroed for
   cm-screen scenes; the safe area is reinstated as CONTENT padding on .cm-stack
   so nothing hides under the notch / home indicator. (game-scene doesn't use
   .cm-screen, so its safe-area handling is untouched.) */
#app:has(.cm-screen) {
  padding: 0;
}
#scene:has(.cm-screen) {
  padding: 0;
  align-items: stretch;
}
/* App-shell colour follows the active scene so the iOS safe-area zones (status
   bar / home indicator) are NEVER black. The <html> background propagates to
   the viewport canvas, covering the safe areas + overscroll; body/#app are set
   too as belt-and-suspenders. JS-independent and immediate. */
html:has(.cm-screen--red),
body:has(.cm-screen--red),
#app:has(.cm-screen--red) {
  background: var(--cm-hub-shell);
}
html:has(.cm-screen--ink),
body:has(.cm-screen--ink),
#app:has(.cm-screen--ink),
html:has(.hero-screen),
body:has(.hero-screen),
#app:has(.hero-screen) {
  background: var(--cm-ink);
}
html:has(.cm-screen--black),
body:has(.cm-screen--black),
#app:has(.cm-screen--black) {
  background: #000;
}
/* Calm ambient "red field": dark oxblood base + soft top glow. The two
   slow-drifting aurora layers live in ::before/::after. Only transform/opacity
   animate (GPU-composited), so it stays cheap on mobile + the weak VPS. */
.cm-screen--red {
  background:
    radial-gradient(150% 80% at 50% -8%, var(--cm-hub-bg-top) 0%, transparent 46%),
    linear-gradient(180deg, var(--cm-hub-bg-mid) 0%, var(--cm-hub-bg-bot) 100%);
  color: #fff;
  isolation: isolate; /* contain the screen blend-mode so it can't bleed onto app chrome */
}
.cm-screen--red::before,
.cm-screen--red::after {
  content: '';
  position: absolute;
  inset: -30%; /* oversize: blurred edges never reveal a seam while drifting */
  z-index: 0;
  pointer-events: none;
  background-repeat: no-repeat;
  will-change: transform;
  transform: translateZ(0);
}
/* Layer A — two crimson/deep-red blobs drifting one way. */
.cm-screen--red::before {
  background:
    radial-gradient(36% 30% at 30% 32%, rgba(var(--cm-hub-aurora-1), 0.5), transparent 70%),
    radial-gradient(32% 26% at 74% 60%, rgba(var(--cm-hub-aurora-2), 0.42), transparent 72%);
  filter: blur(44px);
  animation: cm-aurora-a 34s ease-in-out infinite alternate;
}
/* Layer B — an ember-rose + oxblood blob drifting the other way. Alphas are
   nudged up ~0.05 to replace the glow that mix-blend-mode:screen used to add;
   on the near-black oxblood base source-over ≈ screen, so it looks the same
   without forcing a full-viewport backdrop read-back + blend every frame. */
.cm-screen--red::after {
  background:
    radial-gradient(34% 28% at 70% 24%, rgba(var(--cm-hub-aurora-3), 0.33), transparent 72%),
    radial-gradient(40% 34% at 24% 80%, rgba(var(--cm-hub-aurora-2), 0.42), transparent 70%);
  filter: blur(56px);
  animation: cm-aurora-b 46s ease-in-out infinite alternate;
}
/* Pure translate drift — no scale/rotate. Animating scale/rotate on a 44-56px
   blurred, larger-than-screen layer forces Chromium to RE-SAMPLE the blurred
   texture every frame (a sustained GPU/bandwidth cost on Adreno/Mali while the
   idle hub is open); translating a cached blurred layer is the one genuinely
   cheap composited path. Range stays inside the -30% oversize so no seam shows. */
@keyframes cm-aurora-a {
  from {
    transform: translate3d(-5%, -4%, 0);
  }
  to {
    transform: translate3d(6%, 5%, 0);
  }
}
@keyframes cm-aurora-b {
  from {
    transform: translate3d(5%, 4%, 0);
  }
  to {
    transform: translate3d(-6%, -5%, 0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .cm-screen--red::before,
  .cm-screen--red::after {
    animation: none;
  }
}
.cm-screen--ink {
  background: var(--cm-ink);
  color: #fff;
}
.cm-screen--black {
  background: #000;
  color: #fff;
}
/* per-hero suit gradients (hero select) */
.cm-suit-grad-kai {
  background: linear-gradient(#f0922e, #d9701b, #b45415, #7a380f, var(--cm-ink));
}
.cm-suit-grad-lira {
  background: linear-gradient(var(--cm-red), #a8121a, #6e0c12, #3a0810, var(--cm-ink));
}
.cm-suit-grad-taren {
  background: linear-gradient(var(--cm-violet), #6b3fd9, #4a2a99, #281553, var(--cm-ink));
}
.cm-suit-grad-jager {
  background: linear-gradient(#9aa86a, #6f7d46, #4a542e, #262c18, var(--cm-ink));
}
.cm-suit-grad-chugaister {
  background: linear-gradient(#71904d, #486b38, #29462e, #172b24, var(--cm-ink));
}

/* the inner column that holds a scene's content with safe-area padding */
.cm-stack {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  box-sizing: border-box;
  padding: calc(env(safe-area-inset-top) + 6px) max(18px, env(safe-area-inset-right))
    calc(env(safe-area-inset-bottom) + 16px) max(18px, env(safe-area-inset-left));
}

/* =========================== B.1 STATUS BAR + HEADER =========================== */
.cm-statusbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  opacity: 0.9;
  padding: 4px 4px 0;
  min-height: 22px;
}
.cm-sb-right {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.cm-sb-right svg {
  display: block;
}

.cm-header {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 46px;
  padding: 6px 0 0;
}
.cm-header-back,
.cm-header-spacer {
  width: 38px;
  height: 38px;
  flex: 0 0 38px;
}
.cm-header-back {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: var(--cm-hair-10);
  color: #fff;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition:
    background 0.12s,
    transform 0.12s var(--cm-ease);
}
/* Primary back/close control on every secondary scene — 38px visual, ~48px
   hit area so thumbs don't miss it. */
.cm-header-back::after {
  content: '';
  position: absolute;
  inset: -5px;
  border-radius: 50%;
}
.cm-header-back:hover {
  background: var(--cm-hair-18);
}
.cm-header-back:active {
  transform: scale(0.92);
}
.cm-header-title {
  flex: 1;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 16px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.cm-header-sub {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--cm-fg-mute);
  margin-top: 2px;
}
.cm-header-right {
  width: 38px;
  flex: 0 0 38px;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
}

/* =========================== B.2 FAB =========================== */
.cm-fab {
  width: 66px;
  height: 66px;
  flex: 0 0 66px;
  border: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 26px;
  line-height: 1;
  transition: transform 0.12s var(--cm-ease);
}
.cm-fab:active {
  transform: scale(0.94);
}
.cm-fab--white {
  background: #fff;
  color: var(--cm-red);
  box-shadow: var(--cm-shadow-fab-white);
}
.cm-fab--red {
  background: var(--cm-red);
  color: #fff;
  box-shadow: var(--cm-glow-red-fab);
}
.cm-fab--locked {
  background: var(--cm-hair-18);
  color: var(--cm-fg-dim);
  box-shadow: none;
  pointer-events: none;
}

/* =========================== B.3 PILL / CTA =========================== */
.cm-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  justify-content: center;
  border: 0;
  cursor: pointer;
  border-radius: var(--cm-r-pill);
  font-family: var(--font-mono);
  font-size: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
  padding: 10px 18px;
  box-shadow: var(--cm-shadow-pill);
  transition:
    transform 0.08s ease,
    background 0.12s ease,
    box-shadow 0.2s ease;
}
.cm-pill:active {
  transform: translateY(1px);
}
.cm-pill--primary {
  background: var(--cm-red);
  color: #fff;
}
.cm-pill--primary:hover {
  background: var(--cm-red-pill-hover);
}
.cm-pill--primary:active {
  background: var(--cm-red-deep);
}
.cm-pill--white {
  background: #fff;
  color: var(--cm-ink);
}
.cm-pill--ghost {
  background: var(--cm-hair-08);
  color: #fff;
  border: 1px solid var(--cm-hair-18);
  box-shadow: none;
}
.cm-pill--block {
  width: 100%;
  height: 60px;
  border-radius: var(--cm-r-tile);
  font-size: 15px;
  letter-spacing: 0.14em;
}
.cm-pill--ready {
  box-shadow: var(--cm-glow-red-strong);
}
.cm-pill[disabled] {
  opacity: 0.45;
  pointer-events: none;
}
/* the "PLAY  ·  CHOOSE MODE ›" two-part hub pill */
.cm-pill--split {
  justify-content: space-between;
  padding: 0 22px;
}
.cm-pill-lead {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 17px;
  letter-spacing: 0.16em;
}
.cm-pill-sub {
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--cm-fg-dim);
}
.cm-pill--white .cm-pill-sub {
  /* 0.66 keeps the muted read but clears WCAG AA 4.5:1 at this 10-11px size. */
  color: rgba(20, 10, 11, 0.66);
}

/* =========================== B.4 VERTICAL PICKER-WHEEL =========================== */
.cm-wheel {
  /* responsive height so the wheel + the controls below it (and the forward
     FAB) always fit on a phone. pad is derived from --cm-wh so the snap
     centering math (pad = (height - itemHeight)/2) holds at any height. */
  --cm-wh: clamp(176px, 34vh, 320px);
  position: relative;
  height: var(--cm-wh);
  overflow-y: auto;
  scroll-snap-type: y mandatory;
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 22%, #000 78%, transparent);
  mask-image: linear-gradient(180deg, transparent, #000 22%, #000 78%, transparent);
  scrollbar-width: none;
}
.cm-wheel::-webkit-scrollbar {
  display: none;
}
/* Non-scrolling wrapper that carries the selection hairlines — they must NOT
   live on .cm-wheel itself or they scroll away with the content (visible only
   while the first item is selected). */
.cm-wheel-frame {
  position: relative;
}
.cm-wheel-frame::before,
.cm-wheel-frame::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--cm-hair-30);
  pointer-events: none;
  z-index: 2;
}
.cm-wheel-frame::before {
  top: calc(50% - 32px);
}
.cm-wheel-frame::after {
  top: calc(50% + 32px);
}
.cm-wheel-pad {
  height: calc((var(--cm-wh) - 64px) / 2);
  flex: 0 0 auto;
}
.cm-wheel-item {
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  scroll-snap-align: center;
  font-family: var(--font-mono);
  text-transform: uppercase;
  color: #fff;
  letter-spacing: 0.04em;
  cursor: pointer;
  border: 0;
  background: transparent;
  width: 100%;
  transition:
    font-size 0.18s var(--cm-ease),
    opacity 0.18s var(--cm-ease);
}
.cm-wheel-item[data-d='0'] {
  font-size: 30px;
  opacity: 1;
}
.cm-wheel-item[data-d='1'] {
  font-size: 19px;
  opacity: 0.5;
}
.cm-wheel-item[data-d='2'] {
  font-size: 16px;
  opacity: 0.25;
}
.cm-wheel-item[data-d='3'] {
  font-size: 15px;
  opacity: 0.12;
}
.cm-wheel-item.is-locked {
  opacity: 0.25;
}
.cm-wheel-item .cm-wheel-icon {
  display: inline-flex;
}
/* horizontal carousel variant (hero select) */
.cm-wheel--h {
  height: auto;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 18%, #000 82%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 18%, #000 82%, transparent);
}

/* =========================== B.5 BOTTOM NAV (primitive; sparingly used) =========================== */
.cm-tabbar {
  display: flex;
  align-items: center;
  justify-content: space-around;
  height: 66px;
  padding: 0 6px;
  background: var(--cm-scrim);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid var(--cm-hair-08);
}
.cm-tab {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  border: 0;
  background: transparent;
  cursor: pointer;
  color: var(--cm-fg-dim);
  transition: color 0.12s;
}
.cm-tab .cm-tab-label {
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.cm-tab.is-active {
  color: #fff;
}
.cm-tab.is-active::before {
  content: '';
  position: absolute;
  top: -9px;
  width: 18px;
  height: 3px;
  border-radius: 2px;
  background: var(--cm-red);
}
.cm-tab .cm-tab-dot {
  position: absolute;
  top: -2px;
  right: -4px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  box-sizing: border-box;
  border-radius: 50%;
  background: var(--cm-red);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* the hub's free-floating quick-icon row (NOT a bar — matches prototype hub) */
.cm-quickrow {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 4px;
  padding: 0 2px;
}
.cm-quick {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  /* 48dp Android touch minimum — the 30px icon + label alone land at ~46px. */
  min-height: 48px;
  border: 0;
  background: transparent;
  color: #fff;
  cursor: pointer;
  transition:
    transform 0.12s var(--cm-ease),
    opacity 0.12s;
}
.cm-quick:active {
  transform: scale(0.92);
}
.cm-quick-ico {
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.cm-quick-label {
  font-family: var(--font-mono);
  /* 6 dock items at 393px → ~57px per slot; 8px + tighter tracking keeps
     TUTORIAL/SETTINGS from ellipsizing. */
  font-size: 8px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.85;
  max-width: 100%;
  white-space: nowrap;
}
.cm-quick-badge {
  position: absolute;
  top: -4px;
  right: 18%;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  box-sizing: border-box;
  border-radius: 50%;
  background: var(--cm-red);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 2px var(--cm-red-deep);
}
.cm-quick-badge.is-dot {
  min-width: 9px;
  height: 9px;
  padding: 0;
  font-size: 0;
}

/* =========================== B.6 CURRENCY / WALLET =========================== */
.cm-wallet {
  display: inline-flex;
  gap: 6px;
}
.cm-currency {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  cursor: pointer;
  /* No backdrop-filter: these chips sit on the animated hub aurora, so a blur
     here would re-run over a moving backdrop every frame. The aurora is already
     very soft and low-contrast; a slightly more opaque dark glass reads the same
     without the per-frame cost. */
  background: rgba(0, 0, 0, 0.6);
  border-radius: 16px;
  padding: 5px 6px 5px 9px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: #fff;
  transition: transform 0.12s var(--cm-ease);
}
/* Invisible hit-area extender: the chip renders ~27px tall, far under the
   44pt/48dp touch minimum. -11px vertical reaches ~49px; -3px horizontal fills
   the 6px inter-chip gap without overlapping the neighbour. */
.cm-currency::before {
  content: '';
  position: absolute;
  inset: -11px -3px;
}
.cm-currency:active {
  transform: scale(0.95);
}
.cm-currency-glyph {
  font-size: 12px;
  line-height: 1;
}
.cm-currency--coins .cm-currency-glyph {
  color: var(--cm-gold);
}
.cm-currency--gems .cm-currency-glyph {
  color: var(--cm-blue);
}
.cm-currency-val {
  min-width: 1ch;
}
.cm-currency-plus {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--cm-red);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  line-height: 1;
  color: #fff;
}

/* =========================== B.7 PROGRESS-DOT ROW =========================== */
.cm-dotrow {
  display: inline-flex;
  gap: 3px;
  align-items: center;
}
.cm-dotrow > i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--cm-hair-16);
  transition: background 0.22s;
}
.cm-dotrow > i.is-on {
  background: #fff;
}
.cm-dotrow.is-mana > i.is-on {
  background: var(--cm-mana);
}
.cm-dotrow.is-rose > i.is-on {
  background: var(--cm-rose);
}
.cm-dotrow > i.is-tip {
  width: 7px;
  height: 7px;
  box-shadow: 0 0 0 2.5px rgba(255, 255, 255, 0.28);
}

/* thin progress bar (ABBar) + labelled bar row */
.cm-bar {
  position: relative;
  width: 100%;
  height: 4px;
  border-radius: 999px;
  background: var(--cm-hair-22);
  overflow: hidden;
}
.cm-bar-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  display: block;
  border-radius: 999px;
  background: #fff;
  transition: width 0.45s var(--cm-ease);
  /* prototype bar-loader: fill from 0 to the set width on mount (fillKf) */
  animation: cm-fill 0.9s var(--cm-ease) both 0.3s;
}
@media (prefers-reduced-motion: reduce) {
  .cm-bar-fill {
    animation: none;
  }
}
.cm-barrow {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 7px;
}
.cm-barrow-code {
  width: 34px;
  flex: 0 0 34px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cm-fg-dim);
}
.cm-barrow-bar {
  flex: 1;
}
.cm-barrow-val {
  width: 36px;
  flex: 0 0 36px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 10px;
  color: #fff;
  font-variant-numeric: tabular-nums;
}

/* avatar + level badge (hub profile cluster) */
.cm-avatar {
  position: relative;
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  border-radius: 50%;
  background: var(--cm-shell);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 18px;
  color: #fff;
  border: 1.5px solid var(--cm-hair-30);
}
.cm-avatar-badge {
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--cm-shell);
  border: 1.5px solid rgba(255, 255, 255, 0.5);
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1;
  padding: 2px 5px;
  color: #fff;
}

/* generic ink card / popup surface */
.cm-card {
  background: var(--cm-surface-1);
  border: 1px solid var(--cm-hair-08);
  border-radius: var(--cm-r-tray);
  box-shadow: var(--cm-shadow-card);
}

/* Fail-closed battle entry: malformed online/story state must be visible and
   actionable, never silently replaced with a different engine. */
.game-start-error {
  display: grid;
  place-items: center;
  padding: max(24px, env(safe-area-inset-top)) 20px max(24px, env(safe-area-inset-bottom));
  background: radial-gradient(circle at 50% 18%, rgba(231, 35, 45, 0.17), transparent 38%), #08090a;
}
.game-start-error__panel {
  width: min(100%, 520px);
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 18px 16px;
  align-items: start;
  padding: clamp(22px, 6vw, 34px);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-left: 4px solid var(--cm-red);
  border-radius: var(--cm-r-tray);
  background: rgba(18, 19, 20, 0.96);
  box-shadow: var(--cm-shadow-card);
}
.game-start-error__sigil {
  display: grid;
  place-items: center;
  width: 56px;
  aspect-ratio: 1;
  border: 1px solid rgba(255, 255, 255, 0.32);
  border-radius: 50%;
  color: #fff;
  background: var(--cm-red);
  font: 700 28px/1 var(--font-mono);
}
.game-start-error__copy {
  min-width: 0;
}
.game-start-error__eyebrow {
  margin-bottom: 8px;
  color: var(--cm-red-bright, #ff4650);
  font: 700 10px/1.3 var(--font-mono);
  letter-spacing: 0.14em;
}
.game-start-error__title {
  margin: 0;
  color: #fff;
  font: 700 clamp(23px, 7vw, 34px)/1.05 var(--font-mono);
  letter-spacing: 0.035em;
}
.game-start-error__detail {
  margin: 14px 0 12px;
  color: rgba(255, 255, 255, 0.72);
  font: 14px/1.5 var(--font-body);
}
.game-start-error__code {
  display: inline-block;
  color: rgba(255, 255, 255, 0.52);
  font: 10px/1.3 var(--font-mono);
  letter-spacing: 0.08em;
}
.game-start-error__back {
  grid-column: 1 / -1;
  width: 100%;
}
@media (max-width: 420px) {
  .game-start-error__panel {
    grid-template-columns: 44px 1fr;
    gap: 16px 12px;
  }
  .game-start-error__sigil {
    width: 44px;
    font-size: 22px;
  }
}

/* a hairline divider */
.cm-rule {
  height: 1px;
  background: var(--cm-hair-12);
  border: 0;
  width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  .cm-screen,
  .cm-wheel-item,
  .cm-fab,
  .cm-pill {
    animation: none !important;
    transition: none !important;
  }
}

/* ===================================================================
   ПРОТОТИП А — flow scenes (hub · mode-select · hero-select)
   =================================================================== */
.cm-stack {
  gap: clamp(10px, 2vh, 18px);
}
.cm-footer-fab {
  display: flex;
  justify-content: center;
  margin-top: auto;
  padding-top: 8px;
}

/* ---- HUB ---- */
.hub-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.hub-id {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  border: 0;
  background: transparent;
  color: #fff;
  cursor: pointer;
  padding: 2px;
  max-width: 60%;
}
.hub-name {
  font-family: var(--font-mono);
  font-size: 15px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hub-id--guest {
  opacity: 0.95;
}
.hub-controls {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.hub-ctrl {
  height: 36px;
  min-width: 36px;
  padding: 0 9px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.18);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  transition:
    background 0.12s,
    transform 0.12s var(--cm-ease);
}
.hub-ctrl:active {
  transform: scale(0.93);
}
.hub-lang-code {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
}
.hub-wallet {
  display: flex;
  justify-content: flex-end;
}
.hub-logo {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 8px;
}
.hub-title {
  font-family: var(--font-mono);
  font-size: clamp(34px, 11vw, 56px);
  letter-spacing: 0.16em;
  line-height: 1;
  color: #fff;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.12);
}
.hub-subtitle {
  font-family: var(--font-mono);
  font-size: clamp(9px, 2.6vw, 12px);
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.78);
}
.hub-play {
  margin-top: 4px;
}

/* ---- MODE SELECT ---- */
/* mount with the prototype's circular reveal (fReveal) from bottom-center */
.cm-screen.ms-screen {
  /* Transform/opacity reveal from the bottom (where the wheel/FAB sit). The
     previous `cm-reveal` animated `clip-path: circle()`, which Android WebView
     can't composite — it re-rastered the whole scene each frame (janky on
     Android, fine on desktop/iOS). Scaling a viewport-sized layer is
     compositor-accelerated. */
  transform-origin: 50% 92%;
  animation: cm-reveal-t 0.44s var(--cm-ease) both;
}
@keyframes cm-reveal-t {
  from {
    opacity: 0;
    transform: scale(0.93);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .cm-screen.ms-screen {
    animation: none;
  }
}
/* the stack scrolls as a safety net so the forward FAB is ALWAYS reachable,
   even on the smallest phone (this fixes the "can't start a game" report where
   the FAB was pushed below the fold and clipped by overflow:hidden). */
.ms-stack {
  overflow-y: auto;
  scrollbar-width: none;
}
.ms-stack::-webkit-scrollbar {
  display: none;
}
.ms-wheel-wrap {
  display: flex;
  justify-content: center;
  margin-top: clamp(2px, 1.5vh, 16px);
}
.ms-context {
  /* FIXED height (not min-height): the AI difficulty chips and the online note
     have different heights, so a fixed box keeps the BOARD + FAB below from
     jumping when you switch modes. Content centers inside. */
  height: 104px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
}
.ms-diff {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.ms-diff-label,
.hs-board-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--cm-fg-mute);
}
.ms-diff-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  width: 100%;
}
.ms-diff-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 8px 4px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: var(--cm-r-chip);
  background: rgba(0, 0, 0, 0.16);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  cursor: pointer;
  transition:
    background 0.12s,
    border-color 0.12s,
    transform 0.1s var(--cm-ease);
}
.ms-diff-chip:active {
  transform: scale(0.96);
}
.ms-diff-chip.is-on {
  background: #fff;
  color: var(--cm-ink);
  border-color: #fff;
}
/* Shared Gambit rule toggle — full-width row chip below setup context. */
.ms-gambit-chip {
  width: 100%;
  flex-direction: row;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
}
.ms-diff-stars {
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--cm-gold);
}
.ms-diff-chip.is-on .ms-diff-stars {
  color: var(--cm-red);
}
.ms-online-note {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
}
.ms-auth-note {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: rgba(255, 255, 255, 0.85);
  text-align: center;
}
.ms-tag {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.7);
}

/* ---- HERO SELECT ---- */
.cm-screen.hero-screen {
  overflow: hidden;
  /* No scrollable content here; without this the Android WebView claims
     horizontal drags for scrolling and fires pointercancel, killing the
     hero swipe (same treatment as .game-root / .hand-card / .pack-tear-*). */
  touch-action: none;
}
/* crossfading per-hero gradient layers (behind the character) */
.hs-grad {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  transition: opacity 320ms var(--cm-ease);
}
.hs-grad.is-on {
  opacity: 1;
}
/* legibility scrim behind the bottom controls only (keeps the hero visible) */
.hs-scrim {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 42%;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent,
    color-mix(in srgb, var(--cm-ink) 55%, transparent) 56%,
    color-mix(in srgb, var(--cm-ink) 90%, transparent) 100%
  );
}
/* the stack here only carries the header at the top */
.cm-stack.hero-stack {
  position: relative;
  z-index: 3;
  pointer-events: none;
  padding-bottom: 0;
}
.hero-stack .cm-header {
  pointer-events: auto;
}
.hs-arrow {
  position: absolute;
  top: 42%;
  z-index: 4;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.28);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0.9;
  transition:
    transform 0.12s var(--cm-ease),
    opacity 0.12s;
}
.hs-arrow:active {
  transform: scale(0.9);
}
/* keep the arrows hugging the centered phone column on wide screens */
.hs-arrow--prev {
  left: max(10px, calc(50% - 230px));
}
.hs-arrow--next {
  right: max(10px, calc(50% - 230px));
}
/* identity overlay — over the character, just above the pinned footer */
.hs-id {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(env(safe-area-inset-bottom) + 128px);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
  pointer-events: none;
}
.hs-id--in {
  animation: cm-slide 0.3s var(--cm-ease) both;
}
/* pinned footer: page dots + SELECT */
.hs-foot {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 0 18px calc(env(safe-area-inset-bottom) + 18px);
}
.hs-name {
  font-family: var(--font-mono);
  font-size: clamp(26px, 8vw, 34px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #fff;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
.hs-role {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--cm-fg-mute);
}
.hs-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.hs-dots i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.3);
  transition:
    width 0.2s var(--cm-ease),
    background 0.2s;
}
.hs-dots i.is-on {
  width: 18px;
  border-radius: 4px;
  background: #fff;
}
.hs-board {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
}
.hs-board-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  width: 100%;
}
.hs-board-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 9px 6px;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: var(--cm-r-tile);
  background: rgba(0, 0, 0, 0.32);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: #fff;
  cursor: pointer;
  transition:
    background 0.12s,
    border-color 0.12s,
    transform 0.1s var(--cm-ease);
}
.hs-board-chip:active {
  transform: scale(0.96);
}
.hs-board-chip.is-on {
  background: #fff;
  color: var(--cm-ink);
  border-color: #fff;
}
.hs-board-size {
  font-family: var(--font-mono);
  font-size: 16px;
  letter-spacing: 0.04em;
}
.hs-board-name {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.7;
}
.hs-cta {
  width: 100%;
}
.hs-cta--locked {
  border-color: rgba(181, 215, 154, 0.52) !important;
  background: rgba(20, 36, 27, 0.9) !important;
  color: #d8e7cb !important;
}

/* ===================================================================
   ПРОТОТИП А — responsive hardening (iPhone SE 375 → Galaxy Fold 280)
   Mobile-first: every flow screen must fit with no horizontal overflow
   and no clipped controls on the narrowest supported phone.
   =================================================================== */
/* never let the app scroll sideways */
html,
body,
#app,
#scene {
  max-width: 100%;
  overflow-x: hidden;
}
/* grid cells must be allowed to shrink below their content (long single-word
   tier/board labels were widening columns past the viewport on Fold) */
.ms-diff-row {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
.hs-board-row {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.ms-diff-chip,
.hs-board-chip,
.cm-quick {
  min-width: 0;
}
.ms-diff-chip > span,
.hs-board-name,
.cm-quick-label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  overflow-wrap: anywhere;
}
.cm-quick-label {
  white-space: nowrap;
}
/* brand title scales to fit the narrowest phone without clipping */
.hub-title {
  font-size: clamp(22px, 9vw, 54px);
  letter-spacing: 0.08em;
  max-width: 100%;
}

@media (max-width: 360px) {
  .cm-stack {
    padding-left: 13px;
    padding-right: 13px;
  }
  .cm-header-title {
    font-size: 14px;
    letter-spacing: 0.05em;
  }
  .hub-title {
    font-size: clamp(20px, 8.5vw, 40px);
    letter-spacing: 0.03em;
  }
  .hub-name {
    font-size: 13px;
  }
  .hub-subtitle {
    letter-spacing: 0.3em;
  }
  .cm-quick-label {
    font-size: 8px;
    letter-spacing: 0.04em;
  }
  .ms-diff-chip {
    font-size: 9px;
    padding: 7px 2px;
    gap: 2px;
  }
  .ms-diff-stars {
    font-size: 8px;
  }
  .cm-pill-lead {
    font-size: 15px;
    letter-spacing: 0.1em;
  }
  .cm-pill-sub {
    font-size: 10px;
    letter-spacing: 0.08em;
    white-space: nowrap;
  }
  .cm-pill--split {
    padding: 0 16px;
  }
  .hs-board-size {
    font-size: 14px;
  }
  .hs-board-name {
    font-size: 8px;
  }
}

/* short viewports: the --cm-wh clamp already shrinks the wheel; just tighten
   the surrounding gaps so everything (incl. the FAB) fits above the fold. */
@media (max-height: 700px) {
  .ms-wheel-wrap {
    margin-top: 4px;
  }
  .ms-context {
    height: 96px;
  }
  .cm-footer-fab {
    padding-top: 4px;
  }
}

/* ===================================================================
   ПРОТОТИП А — Settings scene + toggle switch
   =================================================================== */
.hub-top-spacer {
  width: 1px;
}
/* on/off switch */
.cm-switch {
  position: relative;
  width: 46px;
  height: 27px;
  flex: 0 0 46px;
  border: 0;
  border-radius: 999px;
  background: var(--cm-hair-18);
  cursor: pointer;
  transition: background 0.18s var(--cm-ease);
}
.cm-switch.is-on {
  background: var(--cm-red);
}
.cm-switch-knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4);
  transition: transform 0.18s var(--cm-ease);
}
.cm-switch.is-on .cm-switch-knob {
  transform: translateX(19px);
}

.settings-stack {
  gap: 16px;
}
.set-group {
  background: var(--cm-surface-1);
  border: 1px solid var(--cm-hair-08);
  border-radius: var(--cm-r-tray);
  box-shadow: var(--cm-shadow-card);
  overflow: hidden;
  animation: cm-slide 0.4s var(--cm-ease) both;
}
.set-group + .set-group {
  animation-delay: 0.07s;
}
.set-group-title {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--cm-fg-dim);
  padding: 13px 16px 9px;
}
.set-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 54px;
  padding: 10px 16px;
  border-top: 1px solid var(--cm-hair-08);
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  background: transparent;
  color: #fff;
}
.set-row--btn {
  border: 0;
  border-top: 1px solid var(--cm-hair-08);
  cursor: pointer;
  transition: background 0.14s var(--cm-ease);
}
.set-row--btn:hover {
  background: var(--cm-hair-08);
}
.set-row--btn:active {
  background: var(--cm-hair-10);
}
.set-row-icon {
  display: inline-flex;
  color: var(--cm-fg-mute);
}
.set-row-label {
  flex: 1;
  font-family: var(--font-mono);
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.set-row-val {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  color: var(--cm-fg-mute);
}
/* Volume slider row — sits directly under its toggle row, so drop the divider
   and tighten the top so the toggle + slider read as one control. */
.set-row--slider {
  border-top: 0;
  min-height: 0;
  padding-top: 0;
  padding-bottom: 14px;
}
.set-slider {
  flex: 1;
  -webkit-appearance: none;
  appearance: none;
  height: 4px;
  border-radius: 2px;
  background: var(--cm-hair-10);
  outline: none;
  cursor: pointer;
}
.set-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--red);
  border: 2px solid #fff;
  box-shadow: 0 0 6px rgba(215, 25, 33, 0.5);
}
.set-slider::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--red);
  border: 2px solid #fff;
}
.set-slider-val {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--cm-fg-mute);
  min-width: 34px;
  text-align: right;
}
.set-row-chev {
  display: inline-flex;
  color: var(--cm-fg-dim);
}

/* ============================================================================
   3D-ONLY BATTLE STAGE
   One canvas owns the environment and both fighters. The CSS surface underneath
   is a media-free loading/recovery state, never a second visual implementation.
   ========================================================================= */

.bf-stage {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  overflow: visible;
  isolation: isolate;
  background: rgb(6, 10, 14);
  /* The board and hand stay in the readable 480px column; the field is full
     bleed on desktop, tablet, Fold, and phone. */
  left: calc(50% - 50vw);
  width: 100vw;
}
.bf-stage--3d-only {
  --arena-player: rgb(115, 204, 255);
  --arena-opponent: rgb(255, 126, 178);
}

/* The column clips its children, which would undo the stage's full bleed the
   moment the screen is wider than 480px. Safe to open up here: the two layers
   that break out (.bf-stage, .game-bottom-area) own their own bounds. */
.game-root.has-battle-stage {
  overflow: visible;
}

/* Fresh battles are one authored opening transaction: model load, player
   signature, neutral bridge, opponent signature, then input. This transparent
   viewport gate prevents a hand/card/sheet tap from overtaking that sequence;
   scene teardown remains the only cancellation path. */
.battle-intro-input-gate {
  position: fixed;
  inset: 0;
  z-index: 300;
  pointer-events: auto;
  touch-action: none;
  overscroll-behavior: none;
  background: transparent;
  cursor: wait;
}

/* A server-timed Raise/Word decision can become the next authoritative beat
   while receipts wait behind the intro. Lift only its already-open action
   sheet over the transparent gate; the board, HUD, and ordinary chrome remain
   blocked until the queued state frame settles. */
.game-root.battle-intro-decision-ready .game-bottom-area {
  z-index: 301;
}

/* The board zone now sits below the scene band rather than below the HUD, and
   is exactly the grid plus its pad — see rendering/layout.ts. Scoped to the
   stage so a match rendered without one keeps the pre-A2 geometry. */
.game-root.has-battle-stage .board-wrap {
  top: var(--band-h);
  height: var(--board-zone-h);
}

/* ---- Compact A2 HUD ----
   The zone solve gives the banner 66px; the pre-A2 markup (name row + 15
   bullets + bar track + oversized centre timer) was built for ~96px and
   overflows it. Restyled here rather than in the renderer so the DOM contract
   — and every test that asserts it — is untouched. */
.game-root.has-battle-stage .hud-wrapper {
  padding: 0 6px;
  align-items: flex-start;
}
.game-root.has-battle-stage .hud-banner {
  height: auto;
  background: none;
  border: none;
  border-radius: 0;
  overflow: visible;
  clip-path: none;
  contain: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 5px;
  padding-top: 8px;
}
/* Each player becomes a self-contained block instead of a corner of one wide
   banner, so the two can sit side by side with the clock between them. */
.game-root.has-battle-stage .hud-panel {
  /* `relative` so the HP bar can pin to this block's bottom edge. The pre-A2
     rules park each panel with `.hud-player { top: 6px; left: 6px }` /
     `.hud-opponent { bottom: 6px; right: 6px }`, which are inert only while the
     panel is statically positioned — neutralise them explicitly, or making this
     relative shifts both blocks diagonally. */
  position: relative;
  inset: auto;
  flex: 1 1 0;
  min-width: 0;
  max-width: none;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding: 7px 9px 10px;
  border-radius: 10px;
  background: rgba(10, 14, 17, 0.74);
  border: 1px solid rgba(255, 255, 255, 0.09);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  overflow: hidden;
}
/* Mirrored, so the opponent's identity reads outward from the centre clock. */
.game-root.has-battle-stage .hud-opponent {
  flex-direction: row-reverse;
  text-align: right;
}
.game-root.has-battle-stage .hud-opponent .hud-name-row {
  flex-direction: row-reverse;
}
/* The identity column: HP and an optional username on top, hero + mana below. */
.game-root.has-battle-stage .hud-name-wrap {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  flex: 1 1 auto;
}
.game-root.has-battle-stage .hud-opponent .hud-name-wrap {
  align-items: flex-end;
}
.game-root.has-battle-stage .hud-meta-row {
  min-height: 12px;
}
.game-root.has-battle-stage .hud-hp-readout {
  font-size: 10px;
  letter-spacing: -0.01em;
}
.game-root.has-battle-stage .hud-hp-current {
  font-size: 13px;
}
.game-root.has-battle-stage .hud-name-user {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  line-height: 1;
  color: rgba(255, 255, 255, 0.62);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
/* Line 2 — hero name and mana share a baseline so the two read as one label. */
.game-root.has-battle-stage .hud-name-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  max-width: 100%;
}
.game-root.has-battle-stage .hud-name {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Avatar / suit glyph, vertically centred on the two text lines. */
.game-root.has-battle-stage .hud-opp-avatar,
.game-root.has-battle-stage .hud-icon {
  width: 32px;
  height: 32px;
  flex: none;
  align-self: center;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid rgba(255, 255, 255, 0.3);
  background: #1a1a1a;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* HP is one continuous bar along the block's bottom edge, not 15 loose pips.
   It is still the same 15 elements — they carry the per-segment `--fill` the
   damage animation drives, and `.hud-bar`/`.hud-bar-track` are dead
   (display:none globally, never written by the renderer) — but with zero gap
   and square ends the seams disappear and the row reads as a single bar. */
.game-root.has-battle-stage .hud-dots-row {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  gap: 0;
  background: rgba(255, 255, 255, 0.1);
}
.game-root.has-battle-stage .hud-dot {
  flex: 1 1 0;
  width: auto;
  height: 100%;
  border-radius: 0;
  /* Spent HP goes translucent rather than red: with a full-width bar the old
     white/red split read as a second, competing bar. */
  background: linear-gradient(
    90deg,
    var(--hp-color, #33cc66) calc(var(--fill) * 100%),
    rgba(255, 255, 255, 0.06) calc(var(--fill) * 100%)
  );
}
/* Drains from the outside in, mirroring the block. */
.game-root.has-battle-stage .hud-opponent .hud-dots-row {
  flex-direction: row-reverse;
}
/* The clock moves out of the banner centre (where it sat on top of the battle
   scene) and becomes a third block between the two players.
   The order is explicit on all three: the timer is created LAST in the DOM
   (renderer appends player, opponent, then timer), so an `order` on the timer
   alone still sorted it after the opponent — hard against the right screen
   edge, where it clipped. */
.game-root.has-battle-stage .hud-player {
  order: 0;
}
.game-root.has-battle-stage .hud-timer {
  position: static;
  transform: none;
  order: 1;
  flex: 0 0 auto;
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  font-size: 22px;
  border-radius: 10px;
  background: rgba(10, 14, 17, 0.74);
  border: 1px solid rgba(255, 255, 255, 0.09);
}
.game-root.has-battle-stage .hud-opponent {
  order: 2;
}

/* ---- Opened Arcanium (A2) ----
   A SHEET, not a re-proportioning. It rises from the panel it already is and
   covers the whole board, stopping under the scene band so you can still see
   who you are fighting — "all the way up, covering the full board".

   The pre-A2 version squeezed the board to 26svh and grew the panel to 64svh,
   which on the A2 layout collapsed the scene band to ~120px and pushed the BACK
   button off the bottom of the screen. Nothing about the underlying layout
   changes now; only this one element grows. */
.game-root.has-battle-stage.action-sheet-open .game-bottom-area {
  top: var(--band-h);
  bottom: 0;
  height: auto;
  /* Keeps the closed panel's rounded top corners: it is the SAME surface,
     grown — squaring them off on open made it read as a different element
     sliding in over the board. */
  justify-content: flex-start;
}
.game-root.has-battle-stage.gambit-open .game-bottom-area {
  top: auto;
  height: auto;
  max-height: min(340px, calc(100svh - var(--band-h)));
  min-height: 0;
  justify-content: flex-start;
}
/* The hand is not part of the sheet; the abilities replace it. */
.game-root.has-battle-stage.action-sheet-open .game-hand-tray-wrap,
.game-root.has-battle-stage.action-sheet-open .game-action-row {
  display: none;
}
.game-root.has-battle-stage.arcanum-open .arcanium-panel {
  padding-top: clamp(10px, 2svh, 20px);
}

/* Very narrow phones (Galaxy Fold cover, 280px). Two blocks plus a 52px clock
   leaves ~103px per block, and a 32px disc + mana chip ate enough of that to
   ellipsis "TAREN" down to "T". The disc and the type step down so the name —
   the only part that identifies who you are fighting — survives. */
@media (max-width: 340px) {
  .game-root.has-battle-stage .hud-opp-avatar,
  .game-root.has-battle-stage .hud-icon {
    width: 22px;
    height: 22px;
  }
  .game-root.has-battle-stage .hud-panel {
    gap: 5px;
    padding: 6px 6px 9px;
  }
  .game-root.has-battle-stage .hud-name {
    font-size: 11px;
    letter-spacing: 0.02em;
  }
  .game-root.has-battle-stage .hud-name-user {
    font-size: 9px;
    letter-spacing: 0.08em;
  }
  .game-root.has-battle-stage .hud-hp-readout {
    gap: 0;
    font-size: 8px;
  }
  .game-root.has-battle-stage .hud-hp-current {
    font-size: 10px;
  }
  .game-root.has-battle-stage .hud-timer {
    width: 42px;
    font-size: 18px;
  }
}
.game-root.has-battle-stage .hud-timer.hud-timer-hidden {
  display: none;
}
/* The low-time pulse animates `translate(-50%, -50%) scale(...)`, which is
   correct for the pre-A2 clock (absolutely centred) and wrong here: the A2
   clock is a static flex item, so the keyframe yanked it half its own size up
   and left — out of the HUD row entirely — for as long as the timer was low.
   Same pulse, scale only. */
.game-root.has-battle-stage .hud-timer-low {
  animation-name: hud-timer-pulse-a2;
}
@keyframes hud-timer-pulse-a2 {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.08);
  }
}
/* Filler copy the hand panel does not need — the cards say it better. */
.game-root.has-battle-stage .game-hand-info {
  display: none;
}

/* ---- The bottom surface ----
   One surface for the hand panel AND the opened Arcanium, so opening the sheet
   grows the thing already on screen instead of swapping a grey panel for a
   differently-coloured one. The old rgb(50,50,50) was picked before the scene
   existed and read as a slab dropped onto the battlefield. */
.game-root.has-battle-stage .game-bottom-area {
  background:
    linear-gradient(180deg, rgba(33, 40, 47, 0.94), rgba(18, 22, 27, 0.99)), rgb(22, 26, 31);
  /* Bleeds with the stage, for the same reason: a surface that stops at the
     column edge reads as a floating card, not as the bottom of the screen.
     The hand itself stays centred inside it. */
  left: calc(50% - 50vw);
  right: auto;
  width: 100vw;
  /* 42px clears the floating ARCANIUM button (22px of it hangs into the panel)
     AND leaves real space between the button and the first card — 28px did the
     first job only, so the cards sat against the button. */
  padding: 42px 10px calc(10px + env(safe-area-inset-bottom));
  justify-content: center;
}
.game-root.has-battle-stage.gambit-open .game-bottom-area {
  padding: 12px 14px max(10px, env(safe-area-inset-bottom));
  justify-content: flex-start;
}
/* The single live 3D arena runs from the safe-area bleed through the board. */
.bf-arena3d {
  position: absolute;
  inset: 0 0 auto 0;
  height: 100%;
  z-index: 2;
  pointer-events: none;
  overflow: visible;
}
.bf-arena3d__canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* A light-to-dark fall keeps the board legible over the live glade. */
.bf-arena3d::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  /* Above the board the fighters stay untouched. Below it the live scene settles
     into shade so cards and grid outlines retain contrast. */
  top: var(--band-h, 100%);
  bottom: 0;
  background: linear-gradient(
    to bottom,
    rgba(8, 13, 9, 0) 0%,
    rgba(7, 11, 8, 0.42) 16%,
    rgba(6, 10, 7, 0.66) 42%,
    rgba(5, 8, 6, 0.8) 72%,
    rgba(4, 7, 5, 0.86) 100%
  );
  pointer-events: none;
}

/* The Story dialog uses the same arena; the game keeps its input/time gates. */
.game-root:has(> .bf-stage.is-arena-dialogue) > :not(.bf-stage) {
  visibility: hidden !important;
  pointer-events: none !important;
}
.bf-stage.is-arena-dialogue .bf-arena3d::after {
  display: none;
}

/* Media-free arena loading and recovery. This is a semantic status surface,
   not a second fighter renderer. It remains behind the board and disappears
   only after the GLBs survive a real WebGL paint. */
.bf-arena-recovery {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  background:
    radial-gradient(circle at 23% 28%, rgba(53, 113, 154, 0.22), transparent 32%),
    radial-gradient(circle at 77% 24%, rgba(126, 50, 91, 0.2), transparent 30%),
    linear-gradient(160deg, rgb(12, 19, 27), rgb(7, 10, 15) 62%, rgb(4, 7, 10));
  opacity: 1;
  visibility: visible;
  transition:
    opacity 180ms var(--cm-ease),
    visibility 0s linear 0s;
}
.bf-stage.has-arena3d .bf-arena-recovery {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 180ms var(--cm-ease),
    visibility 0s linear 180ms;
}
.bf-arena-recovery__field {
  position: absolute;
  inset: 0 0 auto;
  height: var(--band-h, 38svh);
  min-height: 170px;
}
.bf-arena-recovery__field::before,
.bf-arena-recovery__field::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 55%;
  width: min(64vw, 520px);
  height: 1px;
  transform: translateX(-50%) rotate(-8deg);
  background: linear-gradient(90deg, transparent, rgba(183, 204, 225, 0.24), transparent);
}
.bf-arena-recovery__field::after {
  top: 58%;
  width: min(52vw, 420px);
  opacity: 0.46;
}
.bf-arena-recovery__sigil {
  position: absolute;
  top: 50%;
  display: grid;
  place-items: center;
  width: clamp(46px, 8vw, 72px);
  aspect-ratio: 1;
  border: 1px solid currentColor;
  border-radius: 50%;
  background: rgba(9, 14, 21, 0.88);
  box-shadow:
    0 0 24px color-mix(in srgb, currentColor 34%, transparent),
    inset 0 0 18px color-mix(in srgb, currentColor 18%, transparent);
  color: var(--arena-player);
  transform: translateY(-50%);
}
.bf-arena-recovery__sigil::before {
  content: '◆';
  font-size: clamp(18px, 4vw, 30px);
  line-height: 1;
}
.bf-arena-recovery__sigil.is-player {
  left: clamp(12%, 20vw, 24%);
}
.bf-arena-recovery__sigil.is-opponent {
  right: clamp(12%, 20vw, 24%);
  color: var(--arena-opponent);
}
.is-arena-loading .bf-arena-recovery__sigil {
  animation: bf-recovery-idle 1.6s ease-in-out infinite;
}
.is-arena-loading .bf-arena-recovery__sigil.is-opponent {
  animation-delay: 180ms;
}
.bf-arena-recovery__sigil.is-hit {
  animation: bf-recovery-hit 260ms cubic-bezier(0.25, 1, 0.5, 1);
}
.bf-arena-recovery__sigil.is-victory {
  filter: brightness(1.35);
  transform: translateY(-54%) scale(1.06);
}
.bf-arena-recovery__sigil.is-defeat {
  opacity: 0.34;
  transform: translateY(-46%) scale(0.94);
}
.bf-arena-recovery__status {
  position: absolute;
  top: clamp(92px, calc(var(--band-h, 38svh) * 0.22), 148px);
  left: 50%;
  max-width: min(78vw, 360px);
  padding: 7px 10px;
  border-block: 1px solid rgba(190, 210, 230, 0.2);
  color: rgba(220, 232, 242, 0.72);
  font: 700 10px/1.3 var(--font-mono);
  letter-spacing: 0.18em;
  text-align: center;
  text-transform: uppercase;
  transform: translateX(-50%);
}
.is-arena-loading .bf-arena-recovery__status {
  animation: bf-recovery-status 1.4s ease-in-out infinite;
}
.is-arena-unavailable .bf-arena-recovery__status {
  border-color: rgba(255, 126, 142, 0.32);
  color: rgb(255, 180, 190);
}
.bf-arena-recovery__impact {
  position: absolute;
  top: 50%;
  width: 48%;
  height: 3px;
  border-radius: 999px;
  background: linear-gradient(90deg, transparent, currentColor 24%, currentColor 78%, transparent);
  box-shadow: 0 0 14px currentColor;
  color: var(--arena-player);
  opacity: 0;
  transform: scaleX(0);
}
.bf-arena-recovery__impact.is-player {
  left: 25%;
  transform-origin: left center;
}
.bf-arena-recovery__impact.is-opponent {
  right: 25%;
  color: var(--arena-opponent);
  transform-origin: right center;
}
.bf-arena-recovery__impact.is-active {
  animation: bf-recovery-impact 280ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes bf-recovery-idle {
  0%,
  100% {
    opacity: 0.58;
    transform: translateY(-50%) scale(0.98);
  }
  50% {
    opacity: 1;
    transform: translateY(-50%) scale(1.02);
  }
}
@keyframes bf-recovery-status {
  0%,
  100% {
    opacity: 0.5;
  }
  50% {
    opacity: 1;
  }
}
@keyframes bf-recovery-impact {
  0% {
    opacity: 0;
    transform: scaleX(0);
  }
  35% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: scaleX(1);
  }
}
@keyframes bf-recovery-hit {
  0%,
  100% {
    filter: brightness(1);
    transform: translateY(-50%) translateX(0) scale(1);
  }
  30% {
    filter: brightness(1.7);
    transform: translateY(-50%) translateX(7px) scale(0.94);
  }
}
@media (prefers-reduced-motion: reduce) {
  .is-arena-loading .bf-arena-recovery__sigil,
  .is-arena-loading .bf-arena-recovery__status,
  .bf-arena-recovery__sigil.is-hit {
    animation: none;
  }
  .bf-arena-recovery__impact.is-active {
    animation: none;
    opacity: 1;
    transform: scaleX(1);
  }
}

/* ── SELECT HERO in 3D — the Skyrim loading-screen register ────────────────
   Black field, two hard lights on the model, name and role set small and
   wide underneath. The per-hero suit gradient survives at low opacity so the
   screen still carries the hero's colour without lighting the room. */
.hs-3d {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* the canvas owns the drag: without this the browser's pan gesture wins and
     the model never turns on a phone */
  touch-action: none;
  cursor: grab;
}
.hs-3d:active {
  cursor: grabbing;
}
.hs-3d__canvas {
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 180ms var(--cm-ease);
}
.hs-3d.is-model-ready .hs-3d__canvas {
  opacity: 1;
}
.hs-3d.is-model-failed .hs-3d__canvas {
  opacity: 0;
}
.hs-3d-status {
  position: absolute;
  top: 42%;
  left: 50%;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: max-content;
  max-width: min(72vw, 320px);
  color: rgba(224, 232, 244, 0.68);
  font: 700 10px/1.35 var(--font-mono);
  letter-spacing: 0.18em;
  text-align: center;
  text-transform: uppercase;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.hs-3d-status::before {
  content: '◆';
  display: grid;
  place-items: center;
  width: 42px;
  aspect-ratio: 1;
  border: 1px solid rgba(176, 205, 255, 0.36);
  border-radius: 50%;
  background: rgba(9, 12, 20, 0.72);
  box-shadow:
    0 0 20px rgba(126, 154, 214, 0.2),
    inset 0 0 12px rgba(126, 154, 214, 0.12);
  color: rgba(176, 205, 255, 0.78);
  font-size: 16px;
}
.hs-3d-status[hidden] {
  display: none;
}
.cm-screen.hero-screen.is-3d {
  background: #05050a;
}
.hero-screen.is-3d .hs-grad.is-on {
  opacity: 0.2;
}
/* a floor pool so the figure stands on something rather than floating, and a
   vignette to push the corners down into the black */
.hero-screen.is-3d::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  /* Corners only. An overlay dark enough to be felt across the middle also
     sits on top of the model and undoes the key light — the figure went
     muddy before this was pulled back to the edges. */
  background:
    radial-gradient(115% 34% at 50% 100%, rgba(0, 0, 0, 0.6), transparent 72%),
    radial-gradient(78% 62% at 50% 44%, transparent 62%, rgba(0, 0, 0, 0.5));
}
.hero-screen.is-3d .hs-id {
  align-items: flex-start;
  text-align: left;
  /* .hs-id is left:0/right:0 with no padding — centred text never noticed,
     but left-aligned ran the first glyph off the edge. */
  padding-left: 22px;
  padding-right: 22px;
}
.hero-screen.is-3d .hs-name {
  letter-spacing: 0.14em;
}
.hs-hint {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.42);
  min-height: 12px;
}
.hs-cta.is-locked {
  opacity: 0.35;
  pointer-events: none;
}
/* =================================================================== */
/* Core v2 — faculties + one-use match-item loadout                    */
/* =================================================================== */
.corev2-equip-scroll {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px 16px 22px;
}
.corev2-faculty-summary {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px;
  border: 1px solid color-mix(in srgb, var(--equip-accent) 46%, var(--border));
  border-radius: 16px;
  background:
    radial-gradient(
      circle at 0 0,
      color-mix(in srgb, var(--equip-accent) 18%, transparent),
      transparent 48%
    ),
    rgba(255, 255, 255, 0.035);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.corev2-faculty-summary:focus-visible,
.corev2-item-card:focus-visible,
.corev2-slot:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}
.corev2-faculty-head {
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
}
.corev2-faculty-glyph,
.corev2-faculty-card-glyph {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border: 1px solid color-mix(in srgb, var(--equip-accent) 62%, #fff 10%);
  border-radius: 50%;
  background: #12090b;
  color: color-mix(in srgb, var(--equip-accent) 72%, #fff);
  font: 700 23px/1 var(--font-rank);
}
.corev2-faculty-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.corev2-faculty-eyebrow,
.corev2-faculty-modal-eyebrow {
  color: var(--text-muted);
  font: 600 9px/1.2 var(--font-mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.corev2-faculty-name {
  font: 700 17px/1.15 var(--font-rank);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.corev2-faculty-desc {
  overflow: hidden;
  color: var(--text-dim);
  font-size: 10px;
  line-height: 1.35;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.corev2-faculty-level {
  flex: 0 0 auto;
  padding: 5px 7px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  color: #fff;
  font: 700 9px/1 var(--font-mono);
  letter-spacing: 0.08em;
}
.corev2-faculty-progress {
  display: block;
  height: 4px;
  overflow: hidden;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.1);
}
.corev2-faculty-progress-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: color-mix(in srgb, var(--equip-accent) 76%, #fff);
  box-shadow: 0 0 8px color-mix(in srgb, var(--equip-accent) 60%, transparent);
}
.corev2-faculty-progress-meta {
  color: var(--text-muted);
  font: 600 9px/1 var(--font-mono);
  letter-spacing: 0.08em;
}
.corev2-faculty-perk-dots {
  display: flex;
  gap: 7px;
  align-items: center;
  color: var(--text-muted);
}
.corev2-faculty-perk-dot.unlocked {
  color: color-mix(in srgb, var(--equip-accent) 72%, #fff);
  text-shadow: 0 0 8px var(--equip-accent);
}
.corev2-rail-head,
.corev2-catalog-head {
  margin-top: 2px;
}
.corev2-section-hint {
  margin: -3px 2px 1px;
  color: color-mix(in srgb, var(--text-dim) 92%, #fff);
  font-size: 9px;
  line-height: 1.4;
}
.corev2-item-rail {
  padding: 0;
  gap: clamp(5px, 2vw, 9px);
}
.corev2-slot {
  position: relative;
  min-width: 0;
  overflow: hidden;
  padding: 4px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.045);
}
.corev2-slot.filled {
  border-color: color-mix(in srgb, var(--equip-accent) 54%, #fff 10%);
  background: rgba(0, 0, 0, 0.35);
}
.item-3d.corev2-slot-model {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  flex: 0 0 auto;
  pointer-events: none;
}
.corev2-slot-plus {
  color: rgba(255, 255, 255, 0.28);
  font: 300 clamp(20px, 7vw, 32px)/1 var(--font-mono);
}
.corev2-slot-kind {
  position: absolute;
  right: 4px;
  bottom: 4px;
  display: grid;
  place-items: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.78);
  color: #fff;
  font-size: 9px;
}
.corev2-item-catalog {
  display: grid;
  grid-template-columns: 1fr;
  gap: 7px;
}
.corev2-item-card {
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr);
  gap: 10px;
  align-items: center;
  min-height: 76px;
  padding: 8px 10px 8px 8px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 13px;
  background: rgba(255, 255, 255, 0.035);
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.corev2-item-card.selected {
  border-color: color-mix(in srgb, var(--equip-accent) 62%, #fff 8%);
  background: color-mix(in srgb, var(--equip-accent) 8%, rgba(255, 255, 255, 0.035));
}
.corev2-item-card.locked .corev2-item-art {
  opacity: 0.38;
  filter: grayscale(0.75);
}
.corev2-item-card.locked .corev2-item-desc {
  color: color-mix(in srgb, var(--text-dim) 82%, #fff);
}
.item-3d.corev2-item-art {
  width: 58px;
  height: 58px;
  pointer-events: none;
}
.corev2-item-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.corev2-item-name {
  font: 700 12px/1.1 var(--font-rank);
  letter-spacing: 0.055em;
  text-transform: uppercase;
}
.corev2-item-desc {
  color: var(--text-dim);
  font-size: 10px;
  line-height: 1.35;
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.corev2-item-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.corev2-item-kind,
.corev2-item-state {
  color: var(--text-muted);
  font: 600 8px/1.1 var(--font-mono);
  letter-spacing: 0.09em;
  text-transform: uppercase;
}
.corev2-item-state {
  flex: 0 0 auto;
  padding: 3px 6px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.22);
}
.corev2-item-card.selected .corev2-item-state {
  border-color: color-mix(in srgb, var(--equip-accent) 45%, #fff 8%);
  color: color-mix(in srgb, var(--equip-accent) 64%, #fff);
}
.corev2-loadout-note {
  margin: 2px 4px 0;
  color: var(--text-muted);
  font-size: 9px;
  line-height: 1.45;
  text-align: center;
}
.corev2-item-detail,
.corev2-faculty-modal {
  width: min(440px, calc(100vw - 24px));
  max-height: min(82svh, 720px);
  overflow-y: auto;
  padding: 18px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 20px 20px 0 0;
  background: #12090b;
  color: var(--text);
  box-shadow: 0 -16px 50px rgba(0, 0, 0, 0.62);
}

/* Core v2 battle Arcanium: five real one-shot controls above three native
   abilities, followed by deliberately inert faculty-perk rows. */
.arcanium-rail-host {
  flex: 0 0 auto;
}
.arcanium-item-slot {
  position: relative;
  min-width: 0;
  padding: 3px;
  border: 1px solid rgba(246, 223, 145, 0.28);
  background: radial-gradient(circle at 35% 28%, #332327, #130c0e 72%);
  overflow: hidden;
  cursor: pointer;
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.035),
    0 4px 12px rgba(0, 0, 0, 0.25);
}
.arcanium-item-rail {
  width: min(100%, 320px);
  margin-inline: auto;
}
.arcanium-item-slot.empty {
  border-color: rgba(255, 255, 255, 0.08);
  background: rgba(255, 255, 255, 0.035);
}
.arcanium-item-slot:not(:disabled):hover,
.arcanium-item-slot:not(:disabled):focus-visible {
  border-color: #f6df91;
  outline: none;
  transform: translateY(-2px);
  box-shadow: 0 0 16px rgba(246, 223, 145, 0.25);
}
.arcanium-item-slot.disabled {
  cursor: default;
  opacity: 0.58;
}
.arcanium-item-slot.spent {
  filter: grayscale(0.72);
  opacity: 0.42;
}
.item-3d.arcanium-item-model {
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.corev2-slot-model .item-3d-status,
.arcanium-item-model .item-3d-status,
.loadout-item-banner-art .item-3d-status {
  inset: 12%;
  border-radius: 50%;
  font-size: 6px;
  letter-spacing: 0.08em;
}
.arcanium-item-used {
  position: absolute;
  inset-inline: -7px;
  top: 43%;
  padding: 2px 0;
  transform: rotate(-16deg);
  background: rgba(15, 8, 10, 0.88);
  color: #f2d7d9;
  font: 800 7px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-align: center;
}
.faculty-passives {
  margin-top: 2px;
  padding-top: 7px;
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}
.arcanium-techniques-locked {
  padding: 7px 9px;
  border: 1px dashed rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  color: var(--text-muted);
  font: 700 8px/1.35 var(--font-mono);
  letter-spacing: 0.06em;
  text-align: center;
}
.faculty-passives-title {
  margin: 0 0 5px 4px;
  color: var(--text-muted);
  font: 800 8px/1.1 var(--font-mono);
  letter-spacing: 0.14em;
}
.faculty-passive-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.faculty-passive-row {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  align-items: center;
  gap: 7px;
  min-height: 36px;
  padding: 5px 8px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.025);
  color: var(--text-dim);
  cursor: default;
  user-select: none;
}
.faculty-passive-row.locked {
  border-style: dashed;
  opacity: 0.56;
}
.faculty-passive-row.locked .faculty-passive-glyph,
.faculty-passive-row.locked .faculty-passive-state {
  color: var(--text-muted);
}
.faculty-passive-glyph {
  color: #c9a56a;
  font-size: 15px;
  text-align: center;
}
.faculty-passive-copy {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 1px;
}
.faculty-passive-name {
  color: #d8d0c7;
  font: 700 10px/1.2 var(--font-rank);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.faculty-passive-desc {
  color: #aaa39d;
  font-size: 9px;
  line-height: 1.3;
}
.faculty-passive-state {
  color: #8ba98e;
  font: 800 8px/1 var(--font-mono);
  letter-spacing: 0.08em;
}

.loadout-item-banner {
  min-height: 76px;
  padding: 8px 14px;
  flex-direction: row;
  gap: 10px;
  background: linear-gradient(135deg, rgba(26, 14, 18, 0.97), rgba(54, 34, 25, 0.97));
  border-color: rgba(246, 223, 145, 0.52);
}
.item-3d.loadout-item-banner-art {
  width: 76px;
  height: 76px;
  flex: 0 0 auto;
  pointer-events: none;
}
.loadout-item-banner-copy {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  color: #f2e8dc;
  text-align: left;
}
.loadout-item-banner-copy strong {
  color: #f6df91;
  font: 800 12px/1.1 var(--font-rank);
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.loadout-item-banner-copy span {
  font: 600 9px/1.25 var(--font-mono);
}
.loadout-mark-consumed {
  position: absolute;
  z-index: 37;
  left: 50%;
  top: calc(var(--hud-zone-h, 14vh) + 8px);
  padding: 6px 11px;
  border: 1px solid rgba(255, 219, 113, 0.55);
  border-radius: 999px;
  background: rgba(24, 11, 18, 0.92);
  color: #ffe59a;
  font: 900 9px/1 var(--font-mono);
  letter-spacing: 0.09em;
  opacity: 0;
  transform: translate(-50%, -7px) scale(0.94);
  transition:
    opacity 160ms ease,
    transform 180ms ease;
  pointer-events: none;
}
.loadout-mark-consumed-opponent {
  border-color: rgba(225, 83, 91, 0.62);
  color: #ffc3c7;
}
.loadout-mark-consumed.show {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
}

.loadout-target-panel {
  width: min(420px, calc(100vw - 24px));
  padding: 18px;
  border: 1px solid rgba(246, 223, 145, 0.24);
  border-radius: 18px 18px 0 0;
  background: #12090b;
  color: var(--text);
  text-align: center;
}
.item-3d.loadout-target-art {
  width: 96px;
  height: 96px;
  margin: 0 auto 10px;
  pointer-events: none;
}
.loadout-target-eyebrow {
  color: #c9a56a;
  font: 800 8px/1.1 var(--font-mono);
  letter-spacing: 0.15em;
}
.loadout-target-title {
  margin: 7px 0 4px;
  font: 800 18px/1.15 var(--font-rank);
  text-transform: uppercase;
}
.loadout-target-copy {
  margin: 0;
  color: var(--text-muted);
  font-size: 10px;
  line-height: 1.45;
}
.loadout-target-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(52px, 1fr));
  gap: 8px;
  margin-top: 14px;
}
.loadout-target-card {
  min-height: 72px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 9px;
  background: #f4f0e8;
  color: #21181a;
  cursor: pointer;
}
.loadout-target-card:focus-visible,
.loadout-target-card:hover {
  border-color: #d71921;
  outline: 2px solid rgba(215, 25, 33, 0.32);
  transform: translateY(-2px);
}
.loadout-target-rank {
  display: block;
  font: 800 18px/1 var(--font-rank);
}
.loadout-target-suit {
  display: block;
  margin-top: 5px;
  font-size: 18px;
}
.loadout-target-suit.suit-hearts,
.loadout-target-suit.suit-diamonds {
  color: #b20f18;
}

@media (max-height: 620px) {
  .faculty-passive-row {
    min-height: 34px;
    padding-block: 3px;
  }
  .faculty-passive-desc {
    font-size: 9px;
  }
  .arcanium-panel {
    gap: 5px;
  }
}
.corev2-item-detail {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.corev2-item-detail-head {
  display: flex;
  min-height: 44px;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.corev2-item-detail-summary {
  display: grid;
  grid-template-columns: 112px minmax(0, 1fr);
  gap: 16px;
  align-items: center;
}
.item-3d.corev2-item-detail-art {
  width: 112px;
  aspect-ratio: 1;
  pointer-events: none;
}
.corev2-item-detail-copy {
  display: flex;
  min-width: 0;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
  text-align: left;
}
.corev2-item-detail-name,
.corev2-faculty-modal-title {
  margin: 0;
  font: 700 20px/1.15 var(--font-rank);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.corev2-item-detail-kind {
  color: color-mix(in srgb, var(--text-muted) 82%, #fff);
  font: 600 9px/1.1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.corev2-item-detail-effect-label,
.corev2-item-fact-label {
  color: color-mix(in srgb, var(--equip-accent) 60%, #fff);
  font: 700 8px/1 var(--font-mono);
  letter-spacing: 0.15em;
  text-transform: uppercase;
}
.corev2-item-detail-desc,
.corev2-faculty-modal-intro {
  margin: 0;
  color: color-mix(in srgb, var(--text-dim) 84%, #fff);
  font-size: 11px;
  line-height: 1.5;
}
.corev2-item-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 7px;
}
.corev2-item-fact {
  display: flex;
  min-width: 0;
  min-height: 70px;
  padding: 10px;
  flex-direction: column;
  gap: 7px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 11px;
  background: rgba(255, 255, 255, 0.035);
}
.corev2-item-fact-value {
  color: #f1e8df;
  font-size: 10px;
  font-weight: 500;
  line-height: 1.35;
}
.corev2-faculty-modal {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.corev2-faculty-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.corev2-item-detail-close,
.corev2-faculty-modal-close {
  display: grid;
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  padding: 0;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
  color: #f2e8dc;
  font: 400 25px/1 var(--font-mono);
  cursor: pointer;
}
.corev2-item-detail-close:hover,
.corev2-item-detail-close:focus-visible,
.corev2-faculty-modal-close:hover,
.corev2-faculty-modal-close:focus-visible {
  border-color: rgba(255, 255, 255, 0.48);
  background: rgba(255, 255, 255, 0.12);
  outline: 2px solid rgba(255, 255, 255, 0.68);
  outline-offset: 2px;
}
.corev2-faculty-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 7px;
}
.corev2-faculty-card {
  padding: 10px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.025);
}
.corev2-faculty-card-head {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr) auto;
  gap: 9px;
  align-items: center;
}
.corev2-faculty-card-head > div {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
}
.corev2-faculty-card-head strong {
  font: 700 13px/1.15 var(--font-rank);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.corev2-faculty-card-head div span {
  color: var(--text-muted);
  font-size: 9px;
}
.corev2-faculty-card-glyph {
  width: 38px;
  height: 38px;
  font-size: 20px;
}
.corev2-passive-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: 9px;
}
.corev2-passive-row {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto;
  gap: 7px;
  align-items: center;
  padding: 8px;
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.04);
  cursor: default;
}
.corev2-passive-row.locked {
  background: rgba(255, 255, 255, 0.018);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.045);
}
.corev2-passive-row.locked .corev2-passive-lock {
  color: #8e8783;
}
.corev2-passive-row.locked .corev2-passive-copy strong {
  color: #c5beba;
}
.corev2-passive-row.locked .corev2-passive-copy span,
.corev2-passive-row.locked .corev2-passive-xp {
  color: #9d9692;
}
.corev2-passive-lock {
  color: var(--text-muted);
  text-align: center;
}
.corev2-passive-row.unlocked .corev2-passive-lock {
  color: #e9c873;
}
.corev2-passive-copy {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
}
.corev2-passive-copy strong {
  font: 700 10px/1.2 var(--font-rank);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.corev2-passive-copy span {
  color: var(--text-dim);
  font-size: 9px;
  line-height: 1.3;
}
.corev2-passive-xp {
  color: var(--text-muted);
  font: 600 8px/1 var(--font-mono);
  letter-spacing: 0.07em;
}
.corev2-passive-row.unlocked .corev2-passive-xp {
  color: #e9c873;
}

@media (min-width: 620px) {
  .corev2-item-rail {
    width: min(100%, 420px);
    align-self: center;
  }
  .corev2-item-catalog {
    grid-template-columns: 1fr 1fr;
  }
}
@media (max-width: 340px) {
  .corev2-equip-scroll {
    padding-inline: 4px;
  }
  .corev2-item-rail {
    gap: 4px;
  }
  .corev2-slot,
  .arcanium-item-slot {
    min-width: 44px;
  }
  .arcanium-modifier-rail.arcanium-item-rail {
    gap: 4px;
  }
  .corev2-faculty-summary {
    padding: 11px;
  }
  .corev2-faculty-head {
    grid-template-columns: 36px minmax(0, 1fr) auto;
    gap: 7px;
  }
  .corev2-faculty-glyph {
    width: 36px;
    height: 36px;
    font-size: 20px;
  }
  .corev2-faculty-desc {
    display: none;
  }
  .corev2-item-card {
    grid-template-columns: 46px minmax(0, 1fr);
    gap: 7px;
  }
  .item-3d.corev2-item-art {
    width: 46px;
    height: 46px;
  }
  .corev2-item-state {
    font-size: 7px;
  }
  .corev2-item-detail-summary {
    grid-template-columns: 88px minmax(0, 1fr);
    gap: 11px;
  }
  .corev2-item-detail-art {
    width: 88px;
  }
  .corev2-item-facts {
    grid-template-columns: 1fr;
  }
  .corev2-item-fact {
    min-height: 0;
  }
}

/* Academy coach: one instruction, clear action, quiet progress. */
.tutorial-bubble{border:1px solid #c7ad704d;border-radius:18px;background:linear-gradient(145deg,#172b36f5,#101c27fa);box-shadow:0 10px 32px #0003;padding:18px 20px}
.tutorial-speaker-name{color:#f0d5a0;font:600 12px/1.4 system-ui,sans-serif;letter-spacing:.08em}
.tutorial-counter{font:500 11px/1.4 system-ui,sans-serif;color:#acbbc2}
.tutorial-progress{height:3px;margin:10px 0 14px;background:#ffffff12;border-radius:4px;overflow:hidden}
.tutorial-progress span{display:block;height:100%;background:#ddbe7c;border-radius:4px}
.tutorial-text{font:400 15px/1.6 system-ui,sans-serif;color:#f1ede4;margin:0;max-width:52ch;text-wrap:pretty}
.tutorial-nav{align-items:center;margin-top:16px;gap:10px}
.tutorial-prev,.tutorial-next,.tutorial-next.finish{width:auto;min-width:76px;min-height:44px;height:auto;border-radius:10px;padding:10px 16px;font:600 13px/1.4 system-ui,sans-serif}
.tutorial-prev{background:transparent;color:#b9c9d1;border-color:#ffffff20}
.tutorial-next{background:#e1c38a;color:#17242d;border-color:#e1c38a}
@media(max-width:1179px){.tutorial-root .tutorial-bubble{display:block;padding:12px 16px}.tutorial-progress{margin:7px 0 9px}.tutorial-root .tutorial-nav{margin-top:10px}.tutorial-root .tutorial-text{font-size:14px;line-height:1.45}}
@media(max-height:650px){.tutorial-root .tutorial-text{font-size:13px;line-height:1.35}.tutorial-progress{margin:5px 0 7px}.tutorial-root .tutorial-nav{margin-top:6px}}
