/* game.css — rules-engine chrome: initiative bar, prompts, opponent piles,
   queue, game-over. Matches the classic paper aesthetic of base/zones/card. */

/* ---------- initiative / phase bar ---------- */

#initiative-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 8px 18px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  position: relative;
  /* Above #card-layer (z 10): no peeling/tucked card may ever paint over the
     Pass buttons, no matter how the docks fan. */
  z-index: 20;
}

#turn-info {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  font-size: 14px;
  letter-spacing: 0.04em;
}

/* ---------- floating essence readout ---------- */
.essence-readout { display: inline-flex; gap: 4px; align-items: center; margin-left: 4px; }
.essence-readout--empty { display: none; }
.essence-pip {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 10.5px;
  font-weight: 800;
  line-height: 1;
  color: #14100a;
  box-shadow: inset 0 -2px 3px rgba(0,0,0,0.35), 0 1px 2px rgba(0,0,0,0.45);
  border: 1px solid rgba(0,0,0,0.55);
}
/* The count badge (n > 1): a small dark plate at the pip's top-right — the
 * color letter owns the center, the count never displaces it. */
.essence-pip .essence-pip-n {
  position: absolute;
  top: -4px;
  right: -4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 10px;
  height: 10px;
  padding: 0 2px;
  box-sizing: border-box;
  border-radius: 5px;
  background: #17120b;
  color: #f2e8cf;
  font-size: 8px;
  font-weight: 800;
  font-style: normal;
  line-height: 1;
  border: 1px solid rgba(0,0,0,0.6);
  box-shadow: 0 1px 2px rgba(0,0,0,0.5);
}
.essence-pip--w { background: radial-gradient(circle at 35% 30%, #f7f0d0, #d9c88f); }
.essence-pip--u { background: radial-gradient(circle at 35% 30%, #7db9e8, #1d6aa8); color: #eaf4fb; }
.essence-pip--b { background: radial-gradient(circle at 35% 30%, #8d8178, #3a322b); color: #ece5da; }
.essence-pip--r { background: radial-gradient(circle at 35% 30%, #f0946a, #b8481f); color: #ffeee4; }
.essence-pip--g { background: radial-gradient(circle at 35% 30%, #7fbf74, #2e6b34); color: #e8f4e4; }
.essence-pip--c { background: radial-gradient(circle at 35% 30%, #d9dde4, #a8adb8); }

.turn-pill {

  padding: 3px 12px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: rgba(201, 168, 106, 0.16);
  border: 1px solid var(--line);
  color: #f3e9c8;
  white-space: nowrap;
}
.turn-pill--you {
  background: rgba(120, 180, 110, 0.22);
  border-color: rgba(160, 220, 150, 0.4);
}

#phase-name { font-weight: 700; color: #f0e6c8; white-space: nowrap; }
#initiative-name { opacity: 0.7; font-style: italic; white-space: nowrap; }

#initiative-controls { display: flex; align-items: center; gap: 10px; }

#pass-initiative {
  padding: 9px 20px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: #ffe9bd;
  background: linear-gradient(180deg, #6a5026, #402c12);
  border: 1px solid rgba(255, 220, 150, 0.45);
  border-radius: 9px;
  cursor: pointer;
  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.45);
}
#pass-initiative:hover:not(:disabled) { filter: brightness(1.15); }
#pass-initiative:active:not(:disabled) { transform: translateY(1px); }
#pass-initiative:disabled {
  opacity: 0.4;
  cursor: default;
  box-shadow: none;
}

/* Auto-pass preference + this-turn convenience. Same chrome, quieter. */
#auto-pass-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: #ead9a8;
  background: rgba(201, 168, 106, 0.12);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
#auto-pass-label input { accent-color: #b8a07a; cursor: pointer; margin: 0; }
#auto-pass-label:hover { background: rgba(201, 168, 106, 0.2); }

#end-turn {
  padding: 8px 14px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #ead9a8;
  background: linear-gradient(180deg, rgba(90, 74, 40, 0.7), rgba(56, 44, 20, 0.7));
  border: 1px solid rgba(230, 200, 140, 0.35);
  border-radius: 9px;
  cursor: pointer;
  box-shadow: 0 2px 7px rgba(0, 0, 0, 0.35);
  white-space: nowrap;
}
#end-turn:hover:not(:disabled) { filter: brightness(1.12); background: linear-gradient(180deg, rgba(110, 90, 50, 0.8), rgba(70, 56, 26, 0.8)); }
#end-turn:active:not(:disabled) { transform: translateY(1px); }
#end-turn:disabled {
  opacity: 0.35;
  cursor: default;
  box-shadow: none;
}
#end-turn.end-turn--armed {
  color: #fff3cf;
  border-color: rgba(255, 220, 150, 0.65);
  box-shadow: 0 0 0 1px rgba(255, 214, 130, 0.25), 0 2px 8px rgba(0, 0, 0, 0.4);
}

/* ---------- the queue (spells/abilities awaiting resolution) ---------- */

#queue-view {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  flex: 1;
  justify-content: center;
  overflow: hidden;
}
#queue-view .card {
  width: 51px;
  height: 71px;
  flex: none;
  position: relative;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.5);
  border-radius: 5px;
}
#queue-view .queue-tag {
  position: absolute;
  left: -6px;
  top: -6px;
  z-index: 2;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  color: #ffe9bd;
  background: radial-gradient(circle at 35% 30%, #5a4426, #2c1f0e);
  border: 1px solid rgba(255, 220, 150, 0.4);
  border-radius: 50%;
}
#queue-view:empty::after {
  content: 'queue empty';
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.28;
}

/* ---------- opponent piles ---------- */

#opp-piles {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 8px;
}
/* Both rows render piles at the same compact size — whether they sit in the
   strip (deck) or inside their crypt/void tray drawer — so the two
   player areas mirror each other. */
#piles .pile, #opp-piles .pile,
.pile-tray--you .pile, .pile-tray--opp .pile {
  width: 90px;
  height: 126px;
}
#piles .pile .card.card--static, #opp-piles .pile .card.card--static,
.pile-tray--you .pile .card.card--static, .pile-tray--opp .pile .card.card--static {
  width: 90px;
  height: 126px;
}
/* The opponent's header is at the TOP of the table, so their badge anchors to
   the bottom-right corner — the side facing the player (the board center),
   instead of poking toward the screen edge. (-4px clears the __label, which
   hangs at bottom: -18px.) The player's own piles keep the top-right badge:
   their top edge is what faces the board. */
#opp-piles .pile__count {
  top: auto;
  bottom: -4px;
}

/* Pile labels mirror the badge convention: each row's label hangs off the
   side that faces the board. The opponent's header has the battleground below
   it, so their labels hang at bottom: -18px (zones.css) and arcane over the
   board — visible. The player's strip is the LAST row of the table, flush
   with the viewport bottom, so a label hanging below the pile would fall
   ~16px past the screen edge (html/body clip it) and never show. The
   player's labels hang ABOVE their pile instead — toward the board their
   top edge already faces, the same side their top-right badge occupies. */
.pile-tray--you .pile__label {
  bottom: auto;
  top: -18px;
}

/* ---------- prompt bar (targets / attackers / blockers) ---------- */

#prompt-bar {
  position: fixed;
  left: 50%;
  bottom: 162px;
  transform: translateX(-50%);
  z-index: 400;
  display: none;
  align-items: center;
  gap: 14px;
  padding: 10px 18px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
}
#prompt-bar.prompt-bar--active { display: flex; }

