:root {
  /* "Compass & Glass" — a legible dark client, re-lit warm; brass-gold is the one accent. */
  --bg: #13110b; /* warm oak-charcoal base */
  --surface: #1f1a11; /* leather-charcoal panels */
  --surface-2: #2a2316; /* raised warm panel */
  --ink: #f1e7d2; /* warm parchment text */
  --muted: #a08a63; /* sepia-brass secondary */
  --line: #4a3a22; /* tarnished-brass hairline */
  --line-lit: #7a5e2f; /* active brass edge */
  --accent: #d4a23c; /* brass-gold — affordances, UI light */
  --gold: #f0cf78; /* bright gold — leader frame, victory */
  --hint: #35d2e6; /* bright cyan — SeaKing coach highlight; distinct from the gold/green affordance rings */
  --you: #4f9e72; /* sea-glass jade — your side */
  --bot: #c5402c; /* ember-oxblood — Ace, the threat */
  --don: #c47a2e; /* copper DON */
  --shadow: rgba(18, 10, 0, 0.55); /* warm-tinted shadow */
  --r-sm: 4px;
  --r-md: 8px;
  --r-lg: 13px;
  /* Fluid card sizing — one knob (--card-h) scales the whole mat so the board FILLS a fullscreen window
     and shrinks to fit narrower/shorter ones. Height-driven (9 stacked bands make height the usual
     limiter) with a width cap (so 5 characters + side zones still fit half-screen / narrow windows),
     clamped at both ends. Everything else derives from --card-h. */
  /* height budget: the board stacks 9 bands; with the two character rows reserving a full card height
     each (see .row.minions min-height), the earlier /8 undercounted → the grid starved the char rows
     (they collapsed and played characters overflowed onto the Leader band). To give the focal PLAY cards
     (characters + leader) as much size as possible, the secondary bands are trimmed — hand 1.06·card-h
     (was 1.34), opponent hand 0.8, deck/trash piles 0.9 — which lowers the effective multiplier enough
     that /9 fits all bands + full-height play rows inside calc(100vh−44px) with a little slack at every
     height (verified 0 overflow + no leader overlap at 620 / 900 / 1080). Width cap keeps 5 chars + zones
     on narrow/half-screen; clamped both ends. */
  --card-h: clamp(50px, min((100vh - 96px) / 7.5, 13vw), 132px);
  --card-w: calc(var(--card-h) * 0.715);
  --hero-h: calc(var(--card-h) * 1.17); /* leader sits a touch larger than a character */
  --hero-w: calc(var(--hero-h) * 0.715);
  --hand-h: calc(var(--card-h) * 1.06); /* hand cards ~board-card size — trimmed to grow the play cards */
  --hand-w: calc(var(--hand-h) * 0.715);
  --don-h: calc(var(--card-h) * 0.52); /* DON!! cards ~half a card tall */
  --don-w: calc(var(--don-h) * 0.72);
  --rail-w: calc(var(--card-h) * 1.5); /* side-rail gutter width — holds two sub-columns (Life|library, Cost|DON-deck) */
  --font-display: "Cinzel", Georgia, "Times New Roman", serif;
  --font-body: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-family: var(--font-body);
}

* {
  box-sizing: border-box;
}
html,
body {
  height: 100%;
}
body {
  margin: 0;
  background-color: var(--bg);
  /* lantern-lit warm playmat: a soft center glow + faint canvas grain (no cold dot-grid) */
  background-image: radial-gradient(125% 95% at 50% 34%, #1d1810 0%, #13110b 58%, #0b0905 100%),
    repeating-linear-gradient(94deg, transparent 0 3px, rgba(255, 238, 205, 0.012) 3px 4px);
  color: var(--ink);
  font-size: 14px;
  overflow: hidden;
  overscroll-behavior: none; /* mobile: no pull-to-refresh / rubber-band bounce during play */
}

/* ---- top bar ---------------------------------------------------------------------------------- */
.topbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  row-gap: 6px;
  gap: 12px;
  padding: 8px 16px;
  /* B6: ground the banner with a faint gold hairline + a whisper of warmth, so it reads as part of the
     board's parchment theme rather than a generic web-app header bar. */
  background: linear-gradient(180deg, rgba(31, 26, 17, 0.34), transparent);
  border-bottom: 1px solid rgba(122, 94, 47, 0.28);
}

/* One uniform button height across the topbar; the icon-only toggles (hints/sound) are equal squares
   with a single shared icon size, so they can't render at different sizes. */
.topbar .btn {
  height: 34px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* "More" dropdown — keeps mode-switches (Builder/Sealed) off the flat bar. */
.topbar-more {
  position: relative;
  display: inline-flex;
}
.more-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 80;
  min-width: 178px;
  background: linear-gradient(180deg, rgba(31, 26, 17, 0.98), rgba(19, 17, 11, 0.99));
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.more-menu[hidden] {
  display: none;
}
.more-item {
  display: block;
  padding: 8px 10px;
  border-radius: 7px;
  color: var(--ink);
  text-decoration: none;
  font-size: 13px;
  white-space: nowrap;
}
.more-item:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--gold);
}

/* Inline stroke icons (hamburger, rewind, etc.) — sized to the text, currentColor. */
.ic {
  width: 1.1em;
  height: 1.1em;
  display: inline-block;
  vertical-align: -0.16em;
}
.ic path {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
#rewind .ic {
  margin-right: 4px;
}
#settings {
  margin-left: auto;
}
/* Topbar action icons (Help, Settings) — equal squares, icon-only, no left-floating text. */
.topbar .btn-icon {
  width: 34px;
  padding: 0;
  color: var(--muted);
}
.topbar .btn-icon:hover {
  color: var(--ink);
}
.topbar .btn-icon .ic {
  width: 19px;
  height: 19px;
}
#settings.btn-icon {
  margin-left: 0; /* the flex:1 status already pushes the icon cluster right */
}
#hints svg,
#mute svg {
  width: 18px;
  height: 18px;
  display: block;
}
#learn-mode.active {
  border-color: var(--gold);
  color: var(--gold);
  box-shadow: 0 0 10px rgba(240, 207, 120, 0.26);
}
.difficulty-picker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.difficulty-picker[hidden] {
  display: none;
}
.diff-pill {
  height: 30px;
  min-width: 0;
  padding: 0 11px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(31, 26, 17, 0.84);
  color: var(--muted);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.diff-pill:hover {
  border-color: var(--accent);
  color: var(--ink);
}
.diff-pill.active {
  border-color: var(--gold);
  background: rgba(212, 162, 60, 0.18);
  color: var(--gold);
  box-shadow: 0 0 10px rgba(240, 207, 120, 0.2);
}
.settings-menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 220;
  background: rgba(9, 7, 4, 0.34);
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  padding: 54px 12px 12px;
}
.settings-menu {
  width: min(340px, calc(100vw - 24px));
  border: 1px solid var(--line);
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(31, 26, 17, 0.98), rgba(19, 17, 11, 0.99));
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.46);
  padding: 12px;
  display: grid;
  gap: 10px;
  box-sizing: border-box;
  max-height: calc(100dvh - 66px); /* 10 groups can exceed the viewport — scroll, don't overflow */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
/* B8: settings sheet header — title + close, divider under it */
.settings-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 9px;
  margin-bottom: 2px;
  border-bottom: 1px solid rgba(122, 94, 47, 0.4);
}
.settings-title {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  color: var(--gold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.settings-close {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(19, 17, 11, 0.9);
  color: var(--muted);
  font-size: 12px;
  cursor: pointer;
}
.settings-close:hover {
  border-color: var(--accent);
  color: var(--ink);
}
.settings-group {
  display: grid;
  gap: 7px;
}
.settings-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
.settings-note {
  font-size: 11px;
  line-height: 1.4;
  color: var(--muted);
  margin: -2px 0 2px;
}
.settings-action {
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: rgba(19, 17, 11, 0.9);
  color: var(--ink);
  font: inherit;
  padding: 0 12px;
  text-align: left;
  cursor: pointer;
}
.settings-action:hover {
  border-color: var(--accent);
}
.settings-action.icon-action {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
}
.settings-action.icon-action svg {
  flex: 0 0 auto;
}
.action-state {
  color: var(--muted);
  font-size: 13px;
}
.settings-row {
  width: 100%;
  justify-content: flex-start;
}
.brand {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.brand strong {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 800;
  color: var(--gold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5), 0 0 14px rgba(212, 162, 60, 0.22);
}
.art-flag {
  font-size: 10px;
  color: #d9a441;
  border: 1px solid #5a481f;
  background: #2a2310;
  border-radius: 10px;
  padding: 2px 7px;
  cursor: help;
}
.status {
  flex: 1;
  color: var(--muted);
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.status b {
  color: var(--ink);
}
.manual-badge {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--gold);
  border: 1px solid rgba(122, 94, 47, 0.7);
  background: rgba(212, 162, 60, 0.12);
  border-radius: 999px;
  padding: 2px 9px;
}
/* Turn indicator — plain "Your turn" / "<Leader>'s turn" pill, replacing the debug-looking phase string. */
.turn-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.03em;
  border-radius: 999px;
  padding: 4px 12px;
  border: 1px solid rgba(122, 94, 47, 0.55);
}
.turn-pill.you {
  color: var(--gold);
  background: rgba(212, 162, 60, 0.12);
}
.turn-pill.opp {
  color: var(--muted);
  background: rgba(31, 26, 17, 0.5);
}
.turn-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #4f9e72;
  box-shadow: 0 0 7px rgba(79, 158, 114, 0.6);
}
/* In-match board chips (Take control / Rewind) — float over the board, only when usable. */
.board-actions {
  position: fixed;
  top: 52px;
  right: 10px;
  z-index: 46;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 7px;
  pointer-events: none; /* the container is just a layout shell; the chips re-enable pointer events */
}
.board-chip {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 12px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
  background: rgba(19, 17, 11, 0.92);
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
  cursor: pointer;
}
.board-chip:hover {
  border-color: var(--accent);
}
.board-chip .ic {
  width: 14px;
  height: 14px;
}
#take-control.rec {
  border-color: var(--gold);
  color: var(--gold);
  box-shadow: 0 0 12px rgba(240, 207, 120, 0.3);
}
/* Mobile-only on-board turn indicator — hidden on desktop (the topbar status owns it there). */
.mobile-turn {
  display: none;
}
/* --- Feedback / bug report modal (reuses .onboard-card shell) ----------------------------------- */
.feedback-card { width: min(500px, 94vw); text-align: left; }
.feedback-card .onboard-lede { margin-left: 0; }
.feedback-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin: 2px 0 12px;
}
.feedback-chip {
  border: 1px solid var(--line);
  background: rgba(19, 17, 11, 0.9);
  color: var(--muted);
  border-radius: 999px;
  padding: 5px 13px;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.feedback-chip:hover { border-color: var(--accent); color: var(--ink); }
.feedback-chip.active {
  border-color: var(--gold);
  background: rgba(212, 162, 60, 0.16);
  color: var(--gold);
}
.feedback-textarea,
.feedback-contact {
  width: 100%;
  box-sizing: border-box;
  background: rgba(9, 7, 4, 0.5);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  padding: 10px 12px;
  resize: vertical;
}
.feedback-textarea { min-height: 96px; margin-bottom: 9px; }
.feedback-contact { margin-bottom: 4px; }
.feedback-textarea:focus,
.feedback-contact:focus { outline: none; border-color: var(--accent); }
.feedback-textarea::placeholder,
.feedback-contact::placeholder { color: rgba(160, 138, 99, 0.7); }
.feedback-status {
  min-height: 18px;
  font-size: 12px;
  color: var(--muted);
  margin: 6px 0 2px;
}
.feedback-status.err { color: #d98c6a; }
.feedback-copy {
  background: none;
  border: none;
  color: var(--gold);
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
}
/* footer feedback link */
.disclaimer { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
.footer-link {
  background: none;
  border: none;
  color: #7f8b96;
  font: inherit;
  font-size: 9px;
  text-decoration: underline;
  cursor: pointer;
  padding: 0;
}
.footer-link:hover { color: var(--accent); }
.footer-sep { color: #54616d; }
/* game-over feedback prompt */
.result-feedback {
  display: block;
  margin: 12px auto 0;
  background: none;
  border: none;
  color: var(--muted);
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
}
.result-feedback:hover { color: var(--gold); }
/* Builder / Sealed links inside the Settings sheet (anchors styled as the action buttons). */
.settings-links {
  display: grid;
  gap: 7px;
}
.settings-link {
  display: flex;
  align-items: center;
  text-decoration: none;
}
.eval-bar {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 180px;
  max-width: 260px;
  width: clamp(180px, 22vw, 260px);
}
.eval-bar-track {
  position: relative;
  flex: 1;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: linear-gradient(90deg, rgba(196, 122, 46, 0.22), rgba(196, 122, 46, 0.08));
  border: 1px solid rgba(122, 94, 47, 0.7);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.24);
}
.eval-bar-fill {
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, rgba(79, 158, 114, 0.95), rgba(212, 162, 60, 0.95));
  box-shadow: 0 0 10px rgba(212, 162, 60, 0.28);
  transition: width 180ms ease;
}
.eval-bar-label {
  min-width: 42px;
  text-align: right;
  font-size: 12px;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}
.seaking-debug {
  position: fixed;
  top: 58px;
  right: 16px;
  z-index: 92;
  width: min(380px, calc(100vw - 32px));
  border: 1px solid rgba(122, 94, 47, 0.72);
  border-radius: 12px;
  background: linear-gradient(180deg, rgba(24, 19, 10, 0.97), rgba(12, 10, 6, 0.97));
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.58), inset 0 0 0 1px rgba(240, 207, 120, 0.08);
  color: var(--ink);
  padding: 0;
}
.seaking-debug summary {
  list-style: none;
  cursor: default;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px 8px;
}
.seaking-debug summary::-webkit-details-marker {
  display: none;
}
.seaking-debug-title {
  color: var(--gold);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.seaking-debug-meta {
  color: var(--muted);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}
.seaking-debug-body {
  border-top: 1px solid rgba(122, 94, 47, 0.32);
  padding: 10px 12px 12px;
}
.seaking-debug-score {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 8px;
  font-size: 11px;
  color: var(--muted);
}
.seaking-debug-score b {
  color: var(--ink);
  font-size: 12px;
}
.seaking-debug-lines {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", monospace;
  font-size: 10.5px;
  line-height: 1.45;
  color: #d7c39a;
  max-height: min(40vh, 380px);
  overflow: auto;
}
.tutorial-callout {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 240;
  width: min(400px, calc(100vw - 28px));
  padding: 14px 16px;
  border: 1px solid var(--line-lit);
  border-radius: 14px;
  background: linear-gradient(180deg, rgba(32, 25, 15, 0.97), rgba(19, 17, 11, 0.98));
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.42);
  animation: tutorialFadeIn 0.2s ease-out;
}
.tutorial-copy {
  color: var(--ink);
  line-height: 1.45;
}
.tutorial-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
}
.dismiss-btn {
  border: 1px solid rgba(196, 122, 46, 0.72);
  border-radius: 999px;
  background: rgba(196, 122, 46, 0.16);
  color: #f2d6a0;
  padding: 7px 12px;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}
