/* ============================================================
   RIFT TABLE — local dev client.
   Single dark "felt tabletop" theme, deliberately independent of
   the viewer's OS light/dark setting (see design/BOARD_LAYOUT.md).
   Cards render the real official card image (served locally by
   src/server, cached in data/images/) -- the whole printed face
   already carries name/cost/might/text, so the card element itself
   is just a well-framed <img>.

   Sizing model: cards have no width/height of their own -- just an
   `aspect-ratio`, filling 100% of whatever real space their
   container ends up with. .app is height:100vh, flex-column, with
   .board (flex-grow) as its main content; the two hand bars float
   over it (position:fixed, centered, decks at their corners) at a
   fixed height, and .app reserves exactly that same height as
   padding (both read the same --hand-bar-h/--oppo-bar-h variable,
   so the reservation can't drift out of sync with the bars' actual
   size). Every remaining zone inside .board fills its flexed share
   via aspect-ratio the same way, so nothing ever needs to scroll.
   ============================================================ */
:root{
  /* --- mat (deep navy playmat; property names kept from the felt version) --- */
  --felt-900:#050b14;
  --felt-800:#0a1626;
  --felt-700:#0f2036;
  --felt-650:#132745;
  --line: rgba(201,167,92,.42);      /* gold hairline */
  --line-soft: rgba(201,167,92,.22);

  /* --- card stock (used for empty slots / rune chips) --- */
  --parchment:#0c1d33;
  --parchment-edge: rgba(201,167,92,.7);
  --parchment-dim:#0a1729;
  --ink:#ecdfbd;
  --ink-soft:#b39a63;

  /* --- gold (mat linework + labels) --- */
  --brass:#c9a75c;
  --brass-glow:#efd9a0;
  --brass-dim:#8f7440;

  /* --- text on the mat --- */
  --text-hi:#ecdfbd;
  --text-lo:#b39a63;
  --text-faint:#6b7a90;

  /* --- domain colors (r.220 Domain) --- */
  --dom-fury:#d5544a;
  --dom-calm:#59ab67;
  --dom-mind:#5490e0;
  --dom-body:#da8a3c;
  --dom-chaos:#9c63d8;
  --dom-order:#c3aa3f;

  /* --- player identity chips --- */
  --p0:#7fb9c9;
  --p1:#c98a6f;

  /* --- state semantics --- */
  --state-contested:#d9a53a;
  --state-controlled:#6fae7a;
  /* Under-glows for cards being TARGETED: soft light beneath the card, no hard ring. Red = the opponent's, green = your own. */
  --glow-own: 0 10px 22px -2px rgba(111,214,130,.85), 0 0 18px 2px rgba(111,214,130,.35);
  --glow-own-hot: 0 12px 28px 0 rgba(140,240,160,.95), 0 0 26px 6px rgba(111,214,130,.5);
  --glow-enemy: 0 10px 22px -2px rgba(228,72,62,.85), 0 0 18px 2px rgba(228,72,62,.35);
  --glow-enemy-hot: 0 12px 28px 0 rgba(255,96,84,.95), 0 0 26px 6px rgba(228,72,62,.5);

  --radius-card:9px;
  --radius-slot:10px;
  --radius-panel:14px;

  --font-display:'Cinzel', 'Iowan Old Style', serif;
  --font-body:'Manrope', 'Segoe UI', system-ui, sans-serif;
  --font-mono:'IBM Plex Mono', 'SF Mono', monospace;

  /* Real card aspect ratios -- the only "size" a card has. Everything else
     about its rendered size comes from its container's flexed height. */
  --ratio-portrait: 0.716;  /* 744:1039 -- units/spells/gear/legends/runes */
  --ratio-landscape: 1.395; /* 1038:744 -- battlefields only */

  /* The two floating hand bars are the one place with a fixed pixel size
     instead of a flex share, since they float over the board rather than
     taking a row in it (see .hand-bar). Each is referenced from exactly
     two places -- .app's padding reservation and the bar's own height --
     both reading this same variable, so (unlike the vh-clamp version) they
     cannot independently drift out of sync with each other. */
  --hand-bar-h: 196px;
  --oppo-bar-h: 150px; /* tall enough for a deck zone (card + label + count) plus the phase strip's 30px */

  /* "Standard card size" for reference zones (Legend, Champion, Trash,
     Banishment, deck stacks): a fixed height, width from aspect-ratio.
     These are slots you glance at, not the focus of a row, so they never
     stretch to fill it. Sized so a zone (card + label + padding) fits the
     cluster area of a row on a ~900px-tall window; see the max-height
     media query at the bottom for shorter windows. */
  --card-std-h: 88px;
  --card-std-h-sm: 60px; /* opponent's (shorter) top bar */
  --card-deck-h: 126px; /* Main Deck / Rune Deck: bigger than the standard reference size */
  --card-deck-h-sm: 66px;
}