#prompt-text {
  font-size: 14px;
  letter-spacing: 0.04em;
  color: #f3e9c8;
  white-space: nowrap;
}

#prompt-buttons { display: flex; gap: 8px; }

.prompt-btn {
  padding: 8px 16px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  color: #ffe9bd;
  background: linear-gradient(180deg, #6a5026, #402c12);
  border: 1px solid rgba(255, 220, 150, 0.45);
  border-radius: 8px;
  cursor: pointer;
}
.prompt-btn:hover { filter: brightness(1.15); }
.prompt-btn--confirm { background: linear-gradient(180deg, #2f6b3a, #173d1f); border-color: rgba(150, 220, 160, 0.45); }
.prompt-btn--cancel { background: linear-gradient(180deg, #6b2f2f, #3d1717); border-color: rgba(220, 150, 150, 0.4); }
.prompt-btn:disabled { opacity: 0.4; cursor: default; }

/* ---------- target / selection highlights ---------- */

/* Every selection ring reuses the contact-shadow pseudo (.card::before),
   whose box is tuned for shadows (`inset: 4px 1px -3px` in card.css) — a
   lopsided box makes a box-shadow ring land at uneven offsets and leave a
   visible gap under the bottom border (the reported "playable glow doesn't
   hug the card" bug). Each ring state therefore re-centers the box on the
   card first: the ring's inner edge lands ON the face border on all four
   sides, and the per-state spreads below keep their deliberate hierarchy
   (gold 2px over a 1px dark hairline for playable — see the .card.playable
   rule for the pale-theme contrast contract; 3px for the combat/target
   rings). */
.card.playable::before,
.card.targetable::before,
.card.target-selected::before,
.card.attacker::before,
.card.block-armed::before,
.card.block-paired::before {
  inset: 0;
}

/* The effect hall's demo cards are .card--static clones, and card.css strips
   their shadow pseudo (content: none) — which also stripped every selection
   ring, so the hall's "rings" scene showed no ring language at all. The ring
   states re-enable the pseudo: the rings ARE the content there. */
.card--static.playable::before,
.card--static.targetable::before,
.card--static.target-selected::before,
.card--static.attacker::before,
.card--static.block-armed::before,
.card--static.block-paired::before {
  content: '';
}

.card.targetable { cursor: pointer; }
.card.targetable::before {
  box-shadow:
    0 0 0 3px rgba(120, 200, 255, 0.85),
    0 0 18px 4px rgba(120, 200, 255, 0.5);
}
.card.target-selected::before {
  box-shadow:
    0 0 0 3px rgba(150, 230, 150, 0.95),
    0 0 22px 6px rgba(150, 230, 150, 0.6);
}

.card.playable { }
.card.playable::before {
  /* The gold playable ring (never regress the pale-theme legibility fix):
     measured on the real render, the old bare gold line (255,224,150 @0.7 —
     luma ≈228) sat DIRECTLY against White's pale Carrara marble border
     (luma ≈230) — a Δluma of ~4, perceptually invisible (the reported
     "white cards don't feature the yellow can-be-played border" bug; on
     dark themes the same ring read at Δluma ~200). A DARK SEPARATION
     HAIRLINE hugging the card edge rides under the gold: the 1px dark line
     gives the gold something to contrast against on pale borders, while on
     dark themes it reads as the ring's own crisp inner edge (the glow
     already separated it from the felt). The gold line keeps its
     documented 2px thickness (1→3px spread = 2px of gold). */
  box-shadow:
    0 0 0 1px rgba(24, 16, 8, 0.55),
    0 0 0 3px rgba(255, 224, 150, 0.8),
    0 0 14px 3px rgba(255, 224, 150, 0.35);
}

/* A card you can't legally play right now (ritual timing, essence, no target).
   Identical to every other card — the only difference is the missing gold
   highlight. No opacity, no tint: "playable" is the only visual signal. */
.card.unplayable::before {
  /* intentionally plain: no extra shadow, no dimming */
}

/* Transient "why can't I?" toast near the prompt bar. */
.why-toast {
  position: fixed;
  left: 50%;
  bottom: 150px;
  transform: translateX(-50%);
  z-index: 1200;
  padding: 6px 14px;
  border-radius: 8px;
  background: rgba(30, 26, 36, 0.95);
  border: 1px solid rgba(180, 180, 200, 0.35);
  color: #efe9dd;
  font-size: 13px;
  pointer-events: none;
  animation: why-toast-in 1.6s ease forwards;
}
@keyframes why-toast-in {
  0% { opacity: 0; transform: translateX(-50%) translateY(6px); }
  10% { opacity: 1; transform: translateX(-50%) translateY(0); }
  80% { opacity: 1; }
  100% { opacity: 0; }
}

.card.attacker::before {
  box-shadow:
    0 0 0 3px rgba(255, 150, 120, 0.9),
    0 0 18px 4px rgba(255, 150, 120, 0.5);
}

/* Combat ring language: green = your blocker (armed / assigned),
   ember-red = an attacker that already has blockers on it. */
.card.block-armed::before {
  box-shadow:
    0 0 0 3px rgba(120, 220, 140, 0.9),
    0 0 18px 4px rgba(120, 220, 140, 0.45);
}
.card.block-paired::before {
  box-shadow:
    0 0 0 3px rgba(255, 110, 110, 0.9),
    0 0 18px 4px rgba(255, 110, 110, 0.45);
}

/* Damage-assignment order (the attacker's choice): a small numbered disc pinned to the
   blocker's top-right corner, overlapping the border so it reads as a badge,
   never clipped under it. Rendered on .card (position:absolute) so the badge
   rides the card's transform. */
.card.order-1::after, .card.order-2::after, .card.order-3::after, .card.order-4::after {
  position: absolute;
  right: 5px;
  top: -8px;
  left: auto; bottom: auto; right: 5px;
  z-index: 6;
  width: 23px; height: 23px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800; font-family: Georgia, serif;
  color: #fff3e0;
  background: radial-gradient(circle at 35% 30%, #8a5a1f, #4a2f0c);
  border: 1.5px solid rgba(255, 210, 140, 0.7);
  box-shadow: 0 2px 7px rgba(0, 0, 0, 0.6), 0 0 10px rgba(255, 190, 110, 0.25);
  box-sizing: content-box;
  opacity: 1;
  pointer-events: none;
}
.card.order-1::after { content: '1'; }
.card.order-2::after { content: '2'; }
.card.order-3::after { content: '3'; }
.card.order-4::after { content: '4'; }

/* ---------- living attack / health ----------

   When a being's current A/H differs from the printed face (a bolster, or
   marked damage), the printed digits are covered by a small patch painted in
   the frame's own parchment style and the *current* numbers are drawn in its
   place — the card reads as if its own ink updated, never a foreign block
   pasted on top. Boosts tint the raised number deep green; unhealed damage
   tints the remaining health ember red and shows the reduced total;
   lethal damage throbs crimson. */

.st-live {
  position: absolute;
  --chip-pad: 1px;
  /* The pill is ALWAYS visible on a being in play and reproduces the printed
     chip's parchment + digits. The TABLE pill (CardView) is DIGITS-ONLY —
     the chip's weapon/heart icons are 7.2px specks at 150px scale, so they
     are close-up chrome, stamped by the hover PreviewPane at the chip's
     true fractions × its size factor (the .st-live-w/-h rules below carry
     no size of their own anymore; the pane sizes them inline). A
     boosted/hurt being shows the pill UPDATED in place — never a second,
     smaller pill pasted beside the printed one. Geometry is anchored to
     PANELS.chip (CardView stamps it). */
  right: 10px;      /* PANELS.chip right margin (cardLayout)               */
  bottom: 2px;      /* PANELS.chip bottom margin                           */
  width: 36.4px;    /* PANELS.chip.w                                        */
  height: 12px;     /* PANELS.chip.h                                        */
  display: flex;
  align-items: center;
  justify-content: center; /* digits cluster on the chip's center line      */
  padding: 0;
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 11px;  /* lifted from the bitmap's 9.9px for table legibility  */
  font-weight: 700;
  line-height: 1;
  color: #1c1208;
  text-shadow: 0 0 1.2px rgba(255, 240, 214, 0.5); /* soften onto the copper */
  pointer-events: none;
  z-index: 4;
}
/* The patch that occludes the printed chip — it must be invisible as a seam,
   so it reproduces the chip's rounded-rect silhouette and radial chipFace
   gradient (per-color, stamped by chipTheme.js from the def's theme — the
   specimens' chipFace: #ffdca6 → #ee9c46 → #7c3e0e on red, etc.), light
   concentrated at the upper-left, and lets the face's anti-aliased rim show
   through. It bleeds --chip-pad past every edge, deepening the gradient and
    rim so the pill reads crisply on a dark battleground card at 1.0 scale. */
.st-live::before {
  content: '';
  position: absolute;
  left: calc(-1 * var(--chip-pad)); right: calc(-1 * var(--chip-pad));
  top: calc(-1 * var(--chip-pad)); bottom: calc(-1 * var(--chip-pad));
  border-radius: 999px;
  background: radial-gradient(160% 200% at 35% 28%, var(--chip-a, #ffdca6) 0%, var(--chip-b, #ee9c46) 45%, var(--chip-c, #7c3e0e) 100%);
  box-shadow: inset 0 0 0 1px rgba(58, 27, 6, 0.55);
}
/* The close-up's chip replica svg (PreviewPane): fills the pill box, and the
   chip-space viewBox makes every mark land where the printed face plants it.
   The tints ride the .st-ink/--shadow digit classes + the pill-root classes. */
.st-live-svg {
  position: absolute;
  left: 0; top: 0;
  width: 100%;
  height: 100%;
  z-index: 4;
  pointer-events: none;
}
/* The close-up's digit ink is the TABLE pill's ink — the same dark brown in
   both places, never the theme's close-up ink (the reported "white digits on
   the zoomed card" bug; table renders dark, so the pane must too). The
   shadow pass under the ink uses the same soft cream glow the table pill's
   text-shadow paints, so the dark digits stay legible on a dark chip. */
.st-ink--p { fill: #1c1208; }
.st-live--boost .st-ink--p { fill: #1e5c26; }
.st-ink--t { fill: #1c1208; }
.st-ink--t--up { fill: #1e5c26; }  /* a raised health digit tints green too */
.st-ink--t--hurt { fill: #a52f14; } /* the text element carries --hurt */
.st-live--hurt .st-ink--t { fill: #a52f14; }
.st-live--lethal .st-ink--t { fill: #7d1010; animation: st-throb 0.9s ease-in-out infinite; }
.st-live > span { position: relative; } /* digits sit above the patch */
.st-live-p, .st-live-t { min-width: 7px; text-align: center; }
.st-live[hidden] { display: none; }
/* The slash hugs its digits (the reported "large gap from the slash" bug):
   1px each side, and the min-widths stay on the digits only. */
.st-live-s { opacity: 0.75; margin: 0 1px; }
.st-live-p--up { color: #1e5c26; }
.st-live-t--up { color: #1e5c26; } /* a raised health digit tints green too (2x markers) */
.st-live-t--hurt { color: #a52f14; }
.st-live--boost .st-live-p { text-shadow: 0 0 6px rgba(80, 180, 90, 0.55); }
.st-live--boost .st-live-t--up { text-shadow: 0 0 6px rgba(80, 180, 90, 0.55); }
.st-live--hurt .st-live-t { text-shadow: 0 0 6px rgba(220, 90, 50, 0.5); }
.st-live--lethal .st-live-t {
  color: #7d1010;
  animation: st-throb 0.9s ease-in-out infinite;
}

.st-live--pop { animation: st-pop 0.35s ease; }
@keyframes st-pop {
  0% { transform: scale(1); }
  35% { transform: scale(1.28); }
  100% { transform: scale(1); }
}

/* The negative-health case ("2/-1") needs a visible center: widen the slash
   gap so the sign never smashes against the attack digit at table scale. */
.st-live--wide-sep .st-live-s { margin: 0 2.4px; }
@keyframes st-throb {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* ---------- arcane-queue chrome (tucked duplicates in the arcane dock) ------

   A arcane group with 4+ copies tucks: the first three fan out with a few px
   of right/up offset each, the 4th collapses the pile and the queue shows
   deck-thickness edges riding the base card's transform. From the 5th copy
   the queue stops growing cards and a round badge carries the total count in
   the base card's top-right corner. Used members peel out (rot 90°,
   lifted above the queue) and re-merge on ready. */

/* The two deck-thickness edges are real divs appended to the base card's
   element — .card's own ::before/::after are reserved (contact shadow /
   combat order badges), so the queue chrome uses children instead. Their
   offset points up-LEFT, the way the tucked pile recedes (aura-style tuck),
   so the edges read as the depth those tucked copies occupy.
   CRITICAL: the face is the .card element's OWN background, and a stacking
   context paints negative-z children ABOVE the element's own background —
   an opaque full-card child here blacked out the entire base and barred
   across the tucked copies' exposed name bands (the reported "top card is
   black / name half covered" bug). The edge is therefore a transparent L:
   top+left borders only, offset thin enough to stay below the tucked name's
   baseline (glyph bottoms sit ~4.7px above the base's top edge at dock
   scale; the slivers reach 3.6px). No box-shadow — it would ring the whole
   box over the face. */
.card > .tuck-edge {
  position: absolute;
  inset: 0;
  border-radius: 7.2% / 5.143%;
  background: transparent;
  border-top: 2px solid #0f0d0b;
  border-left: 2px solid #0f0d0b;
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}
.card.tuck-tucked > .tuck-edge { opacity: 1; }
.card > .tuck-edge--a { z-index: -1; transform: translate(-3px, -3px); }
.card > .tuck-edge--b { z-index: -2; transform: translate(-5.5px, -5.5px); }

/* Occupy: a Tower tucked under its being. The bastion reads as a quiet,
   lit-from-within fortification — a cool stone frame with a faint watch-fire
   glow along the exposed edges, so at a glance the being is "holding a
   defence". Because it's occupied it is ALWAYS upright (never the 90° spent
   rotation), so this frame never fights the use transform. Painted as a real
   child div (not a pseudo) for the same reason as .tuck-edge. */
.card > .occupy-edge {
  position: absolute;
  inset: -3px;
  border-radius: 11px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.28s ease;
  box-shadow:
    0 0 0 1px rgba(214, 186, 128, 0.35),
    0 0 14px 2px rgba(255, 196, 96, 0.18),
    inset 0 0 18px rgba(20, 16, 10, 0.35);
  border: 2px solid rgba(58, 50, 38, 0.9);
}
.card.occupied > .occupy-edge { opacity: 1; }


/* Count badge: total copies in the queue, top-right like .pile__count.
   The badge must stay readable even while the base card is mid-flight from a
   use peel-out or an entry spring, so it never crossfades with opacity. */
.card > .tuck-count {
  position: absolute;
  right: -8px;
  top: -9px;
  z-index: 6;
  min-width: 25px;
  height: 25px;
  padding: 0 7px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 800;
  line-height: 1;
  color: #ffe9bd;
  background: radial-gradient(circle at 35% 30%, #5a4426, #2c1f0e);
  border: 1px solid rgba(255, 220, 150, 0.4);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.55);
  box-sizing: content-box;
  visibility: hidden;
  pointer-events: none;
}
.card.tuck-counted > .tuck-count { visibility: visible; }

/* Used members spring out of the queue for clarity; nothing special to
   paint — CardView rotates to 90° and lifts above the base. The class is a
   marker for tests + a slightly brighter edge while separated. */
.card.arcane-separated::before {
  box-shadow:
    0 0 12px 3px rgba(255, 235, 190, 0.18);
}

/* ---------- game log ticker ---------- */
.game-log {
  position: fixed;
  left: 14px;
  bottom: 138px;               /* just above the (130px) player strip + dock */
  width: 296px;
  max-height: 142px;
  overflow-y: auto;
  z-index: 1100;
  padding: 8px 10px;
  border-radius: 10px;
  background: rgba(20, 17, 12, 0.72);
  border: 1px solid rgba(201, 168, 106, 0.35);
  color: #e9e0cd;
  font-size: 12.5px;
  line-height: 1.35;
  backdrop-filter: blur(2px);
  pointer-events: none;         /* never blocks play */
}
.game-log__line { padding: 1px 0; animation: log-in 0.25s ease; border-bottom: 1px solid rgba(201, 168, 106, 0.12); }
.game-log__line:last-child { border-bottom: none; }
@keyframes log-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* Rematch veil: covers the fast of reload so the new table materializes. */
#rematch-veil {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: radial-gradient(ellipse at center, #1d1a12 0%, #0a0806 100%);
  animation: veil-in 0.35s ease forwards;
}
#fx-layer {
  position: fixed;
  inset: 0;
  z-index: 1400;
  pointer-events: none; /* purely visual */
  overflow: hidden;
}
@keyframes veil-in { from { opacity: 0; } to { opacity: 1; } }

/* ---------- spell showcase (cast cinematic & negated shatter) ----------

   A spotlight stage in the table's center: the spell floats up under two
   negate-rotating arcane rings and a breathing halo tinted by its caster,
   name bannered beneath. On resolve the real card blooms back onto its slot;
   on a negate the face shatters into a grid of its own bitmap tiles. */

.spell-showcase {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 1500;
  width: 150px;
  height: 210px;
  margin-left: -75px;          /* center on its translate point */
  margin-top: -105px;
  pointer-events: none;
  transition: transform 0.5s cubic-bezier(0.2, 0.9, 0.25, 1.2);
  will-change: transform;
}
.card--hidden { visibility: hidden; } /* the real card rests while showcase plays */
.card--materialize { animation: card-materialize 0.45s ease; }
@keyframes card-materialize {
  0% { filter: brightness(2.6); transform-origin: center; }
  100% { filter: brightness(1); }
}

.spell-showcase .sg-card {
  position: absolute;
  inset: 0;
  border-radius: 7.2% / 5.143%;
  background-size: 100% 100%;
  box-shadow: 0 10px 40px rgba(0,0,0,0.7);
  /* Hoverable: the staged spell previews its close-up (PreviewPane) — the
     one place the full face + flavor is readable mid-showcase. The pane
     (z 3000) renders above the showcase (z 1500). */
  pointer-events: auto;
  cursor: zoom-in;
}
/* The staged spell's FOIL layer (rare cards): the full laminate over the
   compact face, and the moving rainbow sheen the resting card leaves off
   (the foil rides ONLY zoomed/staged surfaces — the user's call). The bands
   (sweep + flare) inherit their motion from the .card-foil rules in
   card.css; --foil-phase (stamped by SpellShowcase.open) desyncs per id. */
.spell-showcase .sg-foil {
  position: absolute;
  inset: 0;
  border-radius: 7.2% / 5.143%;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;                   /* over the card face, under the banner DOM */
}
.spell-showcase .sg-halo {
  position: absolute;
  inset: -34px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--sg-tint) 0%, transparent 62%);
  opacity: 0.5;
  animation: sg-breathe 1.8s ease-in-out infinite;
}
.spell-showcase .sg-ring {
  position: absolute;
  inset: -22px;
  border-radius: 50%;
  border: 1.5px solid var(--sg-tint);
  border-top-color: transparent;
  border-left-color: transparent;
}
.spell-showcase .sg-ring--a { animation: sg-spin 5s linear infinite; }
.spell-showcase .sg-ring--b { inset: -34px; animation: sg-spin-rev 7s linear infinite; opacity: 0.7; }
.spell-showcase .sg-banner {
  position: absolute;
  left: 50%;
  bottom: -30px;
  transform: translateX(-50%);
  padding: 3px 12px;
  border-radius: 6px;
  background: rgba(18, 14, 8, 0.9);
  border: 1px solid rgba(201, 168, 106, 0.5);
  color: #f3e6c4;
  font-family: 'Radley', Georgia, 'Times New Roman', serif;
  font-size: 14px;
  font-weight: 400;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.spell-showcase--resolve { transition: transform 0.4s ease, opacity 0.4s ease; opacity: 0; transform-origin: center; }
.spell-showcase--negated .sg-ring, .spell-showcase--negated .sg-halo, .spell-showcase--negated .sg-banner, .spell-showcase--negated .sg-foil { opacity: 0; transition: opacity 0.3s; }

.spell-showcase .sg-shatter { position: absolute; inset: 0; }
.spell-showcase .sg-tile {
  position: absolute;
  animation: sg-tile-fly 0.9s ease-in forwards;
  animation-delay: var(--sd);
}
.spell-showcase .sg-ember {
  position: absolute;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: radial-gradient(circle, #ffd88a 0%, #d97a2b 70%, transparent 100%);
  animation: sg-ember 1.1s ease-out forwards;
  animation-delay: var(--ed);
}
@keyframes sg-spin { to { transform: rotate(360deg); } }
@keyframes sg-spin-rev { to { transform: rotate(-360deg); } }
@keyframes sg-breathe { 0%, 100% { transform: scale(1); opacity: 0.5; } 50% { transform: scale(1.12); opacity: 0.8; } }
@keyframes sg-tile-fly {
  from { opacity: 1; transform: translate(0, 0) rotate(0); }
  to { opacity: 0; transform: translate(var(--sx), var(--sy)) rotate(var(--sr)); }
}
@keyframes sg-ember {
  from { opacity: 1; transform: translate(0, 0) scale(1); }
  to { opacity: 0; transform: translate(var(--ex), var(--ey)) scale(0.4); }
}

/* ---------- combat badge / counters ---------- */

.perm-badge {
  position: absolute;
  left: -8px;
  top: -10px;
  z-index: 3;
  padding: 2px 8px;
  font-size: 12px;
  font-weight: 700;
  color: #ffe9bd;
  background: radial-gradient(circle at 35% 30%, #5a4426, #2c1f0e);
  border: 1px solid rgba(255, 220, 150, 0.45);
  border-radius: 10px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
  pointer-events: none;
  white-space: nowrap;
}

/* ---------- game over ---------- */

#game-over {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(7, 10, 16, 0.78);
  backdrop-filter: blur(3px);
  /* The veil rises over ~0.7s so the final board reads (the last combat /
   * the killing blow) before the result covers it — never an abrupt slap
   * over the end-state. GameOverView.show adds a short beat of delay too. */
  animation: gameOverIn 0.7s ease;
}
@keyframes gameOverIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
#game-over[hidden] { display: none; }

/* Dismissed ("View Board"): the veil clears entirely so the final
 * battleground is inspectable, the result card slides/fades out, and only
 * the small reopen chip (#game-over-fab) stays. The overlay itself stops
 * intercepting pointer input while its card is hidden. */
#game-over.game-over--dismissed {
  background: transparent;
  backdrop-filter: none;
  pointer-events: none;
}
#game-over.game-over--dismissed #game-over-card {
  opacity: 0;
  transform: translateY(-14px) scale(0.97);
  pointer-events: none;
  transition: opacity 0.34s ease, transform 0.34s cubic-bezier(0.3, 0.8, 0.4, 1);
}

/* The reopen chip pinned top-center once the card is dismissed. */
#game-over-fab {
  position: fixed;
  top: 64px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 8999;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 18px;
  font-family: inherit;
  font-size: 13px;
  color: #e8d9b0;
  background: linear-gradient(180deg, #31281a, #1c150d);
  border: 1px solid rgba(201, 168, 106, 0.5);
  border-radius: 999px;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.5);
  cursor: pointer;
  animation: gameOverIn 0.3s ease;
}
#game-over-fab[hidden] { display: none; }
#game-over-fab:hover { filter: brightness(1.18); }
#game-over-fab-result {
  font-weight: 700;
  letter-spacing: 0.07em;
  color: #f5eacd;
}
.game-over-fab__cta { opacity: 0.7; font-style: italic; }

#game-over-card {
  text-align: center;
  padding: 40px 64px 34px;
  background: linear-gradient(180deg, #2a2114, #171008);
  border: 1px solid rgba(201, 168, 106, 0.5);
  pointer-events: auto;
  border-radius: 16px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.7);
}
#game-over-title {
  font-size: 34px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: #f5eacd;
}
#game-over-board {
  margin-top: 26px;
  padding: 11px 22px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  color: #e8d9b0;
  background: linear-gradient(180deg, #3d3322, #1f1810);
  border: 1px solid rgba(255, 220, 150, 0.28);
  border-radius: 9px;
  cursor: pointer;
}
#game-over-board:hover { filter: brightness(1.15); }
#game-over-sub {
  margin-top: 10px;
  font-size: 15px;
  opacity: 0.75;
  font-style: italic;
}
#game-over-restart {
  margin-top: 26px;
  padding: 11px 28px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  color: #ffe9bd;
  background: linear-gradient(180deg, #6a5026, #402c12);
  border: 1px solid rgba(255, 220, 150, 0.45);
  border-radius: 9px;
  cursor: pointer;
}
#game-over-restart:hover { filter: brightness(1.15); }
#game-over-actions { display: flex; gap: 12px; justify-content: center; }
#game-over-new {
  margin-top: 26px;
  padding: 11px 28px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  color: #e8d9b0;
  background: linear-gradient(180deg, #453a26, #2a2114);
  border: 1px solid rgba(255, 220, 150, 0.35);
  border-radius: 9px;
  cursor: pointer;
}
#game-over-new:hover { filter: brightness(1.15); }
/* Legal drag destinations: labelled emerald sockets, never free placement.
   The sheen is a child, leaving the card's combat-ring pseudos untouched. */
.drag-guides {
  position: fixed;
  inset: 0;
  z-index: 1100;
  pointer-events: none;
}
.drag-guide {
  position: absolute;
  box-sizing: border-box;
  border: 1px dashed rgba(151, 225, 186, 0.65);
  border-radius: 12px;
  background: radial-gradient(ellipse at center, rgba(65, 173, 124, 0.08), transparent 75%);
  transition: background-color 0.16s, box-shadow 0.16s;
}
.drag-guide--active {
  border: 2px solid #a8edc6;
  background-color: rgba(65, 173, 124, 0.12);
  box-shadow: inset 0 0 24px rgba(99, 230, 162, 0.15), 0 0 16px rgba(99, 230, 162, 0.25);
}
.drag-guide__label {
  color: #e1f8e9;
  background: rgba(17, 43, 34, 0.95);
  border: 1px solid rgba(168, 237, 198, 0.55);
  border-radius: 12px;
  padding: 6px 12px;
  font: 14px Georgia, serif;
  text-align: center;
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: min(280px, 80vw);
}
.drag-sheen {
  position: absolute;
  inset: 0;
  border-radius: 7.2% / 5.143%;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(125deg, transparent 20%, rgba(140, 255, 192, 0.27) 48%, transparent 76%);
  box-shadow: inset 0 0 22px rgba(139, 255, 192, 0.35);
  transition: opacity 0.15s;
}
.card--drag-ready > .drag-sheen { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .drag-guide, .drag-sheen { transition: none; }
}

/* ================================ */
/* The search browser (tutor gate)   */
/* ================================ */

/* A deck-search opened by the SEARCH gate (Timbershade Ranger's family) rides
   the `.pile-fan` held slip in pick mood — the same overlapped surface as a
   clicked crypt/void (see zones.css): the matching deck cards fan out above
   the searching player's hand as gold-breathing pick buttons, hover lifts a
   sliver and shows the enlarged close-up, and Cancel/Continue closes it.
   The old `.search-browser*` spaced-grid modal is retired (never regress — it
   rendered face-less, detached, and often over the hand, the reported
   "Timbershade shows a popup with no visible cards" bug). */

/* ================================ */
/* The pile browser (crypt / void)   */
/* ================================ */

/* The crypt/void pile surfaces are now the `.pile-fan` held slip (see
   zones.css): a clicked pile fans its cards out overlapped (read mood) above
   the open tray, and the reanimation pick AND the deck-search gate ride the
   same fan in pick mood. The old spaced-grid modal — and its `.pile-browser*`
   and `.search-browser*` chrome — is retired: the deck-search now fans out
   over the searching player's hand like every other pile. */

/* The deck-select lobby (codex) now lives in styles/lobby.css — the staged
   hero showcase, codex rail and living atmosphere (see AGENTS.md). */