.dismiss-btn:hover {
  border-color: var(--accent);
  background: rgba(196, 122, 46, 0.24);
}
.tutorial-skip {
  border: 0;
  background: transparent;
  color: var(--muted);
  padding: 0;
  font: inherit;
  font-size: 12px;
  text-decoration: underline;
  cursor: pointer;
}
.tutorial-skip:hover {
  color: var(--ink);
}
@keyframes tutorialFadeIn {
  from {
    opacity: 0;
    transform: translate(-50%, 10px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}
.btn {
  background: #1b2731;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 7px 13px;
  font-size: 13px;
  cursor: pointer;
}
.btn:hover {
  border-color: var(--accent);
}
.btn-ghost {
  background: transparent;
}

/* ---- arena ------------------------------------------------------------------------------------ */
.arena {
  position: relative;
  height: calc(100vh - 44px);
  display: grid;
  /* mobile: the board is a fixed mat (no scroll) — let pointer drags own the touch so the browser never
     hijacks a card-drag as a scroll/pan and fires pointercancel. Modals (#modal-root) sit outside .arena and
     keep normal touch-action, so any scrollable prompt still scrolls. */
  touch-action: none;
  /* 3 columns: a side-rail gutter on each outer edge framing a center-auto combat column. The two side
     columns are equal (minmax rail-w → 1fr), so the auto middle stays board-centered → the midline is a
     true mirror. Each rail drops into the OUTER gutter of its half (mirrored 180° → south Life-rail
     far-left, north Life-rail far-right). */
  grid-template-columns: minmax(var(--rail-w), 1fr) auto minmax(var(--rail-w), 1fr);
  /* Digital-adapted playmat: the Cost Area + DON!! Deck are OFF the back band (which freed ~2 card-heights
     → bigger play cards) and onto a vertical side RAIL, using the otherwise-empty horizontal gutters. Each
     half now has just two play bands — Leader (back) + Characters (front) — flanked by two rails:
       • Life + library (Deck/Trash) rail on one side,  • DON!! resources (Cost Area + DON!! Deck) on the other.
     Mirrored 180°: south Life-rail far-left / DON-rail far-right; north swaps. The two 1fr slack rows hug
     the combat block to the centre line so the midline stays a true mirror. */
  grid-template-areas:
    "opphand  opphand  opphand"
    "slacktop slacktop slacktop"
    "railNW   leader-n railNE"
    "railNW   char-n   railNE"
    "mid      mid      mid"
    "railSW   char-s   railSE"
    "railSW   leader-s railSE"
    "slackbot slackbot slackbot"
    "hand     hand     hand";
  grid-template-rows: auto 1fr auto auto auto auto auto 1fr auto;
  column-gap: calc(var(--card-h) * 0.28); /* breathing room between the side rails and the play area */
  padding: 6px 10px 0;
  overflow: hidden;
}
/* Each play half wraps its leader-band + minion-row. Desktop: display:contents → the wrapper generates
   no box, so both children participate in the .arena grid with their own grid-areas exactly as before
   (desktop layout provably unchanged). The mobile HS rebuild flips it to display:flex to merge the two
   into one centred row (see the HS-STYLE MOBILE REBUILD block). */
.play-half { display: contents; }
.row.opp-hand { grid-area: opphand; }
.leader-band.north { grid-area: leader-n; }
.row.minions.bot { grid-area: char-n; }
.midline { grid-area: mid; }
.row.minions.you { grid-area: char-s; }
.leader-band.south { grid-area: leader-s; }
/* 180°-rotated (opposite sides, diagonal). YOUR Life+library on the LEFT — matching your GREEN health
   segment — and Ace's on the RIGHT, matching the RED segment (DON rails take the opposite side each). */
.rail.life-lib.south { grid-area: railSW; }   /* you: Life + library, bottom-LEFT (green side) */
.rail.don.south { grid-area: railSE; }         /* you: DON resources, bottom-RIGHT */
.rail.don.north { grid-area: railNW; }          /* Ace: DON resources, top-LEFT */
.rail.life-lib.north { grid-area: railNE; }    /* Ace: Life + library, top-RIGHT (red side) */
.row.hand { grid-area: hand; }
/* ---- side rails: each gutter holds two sub-columns of off-band zones, hugging the centre line ------- */
.rail {
  display: flex;
  gap: 12px;
  min-height: 0;
  z-index: 4;
}
.rail.north { align-items: flex-end; }   /* north rails sit at the bottom of their span (by the centre) */
.rail.south { align-items: flex-start; } /* south rails sit at the top of their span (by the centre) */
/* The big zone (Life / Cost Area) sits on the OUTER edge, the small pile(s) (library / DON!! Deck) inboard.
   DOM order is [big, small]; whichever rail is on the RIGHT uses row-reverse so its big zone lands on the
   right. justify-content:flex-end packs each cluster INBOARD (toward the play area) — cohesive AND clears
   the midline's end-of-bar leader-name labels (~12% from each edge). 180° layout → your Life-rail (right)
   + Ace's DON-rail (right) are the row-reversed pair; Ace's Life-rail + your DON-rail sit left. */
.rail.life-lib.south { justify-content: flex-end; }                              /* your Life — left (green) */
.rail.don.north      { justify-content: flex-end; }                              /* Ace DON — left */
.rail.don.south      { flex-direction: row-reverse; justify-content: flex-end; } /* your DON — right */
.rail.life-lib.north { flex-direction: row-reverse; justify-content: flex-end; } /* Ace Life — right (red) */
.rail-lib { /* library sub-column: deck pile over trash heap */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
/* The south rail hugs the centre from below (deck at the top = nearest the midline); the north rail hugs
   it from above, so reverse its library too → Ace's deck also sits nearest the midline, matching you. */
.rail.north .rail-lib {
  flex-direction: column-reverse;
}
/* In the rail the Cost Area lays its DON!! out as a compact 2-wide block (not one long horizontal row),
   with slightly smaller DON so a full 10 fit the rail's height without spilling past the play rows. */
.rail .cost-area .cost-row {
  display: grid;
  grid-template-columns: repeat(2, auto);
  gap: 3px;
  min-width: 0;
  min-height: 0;
}
.rail .cost-area .don-card {
  width: calc(var(--card-h) * 0.3);
  height: calc(var(--card-h) * 0.4);
}
.rail .cost-area.tuck .cost-row {
  gap: 2px;
}
.row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 0;
}
.row.minions {
  align-items: flex-end; /* north characters sit at the bottom of their row, hugging the midline below */
  padding: 4px 0;
  /* reserve a full card height so a played character never overflows its (auto) grid row onto the Leader
     band below/above it — the row used to collapse under the old too-tight height budget. The --card-h
     formula now budgets for this reservation so it never forces an overall overflow. */
  min-height: var(--card-h);
}
.row.minions.you {
  align-items: flex-start; /* south characters sit at the top of their row, hugging the midline above */
}
/* hero rows take the 1fr slack; pin the cluster to its INNER edge so the leader tucks right behind its
   characters (north → bottom of its track, south → top), pushing the empty slack to the outer side */
/* leader band = the only back band now (Leader + any Stage), just behind the character line. Push it a
   touch toward the hand (away from the character row) so there's a clear gap between your characters and
   your leader, not a flush stack. */
.leader-band {
  gap: 14px;
}
/* Pull Ace's whole leader band toward the centre so his leader sits the same distance from the midline as
   yours (mirror), instead of drifting up toward Ace's hand. Moves the card + ability button together, so
   the button stays centred on the card. */
.leader-band.north {
  /* re-tuned 2026-06-28 (Tyler): the old 0.64 pulled Ace's Leader DOWN into his character row (~10px
     overlap). Back it off so Ace's Leader sits the SAME small gap ABOVE his play area as the human Leader
     sits BELOW theirs (≈+9px), mirroring the human leader↔play-area spacing. The Stage is anchored to the
     leader-seat (B5a) so it follows — its distance to the midline shifts slightly vs the old B19 tuning. */
  /* 2026-06-29 (Tyler): nudge each leader band a little further toward its OWN hand (away from the midline) —
     same Δ both sides so the mirror holds. North rises toward Ace's hand (0.47→0.33), south drops toward
     yours (0.10→0.24). */
  transform: translateY(calc(var(--card-h) * 0.33));
}
.leader-band.south {
  transform: translateY(calc(var(--card-h) * 0.24));
}
/* Leader ability control — anchored to the leader card, floating into the empty lower-right space. */
.don-sidecar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
}
/* Both leaders' ability buttons sit on the SAME side (outboard right) and vertically centered on the
   leader card, so south + north read as a clean horizontal mirror instead of a diagonal offset. */
.leader-ability-slot {
  position: absolute;
  left: auto;
  right: calc(var(--card-h) * -0.14);
  top: calc(var(--card-h) * 0.3);
  z-index: 46;
}
.leader-ability-slot.north,
.leader-ability-slot.south {
  left: auto;
  right: calc(var(--card-h) * -0.14);
  top: calc(var(--card-h) * 0.3);
  bottom: auto;
}
.hero-power {
  --hp-size: 70px;
  --hp-shell-size: 92px;
  --hp-inner-inset: 18px;
  position: relative;
  z-index: 45; /* above the log (z-40) so it intercepts pointer events and stays readable */
  width: var(--hp-size);
  height: var(--hp-size);
  border: 0;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--leader-tint, #c9a227) 28%, transparent) 0 44%, transparent 70%),
    radial-gradient(circle at 50% 46%, rgba(38, 27, 13, 0.96) 0 50%, rgba(10, 7, 4, 0.98) 66%);
  color: var(--muted);
  display: grid;
  place-items: center;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-family: var(--font-body);
  cursor: not-allowed;
  perspective: 500px;
  overflow: visible;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
}
/* Nudge the leader-band seat so the button has some air to live in, like Hearthstone's hero power. */
.leader-seat {
  padding-top: calc(var(--card-h) * 0.03);
  padding-bottom: calc(var(--card-h) * 0.6);
}
/* Symmetric reserve: both seats hold the card centered and keep the same room on the right for the
   ability button, so the two leader cards line up on the same vertical axis (mirrored, not offset). */
.leader-seat.north,
.leader-seat.south {
  padding-left: calc(var(--card-h) * 0.14);
  padding-right: calc(var(--card-h) * 0.14);
}
.leader-seat .hero-power {
  position: absolute;
}
/* Nudge each leader ability button toward its OWN player's hand (Tyler): your seat (south) drops toward the
   bottom hand, Ace's (north) rises toward the top hand — mirrored, so both sit nearer their owner. */
.leader-seat.south .hero-power {
  transform: translateY(calc(var(--card-h) * 0.24));
}
.leader-seat.north .hero-power {
  transform: translateY(calc(var(--card-h) * -0.24));
}
.hero-power::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 244, 215, 0.08), rgba(255, 244, 215, 0.02) 58%, transparent 72%);
  box-shadow:
    inset 0 0 0 3px currentColor,
    0 2px 6px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
/* Ace's indicator is read-only but stays in the same DON rail grammar. */
.hero-power.enemy-ability {
  border-color: rgba(180, 80, 60, 0.5);
  background:
    radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--leader-tint, #b4503c) 28%, transparent) 0 44%, transparent 70%),
    radial-gradient(circle at 50% 46%, rgba(42, 16, 11, 0.96) 0 48%, rgba(10, 5, 4, 0.98) 64%);
}
.hero-power.ready {
  cursor: pointer;
  color: var(--gold);
  box-shadow: 0 0 0 1px rgba(240, 207, 120, 0.16), 0 0 15px rgba(240, 207, 120, 0.35);
}
.hero-power.ready:not(.enemy-ability):hover {
  filter: brightness(1.08);
  box-shadow: 0 0 0 1px rgba(240, 207, 120, 0.26), 0 0 19px rgba(240, 207, 120, 0.5);
}
/* Leader ability interior — an owned "Activate" glyph (no art). Color tracks the state machine via
   currentColor: gold when ready, muted when locked, hollow when used. */
.hp-glyph {
  position: relative;
  z-index: 2;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  color: inherit;
  pointer-events: none;
  transform-origin: center;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
}
.hp-glyph svg {
  width: 100%;
  height: 100%;
  display: block;
}
/* DON!! cost pip — fixed bottom-right, shown only when the ability text carries a [DON!! −N] cost.
   Both slice leaders are free, so it renders for future costed leaders only. */
.hp-cost {
  position: absolute;
  z-index: 4;
  right: -2px;
  bottom: -2px;
  min-width: 22px;
  height: 22px;
  padding: 0 4px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: #0f0a07;
  color: var(--gold);
  border: 2px solid currentColor;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}
/* READY — gold ring + glow ("you can press this now"). */
.hero-power.ready::before {
  box-shadow:
    inset 0 0 0 3px currentColor,
    0 0 16px rgba(240, 207, 120, 0.38),
    0 2px 6px rgba(0, 0, 0, 0.55);
}
.hero-power.enemy-ability.ready {
  color: #e0584f; /* Ace's read-only indicator reads red, not "pressable gold" */
}
.hero-power.enemy-ability.ready::before {
  box-shadow:
    inset 0 0 0 3px currentColor,
    0 0 14px rgba(224, 88, 79, 0.32),
    0 2px 6px rgba(0, 0, 0, 0.55);
}
/* a gentle "available" breathing pulse on the glyph (Shadowverse's now-usable tell); paused on the
   one-shot refresh flip so they don't fight, and killed under reduced-motion below. */
.hero-power.ready:not(.used):not(.refreshed) .hp-glyph {
  animation: heroPowerPulse 1.8s ease-in-out infinite;
}
/* LOCKED — dim, muted glyph. */
.hero-power.locked .hp-glyph {
  opacity: 0.45;
}
/* USED — Hearthstone empty socket: glyph gone, ring recessed into a hollow dish. */
.hero-power.used .hp-glyph {
  opacity: 0;
  transform: scale(0.55);
}
.hero-power.used .hp-cost {
  opacity: 0.35;
}
.hero-power.used::before {
  background: radial-gradient(circle at 50% 38%, rgba(0, 0, 0, 0.66), rgba(0, 0, 0, 0.42) 58%, transparent 74%);
  box-shadow:
    inset 0 3px 8px rgba(0, 0, 0, 0.82),
    inset 0 0 0 3px rgba(96, 88, 70, 0.5),
    0 2px 6px rgba(0, 0, 0, 0.55);
}
/* spent → flip the glyph out as it empties; refreshed → flip a fresh glyph back in next turn. */
.hero-power.spent .hp-glyph {
  animation: heroPowerSpent 360ms cubic-bezier(0.2, 0.72, 0.22, 1) both;
}
.hero-power.refreshed .hp-glyph {
  animation: heroPowerRefreshed 420ms cubic-bezier(0.2, 0.72, 0.22, 1) both;
}
/* Label on hover — icon-only at rest; a small pill appears above the button on hover/focus so the
   board stays clean but the action is still nameable (also exposed to AT via the button title). */