*{ box-sizing:border-box; }
html{ color-scheme: dark; height:100%; overflow:hidden; }
body{
  margin:0;
  height:100%;
  overflow:hidden;
  /* A game table, not a document: dragging the cursor across it must never
     select text or start copying card images. (Text inputs, if any, opt back in.) */
  user-select:none;
  -webkit-user-select:none;
  background: radial-gradient(120% 120% at 50% 42%, #0e2038 0%, #08131f 58%, #050b14 100%);
  color: var(--text-hi);
  font-family: var(--font-body);
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3{ text-wrap:balance; margin:0; }
button{ font-family:inherit; }

/* No header: the whole viewport is the board, and the page can never
   scroll (html/body above are overflow:hidden). .app is the single flex
   column that owns 100vh; every child below gets a flex-grow share of it,
   so the reserved space for e.g. the hand bars is never a guess -- it's
   however much flexbox actually gives that child. */
.app{
  height:calc(100vh - 12px);
  margin:6px;
  border:1px solid var(--line);
  border-radius:14px;
  box-shadow: inset 0 0 50px rgba(0,0,0,.45), 0 0 0 1px rgba(201,167,92,.08);
  display:flex;
  flex-direction:column;
  padding: 8px 16px;
  padding-top: calc(var(--oppo-bar-h) + 6px);
  padding-bottom: calc(var(--hand-bar-h) + 6px);
  gap:6px;
}

/* ---------------- settings tab: a small gear next to the Rune Deck ----------------
   New Game / Simulate used to float as two permanent buttons over the
   board; now they're tucked behind one small tab so they don't compete
   visually with the table itself. */
.settings-tab-wrap{ position:relative; margin-left:6px; flex:0 0 auto; }
.settings-tab{
  appearance:none;
  width:26px; height:26px;
  border:1px solid var(--brass-dim);
  background: linear-gradient(180deg, #132845, #0b1a2e);
  color: var(--brass-glow);
  font-size:14px;
  line-height:1;
  border-radius:50%;
  cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  transition: border-color .15s, box-shadow .15s, transform .05s;
}
.settings-tab:hover{ border-color:var(--brass); box-shadow:0 0 0 3px rgba(201,162,75,.14); }
.settings-tab:active{ transform: translateY(1px); }
.settings-tab:focus-visible{ outline:2px solid var(--brass-glow); outline-offset:2px; }

.settings-menu{
  position:absolute;
  left:0; bottom:calc(100% + 8px);
  z-index:30;
  display:none;
  flex-direction:column;
  gap:4px;
  min-width:170px;
  padding:6px;
  border:1px solid var(--brass-dim);
  border-radius:8px;
  background: linear-gradient(180deg, #132845, #0b1a2e);
  box-shadow: 0 8px 22px rgba(0,0,0,.45);
}
.settings-menu.is-open{ display:flex; }
.settings-menu__item{
  appearance:none;
  border:none;
  background:transparent;
  color: var(--brass-glow);
  font-family:var(--font-display);
  font-weight:600;
  font-size:11px;
  letter-spacing:.05em;
  text-transform:uppercase;
  text-align:left;
  padding:7px 9px;
  border-radius:5px;
  cursor:pointer;
}
.settings-menu__item:hover{ background: rgba(201,167,92,.14); }
.settings-menu__item:focus-visible{ outline:2px solid var(--brass-glow); outline-offset:-2px; }
.settings-menu__item:disabled{ color:var(--text-faint); cursor:not-allowed; }
.settings-menu__item:disabled:hover{ background:transparent; }
.settings-menu__item--toggle{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.settings-menu__item-state{
  font-size:10px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--text-faint); border:1px solid var(--line-soft); border-radius:999px;
  padding:1px 7px;
}
.settings-menu__item--toggle.is-active{ color:var(--brass-glow); }
.settings-menu__item--toggle.is-active .settings-menu__item-state{ color:#0b1a2e; background:var(--brass-glow); border-color:var(--brass); }

/* ---------------- board scaffold ---------------- */
/* .board holds the three "playing field" sections: opponent row,
   battlefield strip, player row. It gets the largest single share of
   .app's height (see .app > * flex-grow assignments below). */
.board{ display:flex; flex-direction:column; gap:8px; flex:1 1 auto; min-height:0; }

.row{
  display:flex;
  flex-direction:column;
  gap:4px;
  border:1px solid var(--line-soft);
  border-radius: 8px;
  background: rgba(8,20,36,.35);
  padding:6px;
  flex:1 1 0;
  min-height:0;
}
.row--opponent{ filter: none; }
/* The player without the current decision reads as visually inert at a
   glance -- nothing in their row ever gets wired with move/activate
   handlers anyway, this just dims it slightly so the active side stands out. */
.row:not(.row--active){ opacity:.82; }

.nameplate{ flex:0 0 auto; display:flex; align-items:center; gap:10px; padding: 1px 4px; }
.nameplate__chip{ width:9px; height:9px; border-radius:50%; flex:0 0 auto; box-shadow: 0 0 8px currentColor; }
.nameplate--p0 .nameplate__chip{ background:var(--p0); color:var(--p0); }
.nameplate--p1 .nameplate__chip{ background:var(--p1); color:var(--p1); }

/* PTS/XP moved next to the Rune Deck (see renderHandBar in app.js) instead
   of the row's nameplate -- both live in the same corner as the player's
   other at-a-glance numbers (deck counts). */
.hand-stats{ display:flex; flex-direction:column; gap:7px; margin-left:8px; align-items:flex-start; }
.hand-stats .settings-tab-wrap{ margin-left:0; margin-top:2px; }
/* Points: numbered circles, lit as they're earned (see pointsTrack). */
.pts-track{ display:flex; flex-direction:column; gap:4px; }
.pts-track__row{ display:flex; gap:4px; }
.pts-track__pip{
  width:24px; height:24px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-mono); font-size:12.5px; line-height:1;
  color:var(--text-faint); border:1px solid var(--line);
  background: rgba(9,22,39,.7);
  transition: background .3s, color .3s, border-color .3s, box-shadow .3s;
}
.pts-track__pip.is-lit{
  color:#0b1a2e; font-weight:700;
  background: var(--brass-glow); border-color: var(--brass);
  box-shadow: 0 0 8px rgba(239,217,160,.6);
}
/* Just scored: a few bright pulses as it lights. */
.pts-track__pip.is-new{ animation: pip-score 2.6s ease-out; }
@keyframes pip-score{
  0%   { transform:scale(1);    box-shadow:0 0 8px rgba(239,217,160,.6); }
  12%  { transform:scale(1.35); box-shadow:0 0 18px 6px rgba(239,217,160,.95), 0 0 34px 12px rgba(239,217,160,.45); }
  30%  { transform:scale(1.05); box-shadow:0 0 10px 2px rgba(239,217,160,.7); }
  45%  { transform:scale(1.25); box-shadow:0 0 16px 5px rgba(239,217,160,.9), 0 0 30px 10px rgba(239,217,160,.4); }
  65%  { transform:scale(1.02); box-shadow:0 0 10px 2px rgba(239,217,160,.7); }
  100% { transform:scale(1);    box-shadow:0 0 8px rgba(239,217,160,.6); }
}
@media (prefers-reduced-motion: reduce){ .pts-track__pip.is-new{ animation:none; } }
.hand-bar--top .pts-track__pip{ width:20px; height:20px; font-size:11px; }
/* XP: a green bar filling in segments (see xpBar). */
.xp-bar{ display:flex; align-items:center; gap:6px; font-family:var(--font-mono); font-size:12px; color:var(--text-faint); }
.xp-bar__label{ letter-spacing:.06em; }
.xp-bar__segments{ display:flex; gap:3px; }
.xp-bar__seg{ width:10px; height:11px; border-radius:2px; background: rgba(89,171,103,.15); border:1px solid rgba(89,171,103,.3); transition: background .3s, box-shadow .3s; }
.xp-bar__seg.is-lit{ background: var(--dom-calm, #59ab67); border-color:#8fe0a0; box-shadow: 0 0 6px rgba(89,171,103,.7); }
.xp-bar__count{ color:#8fe0a0; font-weight:600; min-width:1.2em; }

/* The three clusters stretch to fill the row's full height (align-items is
   flex's default "stretch"), and each cluster's own rules below make its
   content fill that stretched box in turn -- no overflow, ever, because
   nothing in this chain has a size independent of what flex actually gives
   it. */
.clusters{ display:flex; gap:8px; flex:1 1 auto; min-height:0; }

.zone{
  height:100%;
  position:relative;
  border:1px solid var(--line);
  border-radius: 5px;
  background: rgba(9,22,39,.55);
  padding:6px;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:4px;
  min-width: 0;
  min-height:0;
}
.zone__label{ flex:0 0 auto; font-family:var(--font-display); font-size:10px; letter-spacing:.16em; color:var(--brass); text-transform:uppercase; white-space:nowrap; }
.zone__count{ flex:0 0 auto; font-family:var(--font-mono); font-size:12px; color:var(--brass-glow); font-variant-numeric: tabular-nums; }

/* Legend/Champion are reference zones you glance at, not the focus of the
   row -- standard, fixed card size, never stretched to the row's height.
   The zone box wraps that fixed card (plus its label) at content size. */
.cluster--side{ flex:0 0 auto; height:100%; display:flex; align-items:center; gap:8px; }
.cluster--side .zone{ flex:0 0 auto; width:auto; height:auto; }

/* Runes-in-play / Units & Gear: fills the rest of the row's width. No
   border, no background, no padding-box wrapping the pair -- just a single
   hairline divider between the two halves (same idea as .bf-half), so the
   zone doesn't cost any of the row's space beyond its own content. */
.cluster--runeunit{ flex:1 1 auto; height:100%; display:flex; min-width:0; border-left:1px solid var(--line-soft); padding-left:12px; }
.ru-half{ flex:1 1 0; height:100%; min-width:0; display:flex; align-items:center; justify-content:center; gap:8px; padding:0 12px; border-radius:5px; transition: background .1s, outline-color .1s; }
.ru-half:first-child{ border-right:1px solid var(--line-soft); }
/* A row of board cards that never overflows: each card sits in a slot whose
   natural width is the card's own (portrait, or square when the card is
   tapped). Slots may shrink to 0 but the card inside keeps its natural size
   and overflows to the right, so as the row gets crowded the cards overlap
   progressively like a fanned hand; the last slot never shrinks, so the
   final card is always fully visible. */
.ru-row{ display:flex; align-items:center; height:100%; min-width:0; max-width:100%; }
.ru-slot{ position:relative; height:100%; aspect-ratio: var(--ratio-portrait); flex:0 1 auto; min-width:0; }
.ru-slot--square{ aspect-ratio:1; }
.ru-slot:last-child{ flex-shrink:0; }
.ru-slot:not(:first-child){ margin-left:6px; }
.ru-slot > *{ position:absolute; left:0; top:0; height:100%; }

/* A legal Move destination while hovering/selecting a movable Unit -- see
   wireMovable/showMovePreview in app.js. Shared by both the Base half
   (.ru-half) and a Battlefield half (.bf-half). */
.ru-half.is-legal-destination, .bf-half.is-legal-destination{
  outline: 2px dashed var(--brass-glow);
  outline-offset: -4px;
  background: rgba(239,217,160,.08);
  cursor:pointer;
}

.base__empty{ font-size:12px; color:var(--text-faint); font-style:italic; }

/* ---------------- card face: real art, sized purely by its container ---------------- */
.card{
  position:relative;
  height:100%;
  width:auto;
  aspect-ratio: var(--ratio-portrait);
  flex:0 0 auto;
  border-radius: var(--radius-card);
  overflow:hidden;
  background: linear-gradient(160deg, #10233d, #0a1729);
  border:1px solid rgba(201,167,92,.65);
  box-shadow: 0 1px 0 rgba(0,0,0,.4), 0 3px 10px rgba(0,0,0,.45);
  cursor:default;
  transition: transform .12s ease, box-shadow .12s ease;
}
/* A card sitting in a .zone (Legend, Champion, Trash, Banishment, the deck
   stacks) is a reference slot: one fixed standard size, centered, never
   stretched to the zone's height. This is the one place a card has a real
   pixel size -- everywhere else it's 100% of a flexed row. */
.zone > .card, .zone > .card-stack{ flex:none; height:var(--card-std-h); width:auto; align-self:center; }
.hand-bar--top .zone > .card-stack{ height:var(--card-std-h-sm); }
/* Main Deck / Rune Deck read bigger than the other reference zones --
   they're the two piles a player actually reaches for every turn. */
.zone--deck > .card-stack{ height:var(--card-deck-h); }
.hand-bar--top .zone--deck > .card-stack{ height:var(--card-deck-h-sm); }
/* Drawn/channeled cards: parked over their deck (inline transform, see
   animateDraws) then released into hand / the rune row one after another.
   Applies to hand cards and to .ru-slot wrappers alike. */
.is-drawing{
  visibility:hidden; /* until animateDraws releases it */
  z-index:5; pointer-events:none;
  box-shadow: 0 14px 30px rgba(0,0,0,.6);
}
.is-drawn{
  transition: transform .46s cubic-bezier(.2,.85,.25,1), box-shadow .46s ease;
  z-index:5;
}
/* A card sliding between zones (see animateMoves): parked over its old
   spot with an inline transform, then released to where it now lives. */
.is-moving, .is-quick{ z-index:6; pointer-events:none; }
.is-moving, .is-moving .card{ transition: transform .5s cubic-bezier(.2,.85,.25,1); }
/* A small settle -- tapping/readying in place, or a neighbor merely
   compacting a few pixels -- is a quick flick, not a journey. */
.is-quick, .is-quick .card{ transition: transform .18s ease-out; }
.card.is-interactive{ cursor:pointer; }
.card.is-interactive:hover{ transform: translateY(-4%); box-shadow: 0 10px 22px rgba(0,0,0,.45); }

/* ---- live interactivity: movable Units, activatable Runes ----
   A glow reads as "this is clickable" at a glance; hovering (or selecting,
   by click) a movable card highlights its legal destinations and draws an
   arrow to each via the fixed #arrowLayer overlay -- see wireMovable /
   drawArrow in app.js. */
.card.is-movable{ box-shadow: 0 0 0 2px var(--brass-glow), 0 0 14px rgba(239,217,160,.55), 0 1px 0 rgba(0,0,0,.4), 0 3px 10px rgba(0,0,0,.45); }
.card.is-movable:hover{ transform: translateY(-4%) scale(1.03); }
.card.is-selected{ box-shadow: 0 0 0 3px var(--brass-glow), 0 0 20px rgba(239,217,160,.85) !important; }
.card.is-activatable{ box-shadow: 0 0 0 2px var(--state-controlled), 0 0 12px rgba(111,174,122,.5); cursor:pointer; }
/* A playable spell rings blue (runes, gear and abilities stay green). */
.card.is-activatable--spell{ box-shadow: 0 0 0 2px #5aa9e6, 0 0 14px rgba(90,169,230,.6); }
.card.is-activatable--spell.is-selected{ box-shadow: 0 0 0 3px #8cc8ff, 0 0 22px rgba(120,190,255,.9) !important; }
.rune-recycle-btn{
  position:absolute; right:2px; bottom:2px; z-index:4;
  appearance:none; width:26px; height:26px; border-radius:50%;
  border:1.5px solid var(--state-controlled); background: rgba(9,22,39,.92); color:var(--state-controlled);
  font-size:14px; line-height:1; display:flex; align-items:center; justify-content:center; cursor:pointer;
  transform-origin: bottom right;
  transition: transform .12s ease, box-shadow .12s ease, background .12s ease;
}
/* Pop out on hover as a "you're about to click this" confirmation, rather
   than reacting only after the click. Scaling from the bottom-right corner
   keeps it anchored to the card instead of drifting over the art. */
.rune-recycle-btn:hover{
  transform: scale(1.45);
  z-index:6;
  background: rgba(9,22,39,1);
  box-shadow: 0 0 0 3px rgba(111,174,122,.45), 0 4px 10px rgba(0,0,0,.5);
}
/* Battlefields print landscape (real card ratio 1038:744, vs. 744:1039 for
   every other card type) -- without this, a landscape image inside a
   portrait-ratio box gets its sides cropped by object-fit:cover. */
.card--landscape{ aspect-ratio: var(--ratio-landscape); }
/* The art is taken OUT of flow on purpose. An in-flow <img> contributes its
   natural 744px width to every ancestor's intrinsic (max-content) size,
   which is what made Legend/Champion zones balloon sideways into cropped
   banners and made the hand's grid column size itself to thousands of px
   of "content" instead of centering. Absolutely positioned, the image just
   paints inside whatever box aspect-ratio + the container already decided. */
.card__art{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; background:var(--felt-700); }

/* Face-down card back: a full-bleed <img class="card__art">, same as a
   face-up card's art (see facedownEl) -- two originals, not a reproduction
   of any printed back. */
.card--facedown{
  /* The back art carries its own gold frame; no extra ring on top of it. */
  border:1px solid rgba(201,167,92,.35);
  box-shadow: 0 3px 10px rgba(0,0,0,.45);
}

/* Live Might badge on every Unit on the board (see mightBadge in app.js). */
.might-badge{
  position:absolute; right:3px; bottom:3px; z-index:3;
  display:flex; flex-direction:column; align-items:center; gap:1px;
  min-width:22px; padding:2px 5px;
  border-radius:7px;
  background: rgba(8,20,36,.9);
  border:1px solid var(--brass);
  color:var(--brass-glow);
  font-family:var(--font-display); font-weight:700; font-size:13px; line-height:1;
  pointer-events:none;
  box-shadow: 0 2px 6px rgba(0,0,0,.5);
}
.might-badge.is-up{ border-color:#5fd38a; color:#8ff0b0; }
.might-badge.is-down{ border-color:#e0674f; color:#ff9a86; }
.might-badge__damage{ font-size:9px; letter-spacing:.02em; color:#ff8f7a; }
.card-tapped > .might-badge{ right:4px; bottom:4px; }

/* Exhausted permanents lie sideways -- a physical card turned 90 degrees
   ("tapped"). The wrapper is square: its height is the row height, so its
   width equals the card's height, which is exactly the rotated card's
   footprint; the card inside is rotated about its own center. Selectors
   that place board cards (the battlefield fan's overlap margins above)
   accept either .card or .card-tapped as the flex child. */
.card-tapped{ position:relative; height:100%; aspect-ratio:1; flex:0 0 auto; }
.card-tapped > .card{ position:absolute; top:50%; left:50%; height:100%; width:auto; transform: translate(-50%,-50%) rotate(90deg); }
.card-tapped > .card.is-interactive:hover{ transform: translate(-50%,-50%) rotate(90deg) scale(1.03); }
/* An overlay button (the rune ♻) is appended to .card-tapped itself, NOT
   the rotated .card inside it -- a corner-anchored child of a rotated,
   overflow:hidden box ends up almost entirely clipped after the rotation
   (see wireRune's comment in app.js). .card-tapped is the square wrapper
   the rotated card sits centered in, letterboxed top/bottom by (1 -
   ratio)/2 of its height; offsetting the button's bottom inset by that
   same amount lands it on the visible card's actual corner instead of in
   that empty letterbox margin. */
.card-tapped > .rune-recycle-btn{ bottom: calc((1 - var(--ratio-portrait)) * 50% + 2px); }

.card-stack{ position:relative; height:100%; width:auto; aspect-ratio: var(--ratio-portrait); flex:0 0 auto; }
.card-stack .card{ position:absolute; inset:0; height:auto; width:auto; }
.card-stack .card:nth-child(1){ transform: translate(3px,3px); }
.card-stack .card:nth-child(2){ transform: translate(0,0); }

/* ---------------- battlefield strip ---------------- */
.battlefields{
  display:flex;
  flex-direction:column;
  gap:9px;
  padding:14px;
  position:relative;
  border-radius: 6px;
  border:1px solid var(--line);
  background:
    radial-gradient(70% 120% at 50% 50%, rgba(201,167,92,.07), transparent 70%),
    rgba(9,22,39,.45);
  box-shadow: 0 0 0 1px rgba(0,0,0,.3), 0 0 30px rgba(201,167,92,.05) inset;
  flex:1.3 1 0;
  min-height:0;
}
/* One shared box, split down the middle by a single border instead of two
   separately-outlined boxes -- the battlefield card centered in each half,
   with each side's units fanned in from their edge, overlapping the card. */
.battlefields__grid{ display:grid; grid-template-columns: 1fr 1fr; gap:0; flex:1 1 auto; min-height:0; }
.bf-half{ position:relative; height:100%; min-height:0; min-width:0; display:flex; flex-direction:column; padding:8px 18px; }
.bf-half:first-child{ border-right:1px solid var(--line); }
.bf-half__pill{ position:absolute; top:6px; right:6px; z-index:3; }
.pill{ font-size:10px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; padding:3px 8px; border-radius:99px; white-space:nowrap; }
.pill--uncontrolled{ background:transparent; color:var(--text-lo); border:1px solid var(--line); }
.pill--contested{ background:rgba(217,165,58,.16); color:var(--state-contested); border:1px solid var(--state-contested); }
.pill--controlled-p0{ background:rgba(127,185,201,.14); color:var(--p0); border:1px solid var(--p0); }
.pill--controlled-p1{ background:rgba(201,138,111,.14); color:var(--p1); border:1px solid var(--p1); }

/* Event ticker: the last few notable happenings (combat, kills, conquers),
   absolutely positioned along the top edge of the battlefield strip so it
   costs the strip no height. Newest line is brightest. */
.ticker{ position:absolute; top:2px; left:50%; transform:translateX(-50%); z-index:4; display:flex; gap:14px; font-family:var(--font-mono); font-size:10.5px; color:var(--text-faint); white-space:nowrap; pointer-events:none; }
.ticker__line:last-child{ color:var(--brass-glow); }

.bf-half__main{ flex:1 1 auto; min-height:0; display:flex; align-items:center; justify-content:center; }
.bf-half__card{ flex:0 0 auto; height:100%; position:relative; z-index:1; }
/* Units fan in from their side's outer edge and overlap the battlefield
   card's edge (negative margin pulls the whole fan toward the center). */
.bf-units{ flex:1 1 0; min-width:0; height:100%; display:flex; align-items:center; }
/* Units sit in .ru-slot wrappers (see buildUnitFan) that overlap a little
   by default and shrink further as the fan gets crowded. */
.bf-units > .ru-slot:not(:first-child){ margin-left:-22px; }
.bf-units--p0{ justify-content:flex-end; margin-right:-16px; z-index:2; }
.bf-units--p1{ justify-content:flex-start; margin-left:-16px; z-index:2; }
.bf-units__empty{ font-size:10px; font-weight:700; letter-spacing:.04em; color:var(--text-faint); }
.bf-half__foot{ flex:0 0 auto; font-size:10.5px; color:var(--text-faint); padding-top:4px; }

/* ---------------- hand bars: floating over the board, centered ----------------
   Rune Deck sits bottom-left of the hand, Main Deck bottom-right (mirrored
   at the top for the opponent) -- within reach of the hand, like a real
   tabletop, rather than bundled into the side cluster with Legend/Champion.
   These float over the board rather than taking a row in it; .app reserves
   exactly their height via padding, reading the same --hand-bar-h /
   --oppo-bar-h variables these rules do, so the reservation can't drift out
   of sync with the bars' actual size (that mismatch was the previous bug). */
/* Columns: [Rune Deck, content-sized] [hand, ALL remaining width] [Main
   Deck, content-sized]. The middle track is minmax(0,1fr), never `auto`, so
   its width comes from the grid -- not from the hand's contents -- and the
   panel centers inside it. Decks sit at the far left/right edges. */
.hand-bar{
  position:fixed;
  left:0; right:0;
  z-index:20;
  display:grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items:stretch;
  column-gap:18px;
  padding: 0 20px;
}
.hand-bar--bottom{ bottom:0; height: var(--hand-bar-h); padding-bottom:6px; }
.hand-bar--top{ top:0; height: var(--oppo-bar-h); padding-top:30px; } /* room for the phase strip, which sits over this bar's top edge */

.hand-corner{ display:flex; align-items:center; min-height:0; }
.hand-corner .zone{ height:auto; }
/* Trash/Banishment sit next to Main Deck as a small stack, rather than a
   separate cluster in the row -- these three are all "cards that left your
   hand", so they read as one group by the deck you actually draw from. */
.deck-group{ display:flex; align-items:center; gap:8px; height:100%; }

.hand-panel{
  justify-self:center;
  width:100%;
  max-width:1100px;
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:6px;
  min-height:0;
  position:relative;
  background: rgba(8,20,36,.82);
  border:1px solid var(--line);
  border-radius:8px;
  padding:8px 16px;
  backdrop-filter: blur(3px);
  box-shadow: 0 -10px 30px rgba(0,0,0,.35);
}
.hand-bar--top .hand-panel{ max-width:700px; }
/* One non-wrapping row, centered; each card is 100% of the row's height with
   its width from aspect-ratio. */
.hand-panel__row{ display:flex; justify-content:center; align-items:stretch; gap:10px; flex-wrap:nowrap; flex:1 1 auto; min-height:0; width:100%; padding:2px 4px; }
/* Backs sit side by side with a hair of overlap; a deep overlap left only
   their gold edges showing, which read as a yellow smear. */
.hand-panel__row.is-facedown .card{ margin-right:-6px; will-change:transform; } /* one compositing path for every back, so they're filtered alike */
.hand-panel__row.is-facedown .card:last-child{ margin-right:0; }

@media (max-width: 900px){
  .hand-bar{ column-gap:8px; padding: 0 8px; }
}

/* ---------------- hover detail: real card text, rendered as text -----------------
   A raster card image only ever gets so legible at tooltip scale -- printed
   rules text stays small no matter how much the image is enlarged. So the
   image here is a recognizable preview, and the actual rules text is real,
   crisp HTML text next to it (from data/cards.json via /api/setup), which is
   legible at any size and doesn't depend on image resolution at all. This
   is a floating overlay, not a board zone, so unlike everything above it
   keeps a fixed pixel size on purpose. */
.detail{
  position:fixed;
  z-index:70; /* above the modals too, so a card in the Trash viewer can be previewed */
  pointer-events:none;
  display:flex;
  gap:12px;
  align-items:flex-start;
  max-width:min(760px, calc(100vw - 24px));
  background: #0b1a2d;
  border:1px solid var(--brass);
  border-radius:8px;
  padding:12px;
  box-shadow: 0 14px 34px rgba(0,0,0,.55);
  opacity:0; transition:opacity .1s;
  /* left/top are computed in JS as the box's literal, viewport-clamped
     position (see positionDetail in app.js) -- no centering transform here,
     since a fixed -50% offset is exactly what let the tooltip run off the
     left/right edge of the screen near the sides. */
}
.detail.is-visible{ opacity:1; }

/* ---------------- move-preview arrows + win banner: viewport-fixed overlays -----------------
   The arrow layer lives outside #board (whose innerHTML is fully rebuilt
   every render()) and draws in plain viewport coordinates -- see
   drawArrow() in app.js. pointer-events:none lets clicks fall through to
   the real destination element underneath. */
.arrow-layer{ position:fixed; inset:0; width:100vw; height:100vh; pointer-events:none; z-index:35; }
.move-arrow{ stroke:var(--brass-glow); stroke-width:2.5; opacity:.9; }

/* Payment banner (r349 step 4): centered over the battlefield strip --
   between the two rows, so it never covers the runes it's pointing at
   whichever player is paying. */
.pay-banner{
  position:fixed; left:50%; top:50%; transform:translate(-50%,-50%);
  z-index:45;
  display:flex; align-items:center; gap:14px;
  max-width:min(760px, calc(100vw - 32px));
  padding:10px 14px;
  background: linear-gradient(180deg, #132845, #0b1a2e);
  border:1px solid var(--brass); border-radius:10px;
  box-shadow: 0 14px 34px rgba(0,0,0,.55);
  font-size:13px; color:var(--text-hi);
}
.pay-banner[hidden]{ display:none; } /* display:flex above would otherwise beat the hidden attribute */
.pay-banner__card{ height:72px; flex:0 0 auto; }
.pay-banner__card .card{ pointer-events:none; }
.pay-banner__text{ flex:1 1 auto; min-width:0; line-height:1.4; }
.pay-banner__text b{ color:var(--brass-glow); }
.pay-banner__hint{ display:block; color:var(--text-lo); font-size:12px; }
.pay-banner__cancel{
  appearance:none; flex:0 0 auto; cursor:pointer;
  border:1px solid var(--brass-dim); background:transparent; color:var(--brass-glow);
  font-family:var(--font-display); font-weight:600; font-size:11px; letter-spacing:.05em; text-transform:uppercase;
  padding:7px 12px; border-radius:6px;
}
.pay-banner__cancel:hover{ background: rgba(201,167,92,.14); }

/* A board card a selected spell may legally choose (r355). Distinct from
   .is-legal-destination (a zone a unit can move/enter) -- this is a hostile
   "pick me" marker on a card, so it reads red rather than brass. */
.is-legal-target{
  border-radius:6px;
  cursor:pointer;
  box-shadow: var(--glow-enemy);
}
.is-legal-target:hover{ box-shadow: var(--glow-enemy-hot); }
/* ...unless it's one of YOUR cards being chosen (a friendly unit to buff): green. */
.is-legal-target.is-target-friendly{ box-shadow: var(--glow-own); }
.is-legal-target.is-target-friendly:hover{ box-shadow: var(--glow-own-hot); }

/* Hide chip (r421) on a hand card with Hidden. */
.hide-chip{
  position:absolute; top:4px; left:4px; z-index:3;
  appearance:none; cursor:pointer;
  border:1px solid var(--dom-mind, #5490e0); background:rgba(8,20,36,.85); color:#9ec4f5;
  font-family:var(--font-display); font-weight:600; font-size:9px; letter-spacing:.06em; text-transform:uppercase;
  padding:3px 6px; border-radius:4px;
  opacity:.85;
}
.hide-chip:hover{ opacity:1; background:rgba(84,144,224,.25); }

/* Facedown Zone (r85): hidden cards tucked under the battlefield card. */
.bf-half__card{ position:relative; }
.bf-facedown{ position:absolute; right:-12px; bottom:-8px; display:flex; gap:4px; z-index:2; }
.bf-facedown__card{ height:64px; border:1px solid var(--dom-mind, #5490e0); box-shadow:0 4px 12px rgba(0,0,0,.5); }
.bf-facedown__card.is-dormant{ opacity:.55; filter:saturate(.6); }

/* Phase strip (r313-317): a fixed pill row at the very top, over the
   opponent bar's top edge (the cards there sit lower). */
.phase-strip{
  position:fixed; top:6px; left:50%; transform:translateX(-50%);
  z-index:32;
  display:flex; align-items:center; gap:4px;
  padding:3px 6px;
  background: rgba(8,20,36,.88);
  border:1px solid var(--line); border-radius:999px;
  box-shadow: 0 6px 18px rgba(0,0,0,.45);
  font-family:var(--font-display); font-size:10px; letter-spacing:.06em; text-transform:uppercase;
  color:var(--text-faint);
}
.phase-strip__turn{ color:var(--brass-glow); font-weight:700; padding:3px 10px 3px 6px; border-right:1px solid var(--line-soft); margin-right:2px; white-space:nowrap; }
.phase-strip__chip{
  appearance:none; border:1px solid transparent; background:transparent; color:inherit;
  font:inherit; letter-spacing:inherit; text-transform:inherit;
  padding:3px 9px; border-radius:999px; white-space:nowrap;
  transition: background .18s, color .18s, border-color .18s;
}
.phase-strip__chip.is-active{
  color:#0b1a2e; background:var(--brass-glow); border-color:var(--brass);
  font-weight:700;
  box-shadow: 0 0 12px rgba(239,217,160,.55);
}
.phase-strip__chip.is-action{ cursor:pointer; border-color:var(--brass-dim); color:var(--brass-glow); }
.phase-strip__chip.is-action:hover{ background: rgba(201,167,92,.2); }
.phase-strip__chip.is-action.is-active{ color:#0b1a2e; }
.phase-strip__note{ color:var(--text-lo); padding:0 8px 0 4px; white-space:nowrap; font-family:var(--font-body); text-transform:none; letter-spacing:0; font-size:11px; }

/* Mulligan modal (r116): dims the whole board and shows the opening hand
   large in the middle. Above every other banner. */
.mulligan-modal{
  position:fixed; inset:0; z-index:60;
  display:flex; align-items:center; justify-content:center;
  background: rgba(3,8,16,.72);
  backdrop-filter: blur(2px);
}
.mulligan-modal[hidden]{ display:none; }
.mulligan-modal__panel{
  display:flex; flex-direction:column; align-items:center; gap:14px;
  max-width:min(980px, calc(100vw - 32px));
  padding:22px 28px 20px;
  background: linear-gradient(180deg, #132845, #0b1a2e);
  border:1px solid var(--brass); border-radius:14px;
  box-shadow: 0 24px 60px rgba(0,0,0,.65), 0 0 0 1px rgba(201,167,92,.15);
  color:var(--text-hi);
}
.mulligan-modal__panel--compact{ padding:16px 24px; gap:8px; }
.mulligan-modal__title{ font-family:var(--font-display); font-size:18px; font-weight:600; letter-spacing:.06em; color:var(--brass-glow); }
.mulligan-modal__hint{ color:var(--text-lo); font-size:13px; min-height:1.4em; }
.mulligan-modal__hand{ display:flex; gap:14px; justify-content:center; flex-wrap:wrap; padding:6px 0; }
.mulligan-modal__card{
  position:relative;
  height:min(300px, 34vh);
  cursor:pointer;
  transition: transform .12s, box-shadow .12s, filter .12s;
}
.mulligan-modal__card:hover{ filter: brightness(1.12); }
/* Selected = set aside: lifted, gold-ringed, and labelled -- so it can't be
   mistaken for a hover. Click again to put it back. */
.mulligan-modal__card.is-selected{
  transform: translateY(-10px);
  box-shadow: 0 0 0 3px var(--brass-glow), 0 0 26px rgba(239,217,160,.75);
}
.mulligan-modal__badge{
  display:none;
  position:absolute; left:50%; bottom:10px; transform:translateX(-50%);
  padding:5px 10px; border-radius:6px;
  background: rgba(8,20,36,.92); border:1px solid var(--brass-glow); color:var(--brass-glow);
  font-family:var(--font-display); font-weight:700; font-size:11px; letter-spacing:.06em; text-transform:uppercase;
  white-space:nowrap; pointer-events:none;
}
.mulligan-modal__card.is-selected .mulligan-modal__badge{ display:block; }
.mulligan-modal__confirm{
  appearance:none; cursor:pointer;
  border:1px solid var(--brass); background: rgba(201,167,92,.14); color:var(--brass-glow);
  font-family:var(--font-display); font-weight:600; font-size:13px; letter-spacing:.06em; text-transform:uppercase;
  padding:10px 22px; border-radius:8px;
}
.mulligan-modal__confirm:hover{ background: rgba(201,167,92,.28); }
.mulligan-modal__confirm:focus-visible{ outline:2px solid var(--brass-glow); outline-offset:2px; }

/* Zone viewer: the Trash / Banishment pile, opened by clicking it. */
.zone.is-openable{ cursor:pointer; }
.zone.is-openable:hover{ box-shadow: inset 0 0 0 1px var(--brass); }
.zone-viewer__panel{ max-width:min(1180px, calc(100vw - 32px)); max-height:calc(100vh - 40px); }
.zone-viewer__head{ display:flex; align-items:center; justify-content:space-between; gap:16px; width:100%; }
.zone-viewer__close{
  appearance:none; cursor:pointer; border:1px solid var(--line); background:transparent; color:var(--text-lo);
  width:30px; height:30px; border-radius:50%; font-size:14px; line-height:1;
}
.zone-viewer__close:hover{ color:var(--brass-glow); border-color:var(--brass); }
.zone-viewer__grid{
  display:flex; flex-wrap:wrap; gap:12px; justify-content:center;
  overflow-y:auto; max-height:calc(100vh - 160px); padding:6px 4px;
}
.zone-viewer__card{ height:min(240px, 28vh); flex:0 0 auto; }

/* Choose-targets banner (r355): sits just under the chain banner. */
.choose-banner{
  position:fixed; left:50%; top:36%; transform:translate(-50%,-50%);
  z-index:46;
  display:flex; align-items:center; gap:14px;
  max-width:min(760px, calc(100vw - 32px));
  padding:10px 14px;
  background: linear-gradient(180deg, #2a1a0f, #160d06);
  border:1px solid var(--dom-body, #da8a3c); border-radius:10px;
  box-shadow: 0 14px 34px rgba(0,0,0,.55);
  font-size:13px; color:var(--text-hi);
}
.choose-banner[hidden]{ display:none; }
.assign-banner{ border-color: var(--danger, #c9563f); }
.play-options{ flex-wrap:wrap; }
.play-options__ways{ display:flex; flex-direction:column; gap:6px; flex:1 1 auto; min-width:0; }
.play-options__way{
  appearance:none; cursor:pointer; text-align:left;
  display:flex; align-items:baseline; justify-content:space-between; gap:12px;
  padding:8px 12px; border-radius:6px;
  border:1px solid var(--brass-dim); background:rgba(218,138,60,.06); color:var(--text-hi);
  font:inherit; font-size:13px;
}
.play-options__way:hover{ background:rgba(218,138,60,.18); border-color:var(--brass-glow); }
.play-options__way b{ font-family:var(--font-display); color:#f2b877; font-weight:600; letter-spacing:.03em; }
.play-options__cost{ flex:0 0 auto; color:var(--text-lo); font-size:12px; white-space:nowrap; }
.choose-banner__card{ height:72px; flex:0 0 auto; }
.choose-banner__card .card{ pointer-events:none; }
.choose-banner__info{ flex:1 1 auto; min-width:0; line-height:1.4; }
.choose-banner__title{ font-family:var(--font-display); color:#f2b877; font-weight:600; letter-spacing:.04em; }
.choose-banner__hint{ color:var(--text-lo); font-size:12px; }
.choose-banner__options{ display:flex; gap:8px; flex:0 0 auto; flex-wrap:wrap; }
.choose-banner__option{ height:84px; cursor:pointer; }
/* Options drawn inside the banner: the prompt keeps the first row to itself;
   the options and the buttons share a second row. */
.choose-banner--rows{ flex-wrap:wrap; max-width:min(880px, calc(100vw - 32px)); }
.choose-banner--rows .choose-banner__info{ flex:1 1 200px; }
.choose-banner--rows .choose-banner__options{ flex:1 1 100%; }
/* "Look at" cards shown with a resolution-time prompt. */
.choose-banner__reveal{ flex:1 1 100%; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.choose-banner__reveal-label{ font-family:var(--font-display); font-size:12px; letter-spacing:.06em; text-transform:uppercase; color:var(--text-lo); }
.choose-banner__reveal-card{ height:120px; }
/* Text choices are buttons, sized to their words, not card-shaped. */
.choose-banner--text .choose-banner__option{ height:auto; text-transform:none; letter-spacing:0; font-family:var(--font-body); font-size:13px; padding:9px 14px; text-align:left; }
.choose-banner--text .choose-banner__option.is-picked{ transform:none; }
.choose-banner__btn{
  appearance:none; flex:0 0 auto; cursor:pointer;
  border:1px solid var(--dom-body, #da8a3c); background:transparent; color:#f2b877;
  font-family:var(--font-display); font-weight:600; font-size:11px; letter-spacing:.05em; text-transform:uppercase;
  padding:7px 12px; border-radius:6px;
}
.choose-banner__btn:hover{ background: rgba(218,138,60,.16); }
.choose-banner__btn--cancel{ border-color:var(--brass-dim); color:var(--text-lo); }
.choose-banner__btn--confirm{ border-color:var(--brass-glow); color:var(--brass-glow); font-weight:700; }
.choose-banner__btn:disabled{ opacity:.45; cursor:not-allowed; }
/* A staged (not yet confirmed) pick: lifted and gold-ringed, so it can't be
   mistaken for the mere "legal" highlight. Click again to unstage. */
.is-legal-target.is-picked{
  box-shadow: var(--glow-enemy-hot), 0 0 0 2px rgba(255,255,255,.55) !important;
  transform: translateY(-6px);
}
.is-legal-target.is-target-friendly.is-picked{ box-shadow: var(--glow-own-hot), 0 0 0 2px rgba(255,255,255,.55) !important; }
.ru-half.is-picked, .bf-half.is-picked{ transform:none; }

/* Combat Showdown banner (r463 step 1): top of the screen, above both the
   chain and payment banners, since a Showdown can have either nested
   inside it (a trick being paid for, or on the chain). */
.showdown-banner{
  position:fixed; left:50%; top:8%; transform:translate(-50%,-50%);
  z-index:43;
  display:flex; align-items:center; gap:14px;
  max-width:min(760px, calc(100vw - 32px));
  padding:10px 14px;
  background: linear-gradient(180deg, #3a1a1a, #1e0d0d);
  border:1px solid var(--dom-fury, #c2413a); border-radius:10px;
  box-shadow: 0 14px 34px rgba(0,0,0,.55);
  font-size:13px; color:var(--text-hi);
}
.showdown-banner[hidden]{ display:none; }
.showdown-banner__info{ flex:1 1 auto; min-width:0; line-height:1.4; }
.showdown-banner__title{ font-family:var(--font-display); color:#ff9b93; font-weight:600; letter-spacing:.04em; }
.showdown-banner__where{ color:var(--text-hi); font-size:12px; }
.showdown-banner__focus{ color:var(--text-lo); font-size:12px; }
.showdown-banner__pass{
  appearance:none; flex:0 0 auto; cursor:pointer;
  border:1px solid var(--dom-fury, #c2413a); background:transparent; color:#ff9b93;
  font-family:var(--font-display); font-weight:600; font-size:11px; letter-spacing:.05em; text-transform:uppercase;
  padding:7px 12px; border-radius:6px;
}
.showdown-banner__pass:hover{ background: rgba(194,65,58,.18); }

/* Chain banner (r327): sits above the payment banner (top:22% vs 50%) so
   the two can coexist -- reacting with a card that itself needs payment
   opens a payCost decision while the Chain is still up. */
.chain-banner{
  position:fixed; left:50%; top:22%; transform:translate(-50%,-50%);
  z-index:44;
  display:flex; align-items:center; gap:14px;
  max-width:min(760px, calc(100vw - 32px));
  padding:10px 14px;
  background: linear-gradient(180deg, #241531, #150c1e);
  border:1px solid var(--brass); border-radius:10px;
  box-shadow: 0 14px 34px rgba(0,0,0,.55);
  font-size:13px; color:var(--text-hi);
}
.chain-banner[hidden]{ display:none; }
.chain-banner__list{ display:flex; gap:8px; flex:0 0 auto; }
.chain-banner__item{ position:relative; height:72px; }
.chain-banner__item .card{ height:100%; } /* hoverable: the preview shows what's on the Chain */
.chain-banner__owner{ pointer-events:none; }
.chain-banner__owner{
  position:absolute; left:2px; bottom:2px;
  font-family:var(--font-display); font-size:10px; font-weight:700; letter-spacing:.03em;
  color:var(--text-hi); background:rgba(0,0,0,.65); border-radius:4px; padding:1px 4px;
}
.chain-banner__info{ flex:1 1 auto; min-width:0; line-height:1.4; }
.chain-banner__title{ font-family:var(--font-display); color:var(--brass-glow); font-weight:600; letter-spacing:.04em; }
.chain-banner__priority{ color:var(--text-lo); font-size:12px; }
.chain-banner__pass{
  appearance:none; flex:0 0 auto; cursor:pointer;
  border:1px solid var(--brass-dim); background:transparent; color:var(--brass-glow);
  font-family:var(--font-display); font-weight:600; font-size:11px; letter-spacing:.05em; text-transform:uppercase;
  padding:7px 12px; border-radius:6px;
}
.chain-banner__pass:hover{ background: rgba(201,167,92,.14); }

/* Runes that can pay for the pending card pulse so the eye goes to them. */
@keyframes rune-pulse{ 0%,100%{ box-shadow: 0 0 0 2px var(--state-controlled), 0 0 12px rgba(111,174,122,.5); } 50%{ box-shadow: 0 0 0 4px var(--brass-glow), 0 0 22px rgba(239,217,160,.8); } }
.card.is-paying{ animation: rune-pulse 1.1s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce){ .card.is-paying{ animation:none; box-shadow: 0 0 0 4px var(--brass-glow), 0 0 22px rgba(239,217,160,.8); } }
/* A rune already tapped for THIS payment: click to un-tap it (see
   performUndoRuneEnergy). Deliberately calmer than is-paying's pulse --
   this reverses a choice already made, rather than inviting a new one --
   a steady dashed ring instead of a glowing/animated one. */
.card.is-undoable{ cursor:pointer; box-shadow: 0 0 0 2px var(--text-lo); outline: 2px dashed var(--text-lo); outline-offset:2px; }
.card.is-undoable:hover{ box-shadow: 0 0 0 2px var(--brass-glow); outline-color: var(--brass-glow); }

/* Drag and drop: draggable cards show a grab cursor; a drop target the
   pointer is over while dragging fills in more strongly than a merely legal one. */
.card.is-movable{ cursor:grab; }
.card.is-dragging{ opacity:.55; }
.ru-half.is-drop-hover, .bf-half.is-drop-hover{ background: rgba(239,217,160,.18); outline-style:solid; }
/* A spell being dragged over one of its targets. */
.is-legal-target.is-drop-hover{ box-shadow: var(--glow-enemy-hot); transform: translateY(-4px); }
.is-legal-target.is-target-friendly.is-drop-hover{ box-shadow: var(--glow-own-hot); }

.win-banner{
  position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
  z-index:60;
  font-family:var(--font-display); font-weight:700; font-size:28px; letter-spacing:.04em;
  color:var(--brass-glow); text-transform:uppercase; text-align:center;
  background: linear-gradient(180deg, #132845, #0b1a2e);
  border:1px solid var(--brass); border-radius:12px;
  padding:22px 36px;
  box-shadow: 0 20px 60px rgba(0,0,0,.6);
}

.detail__media{ flex:0 0 auto; }
/* Portrait default (units/spells/gear/legends/runes -- real card ratio 744:1039). */
.detail__media img{ width:320px; height:447px; object-fit:cover; border-radius:10px; display:block; }
/* Battlefields are printed landscape (real card ratio 1038:744); sizing the
   box to that ratio means object-fit:cover shows the whole card instead of
   cropping the sides off a landscape image stuffed into a portrait box. */
.detail.is-landscape .detail__media img{ width:420px; height:301px; }

.detail__info{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:7px; }
.detail__name{ font-family:var(--font-display); font-weight:600; font-size:20px; line-height:1.25; color:var(--brass-glow); text-wrap:balance; }
.detail__stats{ font-family:var(--font-mono); font-size:12.5px; color:var(--text-lo); display:flex; align-items:center; gap:7px; flex-wrap:wrap; }
.detail__domain-dot{ width:8px; height:8px; border-radius:50%; flex:0 0 auto; box-shadow: inset 0 0 0 1px rgba(0,0,0,.3); }
.detail__text{ font-size:15px; line-height:1.5; color:var(--text-hi); white-space:pre-line; }
.detail__effect{ font-size:14px; line-height:1.45; color:var(--text-lo); white-space:pre-line; border-top:1px dashed var(--line); padding-top:6px; }
.detail__tags{ font-size:12px; color:var(--text-faint); }

/* Keyword highlighting inside rules/effect text (r728: "Keywords can be
   identified by having a colored highlight behind them"). Categorized by
   function rather than by arbitrary per-printing color -- see
   src/server/highlight.ts for the exact keyword -> category mapping. */
.kw{ display:inline-block; padding:0 5px; border-radius:5px; font-weight:700; white-space:nowrap; }
.kw-timing{ background: rgba(123,141,224,.18); color:#a7b3ee; border:1px solid rgba(123,141,224,.45); }
.kw-combat{ background: rgba(200,80,105,.18); color:#e796a8; border:1px solid rgba(200,80,105,.45); }
.kw-triggered{ background: rgba(180,123,209,.18); color:#d3a5e8; border:1px solid rgba(180,123,209,.45); }
.kw-resource{ background: rgba(201,162,75,.2); color:var(--brass-glow); border:1px solid rgba(201,162,75,.45); }
.kw-deckbuild{ background: rgba(127,138,134,.2); color:#b7c0bc; border:1px solid rgba(127,138,134,.45); }



/* Shorter windows: shrink the fixed-size pieces (hand bars, standard-size
   reference cards) so the flexed board keeps enough room. The top bar must
   stay >= its deck card + ~80px (phase strip 30, label, count, padding),
   or the Main/Rune Deck zones spill over the row beneath. */
@media (max-height: 820px){
  :root{ --hand-bar-h:172px; --oppo-bar-h:136px; --card-std-h:74px; --card-std-h-sm:52px; --card-deck-h:106px; --card-deck-h-sm:56px; }
}
@media (max-height: 680px){
  :root{ --hand-bar-h:150px; --oppo-bar-h:128px; --card-std-h:62px; --card-std-h-sm:46px; --card-deck-h:88px; --card-deck-h-sm:48px; }
}

@media (max-width: 760px){
  .detail{ flex-direction:column; max-width:min(88vw, 300px); }
  .detail__media img{ width:100%; height:auto; aspect-ratio: 744 / 1039; }
  .detail.is-landscape .detail__media img{ aspect-ratio: 1038 / 744; }
}

/* ---------------- playmat linework: bracketed gold corners on every box ----------------
   Eight small gradient strokes on an overlay pseudo-element -- no extra
   markup, nothing that can interfere with the flex/grid sizing of the box
   itself (the box establishes position:relative above; this just paints
   over it). */
.zone::after, .cluster--panel::after, .bf-slot::after, .hand-panel::after, .battlefields::after{
  content:""; position:absolute; inset:-1px; pointer-events:none; border-radius:inherit;
  background-image:
    linear-gradient(var(--brass),var(--brass)), linear-gradient(var(--brass),var(--brass)),
    linear-gradient(var(--brass),var(--brass)), linear-gradient(var(--brass),var(--brass)),
    linear-gradient(var(--brass),var(--brass)), linear-gradient(var(--brass),var(--brass)),
    linear-gradient(var(--brass),var(--brass)), linear-gradient(var(--brass),var(--brass));
  background-repeat:no-repeat;
  background-size: 12px 2px, 2px 12px, 12px 2px, 2px 12px, 12px 2px, 2px 12px, 12px 2px, 2px 12px;
  background-position: 0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 100% 100%, 100% 100%;
  opacity:.85;
}


/* ---- Tutorial spotlight (public/tutorial.js) ---- */
.tutorial[hidden]{ display:none; }
.tutorial__dim{
  position:fixed; inset:0; z-index:90;
  background: rgba(2,8,16,.62);
}
.tutorial__rings{ position:fixed; inset:0; z-index:91; pointer-events:none; }
.tutorial__hole{
  position:fixed; pointer-events:none;
  border:2px solid var(--brass-glow); border-radius:10px;
  box-shadow: 0 0 0 4px rgba(239,217,160,.25), 0 0 28px rgba(239,217,160,.55);
  animation: tutorial-pulse 1.6s ease-in-out infinite;
}
@keyframes tutorial-pulse{ 0%,100%{ box-shadow: 0 0 0 4px rgba(239,217,160,.25), 0 0 28px rgba(239,217,160,.55); } 50%{ box-shadow: 0 0 0 7px rgba(239,217,160,.12), 0 0 36px rgba(239,217,160,.8); } }
@media (prefers-reduced-motion: reduce){ .tutorial__hole{ animation:none; } }
.chain-banner__pass.is-blocked{ opacity:.35; cursor:not-allowed; box-shadow:none; }
.tutorial__popup{
  position:fixed; z-index:92;
  /* As wide as its text needs, up to 400px -- a short step gets a snug panel, not a fixed box with dead space. */
  width:max-content; max-width:min(400px, calc(100vw - 32px));
  padding:14px 16px 12px;
  background: linear-gradient(180deg, #16294a, #0c1a30);
  border:1px solid var(--brass); border-radius:12px;
  box-shadow: 0 20px 50px rgba(0,0,0,.7), 0 0 0 1px rgba(201,167,92,.15);
  color:var(--text-hi); font-size:13.5px; line-height:1.5;
}
.tutorial__head{ display:flex; align-items:baseline; gap:10px; margin-bottom:6px; }
.tutorial__count{ font-family:var(--font-mono); font-size:10.5px; color:var(--text-faint); letter-spacing:.04em; }
.tutorial__title{ font-family:var(--font-display); font-size:16px; font-weight:600; letter-spacing:.04em; color:var(--brass-glow); }
.tutorial__body b{ color:var(--brass-glow); font-weight:600; }
.tutorial__body i{ color:var(--text-lo); }
.tutorial__foot{ display:flex; justify-content:space-between; align-items:center; gap:10px; margin-top:12px; }
.tutorial__wait{ font-size:11.5px; color:var(--text-faint); font-style:italic; }
.tutorial__btn{
  appearance:none; cursor:pointer;
  border:1px solid var(--brass); background: rgba(201,167,92,.16); color:var(--brass-glow);
  font-family:var(--font-display); font-weight:600; font-size:12px; letter-spacing:.06em; text-transform:uppercase;
  padding:7px 16px; border-radius:8px;
}
.tutorial__btn:hover{ background: rgba(201,167,92,.3); }
.tutorial__btn--quiet{ border-color:var(--line); color:var(--text-lo); background:transparent; }
.tutorial__btn--quiet:hover{ color:var(--text-hi); border-color:var(--brass-dim); }

/* Reference card in the middle of the screen, with optional callouts. */
.tutorial__figure{
  position:fixed; z-index:92;
  pointer-events:none;
}
.tutorial__figure[hidden]{ display:none; }
.tutorial__figure-card{
  position:absolute; left:0; top:0; height:100%; width:auto;
  border-radius:12px;
  box-shadow: 0 0 0 2px var(--brass), 0 24px 60px rgba(0,0,0,.75), 0 0 40px rgba(239,217,160,.25);
}
.tutorial__figure-svg{ position:absolute; inset:0; width:100%; height:100%; overflow:visible; }
.tutorial__ring{ fill:none; stroke:#ffd766; stroke-width:3; filter: drop-shadow(0 0 6px rgba(255,215,102,.8)); }
.tutorial__leader{ fill:none; stroke:#ffd766; stroke-width:2; stroke-dasharray:5 4; }
.tutorial__leader-dot{ fill:#ffd766; }
.tutorial__callout{
  position:absolute; transform:translateY(-50%);
  text-align:left;
  max-width:210px; padding:6px 10px;
  background: rgba(8,20,36,.94); border:1px solid #ffd766; border-radius:8px;
  color:#ffe9a8; font-size:12.5px; line-height:1.35; font-weight:600;
  box-shadow: 0 6px 18px rgba(0,0,0,.5);
}
.tutorial__callout--left{ text-align:right; }

/* ---- Tutorial: footer layout, skip confirmation ---- */
.tutorial__foot-left{ display:flex; gap:6px; }
.tutorial__btn:disabled{ opacity:.4; cursor:not-allowed; }
.tutorial__confirm{
  position:fixed; left:50%; top:50%; transform:translate(-50%,-50%); z-index:93;
  width:min(380px, calc(100vw - 32px)); padding:16px 18px 14px;
  background: linear-gradient(180deg, #1c1f3a, #0e1024);
  border:1px solid var(--brass-glow); border-radius:12px;
  box-shadow: 0 24px 60px rgba(0,0,0,.8);
  color:var(--text-hi); font-size:13.5px; line-height:1.5;
}
.tutorial__confirm-title{ font-family:var(--font-display); font-size:16px; font-weight:600; letter-spacing:.04em; color:var(--brass-glow); margin-bottom:6px; }
.tutorial__confirm-body b{ color:var(--brass-glow); }
.tutorial__confirm-foot{ display:flex; justify-content:flex-end; gap:8px; margin-top:12px; }

/* ---- Home screen (public/home.js) ---- */
.home{
  position:fixed; inset:0; z-index:95;
  display:flex; align-items:center; justify-content:center;
  background: radial-gradient(80% 70% at 50% 40%, rgba(24,52,90,.96), rgba(6,12,24,.98));
}
.home[hidden]{ display:none; }
.home__panel{ position:relative; display:flex; flex-direction:column; align-items:center; gap:18px; width:min(760px, calc(100vw - 32px)); text-align:center; }
.home__eyebrow{ font-family:var(--font-display); font-size:12px; letter-spacing:.3em; text-transform:uppercase; color:var(--brass); }
.home__title{ margin:0; font-family:var(--font-display); font-size:30px; font-weight:600; letter-spacing:.04em; color:var(--brass-glow); text-wrap:balance; }
.home__modes{ display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); gap:14px; width:100%; margin-top:8px; }
.home__mode{
  appearance:none; cursor:pointer; text-align:left;
  display:flex; flex-direction:column; gap:8px;
  padding:18px 20px; border-radius:12px;
  background: linear-gradient(180deg, rgba(22,41,74,.9), rgba(12,26,48,.9));
  border:1px solid var(--brass-dim); color:var(--text-hi);
  transition: border-color .15s, transform .15s, box-shadow .15s;
}
.home__mode:hover{ border-color:var(--brass-glow); transform:translateY(-2px); box-shadow: 0 12px 30px rgba(0,0,0,.5), 0 0 0 1px rgba(239,217,160,.15); }
.home__mode:focus-visible{ outline:2px solid var(--brass-glow); outline-offset:2px; }
.home__mode-name{ font-family:var(--font-display); font-size:18px; font-weight:600; letter-spacing:.06em; color:var(--brass-glow); }
.home__mode-desc{ font-size:13px; line-height:1.5; color:var(--text-lo); }
.home__continue{
  appearance:none; cursor:pointer; margin-top:6px;
  border:1px solid var(--line); background:transparent; color:var(--text-lo);
  font-family:var(--font-display); font-size:12px; letter-spacing:.08em; text-transform:uppercase;
  padding:8px 18px; border-radius:999px;
}
.home__continue:hover{ color:var(--brass-glow); border-color:var(--brass); }
.home__continue[hidden]{ display:none; }

/* Tutorial: the six Domains panel */
.tutorial__domains{
  position:fixed; z-index:92; pointer-events:none;
  width:min(720px, calc(100vw - 32px));
  padding:14px 18px 12px;
  background: linear-gradient(180deg, #16294a, #0c1a30);
  border:1px solid var(--brass); border-radius:12px;
  box-shadow: 0 20px 50px rgba(0,0,0,.7), 0 0 0 1px rgba(201,167,92,.15);
  color:var(--text-hi); text-align:center;
}
.tutorial__domains[hidden]{ display:none; }
.tutorial__domains-title{ font-family:var(--font-display); font-size:15px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--brass-glow); margin-bottom:10px; }
.tutorial__domains-grid{ display:grid; grid-template-columns:repeat(6, 1fr); gap:10px; }
.tutorial__domain{ display:flex; flex-direction:column; align-items:center; gap:6px; }
.tutorial__domain-rune{ width:84px; aspect-ratio: var(--ratio-portrait); border-radius:6px; box-shadow: 0 0 0 2px var(--dom), 0 6px 16px rgba(0,0,0,.6); }
.tutorial__domain-name{ font-family:var(--font-display); font-size:13px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--dom); }
.tutorial__domains-note{ margin-top:10px; font-size:12px; color:var(--text-lo); }
@media (max-width: 760px){ .tutorial__domains-grid{ grid-template-columns:repeat(3, 1fr); } }
@media (max-width: 1500px){ .tutorial__domain-rune{ width:64px; } .tutorial__domains{ width:min(560px, calc(100vw - 32px)); } }

/* A smaller screen can't fit the big preview: scale the art back down. */
@media (max-height: 640px), (max-width: 900px){
  .detail__media img{ width:220px; height:307px; }
  .detail.is-landscape .detail__media img{ width:300px; height:215px; }
  .detail__text{ font-size:13.5px; }
}

/* Home: the deck picker for a Normal game (see buildDeckPicker in home.js). */
.home__decks{ width:100%; display:flex; flex-direction:column; gap:16px; margin-top:6px; }
.home__decks[hidden], .home__modes[hidden]{ display:none; }
.home__decks-cols{ display:grid; grid-template-columns:repeat(auto-fit, minmax(300px, 1fr)); gap:18px; text-align:left; }
.home__decks-col{ display:flex; flex-direction:column; gap:8px; }
.home__decks-head{ font-family:var(--font-display); font-size:13px; letter-spacing:.18em; text-transform:uppercase; color:var(--brass); padding-bottom:4px; border-bottom:1px solid var(--line-soft, rgba(201,167,92,.14)); }
.home__deck{
  appearance:none; cursor:pointer; text-align:left; font:inherit; color:var(--text);
  display:grid; grid-template-columns:auto 1fr; grid-template-rows:auto auto; column-gap:10px; row-gap:2px; align-items:baseline;
  padding:9px 12px; border-radius:8px; border:1px solid var(--line-soft, rgba(201,167,92,.14)); background:rgba(15,31,54,.6);
  transition: border-color .15s, background .15s, box-shadow .15s;
}
.home__deck:hover{ border-color:var(--brass); background:rgba(19,40,69,.85); }
.home__deck.is-chosen{ border-color:var(--brass-glow); background:rgba(201,167,92,.14); box-shadow: 0 0 0 1px var(--brass-glow), 0 0 18px rgba(239,217,160,.25); }
.home__deck-name{ font-family:var(--font-display); font-weight:600; font-size:15px; color:var(--brass-glow); }
.home__deck-legend{ font-family:var(--font-mono); font-size:11px; color:var(--text-lo); display:flex; align-items:center; gap:5px; }
.home__deck-dot{ width:9px; height:9px; border-radius:50%; display:inline-block; box-shadow: inset 0 0 0 1px rgba(0,0,0,.35); }
.home__deck-desc{ grid-column:1 / -1; font-size:12.5px; line-height:1.4; color:var(--text-lo); }
.home__decks-actions{ display:flex; justify-content:center; align-items:center; gap:14px; }
.home__start{
  appearance:none; cursor:pointer; font-family:var(--font-display); font-weight:600; font-size:14px; letter-spacing:.08em; text-transform:uppercase;
  padding:12px 30px; border-radius:10px; color:var(--brass-glow); border:1px solid var(--brass);
  background: linear-gradient(180deg, rgba(201,167,92,.28), rgba(201,167,92,.12));
  box-shadow: 0 10px 30px rgba(0,0,0,.45);
}
.home__start:hover{ background: linear-gradient(180deg, rgba(201,167,92,.4), rgba(201,167,92,.18)); }

/* A pile-targeted spell being dragged: the whole board is the drop zone. */
#board.is-pile-drop{ outline:2px dashed var(--brass-glow); outline-offset:-6px; }
.play-options--targets{ flex-wrap:wrap; }
.play-options--targets .choose-banner__options{ flex:1 1 100%; flex-wrap:wrap; }
.play-options--targets .choose-banner__option{ height:140px; }

/* Card art is never draggable as an image (only the card element is, when it can act) or copyable by drag-select. */
.card img, .detail img, .figure img{ -webkit-user-drag:none; user-select:none; pointer-events:none; }
input, textarea, [contenteditable]{ user-select:text; -webkit-user-select:text; }

/* Optional sign-in (public/account.js) on the Home screen. */
.home__account{ position:absolute; top:14px; right:16px; }
.account__chip{ display:inline-flex; align-items:center; gap:8px; font-size:12.5px; }
.account__avatar{ width:24px; height:24px; border-radius:50%; border:1px solid var(--line); object-fit:cover; display:inline-flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; color:var(--brass-glow); background:var(--panel); }
.account__name{ color:var(--text-hi); font-weight:600; max-width:14ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.account__btn{ appearance:none; cursor:pointer; padding:5px 10px; border-radius:8px; border:1px solid var(--line); background:rgba(201,167,92,.1); color:var(--text-hi); font:inherit; font-size:12px; font-weight:600; }
.account__btn:hover{ background:rgba(201,167,92,.2); }
.account__menu{ position:relative; }
.account__list{ position:absolute; right:0; top:calc(100% + 8px); min-width:230px; padding:8px; border-radius:12px; border:1px solid var(--line); background:var(--panel); box-shadow:0 20px 50px rgba(0,0,0,.6); display:flex; flex-direction:column; gap:6px; z-index:5; }
.account__provider{ display:block; padding:9px 12px; border-radius:8px; text-decoration:none; color:var(--text-hi); font-weight:600; font-size:13px; border:1px solid var(--line-soft); }
.account__provider:hover{ background:rgba(201,167,92,.1); }
.account__provider--discord{ border-left:3px solid #5865F2; }
.account__provider--google{ border-left:3px solid #4285F4; }
.account__note{ font-size:11px; color:var(--text-lo); padding:2px 4px 0; }

.account__list[hidden]{ display:none; }

/* A unit dying: its ghost stays where it stood for a moment -- a slash cuts
   across, a dark stain spreads, the card jolts, greys and crumbles away. */
.death-ghost{ position:fixed; z-index:8; pointer-events:none; border-radius:10px; overflow:hidden; box-shadow:0 0 0 1px rgba(0,0,0,.5), 0 8px 24px rgba(0,0,0,.6); animation: death-card 1.05s ease-in forwards; }
.death-ghost img{ width:100%; height:100%; object-fit:cover; display:block; transform-origin:center; }
.death-ghost__stain{ position:absolute; inset:-20%; background: radial-gradient(circle at 50% 55%, rgba(120,8,8,.95) 0, rgba(120,8,8,.7) 22%, rgba(60,0,0,.35) 45%, transparent 62%); opacity:0; animation: death-stain 1.05s ease-out forwards; mix-blend-mode:multiply; }
.death-ghost__slash{ position:absolute; left:-30%; top:45%; width:160%; height:9%; background: linear-gradient(90deg, transparent, #ffe6d6 30%, #fff 50%, #ffe6d6 70%, transparent); box-shadow:0 0 14px 4px rgba(255,120,80,.9), 0 0 40px 10px rgba(255,60,30,.5); transform: rotate(-38deg) translateX(-120%); animation: death-slash .42s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes death-slash{
  0%   { transform: rotate(-38deg) translateX(-120%); opacity:0; }
  15%  { opacity:1; }
  60%  { transform: rotate(-38deg) translateX(20%); opacity:1; }
  100% { transform: rotate(-38deg) translateX(120%); opacity:0; }
}
@keyframes death-stain{
  0%   { opacity:0; transform:scale(.2); }
  25%  { opacity:0; }
  45%  { opacity:1; transform:scale(.9); }
  100% { opacity:1; transform:scale(1.15); }
}
@keyframes death-card{
  0%   { filter:none; transform:none; opacity:1; }
  22%  { transform:translate(-3px, 2px) rotate(-1.5deg); }
  30%  { transform:translate(4px, -2px) rotate(1.5deg); filter:brightness(1.6) contrast(1.2); }
  38%  { transform:translate(-2px, 1px) rotate(-.5deg); filter:none; }
  60%  { filter:grayscale(.6) brightness(.8); opacity:1; transform:none; }
  100% { filter:grayscale(1) brightness(.4); opacity:0; transform:translateY(14px) scale(.92); }
}
@media (prefers-reduced-motion: reduce){ .death-ghost{ display:none; } }

/* Normal 1v1 result: who won, and what to do next. */
.win-banner__title{ margin-bottom:14px; }
.win-banner__actions{ display:flex; gap:10px; justify-content:center; flex-wrap:wrap; }
.win-banner__btn{ appearance:none; cursor:pointer; font:inherit; font-family:var(--font-body); font-size:14px; font-weight:700; letter-spacing:.02em; text-transform:none; text-decoration:none;
  padding:9px 16px; border-radius:8px; border:1px solid var(--brass); color:#1a1408; background:linear-gradient(180deg, #f2ddac, var(--brass)); }
.win-banner__btn:hover{ filter:brightness(1.08); }
.win-banner__btn--quiet{ color:var(--brass-glow); background:transparent; }
.win-banner__btn--quiet:hover{ background:rgba(201,167,92,.14); }