.hp-copy {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translateX(-50%) translateY(4px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: 5px 9px;
  border-radius: 8px;
  background: rgba(14, 10, 7, 0.96);
  border: 1px solid rgba(240, 207, 120, 0.28);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 120ms ease, transform 120ms ease;
  z-index: 60;
}
.hero-power:hover .hp-copy,
.hero-power:focus-visible .hp-copy {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}
.hp-copy b {
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gold);
}
.hp-copy small {
  color: #cdc4b0;
  opacity: 0.85;
  font-size: 9px;
}
@keyframes heroPowerSpent {
  0% {
    transform: rotateY(0deg) scale(1);
    filter: drop-shadow(0 0 10px rgba(240, 207, 120, 0.42));
  }
  55% {
    transform: rotateY(88deg) scale(0.92);
  }
  100% {
    transform: rotateY(0deg) scale(0.94);
    filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.55));
  }
}
@keyframes heroPowerRefreshed {
  0% {
    transform: rotateY(-88deg) scale(0.94);
    filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.55));
  }
  55% {
    transform: rotateY(10deg) scale(1.05);
  }
  100% {
    transform: rotateY(0deg) scale(1);
    filter: drop-shadow(0 0 10px rgba(240, 207, 120, 0.42));
  }
}
@keyframes heroPowerPulse {
  0%, 100% {
    transform: scale(1);
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6));
  }
  50% {
    transform: scale(1.06);
    filter: drop-shadow(0 0 7px rgba(240, 207, 120, 0.55));
  }
}
/* Compact: rail-local hero powers shrink a touch so the resource gutter keeps breathing room. */
@media (max-width: 900px) {
  /* Compact: keep the leader ability buttons near full size (only a gentle trim) and tucked beside the
     leader as at full width — no jarring shrink. They inherit the base slot position. */
  .hero-power {
    --hp-size: 64px;
    --hp-shell-size: 84px;
    --hp-inner-inset: 16px;
  }
}
.midline {
  position: relative;
  z-index: 5; /* above the z-4 side rails so the end-of-bar leader-name labels always win any edge overlap */
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 12%; /* pull the bar in so the Ace read-out clears the End-Turn dial; symmetric → mirrored */
}
.ml-track {
  position: relative;
  flex: 1;
  display: flex;
  height: 7px;
  /* fade the line into the board at each end instead of capping it with rounded corners */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.ml-seg {
  height: 100%;
  min-width: 0;
  transition: flex-grow 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.ml-seg.you {
  background: linear-gradient(90deg, color-mix(in srgb, var(--you) 42%, #000), var(--you));
}
.ml-seg.bot {
  background: linear-gradient(90deg, var(--bot), color-mix(in srgb, var(--bot) 42%, #000));
}
/* the seam is a soft jade→oxblood blend that rides the moving boundary (no hard gold line) */
.ml-blend {
  flex: 0 0 52px;
  height: 100%;
  background: linear-gradient(90deg, var(--you), var(--bot));
}
/* life read-out: the NUMBER sits in line with the bar; the leader name stacks onto that player's side
   (Luffy below your 4, Ace above his 5) */
.ml-life {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  text-shadow: 0 1px 2px #000;
}
.ml-life b {
  font-size: 16px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.ml-life span {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.85;
}
.ml-life.you {
  color: var(--you);
}
.ml-life.you span {
  top: 100%;
  margin-top: 1px;
}
.ml-life.bot {
  color: var(--bot);
}
.ml-life.bot span {
  bottom: 100%;
  margin-bottom: 1px;
}
.side-tag {
  position: absolute;
  left: 8px;
  top: -9px;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--bg);
  padding: 0 6px;
}

/* ---- supply cluster (deck/trash/stage/DON) + the leader combatant on the line ----------------- */
.leader-seat {
  display: inline-block;
  position: relative; /* offset parent for the [Activate: Main] plaque */
}
/* ---- life as a vertical face-down OP-back column, in the outer-gutter grid area (official far-left) -- */
.life-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  z-index: 4;
}
/* Life is now a sub-column inside the Life+library side rail (see .rail) — its row/edge placement comes
   from the rail, so no grid-area/justify-self here. */
.life-col-cards {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.life-col-cards .life-card {
  margin-top: calc(var(--card-h) * -0.43); /* overlap vertically → each shows a ~26px strip */
}
.life-col-cards .life-card:first-child {
  margin-top: 0;
}
.life-card {
  width: calc(var(--card-h) * 0.48);
  height: calc(var(--card-h) * 0.67);
  border-radius: 5px;
  background: repeating-linear-gradient(45deg, #1c3a2a, #1c3a2a 6px, #163024 6px, #163024 12px); /* styled fallback back (post-scrub) */
  border: 1px solid #2c5a3e;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  position: relative;
  overflow: hidden;
  flex: none;
}
.life-card .back-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  object-position: center center;
  border-radius: inherit;
}
.life-card.has-back-art {
  border-color: #4a3a2a; /* real OP back present → neutral frame */
  background: #141414;
}
/* last life: a lone card gets a faint oxblood warning glow so the stakes read at a glance */
.life-col:not(.empty) .life-card:only-child {
  box-shadow: 0 0 0 1px rgba(216, 90, 72, 0.6), 0 0 9px 1px rgba(216, 90, 72, 0.45);
}
/* opponent's face-DOWN HAND faces Ace across the table → flip those backs 180° for our view so the fanned
   hand reads naturally from the top. (Only the hand keeps the flip — see below.) */
.opp-hand .card.back .back-art {
  transform: rotate(180deg);
}
/* B1 + follow-up (Tyler backlog): ALL face-down STACKS — Deck, DON!! Deck, AND Life — keep their backs
   UPRIGHT on both seats, so the human and AI stacks read with the SAME perspective (left + right) instead
   of the AI side appearing upside-down. (Previously the AI Life column was rotated 180° and looked mirrored
   vs the human Life column + the now-upright decks.) */
/* Ace's empty-trash anchor mirrors its rotated deck back (both are the north library's directional
   decoration → they face Ace). The trash COUNT/label and the face-up trash cards stay upright/readable,
   exactly like the deck count badge — readable content faces the human; only the decoration mirrors. */
.trash-zone.north .pile-trash:not(.has-card)::after {
  transform: none; /* B1: AI library decoration upright, matching the upright AI deck/DON backs */
}
/* ---- deck (zone 4) + trash (zone 5): the library, now stacked in the Life+library side rail --------- */
.deck-zone,
.trash-zone {
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 4;
}
/* Also sizes the EMPTY-trash placeholder (.trash-zone > .pile.pile-trash, rendered only before the first
   discard) so it matches the deck pile instead of falling through to the tiny base .pile size. The
   non-empty trash uses .trash-pile (below), so this only affects the empty slot.
   Height is kept BELOW the Leader's hero-h (0.9·card-h + label < 1.17·card-h) so the Leader — not the Deck
   pile — drives the Leader-row height, and the pile is a touch shorter to free vertical room for the play
   cards. */
.deck-zone .pile,
.trash-zone .pile {
  /* match a real card's proportions (w/h ≈ 0.715) — was 0.75/0.9 ≈ 0.83, which read as too wide/short. */
  width: var(--card-w);
  height: var(--card-h);
}
/* trash — a fan of the top few face-up cards; each lower card shows a hoverable strip → zoom */
.trash-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}
/* a messy discard heap: the top few face-up cards piled at haphazard (but stable) angles + offsets.
   KEY INVARIANT: the cards are position:absolute, so they NEVER grow this box — that (not the box height)
   is what stops the trash from inflating its grid row. The explicit height keeps the box ≈ the short Cost
   row; the trash-wrap (box + label) ends up a few px taller than .cost-area, and the scaled+rotated cards
   intentionally spill ~10-14px past the box edges — both are absorbed by the adjacent 1fr slack row (so
   don't add overflow:hidden here or trim the slack rows without re-checking the heap doesn't clip). --ts
   scales the (absolute) cards down to the compact zone; the full read is the hover-zoom overlay (every card). */
.trash-pile {
  --ts: 0.66; /* compact heap scale — fits the back-row Cost zone (mirror of the Deck pile) */
  position: relative;
  width: calc(var(--card-w) * 1.4); /* room for the (scaled) messy spread + rotation */
  height: calc(var(--card-h) * 0.52 + 29px); /* ≈ Cost-band height; absolute cards never inflate the row */
}
.trash-pile .trash-card {
  position: absolute;
  left: 50%;
  top: 50%;
  margin: 0;
  transform: translate(-50%, -50%) scale(var(--ts)) translate(var(--dx, 0px), var(--dy, 0px)) rotate(var(--rot, 0deg));
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.trash-pile .trash-card.buried {
  pointer-events: none; /* only the top 3 cards are hoverable/zoomable — the rest are just heap texture */
}
.trash-pile .trash-card:hover {
  transform: translate(-50%, -50%) scale(calc(var(--ts) + 0.14)); /* straighten + lift (full read = hover-zoom) */
  z-index: 20;
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.72);
}
/* ---- DON!! zones (official): Cost Area (zone 6) = real DON!! cards, active=upright/rested=rotated;
   DON!! Deck (zone 7) = face-down stack ticking 10→0 as DON enter play ----------------------------- */
.cost-area {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 7px 9px 6px;
  background: rgba(34, 26, 14, 0.5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.cost-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-width: calc(var(--don-w) + 16px);
  min-height: calc(var(--don-h) + 4px);
}
/* DON tuck lives entirely in the rail 2-col grid now (see `.rail .cost-area`); the old horizontal-row
   negative-margin tuck was retired with the back-band layout. */
.cost-area.empty .cost-row::after {
  content: "—";
  color: var(--muted);
  opacity: 0.4;
}
.cost-lbl {
  display: flex;
  align-items: baseline;
  gap: 2px;
  font-size: 11px;
  color: var(--muted);
}
.ca-active {
  color: #ffb38f;
  font-weight: 800;
  font-size: 14px;
}
.ca-sep {
  opacity: 0.5;
}
.ca-tag {
  margin-left: 7px;
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #cd7e60;
  font-weight: 800;
}
.don-card {
  width: calc(var(--don-w) + 4px);
  height: calc(var(--don-h) + 3px);
  border-radius: 4px;
  background: linear-gradient(150deg, #e6a24e 0%, #b06a26 52%, #7a4417 100%); /* warm copper for the scrub (was raw red) — matches the brass theme */
  border: 1px solid #5a3414;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
  position: relative;
  overflow: hidden;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.14s ease, filter 0.14s ease, box-shadow 0.14s ease;
}
.don-card::before {
  /* gold burst behind the wordmark (the DON!! cards read gold-on-red) */
  content: "";
  position: absolute;
  inset: -25%;
  background: radial-gradient(circle at 50% 44%, rgba(255, 214, 150, 0.65), transparent 55%);
}
.don-card::after {
  content: "DON!!";
  position: relative;
  font-size: 9px;
  font-weight: 900;
  letter-spacing: -0.04em;
  color: #fff2e0;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.55);
}
.don-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 3px;
}
.don-card.has-art::before,
.don-card.has-art::after {
  display: none; /* real art replaces the styled DON face */
}
.don-card.rested {
  transform: rotate(90deg) scale(0.9); /* tapped — the game's "spent" signal */
  filter: brightness(0.62) saturate(0.66);
  box-shadow: none;
}
.don-card.draggable {
  cursor: grab;
}
.don-card.draggable:hover {
  transform: translateY(-4px) scale(1.1);
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.6), 0 0 12px rgba(240, 160, 90, 0.85);
  filter: brightness(1.1);
  z-index: 2;
}
.don-armed .don-card.draggable {
  box-shadow: 0 0 0 2px rgba(196, 122, 46, 0.65), 0 0 14px rgba(240, 160, 90, 0.32);
}
.don-target-arm {
  box-shadow: 0 0 0 2px rgba(196, 122, 46, 0.75), 0 0 16px rgba(240, 160, 90, 0.5);
  outline: 2px solid rgba(240, 207, 120, 0.35);
  outline-offset: 2px;
}
.don-card.gained {
  animation: donDeal 400ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
  animation-delay: calc(max(var(--i, 0), 0) * 45ms); /* cascade the turn-start refresh/deal */
}
@keyframes donDeal {
  0% {
    transform: translateY(-16px) scale(0.5) rotate(-14deg);
    opacity: 0;
  }
  55% {
    transform: translateY(0) scale(1.12) rotate(4deg);
    opacity: 1;
  }
  100% {
    transform: none;
    opacity: 1;
  }
}
/* DON!! Deck — a small face-down stack (a few layered backs for depth) + live count (10→0) */
.don-deck {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}
.dd-stack {
  position: relative;
  width: calc(var(--card-h) * 0.56); /* a small card-back stack (was DON-sized, read as a slab) */
  height: calc(var(--card-h) * 0.78);
}
.dd-card {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background: linear-gradient(150deg, #7a4f22 0%, #4a2e13 58%, #2c1c0c 100%); /* DON!!-themed copper back */
  border: 1px solid #6a3e18;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), inset 0 0 0 2px rgba(230, 150, 74, 0.32);
  overflow: hidden;
  transform: translate(calc(var(--d, 0) * 1.5px), calc(var(--d, 0) * -1.5px));
}
.dd-card::before { /* gold burst behind the wordmark — fallback DON!! face when the card-back art is absent */
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(circle at 50% 42%, rgba(255, 210, 140, 0.42), transparent 60%);
}
.dd-card::after {
  content: "DON!!";
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(var(--card-h) * 0.13);
  font-weight: 900;
  letter-spacing: -0.04em;
  color: rgba(255, 228, 192, 0.85);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
}
.dd-card .back-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  border-radius: 3px;
  filter: none;
  z-index: 0;
}
.dd-card.has-back-art {
  background: #1c1c1c;
  border: 1px solid #d9d9d9;
  box-shadow:
    0 0 0 1px rgba(244, 244, 244, 0.3),
    0 1px 2px rgba(0, 0, 0, 0.5),
    inset 0 0 0 2px rgba(14, 24, 28, 0.28);
  overflow: hidden;
}
.dd-card.has-back-art::after {
  display: none;
}
.dd-card.has-back-art::before {
  display: none;
}
.don-deck.north .dd-card .back-art {
  transform: none; /* B1 (Tyler backlog): AI DON!! Deck back upright, matching the AI deck + human side */
}
/* Stack-depth lean follows the SEAT, not screen side (Tyler): every NORTH (AI) stack leans LEFT and every
   SOUTH (human) stack leans RIGHT — the consistent read from the bottom-middle seat. The AI DON!! Deck
   layers flip their X (vertical kept, matching the human DON!! Deck which uses the default right lean). */
.don-deck.north .dd-card {
  transform: translate(calc(var(--d, 0) * -1.5px), calc(var(--d, 0) * -1.5px));
}
.don-deck.empty .dd-stack {
  border: 1px dashed var(--line);
  border-radius: 4px;
  background: rgba(0, 0, 0, 0.18);
}
.dd-lbl {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.05;
  font-size: 8.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #cd7e60;
  font-weight: 800;
}
.dd-lbl b {
  font-size: 13px;
  color: #ffb38f;
}

/* ---- cards ------------------------------------------------------------------------------------ */
.card {
  width: var(--card-w);
  height: var(--card-h);
  border-radius: var(--r-sm);
  border: 1px solid #0a0703;
  background: #1a150d; /* warm card stock */
  position: relative;
  overflow: hidden;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.55), 0 3px 7px var(--shadow); /* contact + warm ambient */
  flex: none;
  user-select: none;
}
.card .art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}
.card .frame {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 22px 5px 16px; /* top clears the cost orb / life seal, bottom clears the power plate */
  background: linear-gradient(165deg, #3a2c19 0%, #241a0f 60%, #1a120a 100%);
  border-top: 4px solid #9e3326; /* deck-color band (these decks are red) — frame reads intentional once art is scrubbed */
  box-shadow: inset 0 0 0 1px rgba(240, 207, 120, 0.1); /* brass inner hairline → a framed, deliberate card */
}
.card .frame::before {
  /* faint crossed-sabers watermark fills the art-free void so the card reads designed, not broken (original geometry) */
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f0cf78' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M5 5l10 10M15 5L5 15'/%3E%3Cpath d='M3 17l3 3M18 17l-3 3'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center 56%;
  background-size: 44%;
  opacity: 0.13;
}
.card.has-art .frame {
  display: none;
}
.card .frame .nm {
  position: relative; /* above the watermark */
  font-family: var(--font-display);
  font-size: 9.5px;
  font-weight: 600;
  line-height: 1.12;
  text-align: center;
  letter-spacing: 0.01em;
  color: var(--ink);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.card .badge {
  position: absolute;
  font-size: 10px;
  font-weight: 700;
  border-radius: 4px;
  padding: 0 4px;
  line-height: 1.4;
  z-index: 2;
  text-shadow: 0 1px 2px #000;
}
.card .cost {
  top: 3px;
  left: 3px;
  width: 17px;
  height: 17px;
  padding: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 38% 32%, var(--gold), #9c6f1e);
  color: #2a1c06;
  font-weight: 800;
  text-shadow: none;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.5);
}
.card .pow {
  bottom: 3px;
  left: 3px;
  background: linear-gradient(#1c150c, #0d0905);
  color: var(--gold);
  border: 1px solid rgba(240, 207, 120, 0.32); /* stamped brass plate, not a flat chip */
  font-family: var(--font-display);
  letter-spacing: 0.02em;
}
/* N3: pulse a power/cost badge when an EFFECT changes it (modifyPower/setPower/modifyCost). Semantic glow:
   green = good for you (power up / cost down), red = bad. Animate transform + filter only — both revert
   cleanly to none, so the badge's own background/box-shadow is untouched after the pulse. */
.card .badge.stat-good {
  animation: statPulseGood 560ms cubic-bezier(0.2, 0.72, 0.22, 1) both;
}
.card .badge.stat-bad {
  animation: statPulseBad 560ms cubic-bezier(0.2, 0.72, 0.22, 1) both;
}
@keyframes statPulseGood {
  0%, 100% { transform: scale(1); filter: none; }
  35% { transform: scale(1.42); filter: drop-shadow(0 0 6px rgba(120, 235, 145, 0.95)); }
}
@keyframes statPulseBad {
  0%, 100% { transform: scale(1); filter: none; }
  35% { transform: scale(1.42); filter: drop-shadow(0 0 6px rgba(240, 120, 120, 0.95)); }
}
@media (prefers-reduced-motion: reduce) {
  .card .badge.stat-good, .card .badge.stat-bad { animation: none; }
}
/* attached DON!! is on loan — a copper recall ring (⟲ + amount) that breathes, signalling it returns
   to the cost area next turn (no permanent power gain). */
.card .don-badge {
  top: 3px;
  right: 3px;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 0 4px 0 3px;
  background: linear-gradient(150deg, #e0953f, #b5642a);
  border: 1px solid #5a2c12;
  color: #fff2e0;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35), 0 0 6px 1px rgba(224, 149, 63, 0.45);
  animation: donLoan 2s ease-in-out infinite;
}
.card .don-badge .dn-recall {
  display: inline-flex;
  line-height: 0;
  opacity: 0.95;
}
.card .don-badge .dn-recall svg {
  display: block;
}
@keyframes donLoan {
  0%, 100% { box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35), 0 0 6px 1px rgba(224, 149, 63, 0.4); }
  50%      { box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35), 0 0 12px 2px rgba(240, 180, 90, 0.85); }
}
.card.leader {
  width: var(--hero-w);
  height: var(--hero-h);
  outline: 2px solid var(--gold);
  outline-offset: -2px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 14px rgba(240, 207, 120, 0.26), 0 3px 8px var(--shadow);
}
.card.rested {
  transform: rotate(88deg) scale(0.84);
  filter: brightness(0.8);
}
.card.empty {
  background: transparent;
  border: 1px dashed var(--line);
  box-shadow: none;
}
.card.back {
  background: radial-gradient(circle at 50% 38%, #2a3a4e 0%, #16202c 55%, #0e1620 100%);
  border-color: #2c3e52;
  overflow: hidden;
}
.card.back::before {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(135deg, transparent 0 6px, rgba(255, 255, 255, 0.02) 6px 7px);
}
.card.back::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 42%;
  height: 30%;
  border: 1.5px solid rgba(120, 160, 200, 0.4);
  transform: rotate(45deg);
  box-shadow: inset 0 0 8px rgba(120, 160, 200, 0.18);
}
/* real card-back art (CARD_BACK) replaces the styled back on any .card.back or the deck pile */
.back-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  object-position: center center;
  border-radius: inherit;
  pointer-events: none;
}
.card.back.has-back-art,
.pile-deck.has-back-art {
  background: none;
}
.card.back.has-back-art::before,
.card.back.has-back-art::after,
.pile-deck.has-back-art::after {
  display: none;
}

/* affordances */
.card.playable {
  cursor: grab;
  box-shadow: 0 0 0 2px var(--accent), 0 0 14px 2px rgba(240, 192, 74, 0.5);
}
.card.can-attack {
  cursor: grab;
  box-shadow: 0 0 0 2px var(--you), 0 0 12px 1px rgba(70, 200, 96, 0.5);
}
.card.activatable {
  cursor: pointer;
}
/* a gold ring when activating IS the primary action (no green attack ring competing) */
.card.activatable:not(.can-attack) {
  box-shadow: 0 0 0 2px var(--gold), 0 0 12px 1px rgba(240, 207, 120, 0.5);
}
/* [Activate: Main] on Characters: a small inset brass seal in the card's free corner. The Leader uses the
   separate hero-power button beside the Leader card. */
.ability-gem {
  position: absolute;
  bottom: 3px;
  right: 3px;
  z-index: 7;
  width: 21px;
  height: 21px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid #5a4012;
  background: radial-gradient(circle at 38% 30%, #f5dc8c, var(--gold) 55%, #b8881f);
  color: #3a2606;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 0.12s ease, filter 0.12s ease;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.18), 0 2px 5px var(--shadow),
    0 0 7px 1px rgba(240, 207, 120, 0.55);
  animation: gemReady 1.7s ease-in-out infinite;
}
.ability-gem:hover {
  transform: scale(1.12);
  filter: brightness(1.1);
}
.ability-gem svg {
  width: 12px;
  height: 12px;
  display: block;
}
@keyframes gemReady {
  0%,
  100% {
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.18), 0 2px 5px var(--shadow),
      0 0 7px 1px rgba(240, 207, 120, 0.5);
  }
  50% {
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.18), 0 2px 5px var(--shadow),
      0 0 14px 3px rgba(240, 207, 120, 0.9);
  }
}
.card.drop-ok {
  box-shadow: 0 0 0 2px var(--accent);
  animation: targetPulse 1s ease-in-out infinite;
}
.card.drop-hover {
  box-shadow: 0 0 0 3px #fff, 0 0 20px 5px var(--accent);
  animation: none;
}
@keyframes targetPulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--accent), 0 0 9px 2px rgba(240, 192, 74, 0.35); }
  50% { box-shadow: 0 0 0 2px var(--accent), 0 0 18px 6px rgba(240, 192, 74, 0.7); }
}
/* dragging a DON!! card → tint the valid-target glow copper (vs the gold attack glow), so "attach here
   for +1000" reads distinctly from "attack here". */
body.dragging-don .card.drop-ok {
  box-shadow: 0 0 0 2px var(--don), 0 0 12px 3px rgba(196, 122, 46, 0.5);
  animation: targetPulseDon 1s ease-in-out infinite;
}
body.dragging-don .card.drop-hover {
  box-shadow: 0 0 0 3px #ffe8c4, 0 0 22px 6px rgba(224, 149, 63, 0.85);
  animation: none;
}
@keyframes targetPulseDon {
  0%, 100% { box-shadow: 0 0 0 2px var(--don), 0 0 9px 2px rgba(196, 122, 46, 0.4); }
  50% { box-shadow: 0 0 0 2px var(--don), 0 0 18px 6px rgba(240, 180, 90, 0.75); }
}
/* board play-zone: invite the drop by pulsing the empty slots while dragging a hand card */
.row.minions.you.drop-ok .card.empty {
  border-color: var(--accent);
  animation: slotPulse 1.1s ease-in-out infinite;
}
@keyframes slotPulse {
  0%, 100% {
    background: rgba(240, 192, 74, 0.05);
    box-shadow: inset 0 0 0 1px rgba(240, 192, 74, 0.3);
  }
  50% {
    background: rgba(240, 192, 74, 0.14);
    box-shadow: inset 0 0 14px rgba(240, 192, 74, 0.3);
  }
}
.card.dragging {
  opacity: 0.35;
}
.card.popped {
  animation: pop 0.28s ease;
}
.card.struck {
  animation: shake 0.32s ease;
}
@keyframes pop {
  from {
    transform: scale(0.4);
    opacity: 0;
  }
}
@keyframes shake {
  20% {
    transform: translateX(-4px);
  }
  40% {
    transform: translateX(4px);
  }
  60% {
    transform: translateX(-3px);
  }
  80% {
    transform: translateX(2px);
  }
}

/* ---- deck / trash piles ----------------------------------------------------------------------- */
.pile-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}
.pile-lbl {
  font-size: 8px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  font-weight: 700;
}
.pile {
  position: relative;
  width: 48px;
  height: 67px;
  border-radius: 5px;
}
/* deck — a face-down stack with depth. The default depth recedes toward bottom-RIGHT (right-rail piles:
   your DON-side library + Ace's top-right deck). LEFT-rail piles flip it below so the lean follows the
   board's outer corner by screen side, not by seat (Tyler). */
.pile-deck {
  background: repeating-linear-gradient(45deg, #1c3a2a, #1c3a2a 6px, #163024 6px, #163024 12px);
  border: 1px solid #2c5a3e;
  box-shadow: 1.5px 1.5px 0 #15301f, 3px 3px 0 #112619, 4.5px 4.5px 0 #0d2014,
    5px 7px 9px rgba(0, 0, 0, 0.5);
}
/* Stack depth leans by SEAT, not screen side (Tyler): every SOUTH (human) pile recedes bottom-RIGHT (the
   default above), every NORTH (AI) pile recedes bottom-LEFT — the consistent look from the bottom-middle
   seat. The white DON!! Decks already follow this; here the green deck/trash piles match. */
.rail.north .pile-deck {
  box-shadow: -1.5px 1.5px 0 #15301f, -3px 3px 0 #112619, -4.5px 4.5px 0 #0d2014,
    -5px 7px 9px rgba(0, 0, 0, 0.5);
}
.pile-deck.has-back-art {
  overflow: hidden;
  background: #141414;
}
.pile-deck::after {
  content: "";
  position: absolute;
  inset: 30% 32%;
  border-radius: 50%;
  border: 2px solid #2c5a3e;
  background: radial-gradient(circle, rgba(44, 90, 62, 0.3), transparent 70%);
}
/* trash — discard pile: top card face-up, or an empty graveyard slot */
.pile-trash {
  border: 1px dashed #4a3a40;
  background: rgba(36, 22, 26, 0.35);
  overflow: hidden;
  box-shadow: 1.5px 1.5px 0 rgba(74, 58, 64, 0.35), 3px 3px 0 rgba(58, 44, 50, 0.28),
    0 4px 8px rgba(0, 0, 0, 0.45);
}
/* NORTH (AI) trash recedes bottom-LEFT, matching its deck + the seat rule above. */
.rail.north .pile-trash {
  box-shadow: -1.5px 1.5px 0 rgba(74, 58, 64, 0.35), -3px 3px 0 rgba(58, 44, 50, 0.28),
    0 4px 8px rgba(0, 0, 0, 0.45);
}
.pile-trash.has-card {
  border-style: solid;
  border-color: #5a3a42;
  cursor: pointer;
}
.pile-trash .pile-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.pile-trash .pile-frame {
  position: absolute;
  inset: 0;
  padding: 4px;
  background: linear-gradient(160deg, #2a2024, #1a1216);
}
.pile-trash .pile-nm {
  font-size: 9px;
  font-weight: 700;
  line-height: 1.1;
  color: #d9b8be;
}
.pile-trash:not(.has-card)::after {
  content: "";
  position: absolute;
  inset: 0;
  background: center / 44% no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235a4326' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='5' r='2'/%3E%3Cpath d='M12 7v13M8 11h8M5 14a7 7 0 0 0 14 0'/%3E%3C/svg%3E");
  opacity: 0.7;
}
.pile-count {
  position: absolute;
  bottom: 2px;
  right: 2px;
  min-width: 18px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: rgba(8, 12, 18, 0.92);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 800;
  color: #fff;
  z-index: 4;
}

/* ---- hand (fanned, hover-lift) ---------------------------------------------------------------- */
.hand {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  height: calc(var(--hand-h) + 18px);
  padding-bottom: 12px; /* raise the hand off the very bottom edge (Hearthstone-style) */
  perspective: 1000px;
  position: relative;
  z-index: 15; /* keep the whole hand layer (and any lifted/tilted card) above the hero/leader rows */
}
.hand .card {
  width: var(--hand-w);
  height: var(--hand-h);
  margin-left: -26px;
  transition: transform 0.14s ease, margin 0.14s ease, box-shadow 0.14s ease;
  transform-origin: bottom center;
  /* --fan = per-card fan rotation, --arc = arc lift (both set in render via applyFan) */
  transform: translateY(calc(var(--lift, 0px) + var(--arc, 0px))) scale(var(--scale, 1))
    rotate(var(--fan, 0deg)) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
}
.hand .card:first-child {
  margin-left: 0;
}
/* Draw-in: a freshly-drawn card rises into its fan slot, staggered (--i). `backwards` so after the deal
   the base transform (hover/fan) takes over cleanly; 100% matches the steady transform to avoid a snap. */
@keyframes handDraw {
  0% {
    opacity: 0;
    transform: translateY(86px) scale(0.7) rotate(var(--fan, 0deg));
  }
  60% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: translateY(calc(var(--lift, 0px) + var(--arc, 0px))) scale(var(--scale, 1)) rotate(var(--fan, 0deg));
  }
}
.hand .card.drawn {
  animation: handDraw 0.44s cubic-bezier(0.22, 0.85, 0.3, 1) backwards;
  animation-delay: calc(max(var(--i, 0), 0) * 55ms);
}
@media (prefers-reduced-motion: reduce) {
  .hand .card.drawn {
    animation: none;
  }
}
.hand .card.lifted {
  --lift: -34px;
  --scale: 1.16;
  --fan: 0deg; /* straighten + pop the hovered card out of the fan */
  --arc: 0px;
  z-index: 60;
  margin-right: 24px;
  box-shadow: 0 14px 26px rgba(0, 0, 0, 0.6);
}
/* opponent hand — actual-size fanned face-down cards, hanging from a top-centre origin */
.opp-hand {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  height: calc(var(--card-h) * 0.8 + 8px); /* face-down backs — kept compact to free height for play cards */
  padding-top: 6px;
  perspective: 1000px;
}
.opp-hand .card.back {
  width: calc(var(--card-h) * 0.572); /* 0.8·card-w — smaller than your play cards */
  height: calc(var(--card-h) * 0.8);
  margin-left: -34px;
  transform-origin: top center;
  transform: translateY(var(--arc, 0px)) rotate(var(--fan, 0deg));
  transition: transform 0.14s ease;
}
.opp-hand .card.back:first-child {
  margin-left: 0;
}

/* ---- end-turn rail ---------------------------------------------------------------------------- */
.endturn {
  position: absolute;
  right: 26px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 30;
  background: none;
  border: none;
  padding: 4px;
  color: var(--muted);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: not-allowed;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  text-align: center;
  line-height: 1.2;
}
.endturn .compass {
  width: 66px;
  height: 66px;
  opacity: 0.45; /* the brass rose IS the circle (no medallion) — dim when it's not your move */
  transition: opacity 0.25s ease, filter 0.25s ease;
}
/* compact variant: when the dial drops into the bottom-right gutter on a cramped board, shrink it so it
   fits the open space below the Cost Area without crowding the hand. */
.endturn.compact {
  gap: 3px;
  font-size: 9px;
}
.endturn.compact .compass {
  width: 48px;
  height: 48px;
}
.endturn.ready {
  cursor: pointer;
  color: var(--gold);
}
.endturn.ready .compass {
  opacity: 1;
  animation: compassGlow 2.6s ease-in-out infinite;
}
@keyframes compassGlow {
  0%,
  100% {
    filter: drop-shadow(0 0 5px rgba(212, 162, 60, 0.45));
  }
  50% {
    filter: drop-shadow(0 0 12px rgba(240, 207, 120, 0.75));
  }
}
.bot-thinking {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 30;
  width: 94px;
  text-align: center;
  color: var(--muted);
  font-family: var(--font-display);
  font-size: 11px;
  font-style: italic;
}

/* ---- attack arrows ---------------------------------------------------------------------------- */
.arrow-layer {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  z-index: 80;
}

/* ---- floating log ----------------------------------------------------------------------------- */
/* RuneScape-style chat: a see-through box (no frame) overlaid on the board; older lines fade out toward
   the top, and the scrollbar stays hidden until you interact (hover), when a faint backdrop fades in and
   the full history becomes readable. Hard text-shadow keeps every line legible over any board content. */
.log {
  position: fixed;
  left: 10px;
  top: 120px;
  bottom: auto;
  width: 286px;
  max-height: 156px;
  overflow-y: auto;
  background: transparent;
  border: none;
  border-radius: var(--r-md);
  padding: 7px 10px;
  font-size: 12px;
  line-height: 1.55;
  z-index: 40;
  transition: background 0.18s ease;
  /* fade the older (upper) messages into the board */
  -webkit-mask-image: linear-gradient(to top, #000 58%, rgba(0, 0, 0, 0));
  mask-image: linear-gradient(to top, #000 58%, rgba(0, 0, 0, 0));
  scrollbar-width: none; /* hidden until hover (Firefox) */
}
.log::-webkit-scrollbar {
  width: 0; /* hidden until hover (WebKit) */
}
.log:hover {
  background: rgba(10, 8, 4, 0.5); /* faint readable backdrop once you reach for it */
  -webkit-mask-image: none; /* drop the fade so the full history reads */
  mask-image: none;
  scrollbar-width: thin;
  scrollbar-color: rgba(240, 207, 120, 0.45) transparent;
}
.log:hover::-webkit-scrollbar {
  width: 7px;
}
.log:hover::-webkit-scrollbar-thumb {
  background: rgba(240, 207, 120, 0.4);
  border-radius: 4px;
}
.log .entry {
  color: #e9dcbf;
  padding: 1px 0;
  text-shadow: 0 1px 2px #000, 0 0 3px #000, 0 0 2px #000; /* outline so text reads over any board content */
}
.log .entry:last-child {
  color: #fff8e6; /* newest line brightest */
  font-weight: 500;
}
.log .entry.reject {
  color: #f0857a;
  font-style: italic;
  text-shadow: 0 1px 2px #000, 0 0 3px #000;
}
.log .entry.reject .rj {
  font-style: normal;
  font-weight: 600;
  letter-spacing: 0.03em;
}

/* ---- modal (prompts: counter step, etc.) ------------------------------------------------------ */
.modal-root {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
}
.modal-root.open {
  display: flex;
}
/* N3: gentle scale+fade entrance, played once on open (the `.entering` class is only added when the modal
   transitions from closed→open, not on the re-renders a chip selection triggers). House easing. */
.modal.entering {
  animation: modalIn 200ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes modalIn {
  from { opacity: 0; transform: translateY(10px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .modal.entering { animation: none; }
}
.modal {
  background: #131c25;
  border: 1px solid var(--accent);
  border-radius: 12px;
  padding: 18px 20px;
  max-width: 560px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
}
.modal h3 {
  margin: 0 0 4px;
  font-size: 15px;
}
.modal p {
  margin: 0 0 12px;
  color: var(--muted);
  font-size: 12px;
}
.battle-ctx {
  background: #0b121a;
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 10px 12px;
  margin: 4px 0 14px;
}
.bc-line {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: 14px;
  font-weight: 700;
}
.bc-atk { color: var(--bot); }
.bc-def { color: var(--you); }
.bc-vs { color: var(--muted); font-size: 16px; }
.bc-verdict {
  text-align: center;
  font-size: 12px;
  margin-top: 7px;
}
.bc-verdict.good { color: #8fe79c; }
.bc-verdict.bad { color: #f0a09a; }
.modal .opts {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
/* "look at the top N" reveal — show the cards as cards */
.reveal-row {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
  margin: 8px 0 16px;
}
.reveal-card {
  width: 86px;
  height: 120px;
  border-radius: 7px;
  position: relative;
  overflow: hidden;
  border: 2px solid var(--line);
  background: #1a150d;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
  transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
  user-select: none;
}
.reveal-card .art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}
.reveal-card .frame {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: 5px;
  background: linear-gradient(165deg, #3a2c19, #1a120a);
  border-top: 4px solid #9e3326;
}
.reveal-card.has-art .frame {
  display: none;
}
.reveal-card .nm {
  font-size: 11px;
  font-weight: 700;
  line-height: 1.15;
}
.reveal-card.selectable {
  cursor: pointer;
  border-color: rgba(70, 200, 96, 0.55);
}
.reveal-card.selectable:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.6);
}
.reveal-card.selected {
  border-color: var(--you);
  box-shadow: 0 0 0 3px var(--you), 0 0 16px 3px rgba(70, 200, 96, 0.55);
  transform: translateY(-4px);
}
.reveal-card.selected::after {
  content: "Add";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--you);
  color: #06140a;
  font-size: 10px;
  font-weight: 800;
  text-align: center;
  padding: 2px 0;
  z-index: 2;
}
.reveal-card.recommended {
  border-color: var(--gold);
  animation: ctrRec 1.6s ease-in-out infinite;
}
.reveal-card.locked {
  opacity: 0.62;
  filter: grayscale(0.5);
}
.reveal-card.locked::after {
  content: attr(data-lock); /* the reason it can't be taken, e.g. "Not Impel Down" */
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, 0.78);
  color: #e6a9a0;
  font-size: 8.5px;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  padding: 3px 2px;
  z-index: 2;
}
/* counter step — the defender's hand as card faces with guard values + a recommended (gold) line */
.counter-advice {
  font-size: 12px;
  text-align: center;
  margin: 4px 0 10px;
  padding: 5px 9px;
  border-radius: var(--r-sm);
}
.counter-advice.need {
  color: var(--gold);
  background: rgba(240, 207, 120, 0.08);
}
.counter-advice.safe {
  color: #8fe79c;
}
.counter-advice.lost {
  color: #f0a09a;
}
.counter-advice.lethal {
  color: #ffd9a0;
  background: rgba(197, 64, 44, 0.22);
  font-weight: 700;
}
.counter-row {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
  margin: 6px 0 16px;
}
.counter-card {
  width: 86px;
  height: 120px;
  border-radius: 7px;
  position: relative;
  overflow: hidden;
  border: 2px solid var(--line);
  background: #1a150d;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
  user-select: none;
}
.counter-card .art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}
.counter-card .frame {
  position: absolute;
  inset: 0;
  display: flex;
  padding: 7px 4px;
  background: linear-gradient(165deg, #3a2c19, #1a120a);
  border-top: 4px solid #9e3326;
}
.counter-card.has-art .frame {
  display: none;
}
.counter-card .nm {
  font-family: var(--font-display);
  font-size: 10px;
  font-weight: 600;
  line-height: 1.15;
  text-shadow: 0 1px 2px #000;
}
.counter-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.6);
}
.ctr-badge {
  position: absolute;
  bottom: 3px;
  right: 3px;
  z-index: 2;
  background: linear-gradient(#1c150c, #0d0905);
  color: var(--gold);
  border: 1px solid rgba(240, 207, 120, 0.4);
  border-radius: 4px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 11px;
  padding: 1px 5px;
  text-shadow: 0 1px 2px #000;
}
.ctr-badge.ev {
  color: var(--you);
  border-color: rgba(79, 158, 114, 0.5);
}
.counter-card.selected {
  border-color: var(--you);
  box-shadow: 0 0 0 3px var(--you), 0 0 16px 3px rgba(70, 200, 96, 0.5);
  transform: translateY(-4px);
}
.counter-card.selected::after {
  content: "Counter";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  background: var(--you);
  color: #06140a;
  font-size: 9px;
  font-weight: 800;
  text-align: center;
  padding: 2px 0;
}
.counter-card.recommended {
  border-color: var(--gold);
  animation: ctrRec 1.6s ease-in-out infinite;
}
@keyframes ctrRec {
  0%,
  100% {
    box-shadow: 0 0 0 2px var(--gold), 0 0 13px 2px rgba(240, 207, 120, 0.45);
  }
  50% {
    box-shadow: 0 0 0 2px var(--gold), 0 0 20px 4px rgba(240, 207, 120, 0.78);
  }
}
.rec-tag {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  background: var(--gold);
  color: #2a1c06;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: center;
  padding: 2px 0;
}
/* --- SeaKing board hint (B21): the recommended play, highlighted live on the board -------------- */
/* A pulsing CYAN ring + "BEST" tag on the card SeaKing recommends (play / attach / activate / attacker) —
   cyan so the coach highlight reads as its OWN layer, distinct from the gold (.playable/.activatable) and
   green (.can-attack) affordance rings it sits on top of. The attack target gets a red "TARGET" ring.
   Source order beats the affordance rings so the hint wins. */
.card.hint-rec {
  animation: hintRec 1.6s ease-in-out infinite;
  z-index: 6;
}
.card.hint-rec.hint-target {
  animation: hintTarget 1.6s ease-in-out infinite;
}
.card.hint-rec::after {
  content: attr(data-hint-label); /* action verb (★ PLAY / ★ DON!! / ★ USE / ★ ATTACK / ✕ TARGET) — set in applyHintHighlight */
  position: absolute;
  /* Sits just INSIDE the top edge — the card's overflow:hidden was clipping a top:-8px float to an
     illegible sliver. A flush top ribbon (only bottom corners rounded) reads cleanly and never clips. */
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  background: var(--hint);
  color: #06222a;
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.04em;
  line-height: 1.3;
  padding: 1.5px 6px;
  border-radius: 0 0 6px 6px;
  white-space: nowrap;
  z-index: 7;
  pointer-events: none;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}
.card.hint-rec.hint-target::after {
  /* content comes from data-hint-label ("✕ TARGET"); this rule only recolors it red (the attack target). */
  background: var(--bot);
  color: #fff;
}
.endturn.hint-rec {
  animation: hintRec 1.6s ease-in-out infinite;
}
@keyframes hintRec {
  0%,
  100% {
    box-shadow: 0 0 0 3px var(--hint), 0 0 14px 3px rgba(53, 210, 230, 0.55);
  }
  50% {
    box-shadow: 0 0 0 3px var(--hint), 0 0 24px 6px rgba(53, 210, 230, 0.9);
  }
}
@keyframes hintTarget {
  0%,
  100% {
    box-shadow: 0 0 0 2px var(--bot), 0 0 13px 2px rgba(197, 64, 44, 0.5);
  }
  50% {
    box-shadow: 0 0 0 2px var(--bot), 0 0 22px 5px rgba(197, 64, 44, 0.85);
  }
}
/* Recommended LEADER ability: pulse the hero-power BUTTON (not a banner on the leader card). */
.hero-power.hint-rec-hp {
  animation: heroPowerHint 1.2s ease-in-out infinite;
}
@keyframes heroPowerHint {
  0%, 100% { box-shadow: 0 0 0 2px var(--hint), 0 0 14px 3px rgba(53, 210, 230, 0.5); }
  50% { box-shadow: 0 0 0 3px var(--hint), 0 0 24px 6px rgba(53, 210, 230, 0.9); }
}
/* ── Option B: status treatment wins over affordance glow ──────────────────────────────────────────
   When a card carries a status (.has-status), dim the competing playable/attack/activate/target GLOWS to
   a faint neutral ring so the colored status frame is the dominant signal. The hint LABEL still shows (it's
   text, not a glow). Higher specificity (3–4 classes) beats the base affordance rules; removed entirely when
   status FX are off (body.no-status-fx restores normal affordances). */
body:not(.no-status-fx) .card.has-status.playable,
body:not(.no-status-fx) .card.has-status.can-attack,
body:not(.no-status-fx) .card.has-status.activatable:not(.can-attack),
body:not(.no-status-fx) .card.has-status.drop-ok {
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.34);
  animation: none;
}
body:not(.no-status-fx) .card.has-status.hint-rec,
body:not(.no-status-fx) .card.has-status.hint-rec.hint-target {
  animation: none;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.3);
}
.counter-card.locked {
  opacity: 0.5;
  filter: grayscale(0.55);
  cursor: default;
}
.counter-card.locked:hover {
  transform: none;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
}
.counter-card.locked::after {
  content: attr(data-lock);
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 2;
  background: rgba(0, 0, 0, 0.8);
  color: #c9a06a;
  font-size: 8px;
  font-weight: 700;
  text-align: center;
  padding: 3px 2px;
}
.btn.rec {
  border-color: var(--gold);
  color: var(--gold);
  box-shadow: 0 0 10px rgba(240, 207, 120, 0.4);
}
.chip {
  border: 1px solid var(--line);
  background: #1b2731;
  border-radius: 14px;
  padding: 5px 11px;
  font-size: 12px;
  cursor: pointer;
}
.chip.selected {
  background: var(--accent);
  color: #1a1200;
  border-color: var(--accent);
  font-weight: 700;
}
.modal .actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}
/* the setup (mulligan / set sail) + finished overlays center their buttons — make it a flex row with a gap
   so Mulligan and Set sail don't sit flush against each other (the inline style only set justify-content). */
.overlay-center .actions {
  display: flex;
  gap: 14px;
}
.btn-primary {
  background: var(--you);
  border-color: var(--you);
  color: #06140a;
  font-weight: 700;
}
.btn-primary:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ---- banners / setup -------------------------------------------------------------------------- */
.overlay-center {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 70;
  pointer-events: none;
}
.banner {
  pointer-events: auto;
  padding: 20px 30px;
  border-radius: 14px;
  text-align: center;
  font-size: 22px;
  font-weight: 800;
  background: #0e151c;
  border: 2px solid var(--line);
}
.banner.win {
  border-color: var(--you);
  color: #8fe79c;
}
.banner.lose {
  border-color: var(--bot);
  color: #f0a09a;
}
.banner .sub {
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  margin-top: 10px;
}
.coach-panel {
  position: fixed;
  top: 58px;
  left: 16px;
  z-index: 76;
  width: min(310px, calc(100vw - 32px));
  border: 1px solid color-mix(in srgb, var(--gold), transparent 32%);
  border-radius: var(--r-md);
  background: linear-gradient(180deg, rgba(24, 19, 10, 0.96), rgba(12, 10, 6, 0.96));
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.62), inset 0 0 0 1px rgba(240, 207, 120, 0.08);
  padding: 11px 13px 12px;
  pointer-events: none;
}
.coach-kicker {
  color: var(--gold);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 4px;
}
.coach-title {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 1.15;
  margin-bottom: 5px;
}
.coach-body {
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.4;
}
.phase-flow {
  margin-top: 11px;
  padding-top: 10px;
  border-top: 1px solid rgba(240, 207, 120, 0.18);
}
.phase-flow-head {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  color: var(--muted);
  font-size: 11px;
  margin-bottom: 8px;
}
.phase-flow-head b {
  color: var(--ink);
}
.phase-flow-steps {
  display: grid;
  gap: 5px;
}
.phase-step {
  position: relative;
  display: grid;
  grid-template-columns: 12px minmax(54px, 0.52fr) 1fr;
  align-items: center;
  gap: 7px;
  min-height: 28px;
  color: color-mix(in srgb, var(--muted), #000 8%);
  opacity: 0.72;
}
.phase-step:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 20px;
  bottom: -7px;
  width: 2px;
  background: rgba(240, 207, 120, 0.2);
}
.phase-step.past,
.phase-step.active {
  opacity: 1;
}
.phase-step.active {
  color: var(--ink);
}
.phase-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid rgba(240, 207, 120, 0.42);
  background: rgba(240, 207, 120, 0.12);
}
.phase-step.past .phase-dot {
  background: var(--gold);
}
.phase-step.active .phase-dot {
  background: var(--you);
  border-color: var(--you);
  box-shadow: 0 0 10px rgba(70, 200, 96, 0.55);
}
.phase-label {
  color: currentColor;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
}
.phase-step small {
  min-width: 0;
  color: var(--muted);
  font-size: 10.5px;
  line-height: 1.2;
}
.learn-focus {
  position: relative;
  z-index: 77;
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  box-shadow: 0 0 16px rgba(240, 207, 120, 0.42);
}
.modal.learn-focus,
.banner.learn-focus {
  outline-offset: 6px;
}
.banner.result {
  width: min(420px, calc(100vw - 36px));
  padding: 20px 24px 22px;
}
.postgame-recap {
  margin-top: 16px;
  padding-top: 13px;
  border-top: 1px solid var(--line);
  text-align: left;
}
.recap-title {
  color: var(--muted);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 9px;
}
.recap-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.recap-stat {
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, 0.035);
  padding: 8px 9px;
}
.recap-stat span {
  display: block;
  color: var(--muted);
  font-size: 10px;
  line-height: 1.15;
}
.recap-stat b {
  display: block;
  color: var(--ink);
  font-size: 17px;
  line-height: 1.1;
  margin-top: 4px;
}

.toast {
  position: fixed;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--bot);
  color: #fff;
  padding: 8px 14px;
  border-radius: 6px;
  font-size: 13px;
  z-index: 120;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
  animation: toastIn 220ms cubic-bezier(0.22, 1, 0.36, 1) both; /* N3: slide-up + fade entrance */
}
.toast.leaving {
  animation: toastOut 260ms cubic-bezier(0.4, 0, 1, 1) both; /* N3: fade-down exit before removal */
}
/* keyframes keep the translateX(-50%) centering while animating the Y + opacity */
@keyframes toastIn {
  from { opacity: 0; transform: translateX(-50%) translateY(14px); }
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@keyframes toastOut {
  from { opacity: 1; transform: translateX(-50%) translateY(0); }
  to { opacity: 0; transform: translateX(-50%) translateY(10px); }
}
@media (prefers-reduced-motion: reduce) {
  .toast, .toast.leaving { animation: none; }
}

.disclaimer {
  position: fixed;
  bottom: 2px;
  right: 10px;
  color: #54616d;
  font-size: 9px;
  z-index: 5;
}

/* ---- card hover-zoom (read the full card before playing) -------------------------------------- */
.zoom {
  position: fixed;
  z-index: 130; /* above the modal (100) so the reveal/search hover-preview is readable beside the cards */
  width: 252px;
  background: #0f1822;
  border: 1px solid var(--accent);
  border-radius: 11px;
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.72);
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 0.1s ease, transform 0.1s ease;
}
.zoom.show {
  opacity: 1;
  transform: scale(1);
}
.zoom-art {
  width: 100%;
  display: block;
  aspect-ratio: 5 / 7;
  object-fit: cover;
  border-bottom: 1px solid #05080b;
}
.zoom-info {
  padding: 9px 12px 12px;
}
.zoom-name {
  font-size: 14px;
  font-weight: 800;
  margin-bottom: 5px;
}
.zoom-badges {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 7px;
}
.zoom-badges .zb {
  font-weight: 800;
  color: var(--ink);
}
.zoom-badges .cost { color: #7fb3ff; }
.zoom-badges .pow { color: var(--accent); }
.zoom-badges .ctr { color: var(--you); }
.zoom-badges .life { color: var(--bot); }
.zoom-badges .zsep { opacity: 0.4; }
.zoom-traits {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.ztrait {
  font-size: 10px;
  background: #1b2731;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 1px 6px;
  color: var(--muted);
}
.zoom-effect {
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink);
}
.zoom-effect.muted {
  color: var(--muted);
  font-style: italic;
}
.zoom-meta {
  font-size: 10px;
  color: var(--muted);
  margin-top: 9px;
  text-transform: capitalize;
}

/* ================================================================================================
   ANIMATION ENGINE — event-driven juice (Phase E). The #fx layer hosts transient sprites; board cards
   are FLIP-animated in place. All driven by the engine event stream via runAnimations() in app.js.
   ================================================================================================ */
.fx-layer {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  z-index: 85;
  overflow: visible;
}
.fx-ghost {
  border-radius: 6px;
  overflow: hidden;
}

/* ---- card-play fly-in (hand/effect → board, FLIP) --------------------------------------------- */
@keyframes cardFlyIn {
  0% {
    transform: translate(var(--dx, 0), var(--dy, 0)) scale(0.82) rotate(-4deg);
    opacity: 0.5;
  }
  55% {
    box-shadow: 0 0 24px 7px rgba(240, 192, 74, 0.85);
  }
  60% {
    transform: translate(0, 0) scale(1.06) rotate(0);
    opacity: 1;
  }
  100% {
    transform: translate(0, 0) scale(1);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
  }
}
.card.flying {
  animation: cardFlyIn 300ms cubic-bezier(0.22, 1, 0.36, 1) both;
  z-index: 70;
}

/* ---- attack lunge (clone on #fx leaps at the target) ------------------------------------------ */
@keyframes lunge {
  0% {
    transform: translate(0, 0) scale(1);
  }
  24% {
    transform: translate(calc(var(--lx) * -0.14), calc(var(--ly) * -0.14)) scale(0.96);
  }
  100% {
    transform: translate(var(--lx), var(--ly)) scale(1.08);
  }
}
.fx-ghost.lunge {
  animation: lunge 240ms cubic-bezier(0.5, 0, 0.9, 0.2) forwards;
  z-index: 90;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.6);
}

/* ---- KO shatter ------------------------------------------------------------------------------- */
@keyframes koShatter {
  0% {
    transform: scale(1) rotate(0);
    opacity: 1;
  }
  18% {
    transform: scale(1.12) rotate(-3deg);
    filter: brightness(1.9);
  }
  100% {
    transform: scale(0.2) rotate(10deg);
    opacity: 0;
    filter: grayscale(1) brightness(0.5);
  }
}
.fx-ghost.ko-shatter {
  animation: koShatter 420ms cubic-bezier(0.55, 0.06, 0.68, 0.19) forwards;
  z-index: 88;
}
.fx-shard {
  position: absolute;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--accent);
  animation: shardFly 600ms ease-in forwards;
}
@keyframes shardFly {
  from {
    transform: translate(0, 0) scale(1);
    opacity: 1;
  }
  to {
    transform: translate(var(--sx), var(--sy)) scale(0.3);
    opacity: 0;
  }
}
/* fast bright sparks at the moment of impact (distinct from the slow falling KO shards) */
.fx-spark {
  position: absolute;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 6px 1px rgba(255, 220, 120, 0.9);
  animation: sparkFly 300ms ease-out forwards;
}
@keyframes sparkFly {
  from {
    transform: translate(0, 0) scale(1);
    opacity: 1;
  }
  to {
    transform: translate(var(--kx), var(--ky)) scale(0.2);
    opacity: 0;
  }
}

/* ---- impact ring / shield flash --------------------------------------------------------------- */
.fx-ring {
  position: absolute;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  border: 3px solid #fff;
  box-shadow: 0 0 16px 3px rgba(255, 220, 120, 0.9);
  animation: ringExpand 380ms ease-out forwards;
}
@keyframes ringExpand {
  0% {
    transform: translate(-50%, -50%) scale(0.5);
    opacity: 0.9;
  }
  100% {
    transform: translate(-50%, -50%) scale(1.6);
    opacity: 0;
  }
}
.fx-shield {
  position: absolute;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  border: 3px solid var(--don);
  box-shadow: 0 0 16px 3px rgba(91, 155, 255, 0.7);
  animation: shieldPop 460ms ease-out forwards;
}
@keyframes shieldPop {
  0% {
    transform: translate(-50%, -50%) scale(0.4);
    opacity: 0.9;
  }
  60% {
    opacity: 0.85;
  }
  100% {
    transform: translate(-50%, -50%) scale(1.15);
    opacity: 0;
  }
}

/* ---- floating damage numbers ------------------------------------------------------------------ */
.fx-dmg {
  position: absolute;
  font-weight: 900;
  font-size: 26px;
  text-shadow: 0 2px 4px #000, 0 0 8px #000;
  animation: dmgFloat 800ms ease-out forwards;
}
.fx-dmg.dmg {
  color: #ff6b6b;
}
.fx-dmg.heal {
  color: var(--you);
}
@keyframes dmgFloat {
  0% {
    transform: translate(-50%, -50%) scale(0.6);
    opacity: 0;
  }
  15% {
    transform: translate(-50%, -62%) scale(1.25);
    opacity: 1;
  }
  70% {
    opacity: 1;
  }
  100% {
    transform: translate(-50%, calc(-50% - 46px)) scale(1);
    opacity: 0;
  }
}

/* ---- life-card flip + toss to hand ------------------------------------------------------------ */
/* ---- opening coin flip --------------------------------------------------------------------------- */
.coinflip-overlay {
  position: absolute;
  inset: 0;
  z-index: 140; /* above the setup/mulligan overlay (100) and reveal preview (130) so the flip reads first */
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at center, rgba(12, 9, 4, 0.74), rgba(8, 6, 3, 0.92));
}
.coinflip-overlay.fade {
  animation: coinFade 0.45s ease forwards;
}
@keyframes coinFade {
  to { opacity: 0; }
}
.coin-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
}
.coin {
  width: 100px;
  height: 100px;
  position: relative;
  transform-style: preserve-3d;
}
.coin.lands-you {
  animation: coinSpinYou 1.5s cubic-bezier(0.18, 0.7, 0.18, 1) forwards;
}
.coin.lands-ace {
  animation: coinSpinAce 1.5s cubic-bezier(0.18, 0.7, 0.18, 1) forwards;
}
@keyframes coinSpinYou {
  from { transform: rotateY(0); }
  to { transform: rotateY(1800deg); } /* 5 turns → lands on the front (you) face */
}
@keyframes coinSpinAce {
  from { transform: rotateY(0); }
  to { transform: rotateY(1980deg); } /* 5.5 turns → lands on the back (Ace) face */
}
.coin-face {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 24px;
  letter-spacing: 0.08em;
  border: 3px solid #6a4d17;
  box-shadow: inset 0 0 0 3px rgba(255, 240, 200, 0.25), 0 6px 18px rgba(0, 0, 0, 0.6);
  backface-visibility: hidden;
}
.coin-face.you {
  background: radial-gradient(circle at 38% 30%, #6fae8e, #2f7d5a 60%, #1d5a3e);
  color: #eafff4;
}
.coin-face.ace {
  background: radial-gradient(circle at 38% 30%, #d0584a, #9a2c22 60%, #6a1a14);
  color: #ffe9e3;
  transform: rotateY(180deg);
}
.coin-result {
  font-family: var(--font-display);
  font-size: 19px;
  color: var(--gold);
  text-shadow: 0 2px 6px #000;
  opacity: 0;
  animation: coinResult 0.5s ease 1.15s forwards;
}
@keyframes coinResult {
  to { opacity: 1; }
}

.fx-lifecard {
  position: absolute;
  width: 50px; /* launches at ~life-card size, then drifts/scales toward the hand */
  height: 70px;
  border-radius: 5px;
  background: repeating-linear-gradient(45deg, #1c3a2a, #1c3a2a 5px, #163024 5px, #163024 10px);
  border: 1px solid #2c5a3e;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.6);
}
.fx-lifecard.lifecard-go {
  animation: lifeGo 440ms ease-in-out forwards;
}
@keyframes lifeGo {
  0% {
    transform: rotateY(0) translate(0, 0) scale(1);
    opacity: 1;
  }
  40% {
    transform: rotateY(180deg) translate(calc(var(--fx) * 0.4), calc(var(--fy) * 0.4 - 14px)) scale(1.05);
    opacity: 1;
  }
  100% {
    transform: rotateY(180deg) translate(var(--fx), var(--fy)) scale(0.8);
    opacity: 0;
  }
}

/* ---- per-turn draw (face-down card flies deck → hand) ----------------------------------------- */
.fx-drawcard {
  position: absolute;
  width: 54px;
  height: 74px;
  border-radius: 6px;
  background: repeating-linear-gradient(45deg, #1c3a2a, #1c3a2a 7px, #14302230 7px, #143022 14px);
  border: 1px solid #2c5a3e;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.6);
}
.fx-drawcard.draw-go {
  animation: drawGo 280ms cubic-bezier(0.4, 1.3, 0.5, 1) forwards;
}
@keyframes drawGo {
  0% {
    transform: translate(0, 0) scale(0.7) rotate(-8deg);
    opacity: 0;
  }
  20% {
    opacity: 1;
  }
  100% {
    transform: translate(var(--dxf), var(--dyf)) scale(1) rotate(0);
    opacity: 0.9;
  }
}

/* ---- DON gem attach --------------------------------------------------------------------------- */
.fx-don {
  position: absolute;
  width: 20px;
  height: 27px;
  border-radius: 3px;
  background: linear-gradient(150deg, #ec5f53, #c4302a 52%, #8c1f1b);
  border: 1px solid #5a1411;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.6), 0 0 9px rgba(240, 160, 90, 0.7);
}
.fx-don.don-go {
  animation: donGo 320ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
@keyframes donGo {
  0% {
    transform: translate(0, 0) scale(1.3);
    opacity: 0;
  }
  25% {
    opacity: 1;
  }
  100% {
    transform: translate(var(--gx), var(--gy)) scale(1);
    opacity: 1;
  }
}

/* ---- on-board reactions (flash / flinch / buff) ----------------------------------------------- */
.card.hit-flash {
  animation: hitFlash 300ms ease-out;
}
@keyframes hitFlash {
  0% {
    filter: brightness(2.4);
  }
  100% {
    filter: none;
  }
}
.card.flinch {
  animation: flinch 300ms ease-out;
}
@keyframes flinch {
  0% {
    transform: translateY(0);
  }
  35% {
    transform: translateY(5px);
  }
  100% {
    transform: translateY(0);
  }
}
.card.buff-pulse {
  animation: buffPulse 300ms ease-out;
}
@keyframes buffPulse {
  0% {
    box-shadow: 0 0 0 0 rgba(91, 155, 255, 0);
  }
  40% {
    box-shadow: 0 0 16px 4px rgba(91, 155, 255, 0.85);
  }
  100% {
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
  }
}

/* ---- screen shake (board container only — HUD/log/fx stay steady) ----------------------------- */
.arena.shake-sm {
  animation: shakeSm 240ms ease-in-out;
}
.arena.shake-lg {
  animation: shakeLg 260ms ease-in-out;
}
@keyframes shakeSm {
  10%, 90% { transform: translate(-2px, 1px); }
  30%, 70% { transform: translate(3px, -2px); }
  50% { transform: translate(-3px, 2px); }
}
@keyframes shakeLg {
  10%, 90% { transform: translate(-4px, 2px); }
  30%, 70% { transform: translate(6px, -3px); }
  50% { transform: translate(-6px, 4px); }
}

/* ---- turn banner + refresh sweep -------------------------------------------------------------- */
.fx-turn {
  position: absolute;
  left: 0;
  right: 0;
  top: 38%;
  text-align: center;
  font-size: 30px;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-shadow: 0 2px 10px #000;
  animation: turnIn 1200ms ease-out forwards;
}
.fx-turn.you {
  color: var(--you);
}
.fx-turn.bot {
  color: var(--bot);
}
@keyframes turnIn {
  0% { opacity: 0; transform: translateX(-50px); }
  14% { opacity: 1; transform: none; }
  78% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateX(40px); }
}
.row.minions {
  position: relative;
}
/* (removed the full-rectangle refresh gold-wash sweep — replaced by the per-card refresh-tilt below) */
.card.refresh-tilt {
  animation: refreshTilt 500ms ease-out;
}
@keyframes refreshTilt {
  0% { transform: rotate(-6deg); }
  60% { transform: rotate(2deg); }
  100% { transform: none; }
}

/* ---- win/lose screen flash -------------------------------------------------------------------- */
body.flash-win::after,
body.flash-lose::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 95;
  animation: flashFade 700ms ease-out forwards;
}
body.flash-win::after {
  background: radial-gradient(circle, rgba(70, 200, 96, 0.4), transparent 70%);
}
body.flash-lose::after {
  background: radial-gradient(circle, rgba(224, 88, 79, 0.4), transparent 70%);
}
@keyframes flashFade {
  from { opacity: 1; }
  to { opacity: 0; }
}
.banner {
  animation: bannerIn 360ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes bannerIn {
  from { opacity: 0; transform: scale(0.8) translateY(10px); }
  to { opacity: 1; transform: none; }
}

/* ---- respect reduced-motion: kill the keyframe juice (JS also short-circuits) ----------------- */
@media (prefers-reduced-motion: reduce) {
  .card.flying, .fx-ghost, .fx-shard, .fx-spark, .fx-ring, .fx-shield, .fx-dmg, .fx-lifecard, .fx-don, .fx-drawcard,
  .card.hit-flash, .card.flinch, .card.buff-pulse, .arena.shake-sm, .arena.shake-lg, .fx-turn,
  .row.minions.refresh-sweep::before, .card.refresh-tilt, body.flash-win::after, body.flash-lose::after,
  .banner, .card.drop-ok, .row.minions.you.drop-ok .card.empty, .don-card.gained, .endturn.ready .compass,
  .counter-card.recommended, .reveal-card.recommended, .ability-gem, .hero-power, .hero-power.spent .hp-glyph,
  .hero-power.refreshed .hp-glyph, .hero-power.ready .hp-glyph,
  .card.hint-rec, .endturn.hint-rec,
  .card .don-badge {
    animation: none !important;
  }
  /* keep the gold ring (just lose the pulse) so the recommended card still reads */
  .counter-card.recommended, .reveal-card.recommended {
    box-shadow: 0 0 0 2px var(--gold), 0 0 14px 2px rgba(240, 207, 120, 0.55);
  }
  .card.hint-rec, .endturn.hint-rec {
    box-shadow: 0 0 0 3px var(--hint), 0 0 14px 3px rgba(53, 210, 230, 0.6);
  }
  .card.hint-rec.hint-target {
    box-shadow: 0 0 0 2px var(--bot), 0 0 14px 2px rgba(197, 64, 44, 0.6);
  }
}

/* ---- "Compass & Glass" identity: display type, warm surfaces, SVG icons ------------------------ */
.ico,
.compass-slot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
}
.ico svg,
.compass-slot svg,
.bc-vs svg {
  display: block;
}
#mute {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  padding: 6px 9px;
}
#mute:hover {
  color: var(--accent);
  border-color: var(--accent);
}
.btn {
  background: var(--surface-2);
  border-color: var(--line);
  color: var(--ink);
}
.btn:hover {
  border-color: var(--accent);
}
.btn-ghost {
  background: transparent;
}
.btn-primary {
  background: var(--you);
  border-color: var(--you);
  color: #07140a;
}
.fx-turn {
  font-family: var(--font-display);
  font-weight: 700;
}
.banner {
  font-family: var(--font-display);
  background: var(--surface);
  border-color: var(--line-lit);
}
.banner.win {
  border-color: var(--gold);
  color: var(--gold);
}
.banner.lose {
  border-color: var(--bot);
  color: #e8a99f;
}
.modal {
  background: var(--surface);
  border-color: var(--line-lit);
}
.modal h3 {
  font-family: var(--font-display);
  color: var(--gold);
}
.battle-ctx {
  background: var(--bg);
  border-color: var(--line);
}
.chip {
  background: var(--surface-2);
  border-color: var(--line);
}
.chip.selected {
  background: var(--accent);
  color: #1a1200;
  border-color: var(--accent);
}
.zoom {
  background: var(--surface);
  border-color: var(--line-lit);
}
.zoom-name {
  font-family: var(--font-display);
  color: var(--gold);
}
.zoom-badges .cost {
  color: var(--accent);
}
.ztrait {
  background: var(--surface-2);
  border-color: var(--line);
}

/* --- deck picker ("choose your deck vs Ace") ------------------------------------------------------ */
#deck-picker {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.deck-btn-name {
  font-weight: 600;
}
.deck-picker-overlay {
  position: fixed;
  inset: 0;
  z-index: 200; /* above the prompt modal (z-100) and the board rails (z-4/5) */
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(2px);
}
.deck-picker {
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: 14px;
  padding: 22px 24px 26px;
  max-width: 760px;
  width: min(92vw, 760px);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.55);
  max-height: 88vh; /* the roster grew to 19 decks; the modal must scroll, not overflow the viewport */
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}
/* heading stays put while the deck grid scrolls under it */
.deck-picker-title,
.deck-picker-sub {
  position: sticky;
  left: 0;
  background: var(--surface);
  z-index: 1;
}
.deck-picker-title {
  top: 0;
  padding-top: 2px;
}
.deck-picker-title {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 700;
  color: var(--gold);
  letter-spacing: 0.3px;
}
.deck-picker-sub {
  color: var(--muted);
  font-size: 13px;
  margin: 4px 0 16px;
}
.deck-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
}
.deck-card {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 14px 15px 16px;
  background: rgba(255, 255, 255, 0.02);
  cursor: pointer;
  transition: border-color 0.15s, transform 0.12s, box-shadow 0.15s;
}
.deck-card:hover {
  border-color: var(--line-lit);
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}
.deck-card.active {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.deck-card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.deck-card-name {
  font-family: var(--font-display);
  font-size: 18px;
  color: var(--ink);
}
.deck-card-arch {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--accent);
}
.deck-chips {
  display: flex;
  gap: 5px;
  margin: 9px 0 8px;
}
.deck-chip {
  font-size: 10px;
  text-transform: capitalize;
  color: #fff;
  padding: 1px 8px;
  border-radius: 999px;
  opacity: 0.92;
}
.deck-card-blurb {
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.45;
}
.deck-card-tag {
  position: absolute;
  top: 12px;
  right: 13px;
  display: none; /* archetype already shown; reserved for the default marker if wanted */
}
.deck-legality {
  margin-top: 9px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.deck-legality.ok {
  color: #6fbf73;
}
.deck-legality.warn {
  color: var(--accent);
}
/* --- Onboarding: welcome gate + How-to-play reference ------------------------------------------ */
.onboard-overlay {
  position: fixed;
  inset: 0;
  z-index: 320; /* above coin flip (z-240), settings (z-220), deck picker (z-200) */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(6, 5, 3, 0.72);
  backdrop-filter: blur(3px);
}
.onboard-card {
  position: relative;
  box-sizing: border-box;
  width: min(540px, 94vw);
  max-height: 90dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--accent);
  border-radius: 16px;
  background: linear-gradient(180deg, rgba(31, 26, 17, 0.99), rgba(17, 15, 10, 1));
  box-shadow: 0 22px 70px rgba(0, 0, 0, 0.6);
  padding: 26px 26px 20px;
  text-align: center;
}
.help-card { width: min(620px, 94vw); text-align: left; }
.onboard-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(19, 17, 11, 0.92);
  color: var(--muted);
  cursor: pointer;
}
.onboard-close:hover { border-color: var(--accent); color: var(--ink); }
.onboard-mark { display: block; width: 56px; height: 56px; margin: 2px auto 10px; }
.onboard-mark .compass { width: 56px; height: 56px; }
.onboard-kicker {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--accent);
  font-weight: 700;
}
.onboard-title {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 800;
  color: var(--gold);
  margin: 4px 0 8px;
}
.onboard-lede {
  color: var(--ink);
  font-size: 14px;
  line-height: 1.55;
  margin: 0 auto 16px;
  max-width: 46ch;
}
.help-card .onboard-lede { margin-left: 0; }
.onboard-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin-top: 4px;
}
.onboard-actions-col { flex-direction: column; align-items: stretch; max-width: 320px; margin: 4px auto 0; }
.onboard-actions .btn { width: 100%; justify-content: center; }
.onboard-foot {
  margin-top: 16px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--muted);
}
/* reference body */
.help-body { display: grid; gap: 18px; margin: 4px 0 8px; }
.help-section-title {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: 0.04em;
  margin: 0 0 8px;
  padding-bottom: 5px;
  border-bottom: 1px solid rgba(122, 94, 47, 0.4);
}
.help-list { margin: 0; display: grid; gap: 9px; }
.help-list dt {
  font-size: 13px;
  font-weight: 700;
  color: var(--gold);
}
.help-list dd {
  margin: 1px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink);
}
@media (prefers-reduced-motion: reduce) {
  .onboard-overlay { backdrop-filter: none; }
}

/* legality heads-up (reuses the .deck-picker shell) */
.legality-sec {
  margin-top: 14px;
}
.legality-sec-h {
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 6px;
}
.legality-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.legality-card {
  font-size: 11px;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
}
.legality-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 18px;
}
@media (prefers-reduced-motion: reduce) {
  .deck-card,
  .deck-card:hover {
    transition: none;
    transform: none;
  }
}

/* ---- compact board (narrow viewport) ---------------------------------------------------------- */
/* Keep the log anchored top-left across breakpoints; compact only trims the footprint. */
@media (max-width: 900px) {
  /* Compact: keep the chat readable — only a hair narrower, same font, instead of the old hard shrink. */
  .log {
    top: 120px;
    width: 264px;
    max-height: 150px;
    font-size: 12px;
  }
}

/* ── Keyword affordances: Blocker / Rush / Double Attack / Banish + hard-taunt ──────────────────
   OPCG's real keyword set (NOT Hearthstone's — no Divine Shield/Windfury in OPCG). Badges read from the
   projection's effective keywords (printed + effect-granted); per-keyword glows are the "FX suite". */
.card .kw-badges {
  position: absolute;
  top: 3px;
  right: 3px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  z-index: 5;
}
.kw-badge {
  width: 19px;
  height: 19px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  line-height: 1;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35), 0 0 6px 1px var(--kw-glow, rgba(0, 0, 0, 0.5));
  background: var(--surface-2);
  animation: kwBadgePulse 1.6s ease-in-out infinite;
}
.kw-badge .kw-ic { filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9)); color: #fff; display: flex; align-items: center; justify-content: center; }
.kw-badge .kw-svg { width: 64%; height: 64%; display: block; fill: #fff; }
@keyframes kwBadgePulse {
  0%, 100% { transform: scale(1); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3), 0 0 5px 1px var(--kw-glow, rgba(0, 0, 0, 0.5)); }
  50% { transform: scale(1.12); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.55), 0 0 11px 3px var(--kw-glow, rgba(0, 0, 0, 0.6)); }
}
/* Brighter gradients + a per-keyword glow colour driven into the shared pulse via --kw-glow. */
.kw-badge.kw-blocker { background: radial-gradient(circle at 36% 30%, #aed6ff, #2f7fe0); --kw-glow: rgba(90, 170, 255, 0.85); }
.kw-badge.kw-rush { background: radial-gradient(circle at 36% 30%, #ffe98f, #f0a323); --kw-glow: rgba(255, 200, 70, 0.9); }
.kw-badge.kw-double { background: radial-gradient(circle at 36% 30%, #ffb199, #e1402c); --kw-glow: rgba(255, 110, 80, 0.9); }
.kw-badge.kw-banish { background: radial-gradient(circle at 36% 30%, #ddc0ff, #8a4fe0); --kw-glow: rgba(180, 120, 255, 0.9); }

/* N3 Tier 1: status badges (rule-flags). Bottom-right column, clear of the cost (top-left), keyword/DON
   (top-right) and power (bottom-left) badges. Glyph + per-flag tint, with a one-time grant flash (.st-new). */
.card .status-badges {
  position: absolute;
  bottom: 3px;
  right: 3px;
  display: flex;
  flex-direction: column-reverse;
  gap: 3px;
  z-index: 5;
}
.status-badge {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  line-height: 1;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3), 0 0 5px 1px var(--st-glow, rgba(0, 0, 0, 0.5));
}
.status-badge .st-ic {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  line-height: 1;
  filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9));
  color: #fff;
  font-style: normal;
}
/* Inline SVG badge icon — centered by the 24×24 viewBox, so no font-baseline drift. */
.status-badge .st-svg {
  width: 66%;
  height: 66%;
  display: block;
}
.status-badge .st-svg path,
.status-badge .st-svg circle,
.status-badge .st-svg rect {
  fill: none;
  stroke: #fff;
  stroke-width: 2.1;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.status-badge.st-frozen { background: radial-gradient(circle at 36% 30%, #bfe6ff, #3aa0e0); --st-glow: rgba(120, 200, 255, 0.9); }
.status-badge.st-noattack { background: radial-gradient(circle at 36% 30%, #ffb1a8, #d8392c); --st-glow: rgba(240, 100, 90, 0.9); }
/* Category-coherent palette: badge color === the card's frame/overlay color. */
/* bad / restriction → red (matches the red slash overlay) */
.status-badge.st-restlock { background: radial-gradient(circle at 36% 30%, #ffb1a8, #d8392c); --st-glow: rgba(240, 100, 90, 0.9); }
.status-badge.st-silenced { background: radial-gradient(circle at 36% 30%, #ffb1a8, #d8392c); --st-glow: rgba(240, 100, 90, 0.9); }
/* good / protection → green (matches the shield overlay) */
.status-badge.st-koproof { background: radial-gradient(circle at 36% 30%, #b6f0c0, #2faa55); --st-glow: rgba(110, 230, 140, 0.9); }
.status-badge.st-anchored { background: radial-gradient(circle at 36% 30%, #b6f0c0, #2faa55); --st-glow: rgba(110, 230, 140, 0.9); }
/* buff / offense → purple (matches the up-arrow overlay + float ring) */
.status-badge.st-pierce { background: radial-gradient(circle at 36% 30%, #d8c0ff, #7d4fe0); --st-glow: rgba(170, 120, 255, 0.9); }
.status-badge.st-new { animation: stBadgeIn 520ms cubic-bezier(0.2, 0.72, 0.22, 1) both; } /* grant flash, once */
@keyframes stBadgeIn {
  0% { transform: scale(0.2); opacity: 0; filter: drop-shadow(0 0 8px var(--st-glow, #fff)); }
  60% { transform: scale(1.3); opacity: 1; }
  100% { transform: scale(1); opacity: 1; filter: none; }
}
@media (prefers-reduced-motion: reduce) {
  .status-badge.st-new { animation: none; }
}
/* Hearthstone-style status TREATMENTS — each category CHANGES THE CARD'S LOOK distinctly (not just a tinted
   border), so a status is unmistakable and reads apart from the cyan hint ring + blue blocker glow.
   frozen = iced over (desaturated art + frost overlay), bad = darkened + dashed red bar, good = green shield
   sheen, buff = gold energized. The frame is a separate overlay layer → coexists with keyword card-glows. */
.card .status-frame {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-sizing: border-box;
  pointer-events: none;
  overflow: hidden;
  z-index: 3;
}
/* FROZEN — ice block: heavily desaturate + blue-cool the art, then a dense frosty BLUE wash with corner ice
   crystals, so the card looks properly iced over (not a thin glow). */
.card.st-on-frozen .art { filter: saturate(0.26) brightness(0.88) contrast(1.05); }
.status-frame.st-frame-frozen {
  border: 2px solid rgba(205, 232, 255, 0.95);
  background:
    /* angular ice-crystal facets (sharp white streaks) for the "frozen in a block" read */
    linear-gradient(58deg, transparent 37%, rgba(255, 255, 255, 0.22) 40%, transparent 43%),
    linear-gradient(-46deg, transparent 58%, rgba(255, 255, 255, 0.16) 62%, transparent 65%),
    linear-gradient(120deg, transparent 22%, rgba(255, 255, 255, 0.14) 25%, transparent 28%),
    /* blue frost wash + corner crystals */
    linear-gradient(135deg, rgba(150, 205, 255, 0.5), rgba(108, 172, 244, 0.26) 42%, rgba(160, 212, 255, 0.46)),
    radial-gradient(circle at 16% 12%, rgba(255, 255, 255, 0.62), transparent 27%),
    radial-gradient(circle at 84% 86%, rgba(232, 246, 255, 0.5), transparent 25%);
  box-shadow:
    inset 0 0 22px 3px rgba(150, 205, 255, 0.6),
    inset 0 0 0 1px rgba(222, 240, 255, 0.5),
    0 0 14px 1px rgba(150, 200, 255, 0.55);
  animation: stFramePulse 3s ease-in-out infinite;
}
/* BAD — restriction: lightly darken (keep stats/art readable per the research) + a big corner-to-corner red
   "prohibited" slash (SVG, see below). */
.card.st-on-bad .art { filter: saturate(0.78) brightness(0.84); }
.status-frame.st-frame-bad {
  background: linear-gradient(rgba(120, 22, 18, 0.2), rgba(120, 22, 18, 0.05));
  box-shadow: inset 0 0 14px 2px rgba(180, 44, 38, 0.35), 0 0 11px 1px rgba(232, 84, 74, 0.55);
  border: 1.5px solid rgba(232, 84, 74, 0.5);
  animation: stFramePulse 1.7s ease-in-out infinite;
}
.status-frame.st-frame-bad .st-slash {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.status-frame.st-frame-bad .st-slash line {
  stroke: rgba(255, 96, 84, 0.95);
  stroke-width: 6;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke; /* constant 6px width despite the non-uniform stretch */
  filter: drop-shadow(0 0 3px rgba(255, 70, 60, 0.8));
}
/* Big FAINT centered symbol overlays (Tyler's idea) — keep aspect, centered ~70% of the card. */
.status-frame .st-symbol {
  position: absolute;
  top: 15%;
  left: 15%;
  width: 70%;
  height: 70%;
  pointer-events: none;
}
.st-symbol.st-shield path {
  fill: rgba(150, 255, 185, 0.08);
  stroke: rgba(196, 255, 216, 0.5);
  stroke-width: 4;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 4px rgba(120, 250, 160, 0.55));
}
/* protected + pierce: a bright thrust-arrow nested inside the shield (both states read at once) */
.st-symbol.st-shield .sh-arrow {
  fill: none;
  stroke: rgba(228, 212, 255, 0.95);
  stroke-width: 6.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 3px rgba(176, 130, 255, 0.85));
}
/* frozen → faint crystalline snowflake, centered, on top of the ice frame */
.st-symbol.st-snowflake {
  top: 12%;
  left: 12%;
  width: 76%;
  height: 76%;
}
.st-symbol.st-snowflake line {
  stroke: rgba(226, 245, 255, 0.62);
  stroke-width: 3;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 3px rgba(150, 210, 255, 0.7));
}
.st-symbol.st-arrow {
  top: -5%;
  left: 7%;
  width: 86%;
  height: 86%;
}
.st-symbol.st-arrow path {
  fill: none;
  stroke: rgba(220, 195, 255, 0.92);
  stroke-width: 11;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.6)) drop-shadow(0 0 8px rgba(172, 122, 252, 0.9));
}
/* GOOD — protection: HS divine-shield. The bright pulsing emerald shimmer Tyler liked stays ON the card (the
   child frame: rim + dome + sheen), and the shield BUBBLE floats OFF the card edges via a card-level
   box-shadow (.card.st-on-good below) — the card clips its children, but its own box-shadow is not clipped,
   so the bubble wraps around the outside like Hearthstone's divine shield. */
.status-frame.st-frame-good {
  border: 1.5px solid rgba(220, 255, 232, 0.65); /* inner shell edge, on the card */
  background:
    /* glossy top-left highlight, like light catching a glass dome */
    radial-gradient(ellipse 62% 34% at 38% 4%, rgba(255, 255, 255, 0.5), rgba(220, 255, 232, 0.16) 55%, transparent 72%),
    /* travelling sheen */
    linear-gradient(115deg, transparent 38%, rgba(228, 255, 238, 0.4) 47%, rgba(255, 255, 255, 0.66) 50%, rgba(228, 255, 238, 0.4) 53%, transparent 62%);
  background-size: 100% 100%, 280% 100%;
  background-repeat: no-repeat;
  box-shadow: inset 0 0 11px 1px rgba(120, 248, 158, 0.4);
  animation: shieldSheen 2.1s linear infinite;
}
@keyframes shieldSheen { 0% { background-position: 38% 4%, 180% 0; } 100% { background-position: 38% 4%, -80% 0; } }
/* the floating wrap-around shield SHELL — a CRISP bright glassy membrane + a defined green shell + only a
   TIGHT glow (not an aggressive halo), on the card's OWN box-shadow so it escapes overflow:hidden and reads
   as a defined bubble hugging the card, like Hearthstone divine shield. */
.card.st-on-good {
  animation: shieldFloat 1.6s ease-in-out infinite;
}
@keyframes shieldFloat {
  0%, 100% {
    box-shadow:
      0 0 0 1.5px rgba(248, 255, 252, 0.96),
      0 0 0 3px rgba(132, 250, 178, 0.72),
      0 0 9px 2px rgba(120, 246, 160, 0.4);
  }
  50% {
    box-shadow:
      0 0 0 2px rgba(255, 255, 255, 1),
      0 0 0 4.5px rgba(155, 255, 198, 0.82),
      0 0 14px 4px rgba(132, 250, 172, 0.55);
  }
}
/* BUFF (canAttackActive / "piercing") — a violet "empowered" shell, SAME float-off mechanic as protection but
   PURPLE so it never collides with the GOLD affordance glows (playable/can-attack) or the green shield. */
.status-frame.st-frame-buff {
  border: 1.5px solid rgba(228, 205, 255, 0.62);
  box-shadow: inset 0 0 11px 1px rgba(170, 120, 255, 0.4);
}
.card.st-on-buff {
  animation: buffFloat 1.6s ease-in-out infinite;
}
@keyframes buffFloat {
  0%, 100% {
    box-shadow:
      0 0 0 1.5px rgba(244, 235, 255, 0.95),
      0 0 0 3px rgba(176, 130, 255, 0.72),
      0 0 9px 2px rgba(160, 110, 250, 0.42);
  }
  50% {
    box-shadow:
      0 0 0 2px rgba(252, 248, 255, 1),
      0 0 0 4.5px rgba(196, 158, 255, 0.82),
      0 0 14px 4px rgba(172, 122, 252, 0.58);
  }
}
@keyframes stFramePulse { 0%, 100% { opacity: 0.9; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .card .status-frame { animation: none; opacity: 1; }
  /* the protection/buff FLOAT rings live on the card itself (box-shadow animation) — stopping the animation
     drops the ring entirely (the box-shadow is keyframe-only), so re-supply it as a STATIC ring here. */
  .card.st-on-good {
    animation: none;
    box-shadow: 0 0 0 1.5px rgba(248, 255, 252, 0.96), 0 0 0 3px rgba(132, 250, 178, 0.72), 0 0 9px 2px rgba(120, 246, 160, 0.4);
  }
  .card.st-on-buff {
    animation: none;
    box-shadow: 0 0 0 1.5px rgba(244, 235, 255, 0.95), 0 0 0 3px rgba(176, 130, 255, 0.72), 0 0 9px 2px rgba(160, 110, 250, 0.42);
  }
}
/* ── "Status FX" off (Settings toggle) ────────────────────────────────────────────────────────────
   Some players prefer a clean board. body.no-status-fx strips the Hearthstone-style treatment — frame, big
   overlay symbol, art tint, and the floating glow rings — while KEEPING the compact corner badges so the
   information (frozen / locked / protected / pierce) is never lost. Affordance glows return to normal because
   the dim rules above are gated on body:not(.no-status-fx). */
body.no-status-fx .status-frame { display: none; }
body.no-status-fx .card.st-on-frozen .art,
body.no-status-fx .card.st-on-bad .art { filter: none; }
body.no-status-fx .card.st-on-good,
body.no-status-fx .card.st-on-buff { animation: none; }

/* Per-keyword card glows — bolder + all animated (breathing). */
.card.kw-blocker { --kw-card: 90, 160, 255; animation: kwBreathe 1.9s ease-in-out infinite; }
.card.kw-double { --kw-card: 255, 110, 80; animation: kwBreathe 1.6s ease-in-out infinite; }
.card.kw-banish { --kw-card: 185, 125, 255; animation: kwBreathe 2.1s ease-in-out infinite; }
.card.kw-rush { --kw-card: 255, 200, 70; animation: kwBreathe 1.3s ease-in-out infinite; }
@keyframes kwBreathe {
  0%, 100% { box-shadow: 0 0 0 1.5px rgba(var(--kw-card), 0.65), 0 0 10px 1px rgba(var(--kw-card), 0.4); }
  50% { box-shadow: 0 0 0 2px rgba(var(--kw-card), 0.95), 0 0 20px 4px rgba(var(--kw-card), 0.7); }
}

/* Hard taunt (action:attackRestriction cannotAttackOtherThan) — a bold pulsing red ring overlay. */
.kw-taunt-ring {
  position: absolute;
  inset: -3px;
  border-radius: inherit;
  pointer-events: none;
  z-index: 6;
  box-shadow: 0 0 0 2.5px var(--bot), 0 0 18px 3px rgba(220, 70, 48, 0.85);
  animation: kwTaunt 0.95s ease-in-out infinite;
}
@keyframes kwTaunt {
  0%, 100% { opacity: 0.5; box-shadow: 0 0 0 2px var(--bot), 0 0 12px 2px rgba(220, 70, 48, 0.6); }
  50% { opacity: 1; box-shadow: 0 0 0 3px var(--bot), 0 0 22px 5px rgba(220, 70, 48, 0.95); }
}

@media (prefers-reduced-motion: reduce) {
  .kw-badge, .card.kw-blocker, .card.kw-double, .card.kw-banish, .card.kw-rush, .kw-taunt-ring { animation: none; }
  .card.kw-blocker { box-shadow: 0 0 0 2px rgba(90, 160, 255, 0.8), 0 0 12px rgba(90, 160, 255, 0.5); }
  .card.kw-double { box-shadow: 0 0 0 2px rgba(255, 110, 80, 0.8), 0 0 12px rgba(255, 110, 80, 0.5); }
  .card.kw-banish { box-shadow: 0 0 0 2px rgba(185, 125, 255, 0.8), 0 0 12px rgba(185, 125, 255, 0.5); }
  .card.kw-rush { box-shadow: 0 0 0 2px rgba(255, 200, 70, 0.8), 0 0 12px rgba(255, 200, 70, 0.5); }
  .kw-taunt-ring { opacity: 0.95; }
}

/* ── Dedicated Stage zone (left of the leader; clears the leader-ability button on the right) ─────── */
/* B5 (Tyler backlog): the Stage floats absolutely to the LEFT of the leader card (mirror of the ability
   button on the right), so the leader card itself centers on the board/hand axis. Anchored to the
   position:relative .leader-seat; sits a small gap left of the card, vertically aligned with it. */
.stage-slot {
  position: absolute;
  left: auto;
  right: calc(100% + 8px);
  top: calc(var(--card-h) * 0.03);
  flex: 0 0 auto;
  width: var(--card-w);
  height: var(--card-h);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 46;
}
.stage-slot .stage-ph {
  width: 100%;
  height: 100%;
  border: 1px dashed var(--line-lit);
  border-radius: var(--r-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  opacity: 0.45;
  background: rgba(255, 255, 255, 0.015);
}

/* ── Mobile: rotate-to-landscape gate ─────────────────────────────────────────────────────────────
   The mat is landscape-first (it scales + fits cleanly at ~812×375; portrait crams a wide board into a
   narrow column). On a small PORTRAIT viewport we cover the board with a rotate prompt rather than ship a
   broken layout. CSS-only — no JS, flips automatically with orientation. Desktop/landscape never see it. */
.rotate-gate { display: none; }
/* body.portrait-ok = the user tapped "continue in portrait" — e.g. their phone has rotation-lock ON, so
   physically rotating never clears the gate (a common iOS trap). Then we DON'T gate: show the board even in
   portrait (cramped but usable beats being stuck on the rotate screen — first friend feedback 2026-06-30). */
@media (orientation: portrait) and (max-width: 820px) {
  body:not(.portrait-ok) .rotate-gate {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 9999;
    align-items: center;
    justify-content: center;
    padding: 32px;
    text-align: center;
    background: radial-gradient(circle at 50% 38%, #15110b, #0a0705 70%);
  }
  /* hide the (mis-laid-out) board behind the opaque gate so nothing peeks or scrolls */
  body:not(.portrait-ok) :is(.topbar, .arena, .arrow-layer, .fx-layer, .log, .disclaimer) { visibility: hidden; }
  /* portrait escape: the landscape board doesn't fit a narrow column, so shrink the cards AND let it pan
     (overflow:auto) rather than clip content off-screen with the arena's overflow:hidden. Cramped but every
     card is reachable — an escape hatch for rotation-locked phones, not the primary experience. */
  body.portrait-ok { --card-h: clamp(38px, 12vw, 68px); }
  body.portrait-ok .arena { overflow: auto; touch-action: pan-x pan-y; }
}
.rotate-gate-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  max-width: 340px;
}
.rotate-gate-icon {
  width: 84px;
  height: 84px;
  fill: none;
  stroke: var(--accent, #e0b94a);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  animation: rotateHint 2.4s ease-in-out infinite;
  transform-origin: 50% 50%;
}
.rotate-gate-icon .rg-arrow,
.rotate-gate-icon .rg-arrow-head { stroke: var(--you, #4caf76); }
@keyframes rotateHint {
  0%, 18% { transform: rotate(0deg); }
  46%, 64% { transform: rotate(-90deg); }
  92%, 100% { transform: rotate(0deg); }
}
.rotate-gate-title {
  font-family: var(--font-display, "Cinzel", serif);
  font-size: 22px;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--accent, #e0b94a);
}
.rotate-gate-sub {
  font-family: var(--font-body, "Inter", sans-serif);
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted, #b6ab97);
}
/* Escape hatch for phones with rotation-lock ON (rotating never clears the gate). Cramped but not stuck. */
.rotate-gate-skip {
  margin-top: 6px;
  padding: 9px 16px;
  border: 1px solid rgba(122, 94, 47, 0.7);
  border-radius: 999px;
  background: rgba(19, 17, 11, 0.9);
  color: var(--muted, #b6ab97);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.rotate-gate-skip:hover { border-color: var(--accent, #e0b94a); color: var(--ink, #f1e7d2); }
@media (prefers-reduced-motion: reduce) {
  .rotate-gate-icon { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════════════════════════════
   MOBILE LANDSCAPE  (perfect-the-mobile-port pass, 2026-06-29)
   Gate = short landscape OR coarse-pointer landscape. A landscape viewport ≤500px tall is a phone; the
   coarse arm covers real touch devices the height test might miss. Tall desktop landscape windows match
   NEITHER, so desktop is provably untouched. All rules below live under this gate (or pure coarse no-ops).
   ════════════════════════════════════════════════════════════════════════════════════════════════ */
/* desktop defaults: hamburger hidden; collapse-wrapper is layout-transparent so the flat topbar is unchanged */
.menu-toggle { display: none; }
.topbar-collapse { display: contents; }

@media (orientation: landscape) and (max-height: 500px),
       (pointer: coarse) and (orientation: landscape) {
  /* ── Topbar → floating hamburger; arena reclaims the full height ─────────────────────────────── */
  .topbar {
    position: fixed;
    inset: 0 0 auto 0;
    padding: 0;
    background: none;
    border: none;
    z-index: 60;
    pointer-events: none; /* board interaction passes through; re-enabled on the toggle + open sheet */
  }
  /* Mobile: the hamburger owns the top-right; tuck the contextual board chips top-LEFT (the event log is
     hidden on mobile, so that corner is free) and lay them out as a compact row. Real-phone pass pending. */
  .board-actions {
    top: max(6px, env(safe-area-inset-top));
    left: max(8px, env(safe-area-inset-left));
    right: auto;
    flex-direction: row;
    align-items: flex-start;
  }
  /* Mobile: surface the turn pill on-board, top-centre (the topbar status is hidden in the hamburger sheet).
     pointer-events:none so it never intercepts a card drag over the opponent's hand strip behind it. */
  .mobile-turn {
    display: flex;
    position: fixed;
    top: max(6px, env(safe-area-inset-top));
    left: 50%;
    transform: translateX(-50%);
    z-index: 47;
    pointer-events: none;
  }
  .mobile-turn .turn-pill {
    background: rgba(18, 14, 9, 0.92);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
  }
  .menu-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    pointer-events: auto;
    position: absolute;
    top: max(6px, env(safe-area-inset-top));
    right: max(8px, env(safe-area-inset-right));
    width: 40px;
    height: 40px;
    border-radius: 10px;
    border: 1px solid rgba(122, 94, 47, 0.55);
    background: rgba(20, 16, 10, 0.86);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    color: var(--accent, #e0b94a);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
  }
  .topbar-collapse { display: none; } /* collapsed until the hamburger opens it */
  .topbar.menu-open { pointer-events: auto; }
  .topbar.menu-open .topbar-collapse {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    position: absolute;
    top: 52px;
    right: max(8px, env(safe-area-inset-right));
    width: 210px;
    max-height: calc(100dvh - 64px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 10px;
    border-radius: 12px;
    background: rgba(18, 14, 9, 0.97);
    border: 1px solid rgba(122, 94, 47, 0.55);
    box-shadow: 0 10px 32px rgba(0, 0, 0, 0.6);
    pointer-events: auto;
  }
  .topbar.menu-open .topbar-collapse .brand {
    font-size: 14px;
    padding: 2px 2px 6px;
    border-bottom: 1px solid rgba(122, 94, 47, 0.3);
  }
  .topbar.menu-open .topbar-collapse #status {
    font-size: 11px;
    padding: 2px;
    opacity: 0.86;
    flex-wrap: wrap;
  }
  .topbar.menu-open .topbar-collapse .btn {
    height: 44px; /* 44px touch target inside the sheet */
    width: 100%;
    justify-content: flex-start;
    margin: 0;
  }
  #settings { margin-left: 0; } /* undo the desktop margin-left:auto in the vertical sheet */

  /* ── Fit the 9-band grid into the (now full-height) arena so the hand isn't clipped ───────────── */
  :root { --card-h: clamp(44px, (100dvh - 54px) / 8, 132px); } /* shrink instead of slamming the 50px floor */
  .arena {
    height: 100dvh; /* topbar is an overlay now → use the whole viewport */
    padding-left: max(10px, env(safe-area-inset-left));
    padding-right: max(10px, env(safe-area-inset-right));
  }
  .leader-seat { padding-bottom: calc(var(--card-h) * 0.18); } /* was 0.6 → reclaims ~2 leader-rows of height */
  .row.minions { padding: 1px 0; }
  .row.hand { padding-bottom: max(2px, env(safe-area-inset-bottom)); }
  .row.opp-hand { padding-top: 2px; }
  /* damp the toward-hand nudges so the bands don't ride into the (tight) hand zones */
  .leader-band.north { transform: translateY(calc(var(--card-h) * 0.12)); }
  .leader-band.south { transform: translateY(calc(var(--card-h) * 0.10)); }
  .leader-seat.south .hero-power { transform: translateY(calc(var(--card-h) * 0.12)); }
  .leader-seat.north .hero-power { transform: translateY(calc(var(--card-h) * -0.12)); }

  /* ── Touch targets ──────────────────────────────────────────────────────────────────────────── */
  /* rail DON!! cards were ~15×20 (untappable) — the only DON the player interacts with. Enlarge + add a
     transparent ~44px hit-halo so a thumb can grab the drag/arm without changing the visual much. */
  .rail .cost-area .don-card {
    width: calc(var(--card-h) * 0.42);
    height: calc(var(--card-h) * 0.54);
    min-width: 22px;
    min-height: 28px;
  }
  .rail .cost-area .don-card.draggable::after {
    content: "";
    position: absolute;
    inset: -9px;
    z-index: 1; /* expands the tappable region; transparent */
  }
  /* the character ability-gem fires an activated effect — give it a real hit area, and move the status
     badges off its bottom-right corner (their meaning is reachable via long-press zoom on mobile). */
  .ability-gem { width: 30px; height: 30px; }
  .ability-gem svg { width: 15px; height: 15px; }
  .ability-gem::after {
    content: "";
    position: absolute;
    inset: -8px;
    border-radius: 50%;
  }
  .card .status-badges {
    bottom: auto;
    top: 3px;
    left: 3px;
    right: auto;
    flex-direction: column;
  }
  /* expose more of each fanned hand card to the thumb (was -26px → ~12px sliver each) */
  .hand .card { margin-left: -18px; }
  .hand .card:first-child { margin-left: 0; }

  /* ── Prompt modals: scrollable + reachable actions (counter/reveal were clipping Confirm/Pass) ── */
  .modal {
    max-height: calc(100dvh - 20px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 12px 14px;
  }
  .modal h3 { font-size: 14px; margin-bottom: 2px; }
  .battle-ctx { padding: 7px 10px; margin: 3px 0 8px; }
  .reveal-card, .counter-card { width: 62px; height: 87px; } /* 5×62 + gaps fits one row at 812px wide */
  .reveal-row, .counter-row { gap: 8px; margin: 6px 0 10px; }
  .modal .actions {
    position: sticky;
    bottom: 0;
    padding-top: 8px;
    background: linear-gradient(transparent, var(--surface-2, #131c25) 24%);
  }
  /* Settings sheet + deck picker: scroll instead of overflowing the short viewport */
  .settings-menu-overlay { padding: 46px 10px 10px; }
  .settings-menu { max-height: calc(100dvh - 56px); overflow-y: auto; -webkit-overflow-scrolling: touch; gap: 7px; }
  .settings-group { gap: 4px; }
  .settings-action { height: 44px; }
  .deck-picker { max-height: calc(100dvh - 20px); overflow-y: auto; -webkit-overflow-scrolling: touch; }

  /* ── Edge UI under the notch / home-indicator (env() is 0 off-notch → max() keeps current values) ── */
  .endturn { right: max(26px, calc(env(safe-area-inset-right) + 12px)); }
  .disclaimer {
    bottom: max(2px, env(safe-area-inset-bottom));
    right: max(10px, env(safe-area-inset-right));
  }
  .toast { bottom: max(18px, calc(env(safe-area-inset-bottom) + 8px)); }
}

/* ════════════════════════════════════════════════════════════════════════════════════════════════
   HS-STYLE MOBILE REBUILD (2026-06-29) — Hearthstone-style BIG central board + compact corner HUD.
   Same landscape/coarse gate as the block above; these rules come LATER in source order so they win at
   equal specificity. Desktop matches NEITHER arm (tall fine-pointer window) → provably untouched.
   Root cause this fixes: the old 7.5-band stack floored --card-h at ~44px (unreadable/unthumbable). We
   cut to 5 bands (thin opp-hand strip · opp leader+chars · thin midline · your chars+leader · big hand)
   and move the 4 side rails OFF the grid into corner widgets, so play cards grow to ~70px (HS size).
   ════════════════════════════════════════════════════════════════════════════════════════════════ */
@media (orientation: landscape) and (max-height: 500px),
       (pointer: coarse) and (orientation: landscape) {
  /* HS-size cards: the board now stacks ~4.5 card-heights instead of 7.5 → far bigger play cards. */
  :root { --card-h: clamp(56px, (100dvh - 38px) / 4.5, 96px); }

  /* 9-row × 3-col grid → 5 single-column bands. Rails leave the grid (absolute corner HUD below) so the
     play area reclaims the full width; each player's LEADER + CHARACTERS merge into ONE centred flex row
     (.play-half), HS hero-beside-minions. North = [leader, chars]; south = [chars, leader] (180° mirror). */
  .arena {
    grid-template-columns: 1fr;
    grid-template-areas:
      "opphand"
      "north"
      "mid"
      "south"
      "hand";
    grid-template-rows: auto auto auto auto auto;
    padding: 2px 6px 0;
  }
  /* the wrapper becomes a real flex row on mobile (display:contents on desktop) → leader inline with chars */
  .play-half {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-height: var(--hero-h);
  }
  .play-half.north { grid-area: north; }
  .play-half.south { grid-area: south; }
  .play-half .row.minions { flex: 0 1 auto; min-height: 0; padding: 0; }
  .play-half .leader-band { flex: 0 0 auto; }

  /* the 1fr slack rows are gone → drop the toward-hand nudge transforms that compensated for them */
  .leader-band.north, .leader-band.south,
  .leader-seat.south .hero-power, .leader-seat.north .hero-power { transform: none; }
  .row.minions.you, .row.minions.bot { align-items: center; }
  .leader-band { align-items: center; justify-content: center; }
  /* Leader cluster → ONE flex row so the leader's satellites stop floating absolutely (which collided once
     the leader went inline with the chars). Order per seat: [dial][leader][STAGE], mirrored — the ability
     dial lands in the OUTER gutter, the leader in the middle, and a played Stage sits INNER (between leader
     and chars) where flexbox makes room. Empty Stage stays hidden (declutter); a real Stage now has a home. */
  .leader-seat { display: flex; align-items: center; gap: 4px; padding: 0; }
  .leader-seat.north { flex-direction: row-reverse; } /* north leader is at the row's LEFT → dial to far left */
  .leader-seat.south { flex-direction: row; }          /* south leader at the RIGHT → dial to far right */
  .stage-slot { position: static; }            /* in-flow beside the leader (was absolute, floating left) */
  .leader-ability-slot { position: static; }   /* in-flow dial (was absolute) */
  .leader-seat .hero-power { position: relative; } /* override the base `position:absolute` → flow in its slot */
  /* shrink the dial via the size vars (not just w/h) so its glow shell shrinks too */
  .hero-power { --hp-size: 42px; --hp-shell-size: 54px; --hp-inner-inset: 10px; width: 42px; height: 42px; }
  .hero-power .hp-glyph { width: 19px; height: 19px; }

  /* opp hand → thin overlapping card-back strip (it was eating ~a full card-height) */
  .row.opp-hand { height: calc(var(--card-h) * 0.5); padding-top: 2px; }
  .opp-hand .card.back {
    height: calc(var(--card-h) * 0.46);
    width: calc(var(--card-h) * 0.33);
    margin-left: -26px;
  }

  /* declutter the leader's absolute floats: hide the empty Stage placeholder (show only a real Stage) */
  .stage-slot:not(.filled) { display: none; }

  /* ── Corner HUD: the 4 rails leave the grid and pin to the corners as compact widgets ─────────── */
  .rail {
    position: absolute;
    flex-direction: row;
    align-items: center;
    gap: 5px;
    z-index: 30;
    pointer-events: none; /* board shows through the gutters; interactive children re-enable below */
  }
  .rail > * { pointer-events: auto; }
  /* Life is shown in the central midline health bar → drop the rail's face-down life CARD STACK. */
  .rail .life-col { display: none; }
  /* shrink the library piles + DON!! deck into small corner stacks; drop the verbose corner labels */
  .deck-zone .pile, .pile { width: calc(var(--card-h) * 0.42); height: calc(var(--card-h) * 0.59); }
  .dd-stack { width: calc(var(--card-h) * 0.42); height: calc(var(--card-h) * 0.59); }
  .pile-lbl, .dd-lbl span, .cost-lbl .ca-tag { display: none; }
  /* library = deck BESIDE trash (was a tall vertical stack that crowded the trash + reached the midline),
     with a clear gap between the two piles. */
  .rail-lib, .rail.north .rail-lib { flex-direction: row; gap: 10px; align-items: flex-start; }

  /* corner placement (180° mirror): you bottom, Ace top. DON clusters put the deck pile at the screen EDGE
     with the interactive cost area INNER (near the leader) so the deck isn't crowding the play area. */
  .rail.life-lib.south { left: max(6px, env(safe-area-inset-left)); bottom: calc(var(--hand-h) + 8px); }
  .rail.don.south {
    right: max(6px, env(safe-area-inset-right));
    bottom: calc(var(--hand-h) + 34px); /* lift clear of the END TURN dial below */
    flex-direction: row; /* cost area inner (toward leader), DON deck to the right edge */
  }
  /* Ace library dropped BELOW the floating hamburger (40px tall @ top:6) so they stop overlapping */
  .rail.life-lib.north { right: max(6px, env(safe-area-inset-right)); top: 60px; }
  .rail.don.north {
    left: max(6px, env(safe-area-inset-left));
    /* clear the floating .board-actions chip band (top:6px, ~30px tall) that shares this top-left corner */
    top: max(44px, calc(var(--card-h) * 0.5 + 6px));
    flex-direction: row-reverse; /* DON deck to the left edge, cost area inner (toward leader) */
  }

  /* ── DON!! counter (HS-mana style) ──────────────────────────────────────────────────────────────
     A compact available/total readout sits over a tight cluster of DON pips, near your leader. Primary
     attach path on touch = TAP a DON to arm it, then tap a character (donArmed, app.js:1534) — no tiny
     drag needed (drag still works). The pips stay tappable via the existing ~40px hit-halo. (A literal
     collapse→expand toggle is deferred; the always-visible compact cluster already serves the intent.) */
  .rail.don .cost-area { align-items: center; gap: 1px; }
  .cost-lbl { font-size: 10px; gap: 2px; line-height: 1; }
  .cost-lbl .ca-active { font-size: 16px; line-height: 1; } /* available DON — the number that matters */
  .cost-lbl .ca-sep, .cost-lbl span { opacity: 0.8; }

  /* ── Hand: thin the overlap so more of each (now larger) card reads; lift-on-touch unchanged. */
  .hand .card { margin-left: -12px; }
  .hand .card:first-child { margin-left: 0; }

  /* the event log adds clutter on a phone (the board + toasts already convey state) → hide it on mobile. */
  .log { display: none; }
}

/* Pure-touch no-ops (kept out of the layout gate so they apply on any touch device): kill the grey iOS
   tap-flash and the press-and-hold "Save Image" callout that hijacks a card-art drag. Inert on desktop. */
@media (pointer: coarse) {
  * { -webkit-tap-highlight-color: transparent; }
  .card, .card .art, .don-card, .don-art, .arena img {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }
}

/* Touch only: the card-inspect zoom (hover on desktop, long-press on touch) centers as a readable overlay
   instead of a side panel, and becomes tappable+scrollable so the next tap dismisses it. Gated COARSE-ONLY
   to match the JS path (coarsePointer = matchMedia('(pointer: coarse)') in app.js) — a fine-pointer short
   landscape desktop window must keep the side-panel zoom, not snap the hover preview to centre. */
@media (pointer: coarse) {
  .zoom {
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%, -50%) scale(0.96);
    width: min(86vw, 360px);
    max-height: calc(100dvh - 16px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    pointer-events: auto;
  }
  .zoom.show { transform: translate(-50%, -50%) scale(1); }
  .zoom-art { max-height: 46dvh; object-fit: contain; }
}

/* Mobile GPUs: the always-on box-shadow / filter-blur animations aren't compositor-accelerated, so N
   concurrent keyword/status cards = per-frame repaints that jank the 60fps board during the very drags that
   need smoothness. Freeze them to their static styling on mobile (the badges/frames still convey each state).
   Hints are deliberately KEPT animated — they're low-count and the pulse is load-bearing UX. */
@media (orientation: landscape) and (max-height: 500px),
       (pointer: coarse) and (orientation: landscape) {
  .card.kw-blocker, .card.kw-double, .card.kw-banish, .card.kw-rush,
  .card.st-on-good, .card.st-on-buff, .card.st-on-frozen .status-frame,
  .kw-taunt-ring, .kw-badge, .card .don-badge, .ability-gem,
  .hero-power.ready .hp-glyph, .endturn.ready .compass {
    animation: none !important;
  }
  /* the keyword/protection/buff glow rings are KEYFRAME-ONLY box-shadows, so freezing the animation above
     would drop the ring entirely. Re-supply each as a STATIC ring (values mirror the prefers-reduced-motion
     fallbacks at styles.css ~3816-3820 / ~3769-3776) so mobile keeps the state cue without the per-frame jank. */
  .card.kw-blocker { box-shadow: 0 0 0 2px rgba(90, 160, 255, 0.8), 0 0 12px rgba(90, 160, 255, 0.5); }
  .card.kw-double  { box-shadow: 0 0 0 2px rgba(255, 110, 80, 0.8), 0 0 12px rgba(255, 110, 80, 0.5); }
  .card.kw-banish  { box-shadow: 0 0 0 2px rgba(185, 125, 255, 0.8), 0 0 12px rgba(185, 125, 255, 0.5); }
  .card.kw-rush    { box-shadow: 0 0 0 2px rgba(255, 200, 70, 0.8), 0 0 12px rgba(255, 200, 70, 0.5); }
  .card.st-on-good { box-shadow: 0 0 0 1.5px rgba(248, 255, 252, 0.96), 0 0 0 3px rgba(132, 250, 178, 0.72), 0 0 9px 2px rgba(120, 246, 160, 0.4); }
  .card.st-on-buff { box-shadow: 0 0 0 1.5px rgba(244, 235, 255, 0.95), 0 0 0 3px rgba(176, 130, 255, 0.72), 0 0 9px 2px rgba(160, 110, 250, 0.42); }
  .card.st-on-frozen .status-frame { opacity: 1; }
  .kw-taunt-ring { opacity: 0.95; }
  .card.dragging { will-change: transform; } /* only the actively-dragged card gets a GPU layer */
}
