/* coscryption — UI styles
 *
 * The auth/lobby chrome keeps the original dark/amber/Cinzel look (root
 * tokens below). The run screen uses a separate set of tokens to match the
 * Claude Design mockup (design/coscryption Run Screen.html): warm card
 * stock, dark espresso borders, Special Elite for card names, Manrope
 * everywhere else. Both palettes share the same page background so the
 * transition from lobby → run is seamless.
 */

:root {
  /* Card sizes: board slots, hand and pickers; phones shrink them (body.is-compact). */
  /* Cards scale with the window's height on desktop, so the header, the
     board's three rows and the whole hand fit on screen (96px in a tall
     window; tester 2026-10-04). Phones set their own (body.is-compact). */
  --card-w: max(64px, min(96px, calc((100vh - 244px) / 5.6)));
  --card-w-reward: 130px;
  --card-w-deck: 58px;
  /* Existing auth/lobby palette */
  --bg-dark: #1a1410;
  --bg-medium: #2a1f18;
  --text: #d4c8a8;
  --accent: #b88a3e;

  /* Run-screen palette (mockup) */
  --card-stock: #c7c0ab;       /* face-up card body */
  --card-espresso: #1c1712;    /* name bar bg, borders, stat-pill text */
  --card-cream: #e9dfc9;       /* name text, sigil badge text, stat-pill bg */
  --card-placeholder: #8f9077; /* behind card art when no image */
  --card-shadow: rgba(0, 0, 0, 0.5);

  /* Face-down (deep green stripes) */
  --face-down-1: #16211a;
  --face-down-2: #10170f;

  /* Empty slot (translucent over page bg) */
  --empty-border: rgba(184, 147, 90, 0.28);
  --empty-label: rgba(184, 147, 90, 0.4);
}

body {
  margin: 0;
  background: var(--bg-dark);
  color: var(--text);
  font-family: 'Manrope', 'Cinzel', 'Georgia', serif;
}

#app {
  /* No top padding: the topbar sticks flush to the viewport edge (see
     .run-header below) so its own gap doesn't cost the page vertical
     space above it. */
  padding: 0 24px 24px;
}

/* ---- Auth / lobby / shared chrome (existing) ---- */

button {
  background: var(--bg-medium);
  color: var(--text);
  border: 1px solid var(--accent);
  padding: 8px 16px;
  cursor: pointer;
  font-family: inherit;
  font-family: 'Cinzel', serif;
  letter-spacing: 0.02em;
}

button:hover {
  background: var(--accent);
  color: var(--bg-dark);
}

input {
  background: var(--bg-medium);
  color: var(--text);
  border: 1px solid var(--accent);
  padding: 8px;
  font-family: inherit;
}

h1, h2, h3 {
  font-family: 'Cinzel', serif;
  letter-spacing: 0.04em;
}

/* Lobby identity line: "Logged in as <username>" + a small Log out
   button. Sits between the page title and the room buttons. Username
   inherits the muted body color so it reads as secondary chrome, not
   the main subject; the username itself is bolded via <strong> in the
   HTML. Flex-end justifies the Log out button to the right edge of
   the line without making the username wrap awkwardly. */
.lobby-identity {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 8px 0 4px;
  color: #a89a7c;
  font-size: 12px;
  letter-spacing: 0.04em;
}

.lobby-identity-name strong {
  color: var(--text);
  font-weight: 700;
}

.lobby-logout {
  /* Smaller than the room buttons so the line doesn't compete with
     the page's primary actions below it. */
  padding: 4px 10px;
  font-size: 11px;
}

.chosen-pool {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}


/* ---- Run screen ---- */

.action-error-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: rgba(139, 30, 30, 0.22);
  border: 1.5px solid rgba(139, 30, 30, 0.6);
  color: #e6b8b8;
  border-radius: 8px;
  padding: 10px 14px;
  margin-bottom: 12px;
  font-family: 'Manrope', sans-serif;
  font-size: 13px;
}

.action-error-dismiss {
  background: transparent;
  border: none;
  color: #e6b8b8;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 2px 4px;
}

.run-header {
  display: grid;
  /* Outer columns equal (1fr each) so the center column — HP scale +
     Trade/End Turn — sits at the true horizontal middle of the bar,
     not just "whatever's left after the title" (which used to leave it
     hugging the right edge, next to the sidebar toggle, since the old
     `1fr auto auto` gave column 1 all the leftover space). Requested:
     "move the end turn and trade button in the topbar to the middle so
     we can more easily reach it". */
  grid-template-columns: 1fr auto 1fr;
  gap: 24px;
  align-items: center;
  /* v0.2.0: padding reduced from `12px 18px` to `4px 12px` to free
     vertical space — the user is still scrolling. Border-radius /
     shadow kept so the header still reads as a distinct bar. */
  padding: 4px 12px;
  background: var(--card-espresso);
  border: 1.5px solid var(--card-espresso);
  border-radius: 6px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
  /* margin-bottom reduced from 18px to 6px */
  margin-bottom: 6px;
  color: var(--card-cream);
  font-family: 'Manrope', sans-serif;
  /* Sticks to the top of the viewport instead of scrolling away with
     the board — requested so the header (turn/HP/actions) stays
     reachable without losing the vertical space it used to cost as
     ordinary top padding on #app. */
  position: sticky;
  top: 0;
  z-index: 10;
}

.run-header-left {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}

.run-header-center {
  display: flex;
  align-items: center;
  /* v0.2.0: gap reduced from 12px to 8px to shave another row of
     vertical space. The hp-label / pip-bar pair (plus Trade/End Turn)
     needs to fit comfortably without dominating the header height. */
  gap: 8px;
}

.run-header-right {
  display: flex;
  align-items: center;
  /* Column 3 is now 1fr (matches column 1) to center column 2 — pin
     this column's content (just the sidebar toggle) to the far right
     edge instead of letting it drift toward the middle. */
  justify-content: flex-end;
}

.run-title {
  font-family: 'Cinzel', serif;
  /* v0.2.0: font-size reduced from 22px to 16px — the user removed
     the big `<h1>coscryption</h1>` heading and the room code already,
     so this Cinzel title in the top bar is the only title left and
     can afford to be smaller. */
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--card-cream);
  text-transform: uppercase;
}

.run-encounter {
  font-size: 13px;
  color: rgba(233, 223, 201, 0.7);
  letter-spacing: 0.04em;
}

.difficulty-badge {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.14em;
  padding: 2px 8px;
  border-radius: 4px;
  text-transform: uppercase;
}
.difficulty-easy { background: #6f9b58; color: var(--card-espresso); }
.difficulty-normal { background: #c89a3e; color: var(--card-espresso); }
.difficulty-hard { background: #b53a3a; color: var(--card-cream); }
.difficulty-boss { background: #6b1f1f; color: var(--card-cream); }

/* Balance bar — v0.2.0 (round 5) single-row horizontal pip strip
   that reimplements the visual of the old tilting scale. The old
   scale (150×70px, fulcrum/post/beam/pans/weights/labels) was 70px
   tall — way too much vertical space in the top bar for what it
   conveyed. The new bar's geometry is FIXED and its COLORS are
   fixed too:
     [Y Y Y Y Y · B B B B B]    <- max_hp yellow + 1 colorless separator + max_deck brown
   A RED marker pip starts in the separator slot and moves LEFT when
   the players take damage and RIGHT when the monster takes damage.
   Marker offset from separator = (deck_lost_ratio) - (hp_lost_ratio),
   each relative to its own max, scaled separately per direction so
   the needle lands exactly on the physical edge of whichever side is
   actually depleted (matching the real loss/win conditions). The
   pips themselves never change color — the damage state is encoded
   entirely by the marker's distance from center. */
.pip-bar {
  display: flex;
  flex: 0 0 auto;
  padding: 2px 4px;
  background: rgba(0, 0, 0, 0.25);
  border-radius: 4px;
}

.pip-bar-row {
  display: flex;
  gap: 2px;
  align-items: center;
}

.pip {
  position: relative; /* anchors .has-marker's ::after ring */
  width: 8px;
  height: 8px;
  border-radius: 1px;
  flex: 0 0 auto;
}

/* Shared HP pips (left) — always YELLOW-filled, regardless of current
   HP. The damage state lives in the marker's position, not in any
   per-pip fill flip — see .pip-bar's block comment for the why. */
.pip--left {
  background: #f5c542;
  border: 1px solid #8a6d1f;
}
.pip--left.is-filled {
  /* (kept as a no-op alias so existing markups that opt into
     `is-filled` explicitly still resolve to the same yellow, even
     though renderScale always sets it itself now.) */
  background: #f5c542;
  border-color: #8a6d1f;
}

/* Monster HP pips (right) — always brown-filled, regardless of
   current monster HP. Symmetric to .pip--left. */
.pip--right {
  background: #3a2a1c;
  border: 1px solid #6b5a3c;
}
.pip--right.is-filled {
  background: #3a2a1c;
  border-color: #6b5a3c;
}

/* Separator — colorless slot in the middle of the bar. No fill, just
   a thin border so the layout reads as `[...][ | ][...]` instead of
   one continuous block. */
.pip--separator {
  background: transparent;
  border: 1px dashed rgba(233, 223, 201, 0.18);
  width: 4px;
}

/* Red marker — an outlined square drawn OVER whichever pip it
   currently occupies, not a replacement for (or cover of) that pip's
   own look. Went through two earlier attempts that both ended up
   hiding the pip's real fill color: a blurred outer ring
   (`box-shadow`, `inset: -2px`) that visually read as a solid red
   square at this size, then a solid dot INSIDE the pip (`inset: 1px`)
   that still covered most of an 8px square. Requested directly:
   "change the needle look & feel to just a outlined square over the
   existing squares" — transparent fill, just a border, sized to sit
   just outside the pip so 100% of the pip's own color stays visible. */
.pip.has-marker::after {
  content: '';
  position: absolute;
  inset: -2px;
  background: transparent;
  border: 2px solid #d33a3a;
  border-radius: 1px;
  pointer-events: none;
}

.phase-status {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #d8b27a;
  padding: 4px 10px;
  border-radius: 6px;
  background: rgba(216, 178, 122, 0.08);
  border: 1px solid rgba(216, 178, 122, 0.2);
}

.hp-label {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(233, 223, 201, 0.7);
  font-weight: 700;
}

/* .hp-value (the "HP X/Y · Deck X/Y" text readout) removed — requested:
   "this text can be deleted". The pips + needle are the whole display
   now (see renderScale in board.js). */

.phase-badge {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 700;
  padding: 4px 10px;
  background: rgba(233, 223, 201, 0.08);
  border: 1px solid rgba(233, 223, 201, 0.25);
  border-radius: 999px;
  color: var(--card-cream);
}

/* ---- Run screen layout: header, board, sidebar, card pool, action panel */

/* run-main just holds the board now — the sidebar floats independently
   (see .run-sidebar below), so opening/closing it no longer changes
   the board's own available width. Used to be a grid with the sidebar
   as a second column, which squeezed #board's `auto-fit` grid below
   the width needed for 3 columns side by side and wrapped the third
   player's board onto a new row underneath the other two — reported
   exactly this: "if we're in a 3 player room, the third board flies
   underneath the other 2 when you open the side menu." */
.run-main {
  display: block;
  /* Room for the hand dock's peeking cards (fixed to the viewport
     bottom, ~67px visible — see .hand-dock) so they rest below the
     board's own last row instead of covering it. */
  padding-bottom: 76px;
}

body:not(.sidebar-open) .run-sidebar {
  display: none;
}

/* Floating window, not a layout column: requested "can you make the
   log a floatable window?" — position: fixed + a draggable header
   (.sidebar-drag-handle, wired up in board.js's renderSidebar) so it
   can be moved anywhere over the board instead of permanently
   claiming space next to it. Anchored top-right by default; dragging
   overrides left/top inline (see _makeDraggable). Widened from
   240–320px to 420–520px and log lines no longer truncate with an
   ellipsis — requested "make it wider so we can read all the text." */
.run-sidebar {
  position: fixed;
  top: calc(60px + env(safe-area-inset-top, 0px));
  right: 16px;
  z-index: 20;
  background: var(--card-espresso);
  border: 1.5px solid var(--card-espresso);
  border-radius: 8px;
  padding: 12px;
  color: var(--card-cream);
  font-family: 'Manrope', sans-serif;
  font-size: 12px;
  width: 420px;
  max-width: min(92vw, 520px);
  max-height: min(75vh, 640px);
  overflow-y: auto;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
}

.run-sidebar.is-dragging {
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
  user-select: none;
}

.sidebar-drag-handle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: -12px -12px 10px;
  padding: 8px 10px;
  background: rgba(233, 223, 201, 0.06);
  border-bottom: 1px solid rgba(233, 223, 201, 0.15);
  border-radius: 8px 8px 0 0;
  cursor: move;
  font-family: 'Cinzel', serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  touch-action: none;
}

.sidebar-close {
  background: transparent;
  border: none;
  color: var(--card-cream);
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 2px 4px;
}

.sidebar-close:hover {
  color: var(--accent);
}

.sidebar-section {
  margin-bottom: 14px;
}

/* Back-to-lobby lives in its own section at the bottom of the sidebar
   so it doesn't get dragged around with the log/table sections above
   it. */
.sidebar-lobby-nav {
  margin-top: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--accent);
}
.sidebar-back-to-lobby {
  width: 100%;
  padding: 8px 12px;
  background: var(--bg-medium);
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 4px;
  font-size: 13px;
  cursor: pointer;
}
.sidebar-back-to-lobby:hover {
  background: var(--accent);
  color: var(--bg-dark);
}

.sidebar-section h3 {
  font-family: 'Cinzel', serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 6px;
}

.log-list, .opp-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 320px;
  overflow-y: auto;
}

.log-list li, .opp-list li {
  padding: 3px 6px;
  border-bottom: 1px solid rgba(233, 223, 201, 0.08);
  font-size: 11px;
  color: rgba(233, 223, 201, 0.8);
  /* Full lines, no ellipsis truncation — requested "make it wider so
     we can read all the text": the panel is wider now too, but a long
     log sentence ("Player 1's Grizzly destroyed Player 2's Squirrel.")
     could still run past even 420px, so let it wrap instead of cut. */
  white-space: normal;
  overflow-wrap: break-word;
}

.log-empty {
  color: rgba(233, 223, 201, 0.4);
  font-style: italic;
}

.opp-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.opp-name {
  font-weight: 700;
  color: var(--card-cream);
}

.opp-stats {
  color: rgba(233, 223, 201, 0.6);
  font-size: 10px;
}

/* Card pool area (separate from the hand — draft / swap zone). */
.card-pool {
  margin-top: 16px;
  background: var(--card-espresso);
  border: 1.5px solid var(--card-espresso);
  border-radius: 8px;
  padding: 12px;
  color: var(--card-cream);
  font-family: 'Manrope', sans-serif;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
}

.card-pool h3 {
  font-family: 'Cinzel', serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 8px;
}

.card-pool-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  min-height: 80px;
}

.card-pool-empty {
  color: rgba(233, 223, 201, 0.4);
  font-style: italic;
  font-size: 12px;
  align-self: center;
}

/* Sidebar collapse toggle in the header. */
.sidebar-toggle {
  background: rgba(233, 223, 201, 0.08);
  border: 1px solid rgba(233, 223, 201, 0.25);
  color: var(--card-cream);
  border-radius: 6px;
  padding: 4px 10px;
  font-size: 14px;
  cursor: pointer;
  font-family: inherit;
}

.sidebar-toggle:hover {
  background: rgba(233, 223, 201, 0.16);
}

/* Board contains 1-3 player columns side-by-side. Each column is a
   narrow vertical block with monster reserve/front rows + your
   board/reserve rows. */
#board {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 440px));
  justify-content: center;
  gap: 12px;
}

.player-column {
  background: var(--card-espresso);
  border: 1.5px solid var(--card-espresso);
  border-radius: 8px;
  padding: 12px;
  max-width: 440px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
}

.player-column.is-you {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 3px 10px rgba(0, 0, 0, 0.4);
}

/* Reserved-but-unjoined seat: same board shape, dimmed and non-interactive
   (pointer-events: none — the empty slots inside have no click handler
   anyway, but this also stops hover affordances from lighting up). */
.player-column.is-waiting {
  opacity: 0.45;
  filter: grayscale(0.5);
  pointer-events: none;
}

.player-column.is-waiting .player-name {
  color: rgba(233, 223, 201, 0.45);
  font-style: italic;
}

.row-section {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.row-label {
  font-family: 'Manrope', sans-serif;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(233, 223, 201, 0.55);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-top: 2px;
}

.row-label-sub {
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: rgba(233, 223, 201, 0.4);
  text-transform: none;
}

/* Peek row — the encounter monster's upcoming-play queue. Each peek card
   has a ▼ arrow underneath to indicate "advances into monster front on the
   monster's turn" (queue/descend semantics, like the original game). */
.peek-row {
  background: rgba(120, 160, 120, 0.06);
  border: 1px dashed rgba(120, 160, 120, 0.22);
  border-radius: 6px;
  padding: 4px;
}

.peek-arrow {
  position: absolute;
  left: 50%;
  bottom: -14px;
  transform: translateX(-50%);
  color: rgba(120, 160, 120, 0.7);
  font-size: 10px;
  line-height: 1;
  pointer-events: none;
}

/* YOUR FRONT row — the 2D player board. `player-front-row` lets us tune
   spacing separately from the monster row (e.g. tighter if there are 2
   rows stacked). */
.your-front-row {
  background: rgba(184, 147, 90, 0.06);
  border-radius: 6px;
  padding: 4px;
}

.monster-front-row {
  background: rgba(120, 160, 120, 0.06);
  border-radius: 6px;
  padding: 4px;
}

.zone-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(233, 223, 201, 0.12);
}

.column-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(233, 223, 201, 0.12);
}

.column-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(233, 223, 201, 0.12);
}

.player-ident {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: 'Manrope', sans-serif;
  color: var(--card-cream);
}

.avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(233, 223, 201, 0.1);
  border: 1.5px solid var(--card-cream);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.04em;
}

.avatar--empty {
  background: rgba(233, 223, 201, 0.08);
  border-style: dashed;
  color: rgba(233, 223, 201, 0.4);
}

.player-name {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.you-badge {
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 2px 8px;
  background: var(--accent);
  color: var(--card-espresso);
  border-radius: 999px;
}

.pools {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: 'Manrope', sans-serif;
  font-size: 12px;
  font-weight: 700;
}

.pool {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(233, 223, 201, 0.18);
}

/* v0.2.0 (round 6): .pool-blood and its diamond marker removed —
   blood is transient (created + consumed within one PlaceCard
   action), so showing it persistently in the header confused the
   two pools' roles. Only bones is shown now. */

/* No ::before pip here — the 🦴 emoji is the icon (board.js prepends
   it to the bones count directly), matching the design source
   (Run.dc.html: `🦴 {{ player.bones }}`). A CSS square pip briefly
   replaced it at some point; restored per report: "the bone counter's
   icon has been changed recently it was a bone before". */

/* v0.2.0 (round 6): "prominent" variant of the bones chip — used in
   the player column header. Larger font, more padding, brighter color
   so the user notices when it ticks up (their report: "still not
   seeing the bones counter going up after sacrificing cards"). The
   round-1 flash was too subtle at the original chip size. */
.pool-bones--prominent {
  font-family: 'Cinzel', serif;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 4px 12px;
  background: rgba(245, 197, 66, 0.18);
  border-color: rgba(245, 197, 66, 0.45);
  color: #f5c542;
}

/* v0.2.0: bones pool pulse when the counter ticks up — the user
   reported "not receiving bones for cards that die" because the
   counter was silently updating. The flash makes the change visible.
   Triggered by `.pool-up` class added in board.js renderPlayerColumn
   when app.js's poolUps set contains `bones-${idx}` / `bones-monster`.
   Round 6: stronger flash (brighter, longer, larger scale) so it's
   noticeable on the prominent chip. */
.pool.pool-up {
  animation: pool-up-pulse 900ms ease-out;
}

@keyframes pool-up-pulse {
  0%   { background: rgba(245, 197, 66, 0.85); transform: scale(1.45); box-shadow: 0 0 12px rgba(245, 197, 66, 0.9); }
  40%  { background: rgba(245, 197, 66, 0.65); transform: scale(1.20); box-shadow: 0 0 8px rgba(245, 197, 66, 0.6); }
  100% { background: rgba(245, 197, 66, 0.18); transform: scale(1.00); box-shadow: 0 0 0 rgba(245, 197, 66, 0); }
}

/* Loading spinner — reported: "when i click to start a game in the
   lobby it takes a while to load, can you add a 'loading' animation so
   the user knows the game is loading?" Used both standalone (the
   `.loading-screen` full-page state while a room's WS/catalog fetch is
   in flight) and inline (a small one appended to whichever lobby
   button was just clicked, via `.spinner--inline`, since POST /rooms
   itself can also take a moment). */
.spinner {
  display: inline-block;
  width: 28px;
  height: 28px;
  border: 3px solid rgba(233, 223, 201, 0.25);
  border-top-color: #f5c542;
  border-radius: 50%;
  animation: spinner-spin 0.8s linear infinite;
}

.spinner--inline {
  width: 14px;
  height: 14px;
  border-width: 2px;
  margin-left: 8px;
  vertical-align: middle;
}

@keyframes spinner-spin {
  to { transform: rotate(360deg); }
}

.loading-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-height: 60vh;
  color: #e9dfc9;
  font-size: 14px;
}

/* v0.2.0: .row-label removed — the row labels (PEEK / MONSTER FRONT /
   YOUR FRONT / SEAT FRONT) were deleted from the JS renderers as part
   of the vertical-space cleanup. The CSS rule is kept for any leftover
   references during the transition but no longer actively styled. */

.card-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, var(--card-w)));
  justify-content: start;
  gap: 6px;
  min-height: calc(var(--card-w) * 1.25);
}

/* Multi-row player board: stacks 3 (or 4) row strips vertically. The
   monster row uses .card-row directly since it's always 1×4. */
.card-grid {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-height: 120px;
}

/* Make back-row cards slightly smaller so the front row dominates the
   visual weight (Inscryption-style depth cue). */
.card-grid .card-row:not(:first-child) .card {
  transform: scale(0.9);
  transform-origin: top center;
}
.card-grid .card-row:not(:first-child) {
  min-height: var(--card-w);
}

.hand {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(72px, 96px));
  justify-content: start;
  gap: 6px;
  min-height: 96px;
}

.hand-empty {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: 'Manrope', sans-serif;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(233, 223, 201, 0.4);
  border: 1.5px dashed var(--empty-border);
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.18);
  padding: 12px;
}

/* ---- Card (the mockup) ---- */

.card {
  position: relative;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: 'Manrope', sans-serif;
  /* Card-flip animation (Task 33). Uses 2D rotation so the existing art
     <img> stacks correctly without a 3D wrapper. .flipped is added on
     place, removed after 600ms by board.js animatePlace(). */
  transform: rotateY(0deg);
  transition: transform 0.6s ease;
  will-change: transform;
}

.card.flipped {
  transform: rotateY(180deg);
}

.card--slot {
  width: 100%;
  aspect-ratio: 5 / 7;
  background: rgba(0, 0, 0, 0.18);
  border: 1.5px dashed var(--empty-border);
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.card--slot.occupied {
  /* Filled slots use the face-up filled card styling — they look identical
     to in-hand cards but keep the slot grid width. Keep the same 5:7
     ratio as .card--slot/.card--hand: `aspect-ratio: auto` here broke the
     flex-column height chain (.card-art's `flex: 1` + `img { height:
     100% }` had no definite ancestor height to resolve against), letting
     card art balloon to its native pixel size instead of the grid cell. */
  border: 2px solid var(--card-espresso);
  background: var(--card-stock);
  box-shadow: 0 3px 8px var(--card-shadow);
}

.card--hand {
  background: var(--card-stock);
  border: 2px solid var(--card-espresso);
  box-shadow: 0 3px 8px var(--card-shadow);
  aspect-ratio: 5 / 7;
  /* Explicit width: unlike .card--slot (sized by its grid track), this is
     a flex item in .hand-strip-cards with nothing else to give it a
     definite width, so aspect-ratio + the art <img>'s `height: 100%` had
     nothing to resolve against and it rendered near the art's native
     pixel size.
     v0.2.0: width reduced from 130px to 96px — board cards are also
     96px (`.card-row { grid-template-columns: repeat(4, minmax(0, 96px)) }`),
     and the user wants hand cards to match board cards so the hand row
     is the same height as a board row. Saves significant vertical
     space when the hand strip is at the bottom of the page. */
  width: var(--card-w);
}

.card--reward {
  background: var(--card-stock);
  border: 2px solid var(--card-espresso);
  box-shadow: 0 4px 10px var(--card-shadow);
  aspect-ratio: 5 / 7;
  width: var(--card-w-reward);
}

/* Totem pillar — the enemy-side totem card from the Totem Card
   Battle event. Static (no hover lift, no flip, no click); rendered
   to the left of the monster-front row. */
.card--totem {
  width: 110px;
  height: 150px;
  border: 2px solid var(--accent);
  box-shadow: 0 0 12px rgba(170, 130, 80, 0.4);
  cursor: default;
}
.card--totem:hover {
  transform: none;
  filter: brightness(1.05);
}
.totem-slot {
  display: inline-block;
  margin-right: 8px;
  vertical-align: top;
}

.card--pool {
  background: var(--card-stock);
  border: 2px solid var(--card-espresso);
  box-shadow: 0 3px 8px var(--card-shadow);
  aspect-ratio: 5 / 7;
  width: var(--card-w);
}

.card.is-clickable {
  cursor: pointer;
  transition: transform 100ms ease, box-shadow 100ms ease;
}

.card.is-clickable:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 12px var(--card-shadow);
}

.card.is-selected {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* A sacrifice the player didn't choose — forced because they dropped
   the new card directly onto this one's slot. Red outline (matches the
   drag-over sacrifice-drop hint) instead of the normal gold "picked"
   outline, and not clickable — there's nothing to toggle, it's going
   either way. */
.card.is-locked {
  outline: 2px solid #c23b3b;
  outline-offset: 2px;
  cursor: not-allowed;
}

.card.is-dragging {
  opacity: 0.4;
}

.card--slot.is-drop-target {
  cursor: copy;
}

.card--slot.is-drop-target.drag-over {
  border-color: var(--accent);
  background: rgba(184, 147, 90, 0.18);
  box-shadow: 0 0 0 2px var(--accent) inset;
}

/* Dropping a hand card onto an OCCUPIED slot sacrifices whatever's
   there to make room — requested: "i want to place a card on the same
   spot of a card i want to sacrifice." Red tint instead of the normal
   gold drop highlight so it reads as "this card will be destroyed",
   not just an ordinary placement. */
.card--slot.is-sacrifice-drop-target {
  cursor: copy;
}

.card--slot.is-sacrifice-drop-target.drag-over {
  border-color: #c23b3b;
  box-shadow: 0 0 0 2px #c23b3b inset, 0 0 16px 4px rgba(194, 59, 59, 0.5);
}

/* A real, empty slot that isn't placeable yet (no-gaps rule: the row in
   front of it isn't filled). Visually distinct from a normal empty slot
   so it doesn't invite a drop that would just get rejected. */
.card--slot.is-blocked-slot {
  opacity: 0.35;
  background: repeating-linear-gradient(
    45deg,
    rgba(0, 0, 0, 0.18),
    rgba(0, 0, 0, 0.18) 6px,
    rgba(0, 0, 0, 0.28) 6px,
    rgba(0, 0, 0, 0.28) 12px
  );
}

/* Attack-lunge animation: the attacker's slot translates to the victim's
   slot and back. --attack-dx/--attack-dy are set inline (in px) by
   app.js's animateOneAttack() from the two slots' actual bounding-rect
   delta, so the lunge lands exactly on the victim regardless of layout.
   The 100% state matches .card's own resting transform (rotateY(0deg) via
   translate(0,0) here) so removing .is-attacking afterward causes no
   snap-back through the base .card transition. */
@keyframes attack-lunge {
  0% { transform: translate(0, 0); }
  35% { transform: translate(var(--attack-dx, 0px), var(--attack-dy, 0px)); }
  60% { transform: translate(var(--attack-dx, 0px), var(--attack-dy, 0px)); }
  100% { transform: translate(0, 0); }
}

.card--slot.is-attacking {
  animation: attack-lunge 600ms ease-in-out;
  z-index: 5;
}

/* Victim feedback at the moment of impact (timed to attack-lunge's 35%
   arrival point by app.js). is-hit for a card that survives, is-hit-killed
   for one that dies — both cleaned up right before the next snapshot
   repaints (or removes) the slot. */
@keyframes hit-flash {
  0%, 100% { filter: none; }
  30% {
    filter: brightness(1.8) saturate(1.4);
    box-shadow: 0 0 0 3px #ff4d4d inset, 0 0 16px 4px rgba(255, 77, 77, 0.7);
  }
}

.card--slot.is-hit {
  animation: hit-flash 400ms ease-out;
}

@keyframes hit-killed {
  0% { transform: scale(1) rotate(0deg); opacity: 1; filter: brightness(1.8); }
  40% { transform: scale(1.1) rotate(-4deg); opacity: 1; filter: brightness(2) saturate(1.6); }
  100% { transform: scale(0.2) rotate(12deg); opacity: 0; }
}

.card--slot.is-hit-killed {
  animation: hit-killed 450ms ease-in forwards;
}

/* An unblocked attack (no defending card in that lane) hits the shared
   HP pool directly — the empty slot itself flashes, since there's no
   card there to flash instead. */
@keyframes direct-hit-flash {
  0%, 100% { box-shadow: none; }
  30% {
    box-shadow: 0 0 0 3px #ff4d4d inset, 0 0 16px 4px rgba(255, 77, 77, 0.7);
    border-color: #c23b3b;
  }
}

.card--slot.is-direct-hit {
  animation: direct-hit-flash 400ms ease-out;
}

/* The monster's peek card sliding forward, down into its monster-front
   slot, on the turn it's actually played — --descend-dx/--descend-dy
   are set inline by app.js's animateOneDescend() from the two slots'
   actual bounding-rect delta. Unlike attack-lunge this doesn't return to
   0,0 — the card is genuinely relocating, not attacking-and-staying —
   so the class is removed right before the next snapshot's real DOM
   swap (same pattern as attack-lunge), leaving no time for .card's own
   transform transition to visibly snap it back. */
@keyframes descend-forward {
  0% { transform: translate(0, 0); }
  100% { transform: translate(var(--descend-dx, 0px), var(--descend-dy, 0px)); }
}

.card--slot.is-descending {
  /* forwards: hold the end position until animateOneDescend (app.js)
     commits the card into the board slot at 520ms — without it the card
     snapped back to the peek row for a frame after the 500ms slide. */
  animation: descend-forward 500ms ease-in forwards;
  z-index: 5;
}

/* Sprinter/hefty sideways lane move (app.js animateOneMove) — reuses
   descend-forward's translate keyframes; forwards holds the end frame
   until the element is moved into its new slot. */
.card--slot.is-moving {
  animation: descend-forward 350ms ease-in-out forwards;
  z-index: 5;
}

.card-empty-label {
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--empty-label);
}

.card-name {
  padding: 3px 4px;
  background: var(--card-espresso);
  font-family: 'Special Elite', 'Courier New', monospace;
  font-size: 9.5px;
  color: var(--card-cream);
  text-align: center;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}

.card-art {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background: var(--card-placeholder);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.card-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: grayscale(0.15) contrast(1.05);
}

.card-art--missing {
  background: var(--card-placeholder);
}

.card-art--missing::after {
  content: 'Art';
  font-family: 'Manrope', sans-serif;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(28, 23, 18, 0.5);
}

/* No .card-costs top-left badge either — requested: "on the cards you
   added symbols for the blood and bone cost, that's not necessary as
   this information is already on the card itself." Same reasoning as
   the sigil badge removal below. */

/* No .card-sigils/.sigil corner badge — the card art already shows the
   sigil (requested: "we can see the sigil on the card itself so remove
   the icon"). The whole-card hover tooltip (.sigil-tooltip below)
   still surfaces every sigil's real name and description. */

/* Custom whole-card hover tooltip (static/board.js's _showCardTooltip)
   — a single shared element positioned under the hovered card. Not the
   native `title` attribute: that only showed name/power/toughness, has
   a ~1s hover delay, and — when this info lived on the tiny per-sigil
   badge instead of the card — needed pixel-precise aiming a normal
   hover never landed on ("only seeing the name and attack/HP score").
   Attaching it to the whole card and showing instantly fixes both. */
.sigil-tooltip {
  position: fixed;
  z-index: 40;
  max-width: 240px;
  background: var(--card-espresso);
  color: var(--card-cream);
  border: 1.5px solid var(--accent);
  border-radius: 6px;
  padding: 6px 10px;
  font-family: 'Manrope', sans-serif;
  font-size: 11px;
  line-height: 1.4;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
  gap: 4px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 80ms ease-out;
}

.sigil-tooltip.is-visible {
  opacity: 1;
  visibility: visible;
}

.sigil-tooltip > strong {
  color: var(--card-cream);
  font-size: 11px;
  letter-spacing: 0.03em;
}

.sigil-tooltip-row {
  display: block;
  padding-top: 2px;
  border-top: 1px solid rgba(233, 223, 201, 0.2);
}

.sigil-tooltip-row b {
  color: var(--accent);
}

/* Stat scraps (design 2026-10-02): a torn paper scrap pasted over the
   power (left) and health (right) printed on the art, always shown; red
   ink when the value is below the print (a hit). Sized from
   the card's width, so they fit every card size. The art box must
   stretch to the card's width: as a size container it no longer takes
   its width from the image, and board slots centre their content — it
   collapsed to 0px there (2026-10-02, every board card lost its art). */
.card-art { container-type: inline-size; align-self: stretch; }
.card-stat {
  position: absolute;
  z-index: 1;
  width: 27cqw;
  aspect-ratio: 40 / 46;
  pointer-events: none;
  filter: drop-shadow(1px 2px 1px rgba(0, 0, 0, 0.45));
}
.card-power { left: 1%; bottom: 9%; transform: rotate(-5deg); }
.card-toughness { right: 1%; bottom: 1%; transform: rotate(4deg); }
.card-stat svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.card-stat path { fill: #efdcb4; }
.card-stat-value {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding-top: 6%;
  font-family: 'Anton', Impact, 'Arial Narrow', sans-serif;
  font-size: 19cqw;
  line-height: 1;
  color: #1c1410;
  filter: url(#inkrough);
}
.card-stat.is-damaged .card-stat-value { color: #8e1b14; }

/* Sigils a card gained (not drawn in its art). */
.card-extra-sigils {
  position: absolute;
  top: 3px;
  right: 3px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  pointer-events: none;
}
.card-extra-sigils img {
  width: 22px;
  height: 22px;
  padding: 2px;
  border-radius: 50%;
  /* Black, not cream: the sigil icons are pale green (tester 2026-10-02). */
  background: rgba(16, 12, 9, 0.92);
  border: 1px solid #b8935a;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
.card--hand .card-extra-sigils img, .card--slot .card-extra-sigils img { width: 18px; height: 18px; }
.card--reward .card-extra-sigils img { width: 26px; height: 26px; }

.sigil-reward-about {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 14px;
}
.sigil-reward-about img {
  width: 44px;
  height: 44px;
  padding: 4px;
  border-radius: 50%;
  background: rgba(16, 12, 9, 0.92);
  border: 1px solid #b8935a;
}
.sigil-reward-desc { margin: 0; font-size: 13px; opacity: 0.9; }

/* Face-down state — when a card is in the monster's hand/deck and revealed
   only as a striped back. Currently unused (all cards face up) but kept
   for the future "hidden monster hand" feature. */
.card--facedown {
  width: 100%;
  height: 100%;
  border-radius: 6px;
  background: repeating-linear-gradient(
    45deg,
    var(--face-down-1) 0,
    var(--face-down-1) 5px,
    var(--face-down-2) 5px,
    var(--face-down-2) 10px
  );
  border: 2px solid var(--card-espresso);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5) inset;
  display: flex;
  align-items: center;
  justify-content: center;
}

.card--facedown::after {
  content: '';
  width: 55%;
  height: 65%;
  border: 1px solid rgba(120, 160, 120, 0.35);
  border-radius: 4px;
}

/* ---- Player avatars (topbar) ---- */

/* Moved from a standalone row at the bottom of the page into the
   topbar's right section (see renderHeader). Reported: "the icons for
   the players (when you click them you see their hands) can be on the
   topbar" — part of a wider change (see .hand-dock and .deck-flyout
   below) to stop a permanent bottom strip from costing the page a full
   extra band of vertical space ("i still have to scroll to see the
   whole page"). Horizontal now instead of the old vertical column,
   sized down slightly to sit comfortably in the header bar. */
.turn-order {
  display: flex;
  flex-direction: row;
  gap: 6px;
  align-items: center;
  margin-right: 10px;
}

.turn-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(233, 223, 201, 0.1);
  border: 1.5px solid var(--card-cream);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--card-cream);
}

.turn-avatar.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--card-espresso);
}

.turn-avatar.is-clickable {
  cursor: pointer;
}

/* Whose hand the strip below currently shows — independent of whose
   turn it is (.is-active), since you can look at a teammate's hand
   without it being their turn. */
.turn-avatar.is-viewed {
  box-shadow: 0 0 0 2px var(--card-cream);
}

/* ---- Hand dock (fixed overlay at the bottom of the viewport) ---- */

/* Reported: "the hand cards could only show the top half, at the
   bottom of the screen (like an overlay), when you hover them, the
   card you are hovering slides up (over the board)." `position: fixed`
   so this costs the page NO layout height at all (unlike the old
   always-present bottom strip) — cards simply hang half off the bottom
   edge of the viewport. The container itself ignores pointer events so
   clicks pass through its empty margins to the board underneath; only
   the actual card row re-enables them. */
.hand-dock {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  justify-content: center;
  pointer-events: none;
  z-index: 25;
}

.hand-strip-cards {
  display: flex;
  gap: 4px;
  padding-top: 60px; /* room for a hovered card to slide fully into view above the fold */
  pointer-events: auto;
}

/* Default: only the top half is visible — the rest hangs below the
   viewport's bottom edge (the fixed container's own bottom already
   sits flush with it, so anything past that is simply off-screen, no
   `overflow: hidden` needed). Pure-CSS `:hover` — no JS state. */
.hand-dock .card--hand {
  transform: translateY(50%);
  transition: transform 160ms ease, box-shadow 160ms ease;
  cursor: pointer;
}

.hand-dock .card--hand:hover,
.hand-dock .card--hand.is-selected {
  transform: translateY(0);
  z-index: 1;
  box-shadow: 0 -8px 20px rgba(0, 0, 0, 0.6);
}

.hand-dock .card--hand.is-dragging {
  opacity: 0.4;
}

/* ---- Deck flyout (fixed, bottom-right corner) ---- */

/* Reported: "the 2 decks only need to slide in in the bottom right
   corner, when you have to pick cards at the start of your turn."
   Always rendered (see renderDecks) so there's something for this
   transition to animate; slides fully off-screen to the right
   otherwise, costing no layout space either way. */
.deck-flyout {
  position: fixed;
  right: 24px;
  bottom: 0;
  display: flex;
  gap: 8px;
  align-items: flex-end;
  padding: 10px 12px 0;
  background: var(--card-espresso);
  border: 1.5px solid var(--accent);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.5);
  transform: translateX(140%);
  transition: transform 220ms ease;
  z-index: 26;
}

.deck-flyout.is-visible {
  transform: translateX(0);
}

.deck {
  width: 80px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.deck-art {
  width: 70px;
  height: 96px;
  border: 2px solid var(--card-espresso);
  border-radius: 4px;
  background:
    repeating-linear-gradient(
      45deg,
      #1c2820 0px,
      #1c2820 5px,
      #14201a 5px,
      #14201a 10px
    );
}

.deck--side .deck-art {
  background:
    repeating-linear-gradient(
      45deg,
      #2a2238 0px,
      #2a2238 5px,
      #211b2e 5px,
      #211b2e 10px
    );
}

.deck-label {
  font-family: 'Manrope', sans-serif;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--card-cream);
  text-align: center;
}

.deck-count {
  color: var(--accent);
}

.deck.is-clickable {
  cursor: pointer;
  transition: transform 100ms ease;
}

.deck.is-clickable:hover .deck-art {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent);
  transform: translateY(-2px);
}

.deck:not(.is-clickable) .deck-art {
  opacity: 0.4;
}

.deck.is-empty-deck .deck-art {
  opacity: 0.2;
}

/* ---- Action panel ---- */

.action-panel {
  display: flex;
  gap: 6px;
  margin-top: 16px;
  padding: 10px 12px;
  background: var(--card-espresso);
  border-radius: 8px;
  border: 1.5px solid var(--card-espresso);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
}

.action-button {
  background: rgba(233, 223, 201, 0.06);
  color: var(--card-cream);
  border: 1px solid rgba(233, 223, 201, 0.25);
  border-radius: 6px;
  padding: 6px 12px;
  font-family: 'Manrope', sans-serif;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 100ms ease;
}

.action-button:hover {
  background: rgba(233, 223, 201, 0.14);
}

.action-button.is-selected {
  background: var(--accent);
  color: var(--card-espresso);
  border-color: var(--accent);
}

.action-button:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.action-button:disabled:hover {
  background: rgba(233, 223, 201, 0.06);
}

/* v0.2.0: inline variant of .action-button — used in the topbar
   middle (renderHeader appends Trade / End Turn next to the pip-bar).
   Slightly tighter padding so the buttons fit next to the HP readout
   without forcing the topbar to grow. */
.action-button--inline {
  padding: 4px 10px;
  font-size: 11px;
}
.header-actions { display: flex; align-items: center; gap: 8px; }
/* On a phone: Trade, Take back and End Turn share one row under the scale. */
body.is-compact .header-actions { flex: 1 1 100%; justify-content: center; gap: 6px; }
body.is-compact .header-actions .action-button { flex: 1 1 0; min-width: 0; padding: 8px 4px; font-size: 12px; white-space: nowrap; }

/* ---- Reward picker — a full-screen takeover (.modal-overlay is the
   backdrop; this is the box inside it), not an in-flow element at the
   bottom of the board. Requested: "the choose the reward at the end
   of a level when you kill the boss can be on top of the board (it
   can be 'fullscreen') and not at the bottom, we don't need the board
   anymore at this point, the game is done." Wider than the standard
   320px .modal-box since it needs to fit 3 reward-size (130px) cards
   side by side. ---- */

.reward-picker {
  width: min(90vw, 560px);
  max-height: 85vh;
  overflow-y: auto;
  background: var(--card-espresso);
  border: 1.5px solid var(--accent);
  color: var(--card-cream);
  padding: 24px;
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6);
}

.reward-picker h3 {
  font-family: 'Cinzel', serif;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 12px;
}

.reward-picker .chosen-pool {
  gap: 12px;
  justify-content: center;
}

.reward-picker .campfire-note {
  margin: 0 0 14px;
  font-size: 13px;
  opacity: 0.85;
}

.campfire-actions {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 16px;
}

.campfire-actions button {
  background: transparent;
  border: 1px solid rgba(184, 147, 90, 0.4);
  color: var(--card-cream);
  font-family: 'Manrope', sans-serif;
  font-weight: 600;
  font-size: 12px;
  padding: 8px 12px;
  border-radius: 5px;
  cursor: pointer;
}

.campfire-actions button:hover,
.campfire-actions button.is-primary {
  border-color: var(--accent);
  color: var(--accent);
}

/* ---- Trade / Donate + Sacrifice-to-pay modals ---- */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(8, 6, 4, 0.72);
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-box {
  width: 320px;
  max-width: 90vw;
  max-height: 80vh;
  overflow-y: auto;
  background: var(--card-espresso);
  border: 1.5px solid var(--accent);
  border-radius: 10px;
  padding: 18px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6);
  color: var(--card-cream);
  font-family: 'Manrope', sans-serif;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.modal-box h3 {
  font-family: 'Cinzel', serif;
  font-size: 15px;
  font-weight: 700;
  color: var(--accent);
  margin: 0 0 4px;
}

.modal-tabs {
  display: flex;
  gap: 6px;
}

.modal-tab {
  flex: 1;
  text-align: center;
  padding: 6px 4px;
  border-radius: 5px;
  cursor: pointer;
  background: transparent;
  border: 1px solid rgba(233, 223, 201, 0.25);
  font-size: 11px;
  font-weight: 600;
  color: var(--card-cream);
}

.modal-tab.is-selected {
  background: rgba(184, 147, 90, 0.22);
  border-color: var(--accent);
  color: var(--accent);
}

.modal-box select,
.modal-box input[type="number"] {
  width: 100%;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(184, 147, 90, 0.3);
  color: var(--card-cream);
  font-size: 12px;
  padding: 6px 8px;
  border-radius: 5px;
  box-sizing: border-box;
  font-family: inherit;
}

/* Confirm modal — used by the Resume screen's Delete button. Reuses
   .modal-overlay / .modal-box; just sizes the box wider so the body
   text doesn't wrap awkwardly and lays the buttons in a row. */
.confirm-modal {
  width: 420px;
}
.confirm-modal p {
  font-family: 'Manrope', sans-serif;
  font-size: 13px;
  color: var(--card-cream);
  margin: 0;
  line-height: 1.4;
  opacity: 0.85;
}
.confirm-modal-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 4px;
}
.confirm-modal-actions .menu-button {
  min-width: 100px;
}
/* Delete button is destructive — tint it so it doesn't look identical
   to a neutral confirm/cancel pair. */
.confirm-modal-confirm {
  border-color: rgba(229, 110, 110, 0.7) !important;
  color: rgba(229, 110, 110, 0.95) !important;
}
.confirm-modal-confirm:hover {
  background: rgba(229, 110, 110, 0.95) !important;
  color: var(--bg-dark) !important;
}

.modal-box button {
  background: transparent;
  border: 1px solid rgba(184, 147, 90, 0.4);
  color: var(--card-cream);
  font-family: 'Manrope', sans-serif;
  font-weight: 600;
  font-size: 12px;
  padding: 8px;
  border-radius: 5px;
  cursor: pointer;
}

.modal-box button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.modal-box button:not(:disabled):hover {
  border-color: var(--accent);
  color: var(--accent);
}
.menu-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  margin: 32px 0;
}
.menu-button {
  min-width: 240px;
  padding: 12px 24px;
  font-size: 14px;
}
/* Ultracoop's Easy / Hard choice, opened under its menu button. */
.ultracoop-level {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  max-width: 520px;
  text-align: center;
}
.ultracoop-level[hidden], .level-choice[hidden] { display: none; }
.ultracoop-level > span { flex-basis: 100%; opacity: 0.8; font-size: 13px; }
.ultracoop-level .menu-button { min-width: 120px; }

/* ---- Main menu: the candlelit table (design/Menu/Coscryption.html) ----
   The whole window is the table: candle glow, a dark oval of table top, a
   vignette and film grain. Players at the table on the left (Online
   Players), the four modes as cards in the middle. IM Fell English is
   bundled in static/fonts (SIL Open Font License). */
@font-face { font-family: 'IM Fell English'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/static/fonts/im-fell-english.woff2') format('woff2'); }
@font-face { font-family: 'IM Fell English'; font-style: italic; font-weight: 400; font-display: swap;
  src: url('/static/fonts/im-fell-english-italic.woff2') format('woff2'); }
@font-face { font-family: 'IM Fell English SC'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/static/fonts/im-fell-english-sc.woff2') format('woff2'); }

.table-menu {
  --tm-ink: #cdbf9f; --tm-ink-bright: #e6d6b8; --tm-muted: #9c8b74; --tm-faint: #6e5f4c;
  --tm-gold: #c9a46a; --tm-blood: #8e1b14; --tm-line: #3a2a20; --tm-line-warm: #4a3426;
  --tm-sc: 'IM Fell English SC', Georgia, serif;
  position: fixed; inset: 0; overflow-x: hidden; overflow-y: auto; z-index: 1;
  background: #0b0807; color: var(--tm-ink);
  font-family: 'IM Fell English', Georgia, serif;
}
.tm-glow, .tm-table, .tm-vignette, .tm-grain { position: fixed; pointer-events: none; }
.tm-glow { inset: 0; animation: tm-flicker 5s infinite;
  background: radial-gradient(ellipse 720px 520px at 50% 58%, rgba(196,110,40,0.20), rgba(120,40,16,0.10) 45%, rgba(0,0,0,0) 75%); }
.tm-table { left: 11%; right: 11%; top: 41%; height: 54%; border-radius: 50%;
  background: radial-gradient(ellipse at 50% 40%, #2a1b14, #160e0b 60%, rgba(11,8,7,0) 72%); }
.tm-vignette { inset: 0; background: radial-gradient(ellipse at 50% 50%, rgba(0,0,0,0) 45%, rgba(0,0,0,0.92) 100%); }
.tm-grain { inset: 0; width: 100%; height: 100%; mix-blend-mode: overlay; opacity: 0.18; z-index: 2; }
@keyframes tm-flicker { 0%,100% { opacity: 1 } 8% { opacity: .82 } 10% { opacity: .95 } 42% { opacity: .88 }
  44% { opacity: 1 } 71% { opacity: .78 } 73% { opacity: .97 } }

.tm-page { position: relative; min-height: 100%; box-sizing: border-box; padding: 40px 48px;
  display: flex; flex-direction: column; gap: 24px; }
.tm-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px 24px; flex-wrap: wrap; }
.tm-brand { display: flex; flex-direction: column; gap: 6px; }
.table-menu .tm-title { margin: 0; font-family: var(--tm-sc); font-weight: 400; font-size: 64px;
  letter-spacing: 0.18em; color: #d8c8a6; line-height: 1; animation: tm-title-pulse 7s infinite;
  text-shadow: 0 0 18px rgba(142,27,20,.55), 0 2px 0 #000; }
@keyframes tm-title-pulse {
  0%, 48%, 100% { text-shadow: 0 0 18px rgba(142,27,20,.55), 0 2px 0 #000; }
  50% { text-shadow: 0 0 34px rgba(190,30,20,.9), 0 2px 0 #000; }
  52% { text-shadow: 0 0 12px rgba(142,27,20,.35), 0 2px 0 #000; } }
.tm-tagline { margin: 0; font-style: italic; font-size: 16px; color: var(--tm-muted); letter-spacing: 0.04em; }
.tm-seat { display: flex; align-items: center; gap: 20px; padding-top: 10px; }
.tm-seated { margin: 0; font-size: 15px; color: var(--tm-muted); }
.tm-seated-name { color: var(--tm-ink-bright); font-family: var(--tm-sc); font-size: 18px; }

/* The table's plain buttons: Leave the table, Easy / Hard, Join. */
.table-menu .tm-button, .table-menu .online-player-join {
  font-family: var(--tm-sc); font-size: 15px; letter-spacing: 0.08em; color: #b6a585;
  background: transparent; border: 1px solid var(--tm-line-warm); padding: 0 18px; height: 44px; cursor: pointer; }
.table-menu .tm-button:hover:not(:disabled), .table-menu .online-player-join:hover:not(:disabled) {
  background: transparent; color: var(--tm-ink-bright); border-color: var(--tm-blood); }
.table-menu .tm-button:disabled, .table-menu .online-player-join:disabled { opacity: 0.5; cursor: wait; }

.tm-main { flex-grow: 1; display: grid; grid-template-columns: 240px 1fr; gap: 24px; align-items: center; }
.tm-center { display: flex; flex-direction: column; align-items: center; gap: 20px; min-width: 0; }
.table-menu .menu-screen.tm-cards { display: flex; flex-direction: row; justify-content: center; align-items: center;
  gap: 24px; margin: 0; padding-top: 40px; }

/* A mode card: card stock in an inked frame, tilted; it lifts and
   straightens on hover, and its hidden details (.tm-eye) show. */
.table-menu .tm-card { position: relative; flex: none; width: 190px; height: 280px; padding: 10px; display: flex;
  background: var(--tm-ink); border: none; color: #1c1410; font-family: inherit; letter-spacing: normal;
  box-shadow: 0 18px 30px rgba(0,0,0,0.75); cursor: pointer; transform: rotate(var(--tilt, 0deg));
  transition: transform .35s cubic-bezier(.2,.8,.2,1), box-shadow .35s; }
.table-menu .tm-card:hover:not(:disabled), .table-menu .tm-card:focus-visible, .table-menu .tm-card.is-open {
  background: var(--tm-ink); color: #1c1410; transform: translateY(-18px) rotate(0deg); outline: none;
  box-shadow: 0 30px 50px rgba(0,0,0,.8), 0 0 40px rgba(142,27,20,.55); }
.table-menu .tm-card:disabled { cursor: wait; }
.tm-card-frame { flex-grow: 1; border: 1.5px solid var(--tm-line); display: flex; flex-direction: column;
  align-items: center; justify-content: space-between; padding: 14px 10px 12px; }
.tm-card-name { font-family: var(--tm-sc); font-size: 20px; }
.tm-card-foot { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.tm-card-drops { display: flex; gap: 4px; }
.tm-card-tagline { font-size: 14px; font-style: italic; color: #4a3a2c; }
.tm-eye { opacity: 0; transition: opacity .6s; }
.tm-card:hover .tm-eye, .tm-card:focus-visible .tm-eye, .tm-card.is-open .tm-eye { opacity: 1; }
.table-menu .tm-card--dark, .table-menu .tm-card--dark:hover:not(:disabled), .table-menu .tm-card--dark:focus-visible {
  background: #22150f; border: 1px solid #4a2e22; color: var(--tm-ink); }
.tm-card--dark .tm-card-frame { border-color: #5a3a2a;
  background: repeating-linear-gradient(45deg, rgba(142,27,20,0.10) 0 2px, rgba(0,0,0,0) 2px 12px),
              repeating-linear-gradient(-45deg, rgba(142,27,20,0.10) 0 2px, rgba(0,0,0,0) 2px 12px); }
.tm-card--dark .tm-card-name { color: var(--tm-ink-bright); }
.tm-card--dark .tm-card-tagline { color: #b6a585; }
.tm-card > .spinner { position: absolute; right: 18px; bottom: 18px; margin: 0; }

/* Single Player's and Ultracoop's Easy / Hard, under the cards once a card is picked. */
.table-menu .level-choice { display: flex; flex-wrap: wrap; justify-content: center; max-width: 460px;
  gap: 10px 12px; text-align: center; }
.table-menu .level-choice > span { flex-basis: 100%; font-style: italic; font-size: 15px; color: var(--tm-muted); opacity: 1; }
.table-menu .level-choice .tm-button { min-width: 110px; }
.table-menu .level-choice[hidden] { display: none; }   /* beats the display: flex above */
.table-menu #error { min-height: 1.2em; font-style: italic; color: #d0654f; text-align: center; }
.tm-footer { text-align: center; font-style: italic; font-size: 15px; color: var(--tm-faint); letter-spacing: 0.06em; }

/* The left column: who's at the table, and Table talk below it. */
.tm-side { align-self: start; margin-top: 24px; display: flex; flex-direction: column; gap: 16px; min-width: 0; }

/* At the table: the online players, a candle by each name. */
.table-menu .online-players, .table-menu .menu-chat { padding: 20px 20px 22px;
  background: rgba(20,13,10,0.75); border: 1px solid var(--tm-line); box-shadow: inset 0 0 30px rgba(0,0,0,0.6); }

/* Table talk: the menu's chat for everyone online. */
.menu-chat-list { list-style: none; margin: 0 0 12px; padding: 0; display: flex; flex-direction: column; gap: 6px;
  max-height: 220px; overflow-y: auto; font-size: 15px; color: var(--tm-ink); overflow-wrap: anywhere; }
.menu-chat-name { font-family: var(--tm-sc); color: var(--tm-gold); }
.menu-chat-list li.is-mine .menu-chat-name { color: var(--tm-ink-bright); }
.menu-chat-list li.is-mine .menu-chat-name.is-from-menu { color: #f08cc0; }
.menu-chat-empty { font-style: italic; color: var(--tm-muted); }
.menu-chat-form { display: flex; gap: 8px; }
.table-menu .menu-chat-input { flex: 1; min-width: 0; height: 34px; box-sizing: border-box; padding: 0 10px;
  background: rgba(11,8,7,0.8); border: 1px solid var(--tm-line-warm); color: var(--tm-ink-bright);
  font-family: 'IM Fell English', Georgia, serif; font-size: 15px; }
.table-menu .menu-chat-input:focus { outline: none; border-color: var(--tm-gold); }
.table-menu .menu-chat-input::placeholder { color: var(--tm-faint); font-style: italic; }
.table-menu .menu-chat-send { height: 34px; padding: 0 12px; font-size: 14px; }
.menu-chat-note { margin: 8px 0 0; font-size: 13px; font-style: italic; color: #d0654f; }
.table-menu .online-players-title { margin: 0 0 14px; font-family: var(--tm-sc); font-weight: 400; font-size: 18px;
  letter-spacing: 0.08em; color: var(--tm-gold); }
.online-players-count { color: #7d6d58; }
.online-players-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px;
  max-height: 60vh; overflow-y: auto; }
.online-player { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.online-player-row { display: flex; align-items: center; gap: 12px; min-width: 0; }
.tm-candle { flex: none; }
.tm-flame { animation: tm-flame 1.6s ease-in-out infinite alternate; transform-origin: 50% 100%; transform-box: fill-box; }
@keyframes tm-flame { 0% { transform: scaleY(1) skewX(0deg) } 50% { transform: scaleY(1.15) skewX(-4deg) }
  100% { transform: scaleY(.92) skewX(3deg) } }
.online-player-name { font-size: 17px; color: var(--tm-ink-bright); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.online-player-you { font-size: 14px; font-style: italic; color: #8a7a64; }
.online-player--empty { opacity: 0.55; }
.online-player--empty span { font-size: 15px; font-style: italic; color: var(--tm-muted); }
.online-players-empty { font-style: italic; color: var(--tm-muted); }

/* Game status icon after the name (lobby.js statusIcon). */
.online-player-status { flex: none; display: inline-flex; width: 16px; height: 16px; margin-left: auto; cursor: help; }
.online-player-status svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round; }
.online-player-status .sand { stroke: none; fill: none; }
.online-player-status .pommel { fill: currentColor; stroke: none; }
.online-player-status[data-status="playing"] { color: var(--tm-ink-bright); }
.online-player-status[data-status="multi"] { color: var(--tm-gold); }
.online-player-status[data-status="ultracoop"] { color: #6fae93; }
.online-player-status[data-status="ultracoop"] .sand { fill: currentColor; }

/* The lobby they wait in (with Join), or the game they're playing. */
.online-player-lobby { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding-left: 26px; color: var(--tm-muted); font-size: 13px; font-style: italic; }
.table-menu .online-player-join { flex: none; height: 26px; padding: 0 10px; font-size: 13px; font-style: normal; }
.online-player-join .spinner--inline { margin-left: 6px; }

/* The bell by each other player's name (lobby.js bellButton). */
.table-menu .online-player-bell { flex: none; margin-left: auto; padding: 0 4px; border: 1px solid transparent;
  background: transparent; font-size: 13px; line-height: 20px; }
.table-menu .online-player-bell:hover:not(:disabled) { background: transparent; border-color: var(--tm-blood); }
.online-player-bell:disabled { opacity: 0.35; cursor: default; }
.online-player-status + .online-player-bell { margin-left: 0; }

/* Narrower screens: the five cards shrink to fit, then go two by two. */
@media (max-width: 1499px) {
  .table-menu .menu-screen.tm-cards { zoom: 0.8; }
}
@media (max-width: 1239px) {
  .tm-main { grid-template-columns: 220px 1fr; }
  .table-menu .menu-screen.tm-cards { zoom: 0.64; gap: 20px; }
}
@media (max-width: 999px) {
  .table-menu .menu-screen.tm-cards { display: grid; grid-template-columns: repeat(2, 190px); gap: 32px 36px; zoom: 0.8; }
}
@media (prefers-reduced-motion: reduce) {
  .tm-glow, .tm-flame, .table-menu .tm-title { animation: none; }
  .table-menu .tm-card { transition: none; }
}

.lobby-screen {
  margin: 24px 0;
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 24px;
}
.lobby-seats {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.lobby-seat {
  padding: 12px 16px;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: var(--bg-medium);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.lobby-seat.is-open { opacity: 0.5; font-style: italic; }
.lobby-seat.is-ready { border-color: #6bc46b; background: rgba(107, 196, 107, 0.08); }
.lobby-seat.is-filled { border-color: var(--accent); }
.lobby-seat-name { font-weight: bold; }
.lobby-seat-state { font-style: italic; color: var(--dim); font-size: 0.9em; }
.lobby-ready-btn {
  padding: 4px 12px;
  font-size: 0.9em;
  border-radius: 3px;
  border: 1px solid var(--accent);
  background: transparent;
  color: var(--fg);
  cursor: pointer;
}
.lobby-ready-btn.is-ready {
  background: rgba(107, 196, 107, 0.18);
  border-color: #6bc46b;
  color: #b6e8b6;
}
.lobby-ready-btn:hover { filter: brightness(1.2); }
.menu-multiplayer-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}
.menu-players-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 1em;
}
.menu-players-select {
  font-size: 1em;
  padding: 6px 10px;
  background: var(--bg-medium);
  color: var(--fg);
  border: 1px solid var(--accent);
  border-radius: 3px;
}
.lobby-controls {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Resume list — same column-flex + center alignment as .menu-screen so
   the resume screen feels consistent with the menu. .resume-row takes
   100% of the column width; .resume-info and .resume-actions sit
   side-by-side inside. */
/* Back to menu and Delete all, above the list (tester 2026-10-02). */
.resume-toolbar { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px;
  width: 100%; max-width: 640px; }
.resume-toolbar .menu-button { min-width: 0; }
.menu-button.resume-delete-all { border-color: #c23b3b; color: #f0b0a8; }
.menu-button.resume-delete-all:hover:not(:disabled) { background: #c23b3b; color: var(--card-cream); }

.resume-list {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  max-width: 640px;
  margin: 24px auto;
}
.resume-row {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  border: 1px solid var(--accent);
  border-radius: 4px;
  background: var(--bg-medium);
}
.resume-info {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
}
/* Second line: who was in the game. Distinct from the meta line below
   so the eye can scan "who" vs "when/which". */
.resume-players {
  font-size: 0.95em;
  opacity: 0.85;
}
/* Third line: when + short game-id suffix, so two otherwise-identical
   rows ("Single · Encounter 1" with the same username) can still be
   told apart — reported: "now it's all the same name, i don't know
   which is which." */
.resume-meta {
  font-size: 0.85em;
  opacity: 0.6;
}
.resume-actions {
  display: flex;
  gap: 8px;
}

.lobby-waiting {
  font-style: italic;
  color: #b9a886;   /* matches the existing accent dim color */
  text-align: center;
}

/* Per-player turn indicator (2026-09-25). Appended at the top of
   board-root by app.js's _turnIndicator helper. The board CSS owns
   the canvas visual; this banner is a separate, primary affordance
   — sits above the header so it's the first thing the player reads. */
.turn-indicator {
  text-align: center;
  font-size: 1.1em;
  font-weight: bold;
  letter-spacing: 0.04em;
  padding: 10px 14px;
  margin: 0 0 12px 0;
  border-radius: 4px;
  border: 1px solid var(--accent);
}
.turn-indicator.is-my-turn {
  background: rgba(107, 196, 107, 0.18);
  border-color: #6bc46b;
  color: #b6e8b6;
  box-shadow: 0 0 0 2px rgba(107, 196, 107, 0.25);
}
.turn-indicator.is-their-turn {
  background: rgba(184, 168, 134, 0.10);
  color: var(--dim);
  border-color: rgba(184, 168, 134, 0.4);
  font-weight: normal;
  font-style: italic;
}
/* Shown to every player while the monster's AI search runs (server
   broadcasts `monster_thinking`); same amber as the spinner accent. */
.turn-indicator.is-monster-turn {
  background: rgba(245, 197, 66, 0.12);
  border-color: rgba(245, 197, 66, 0.55);
  color: #f5d98a;
  font-style: italic;
}

/* Disable-card-clicks gate (2026-09-25). When it's not your turn, dim
   your hand and dock so the affordance isn't there, AND block clicks
   via CSS pointer-events. (The server's submit_action still validates
   — this is purely UX so the player doesn't try to play out of turn
   and watch the action silently do nothing.) */
/* (Hover still works — the tooltip shows the card; app.js switches off
   clicking and dragging instead of blocking the mouse, 2026-10-01.) */
.hand-strip.is-their-turn .card--hand,
.hand-dock.is-their-turn .card--hand {
  filter: grayscale(0.6) brightness(0.7);
  cursor: not-allowed;
}

/* Item bag next to the hand dock (items spec 2026-09-29). */
/* pointer-events: auto — the .hand-dock container sets none (so clicks
   reach the board behind it) and that inherits; only real controls
   inside it opt back in, like .hand-strip-cards. */
.item-bag-wrap { position: relative; }
.item-bag {
  background: transparent; border: 1px solid rgba(184, 147, 90, 0.4); border-radius: 5px;
  color: var(--card-cream); font-family: 'Manrope', sans-serif; font-weight: 600; font-size: 12px;
  padding: 2px 8px; cursor: pointer;
}
.item-bag:hover { border-color: var(--accent); color: var(--accent); }
.item-bag.is-targeting { border-color: #f5c542; box-shadow: 0 0 0 2px #f5c542; }
.item-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60; width: 240px;
  display: flex; flex-direction: column; gap: 4px; padding: 6px;
  background: var(--card-espresso); border: 1.5px solid var(--accent); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
}
.item-row {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 6px 8px; border-radius: 6px; border: 1px solid transparent;
  background: rgba(245, 197, 66, 0.06); color: var(--card-cream); cursor: pointer;
  font-family: 'Manrope', sans-serif; text-transform: none; font-variant: normal; letter-spacing: normal;
}
.item-row:not(:disabled):hover { border-color: rgba(245, 197, 66, 0.6); }
.item-row:disabled { cursor: default; opacity: 0.6; }
.item-row.is-targeting { border-color: #f5c542; box-shadow: 0 0 0 1px #f5c542; }
.item-row .item-icon { font-size: 20px; }
.item-text { display: flex; flex-direction: column; line-height: 1.2; }
.item-name { font-weight: 700; font-size: 12px; }
.item-desc { font-size: 11px; opacity: 0.8; }
.item-menu-empty { font-size: 12px; opacity: 0.7; padding: 4px 6px; }
.card--slot.is-targetable { box-shadow: 0 0 0 3px #f5c542; cursor: crosshair; }
.player-items { display: flex; gap: 6px; align-items: center; }
.airborne-badge { font-size: 11px; padding: 1px 6px; border-radius: 8px; background: rgba(160, 200, 255, 0.18); }
.monster-skip-note { font-size: 0.85em; font-weight: normal; margin-top: 4px; }
.item-reward { display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 140px; height: 190px; padding: 10px; text-align: center; gap: 8px; }
.item-reward-icon { font-size: 48px; }
.item-reward-name { font-weight: bold; }
.item-reward-desc { font-size: 12px; opacity: 0.8; }

/* Boss fights (phases, the Angler's hook, the Trader's pelts). */
.boss-phase { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent); margin-left: 10px; }
.boss-candles { display: inline-flex; align-items: flex-end; gap: 4px; margin-left: 8px; }
/* Ultracoop: the boss twist badge, Unison's per-section pips, help requests. */
.boss-twist { display: inline-flex; align-items: center; gap: 4px; margin-left: 10px; padding: 2px 8px;
  border: 1px solid #c0533a; border-radius: 10px; color: #f0a58f; font-size: 11px; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase; cursor: help; }
.unison-pip { width: 8px; height: 8px; border-radius: 50%; border: 1px solid #f0a58f; background: transparent; }
.unison-pip.is-hit { background: #f0a58f; }
.help-requests { position: fixed; left: 16px; bottom: 16px; z-index: 26; display: flex; flex-direction: column;
  gap: 6px; align-items: flex-start; }
.help-requests button { background: var(--card-espresso); border: 1px solid var(--accent); border-radius: 5px;
  color: var(--card-cream); font-size: 12px; padding: 6px 10px; cursor: pointer; }
.help-requests button:hover { border-color: #f5c542; color: #f5c542; }

#toast-layer { position: fixed; top: 80px; left: 50%; transform: translateX(-50%); z-index: 60;
  display: flex; flex-direction: column; gap: 8px; pointer-events: none; }
.help-toast { background: var(--card-espresso); border: 1.5px solid #f5c542; border-radius: 6px;
  color: var(--card-cream); padding: 10px 16px; font-weight: 700; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
  animation: help-toast-in 200ms ease-out; }
@keyframes help-toast-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
.boss-phase-note { font-size: 0.85em; font-weight: normal; margin-top: 4px; font-style: italic; }
.card--slot .hook-marker { position: absolute; top: -10px; left: 50%; transform: translateX(-50%); font-size: 20px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.7)); pointer-events: none; z-index: 5; }
.pelt-trade-btn { pointer-events: auto; align-self: center; margin-left: 12px; background: transparent;
  border: 1px solid rgba(184, 147, 90, 0.6); border-radius: 5px; color: var(--card-cream);
  font-family: 'Manrope', sans-serif; font-weight: 600; font-size: 12px; padding: 8px 12px; cursor: pointer; }
.pelt-trade-btn:hover, .pelt-trade-btn.is-targeting { border-color: #f5c542; color: #f5c542; }
.card-extra-sigils .sigil-chip { display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; background: rgba(16, 12, 9, 0.92); border: 1px solid #b8935a;
  color: #f5ecd6; font-size: 10px; font-weight: 800; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); }

/* Leshy: the mask note and The Moon across a monster row. */
.mask-note { font-size: 0.85em; font-weight: normal; margin-top: 4px; font-style: italic; }
.monster-front-row.has-moon { position: relative; }
.moon-overlay { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 4px; border-radius: 8px; border: 1.5px solid #d9d2b8; cursor: help;
  background: radial-gradient(circle at 50% 40%, #f4f1e6 0%, #cfc8b0 55%, #8f8a78 100%);
  color: #2b2a24; box-shadow: 0 0 24px rgba(244, 241, 230, 0.35); z-index: 4; }
.moon-overlay .moon-name { font-family: 'Cinzel', serif; font-weight: 700; font-size: 18px; letter-spacing: 0.08em; }
.moon-overlay .moon-hp { font-family: 'Manrope', sans-serif; font-weight: 700; font-size: 14px; }
/* The Moon strikes (a pulse instead of a lunge) and takes hits on its banner. */
@keyframes moon-strike { 0%, 100% { transform: scale(1); } 40% { transform: scale(1.04); box-shadow: 0 0 36px rgba(244, 241, 230, 0.8); } }
.moon-overlay.is-attacking { animation: moon-strike 600ms ease-in-out; }
.moon-overlay.is-hit, .moon-overlay.is-hit-killed { animation: hit-flash 400ms ease-out; }
/* The team's candles in the header. */
.run-candles { display: inline-flex; align-items: flex-end; gap: 6px; margin-right: 14px; }


/* Player totem (board.js renderTotem): 🪵 button + floating menu, reusing the item bag's styles. */
.totem-button .totem-sigil { width: 1.1em; height: 1.1em; vertical-align: -0.2em; image-rendering: pixelated; }
.totem-row .item-icon img { width: 1.4em; height: 1.4em; image-rendering: pixelated; }
.totem-row.is-worn { outline: 1px solid rgba(240, 200, 120, 0.6); }
.totem-reward .item-reward-icon img { width: 64px; height: 64px; image-rendering: pixelated; }

/* The Moon with its card art (board.js): the art fills the row's height; the
   live health badge covers the card's printed 40 (it's 40 per player). */
.moon-overlay.has-art { background: #1b1712; overflow: hidden; }
.moon-overlay .moon-card { position: relative; height: 100%; display: inline-block; }
.moon-overlay .moon-art { height: 100%; width: auto; display: block; }
.moon-overlay.has-art .moon-hp { position: absolute; right: 2%; bottom: 3%; min-width: 22%; padding: 3px 6px;
  text-align: center; border-radius: 6px; background: #2b2a24; color: #f4f1e6; font-size: 15px; }

/* Candles (board.js renderCandle): the team's lives (run-candle, big) and a
   boss's phases (boss-candle, smaller). Lit = flickering flame; out = grey
   wax and a wisp of smoke. */
.candle { position: relative; display: inline-flex; flex-direction: column; align-items: center; justify-content: flex-end;
  width: 14px; height: 36px; }
.candle.boss-candle { width: 10px; height: 24px; }
.candle-wax { width: 100%; height: 58%; border-radius: 3px 3px 2px 2px;
  background: linear-gradient(90deg, #d8cba6, #f3ead2 45%, #c9b98f); box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.15); }
.candle-wick { width: 2px; height: 4px; background: #2b2118; }
.candle-flame { width: 70%; height: 32%; border-radius: 50% 50% 45% 45% / 65% 65% 35% 35%;
  background: radial-gradient(ellipse at 50% 70%, #fff6c8 0%, #ffd34d 40%, #ff8a1f 75%, rgba(255, 90, 0, 0) 100%);
  box-shadow: 0 0 10px 3px rgba(255, 170, 60, 0.55); transform-origin: 50% 100%;
  animation: candle-flicker 1.6s ease-in-out infinite alternate; }
.candle.boss-candle .candle-flame { box-shadow: 0 0 7px 2px rgba(255, 170, 60, 0.5); }
@keyframes candle-flicker {
  0% { transform: scaleY(1) rotate(-2deg); opacity: 0.95; }
  50% { transform: scaleY(1.12) rotate(2deg); opacity: 1; }
  100% { transform: scaleY(0.92) rotate(-1deg); opacity: 0.9; } }
.candle.is-out .candle-flame { visibility: hidden; animation: none; }
.candle.is-out .candle-wax { background: linear-gradient(90deg, #6b6658, #8a8576 45%, #5d594d); }
.candle-smoke { position: absolute; top: 0; left: 50%; width: 6px; height: 14px; margin-left: -3px; border-radius: 50%;
  background: radial-gradient(ellipse, rgba(200, 200, 200, 0.55), rgba(200, 200, 200, 0)); filter: blur(1px);
  animation: candle-smoke 2.4s ease-out infinite; }
@keyframes candle-smoke { 0% { transform: translateY(6px) scale(0.6); opacity: 0.7; }
  100% { transform: translateY(-8px) scale(1.4); opacity: 0; } }

/* Airborne cards hover above the table and gently bend, like the original
   (board.js renderCard .is-airborne; Harpie's Fan: .is-fanned column). Board
   cards only, and never while the card is attacking, hit, moving or
   descending — those animations win. */
@keyframes airborne-hover {
  0%, 100% { transform: perspective(500px) translateY(0) rotateX(-2deg);
             filter: drop-shadow(0 3px 2px rgba(0, 0, 0, 0.45)); }
  50% { transform: perspective(500px) translateY(-6px) rotateX(4deg);
        filter: drop-shadow(0 10px 6px rgba(0, 0, 0, 0.3)); }
}
/* The bend along the width (board.js: an airborne slot card's art in two
   halves): the halves fold around the centre line like wings, in time with
   the hover — flattest at the bottom, most bent at the top. */
.card-art.is-folding { perspective: 240px; width: 100%; align-self: stretch; }  /* its imgs are overlays: size from the card */
.card-art.is-folding { background: transparent; }
.card-art.is-folding > img { position: absolute; inset: 0; object-fit: contain; transform-origin: 50% 50%; }  /* same fit as unsplit art */
.card-art.is-folding .fold-left { clip-path: inset(0 50% 0 0); }
.card-art.is-folding .fold-right { clip-path: inset(0 0 0 50%); }
@keyframes wing-left { 0%, 100% { transform: rotateY(0deg); } 50% { transform: rotateY(16deg); } }
@keyframes wing-right { 0%, 100% { transform: rotateY(0deg); } 50% { transform: rotateY(-16deg); } }
.card--slot.is-airborne .card-art.is-folding .fold-left {
  animation: wing-left 2.4s ease-in-out infinite; animation-delay: var(--hover-delay, 0ms); }
.card--slot.is-airborne .card-art.is-folding .fold-right {
  animation: wing-right 2.4s ease-in-out infinite; animation-delay: var(--hover-delay, 0ms); }
.card--slot.card--filled.is-airborne:not(.is-attacking):not(.is-hit):not(.is-hit-killed):not(.is-direct-hit):not(.is-moving):not(.is-descending):not(.flipped),
.player-column.is-fanned .your-front-row .card--slot.card--filled:not(.is-attacking):not(.is-hit):not(.is-hit-killed):not(.is-direct-hit):not(.is-moving):not(.flipped) {
  animation: airborne-hover 2.4s ease-in-out infinite;
  animation-delay: var(--hover-delay, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .card--slot.card--filled.is-airborne, .player-column.is-fanned .your-front-row .card--slot.card--filled,
  .card-art.is-folding > img { animation: none !important; }
}

/* Damage preview on your lanes (board.js decoratePreview): your attack's
   damage on the monster's cards, its reply's on yours. */
.preview-pill { position: absolute; top: 4px; left: 50%; transform: translateX(-50%); z-index: 6;
  padding: 1px 7px; border-radius: 9px; font: 700 12px/1.4 'Manrope', sans-serif; white-space: nowrap;
  pointer-events: auto; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6); }
.preview-pill--attack { background: rgba(190, 40, 40, 0.92); color: #fff; }
.preview-pill--attack.preview-pill--direct { background: rgba(214, 160, 40, 0.95); color: #2b1d05; }
.preview-pill--reply { top: auto; bottom: 4px; background: rgba(230, 120, 30, 0.95); color: #1d1206; }
.preview-pill--kill { outline: 1px solid rgba(255, 255, 255, 0.6); }
.moon-overlay .preview-pill { top: 6px; }
/* The scale's preview markers: after your attack (gold), after the monster's reply (orange). */
.pip.ghost-attack { box-shadow: 0 0 0 2px #ffd34d, 0 0 7px 2px rgba(255, 211, 77, 0.75); transform: scale(1.2); z-index: 1; }

/* RPG-style damage numbers (animations.js spawnDamageNumber). */
.float-dmg { position: fixed; z-index: 9999; transform: translate(-50%, -50%); pointer-events: none;
  font: 900 34px/1 'Cinzel', serif; color: #ff4d4d; white-space: nowrap;
  text-shadow: 0 2px 0 #3a0a0a, 0 0 10px rgba(0, 0, 0, 0.8);
  animation: float-dmg 1000ms ease-out forwards; }
.float-dmg--direct { color: #ffcf4d; text-shadow: 0 2px 0 #3a2a05, 0 0 10px rgba(0, 0, 0, 0.8); }
.float-dmg--kill { font-size: 38px; }
@keyframes float-dmg {
  0% { opacity: 0; transform: translate(-50%, -30%) scale(0.6); }
  15% { opacity: 1; transform: translate(-50%, -60%) scale(1.25); }
  30% { transform: translate(-50%, -70%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -190%) scale(1); }
}
@media (prefers-reduced-motion: reduce) { .float-dmg { animation-duration: 1ms; } }

/* "Your deck (N)" under the reward choices (board.js renderDeckStrip). */
.reward-deck { margin-top: 14px; width: 100%; }
.reward-deck h4 { margin: 0 0 6px; font: 700 12px 'Manrope', sans-serif; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent, #d9b36a); text-align: center; }
.reward-deck-cards { display: flex; flex-wrap: wrap; justify-content: center; gap: 5px; max-height: 220px; overflow-y: auto; }
.card--deck { width: var(--card-w-deck); aspect-ratio: 5 / 7; flex: 0 0 auto; border-radius: 5px; transition: transform 0.15s, box-shadow 0.15s, opacity 0.15s; }
.reward-deck-cards:has(.is-lit) .card--deck:not(.is-lit) { opacity: 0.45; }
.card--deck.is-lit { transform: translateY(-4px); box-shadow: 0 0 0 2px #f0c850, 0 4px 10px rgba(0, 0, 0, 0.5); }
.totem-reach { font: 700 12px 'Manrope', sans-serif; color: #f0c850; }

/* Saving between fights: retry buttons (modals.js game over, board.js sidebar). */
.modal-box .retry-fight { margin-bottom: 8px; }
.sidebar-retry-fight { margin: 12px 12px 4px; padding: 6px 10px; border-radius: 6px; border: 1px solid rgba(217, 179, 106, 0.6);
  background: rgba(217, 179, 106, 0.12); color: #e9d8b0; font: 600 12px 'Manrope', sans-serif; cursor: pointer; }
.sidebar-retry-fight:hover { background: rgba(217, 179, 106, 0.25); }

/* Skipping a card pick (board.js renderRewardPicker). */
.reward-skip { margin-top: 12px; padding: 6px 14px; border-radius: 6px; border: 1px solid rgba(217, 179, 106, 0.5);
  background: transparent; color: #d9c79e; font: 600 12px 'Manrope', sans-serif; cursor: pointer; }
.reward-skip:hover { background: rgba(217, 179, 106, 0.15); }

/* The turn indicator as a small pill in the header (app.js, 2026-10-01). */
.run-header-center .turn-indicator {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  margin: 0 10px 0 0; padding: 3px 11px; border-radius: 999px;
  font-size: 12px; line-height: 1.4; letter-spacing: 0.03em; white-space: nowrap;
}
.run-header-center .turn-indicator.is-my-turn { box-shadow: 0 0 0 1px rgba(107, 196, 107, 0.25), 0 0 10px rgba(107, 196, 107, 0.25); }
.run-header-center .turn-indicator .mask-note,
.run-header-center .turn-indicator .monster-skip-note { font-style: normal; font-weight: normal; opacity: 0.9; }
.run-header-center .turn-indicator .spinner--inline { width: 10px; height: 10px; }

/* A direct hit on your team: shown below your row, drifting down (animations.js). */
.float-dmg--below { animation-name: float-dmg-down; }
@keyframes float-dmg-down {
  0% { opacity: 0; transform: translate(-50%, -70%) scale(0.6); }
  15% { opacity: 1; transform: translate(-50%, -40%) scale(1.25); }
  30% { transform: translate(-50%, -30%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, 90%) scale(1); }
}

/* The Mycologists (board.js renderMycologists). */
.myco-pair { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.myco-label { font: 600 12px 'Manrope', sans-serif; color: #e3d2a8; text-align: center; }

/* The monster's totem (board.js renderEnemyTotem): a badge in the corner of
   its row, outside the 4-lane grid. */
.monster-front-row { position: relative; }
.enemy-totem { position: absolute; top: -10px; left: -6px; z-index: 6; padding: 1px 7px; border-radius: 9px;
  background: rgba(43, 36, 24, 0.95); border: 1px solid var(--accent, #d9b36a); font-size: 13px; line-height: 1.5;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6); cursor: default; }
.enemy-totem .totem-sigil { width: 1.1em; height: 1.1em; vertical-align: -0.2em; image-rendering: pixelated; }

/* ---- Phones (spec 2026-10-02-mobile) ------------------------------- */
body.is-compact .lobby-screen { grid-template-columns: 1fr; }
body.is-compact .menu-button { min-width: 0; width: min(100%, 320px); }
/* Phones: the cards first (two by two upright, a row of four on its side),
   then who's at the table. */
body.is-compact .tm-page { padding: 16px; gap: 16px; }
body.is-compact .table-menu .tm-title { font-size: 34px; letter-spacing: 0.12em; }
body.is-compact .tm-tagline { font-size: 14px; }
body.is-compact .tm-seat { padding-top: 0; gap: 10px 12px; flex-wrap: wrap; }
body.is-compact .table-menu .tm-button { height: 38px; padding: 0 12px; font-size: 14px; }
body.is-compact .tm-main { grid-template-columns: 1fr; gap: 16px; }
body.is-compact .tm-center { order: -1; }
body.is-compact .table-menu .menu-screen.tm-cards { display: grid; grid-template-columns: repeat(2, 190px);
  gap: 28px 32px; padding-top: 24px; zoom: 0.78; }
body.is-compact.is-landscape .table-menu .menu-screen.tm-cards { grid-template-columns: repeat(5, 190px); zoom: 0.5; }
body.is-compact .tm-side { margin: 0 auto; width: min(100%, 360px); }
body.is-compact .table-menu .menu-chat-input { font-size: 16px; }   /* iOS: no zoom-in on focus */
body.is-compact .resume-row { flex-wrap: wrap; }
body.is-compact h1 { font-size: 22px; margin: 8px 0; }
body.is-compact {
  --card-w: min(96px, calc((100vw - 52px) / 4));
  --card-w-reward: min(130px, calc((100vw - 64px) / 3));
  --card-w-deck: 44px;
}
body.is-compact #app { padding: 0 6px 6px; }
body.is-compact .run-header { grid-template-columns: 1fr auto; gap: 4px 8px; padding: 4px 6px; }
body.is-compact .run-header .run-title,
body.is-compact .run-header .difficulty-badge { display: none; }
body.is-compact .run-header-left { gap: 6px; font-size: 12px; }
body.is-compact .run-header-center { grid-column: 1 / -1; grid-row: 2; flex-wrap: wrap; justify-content: center;
  gap: 6px; min-width: 0; }
body.is-compact .pip-bar { flex: 1 1 auto; min-width: 0; overflow: hidden; }
body.is-compact .pip { width: 6px; height: 6px; }
body.is-compact #board { grid-template-columns: 1fr; gap: 6px; }
body.is-compact .player-column { padding: 6px; gap: 4px; max-width: none; min-width: 0; }
body.is-compact .zone-header { flex-wrap: wrap; gap: 4px; }
body.is-compact .card-row { gap: 4px; }
body.is-compact .run-main { padding-bottom: calc(var(--card-w) * 0.8); }
body.is-compact .run-sidebar { position: fixed; top: 0; right: 0; bottom: 0; left: auto; width: min(86vw, 340px);
  max-height: none; overflow-y: auto; z-index: 40; border-radius: 0; }
body.is-compact:not(.sidebar-open) .run-sidebar { display: none; }
body.is-compact .deck-flyout { right: 6px; padding: 6px 8px 0; }
/* Multiplayer needs landscape on a phone. */
.rotate-overlay { display: none; }
body.is-compact.is-portrait.is-touch .rotate-overlay { display: flex; position: fixed; inset: 0; z-index: 100;
  align-items: center; justify-content: center; text-align: center; padding: 24px; background: var(--card-espresso);
  color: var(--card-cream); font: 700 18px 'Manrope', sans-serif; }
.rotate-icon { font-size: 48px; display: block; }
.rotate-sub { font-weight: 400; font-size: 13px; opacity: 0.8; }
/* Landscape phones: no peek row, its card is a badge on the monster slot. */
.peek-badge { display: none; }
body.is-compact.is-landscape .peek-row { display: none; }
body.is-compact.is-landscape .peek-badge { display: flex; position: absolute; top: -5px; right: -5px; width: 20px;
  height: 20px; border-radius: 50%; overflow: hidden; border: 1.5px solid #f0c850; background: var(--card-espresso);
  align-items: center; justify-content: center; font: 700 10px 'Manrope', sans-serif; color: #f0c850; z-index: 4; }
.peek-badge img { width: 100%; height: 100%; object-fit: cover; }
body.is-compact.is-landscape #board,
body.is-compact.is-landscape .hand-dock {
  /* Height: header + player header + the lane letters (~15px) + two card
     rows + the hand's peeking half. */
  --card-w: max(44px, min(96px, calc((100dvh - 116px) / 3.6),
    calc((100vw - 24px - (var(--sections, 1) - 1) * 6px) / var(--sections, 1) / 4 - 6px)));
}
body.is-compact.is-landscape #board { grid-template-columns: repeat(var(--sections, 1), max-content);
  overflow-x: auto; justify-content: safe center; gap: 6px; }
body.is-compact.is-landscape .player-column { padding: 4px; }
/* Ultracoop's help buttons live in the open drawer on a phone. */
body.is-compact:not(.sidebar-open) .help-requests { display: none; }
body.is-compact.sidebar-open .help-requests { left: auto; right: 12px; bottom: 12px; z-index: 41; }
body.is-compact.is-landscape .run-header { grid-template-columns: auto 1fr auto; }
body.is-compact.is-landscape .run-header-center { grid-column: auto; grid-row: auto; flex-wrap: nowrap; }
body.is-compact.is-landscape .run-encounter { max-width: 150px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
body.is-compact .zone-header { zoom: 0.72; }
body.is-compact .run-header .hp-label { display: none; }
body.is-compact .action-button { white-space: nowrap; padding-left: 10px; padding-right: 10px; }
/* Tap to place: the selected hand card lifts, its lanes light up. */
.slot.is-tap-target { outline: 2px solid #5dcaa5; outline-offset: 1px; cursor: pointer; }
.slot.is-tap-target.occupied { outline-color: #f0997b; }
.hand-dock .card--hand.is-selected { transform: translateY(0); box-shadow: 0 0 0 3px #5dcaa5, 0 6px 14px rgba(0, 0, 0, 0.5); }
/* Long-press card details (touch). */
.card-details-popup { position: fixed; inset: 0; z-index: 90; display: flex; align-items: center;
  justify-content: center; background: rgba(8, 6, 4, 0.6); padding: 16px; }
.card-details-box { max-width: min(86vw, 340px); display: flex; flex-direction: column; gap: 6px;
  background: var(--card-espresso); color: var(--card-cream); border: 1.5px solid var(--accent);
  border-radius: 8px; padding: 14px 16px; font: 14px 'Manrope', sans-serif; }
/* Touch: no double-tap zoom on the game, no text selection or iOS callout on cards. */
body.is-compact #app, .card { touch-action: manipulation; }
.card { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
/* Phones: pickers and pop-ups become full-screen sheets that scroll. */
body.is-compact .modal-overlay { align-items: stretch; justify-content: stretch; }
body.is-compact .reward-picker,
body.is-compact .modal-box:not(.confirm-modal) { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
  border-radius: 0; border-left: 0; border-right: 0; padding: 14px; overflow-y: auto; box-sizing: border-box; }
body.is-compact .modal-overlay .confirm-modal { align-self: center; margin: auto; }
body.is-compact .chosen-pool { gap: 6px; }
/* Phones: the hand strip's slide-up room must not catch taps meant for the
   lanes above it; a big hand shrinks its cards to stay on screen. */
body.is-compact .hand-strip-cards { pointer-events: none; max-width: 100vw; box-sizing: border-box;
  padding-left: 6px; padding-right: 6px; }
body.is-compact .hand-strip-cards > * { pointer-events: auto; }
body.is-compact .hand-dock .card--hand { flex: 0 1 var(--card-w); min-width: 0; }
/* Landscape: a selected card lifts only part-way, so it doesn't cover the lanes. */
body.is-compact.is-landscape .hand-dock .card--hand.is-selected { transform: translateY(30%); }
/* Desktop: the hand sits fully on screen below the board (tester 2026-10-02);
   a hovered card lifts a little. Phones keep the half-hidden hand. */
body:not(.is-compact) .hand-dock .card--hand { transform: none; }
body:not(.is-compact) .hand-dock .card--hand:hover,
body:not(.is-compact) .hand-dock .card--hand.is-selected { transform: translateY(-10px); }
body:not(.is-compact) .hand-strip-cards { padding-bottom: 8px; }
body:not(.is-compact) .run-main { padding-bottom: calc(var(--card-w) * 1.4 + 24px); }
/* Phones: holding a finger down must not select text (it covered the card
   details popup, tester 2026-10-02); text boxes stay selectable. */
body.is-compact, .card-details-popup { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
body.is-compact input, body.is-compact textarea { -webkit-user-select: text; user-select: text; }
/* In-game chat (2026-10-02). */
.chat-list { list-style: none; margin: 0 0 6px; padding: 0; max-height: 180px; overflow-y: auto; font-size: 12px; }
.chat-list li { padding: 3px 6px; border-bottom: 1px solid rgba(233, 223, 201, 0.08); overflow-wrap: anywhere; }
.chat-list li.is-mine { color: #9fe1cb; }
/* Table talk: a name said from the main menu is pink (tester 2026-10-02). */
.chat-name.is-from-menu, .menu-chat-name.is-from-menu { color: #f08cc0; }
.chat-title, .menu-chat-title { display: flex; align-items: center; gap: 8px; }
.chat-mute { margin-left: auto; padding: 0 6px; font-size: 13px; line-height: 22px; background: transparent;
  border: 1px solid transparent; border-radius: 4px; cursor: pointer; }
.chat-mute:hover { background: transparent; border-color: var(--accent); }
.chat-mute[aria-pressed="true"] { opacity: 0.6; }
.chat-form { display: flex; gap: 6px; }
.chat-input { flex: 1; min-width: 0; font-size: 13px; padding: 6px 8px; }
.sidebar-toggle { position: relative; }
.chat-badge { position: absolute; top: -6px; right: -6px; min-width: 18px; height: 18px; padding: 0 4px;
  border-radius: 9px; background: #e24b4a; color: #fff; font: 700 11px/18px 'Manrope', sans-serif; text-align: center; }
body.is-compact .chat-input { font-size: 16px; }   /* iOS: no zoom-in on focus */
/* The sidebar's Log heading folds the log away. */
.log-toggle { background: none; border: 0; padding: 0; color: inherit; font: inherit; letter-spacing: inherit;
  text-transform: inherit; cursor: pointer; }
.log-toggle:hover { color: #f5c542; }
/* The bell (2026-10-02). */
.bell-button { background: transparent; border: 1px solid rgba(184, 147, 90, 0.6); border-radius: 6px;
  color: var(--card-cream); font-size: 16px; padding: 4px 8px; cursor: pointer; margin-right: 6px; }
.bell-button:hover:not(:disabled) { border-color: #f5c542; }
/* Rung: a swing, then grey until it can ring again. */
.bell-button.is-swinging { animation: bell-swing 0.7s ease-in-out; transform-origin: 50% 10%; }
@keyframes bell-swing { 0%, 100% { transform: rotate(0); } 20% { transform: rotate(18deg); } 40% { transform: rotate(-14deg); }
  60% { transform: rotate(9deg); } 80% { transform: rotate(-5deg); } }
.bell-button.is-cooling, .bell-button:disabled { filter: grayscale(1); opacity: 0.45; cursor: default;
  border-color: rgba(184, 147, 90, 0.25); }
.teammate-bells { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin: 4px 0; }
.bell-button.teammate-bell { font-size: 14px; padding: 6px 12px; margin: 0; }
.run-header.is-ringing { animation: bell-shake 0.6s ease-in-out; }
@keyframes bell-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); } 40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); } 80% { transform: translateX(4px); }
}
@media (prefers-reduced-motion: reduce) { .run-header.is-ringing, .bell-button.is-swinging { animation: none; } }

/* "Coscryption was updated — reload" (app.js onOutdated). */
#update-banner { position: fixed; left: 50%; top: calc(8px + env(safe-area-inset-top, 0px)); transform: translateX(-50%);
  z-index: 200; display: flex; align-items: center; gap: 12px; max-width: calc(100vw - 24px); box-sizing: border-box;
  padding: 10px 14px; background: var(--card-espresso); border: 1.5px solid #f5c542; border-radius: 6px;
  color: var(--card-cream); font: 600 14px 'Manrope', sans-serif; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.6); }
#update-banner button { flex: none; padding: 6px 14px; }

/* Sound (static/sounds.js renderSoundButton): 🔊 opens Music and Sound
   effects switches with a volume each. */
.sound-control { position: relative; display: inline-flex; }
.sound-button { background: transparent; border: 1px solid rgba(184, 147, 90, 0.6); border-radius: 6px;
  color: var(--card-cream); font-size: 15px; line-height: 1; padding: 6px 8px; cursor: pointer; margin-right: 6px; }
.sound-button:hover { background: transparent; border-color: #f5c542; color: var(--card-cream); }
.sound-panel { position: absolute; top: calc(100% + 6px); right: 0; z-index: 70; min-width: 220px;
  display: flex; flex-direction: column; gap: 10px; padding: 12px 14px; background: var(--card-espresso);
  border: 1.5px solid rgba(184, 147, 90, 0.7); border-radius: 6px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
  color: var(--card-cream); font: 600 13px 'Manrope', sans-serif; }
.sound-panel[hidden] { display: none; }
.sound-row { display: flex; flex-direction: column; gap: 6px; }
.sound-switch { display: flex; align-items: center; gap: 8px; cursor: pointer; white-space: nowrap; }
.sound-switch input { accent-color: #f5c542; width: 16px; height: 16px; margin: 0; }
.sound-row input[type="range"] { width: 100%; accent-color: #f5c542; padding: 0; border: 0; background: transparent; }
.sound-row input[type="range"]:disabled { opacity: 0.35; }
/* On the candlelit menu: in its colours. */
.table-menu .sound-button { height: 44px; padding: 0 12px; margin: 0; border-color: var(--tm-line-warm);
  color: #b6a585; font-size: 16px; border-radius: 0; }
.table-menu .sound-button:hover { border-color: var(--tm-blood); color: var(--tm-ink-bright); }
.table-menu .sound-panel { background: rgba(20, 13, 10, 0.97); border: 1px solid var(--tm-line);
  border-radius: 0; font-family: 'IM Fell English', Georgia, serif; font-size: 15px; font-weight: 400; color: var(--tm-ink); }
.table-menu .sound-switch input, .table-menu .sound-row input[type="range"] { accent-color: var(--tm-gold); }
body.is-compact .table-menu .sound-button { height: 38px; }

/* The Hall of Fame (lobby.js renderHallOfFame): a section per won run. */
.hof-list { display: flex; flex-direction: column; gap: 20px; width: 100%; max-width: 1100px; margin: 0 auto; }
.hof-empty { text-align: center; font-style: italic; font-size: 17px; color: var(--tm-muted); margin: 40px 0; }
.hof-entry { padding: 18px 20px 20px; background: rgba(20, 13, 10, 0.75); border: 1px solid var(--tm-line);
  box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.6); display: flex; flex-direction: column; gap: 12px; }
.hof-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px; }
.table-menu .hof-name { margin: 0; font-family: var(--tm-sc); font-weight: 400; font-size: 26px;
  letter-spacing: 0.08em; color: var(--tm-gold); }
.hof-meta { margin: 0; font-style: italic; font-size: 15px; color: var(--tm-muted); }
.totem-badge { display: inline-flex; align-items: center; gap: 10px; align-self: flex-start; padding: 6px 12px;
  border: 1px solid var(--tm-line-warm, rgba(184, 147, 90, 0.5)); background: rgba(11, 8, 7, 0.6);
  color: var(--tm-ink, var(--card-cream)); font-size: 15px; }
.totem-badge-icon { font-size: 20px; display: inline-flex; align-items: center; gap: 4px; }
.totem-badge-icon .totem-sigil { width: 22px; height: 22px; vertical-align: middle; }
.hof-cards { display: flex; flex-wrap: wrap; gap: 10px; }
.hof-cards .card { flex: none; }
body.is-compact .table-menu .hof-name { font-size: 20px; }
body.is-compact .hof-entry { padding: 12px; }
body.is-compact .hof-cards { gap: 6px; }

/* The voice room (static/voice.js renderVoiceButton): 🎙️ joins it; green
   while in, a slash when muted, a glow while you talk. */
.voice-control { position: relative; display: inline-flex; }
.voice-button { position: relative; background: transparent; border: 1px solid rgba(184, 147, 90, 0.6);
  border-radius: 6px; color: var(--card-cream); font-size: 15px; line-height: 1; padding: 6px 8px;
  cursor: pointer; margin-right: 6px; }
.voice-button:hover { background: transparent; border-color: #f5c542; color: var(--card-cream); }
.voice-button.is-joined { border-color: #6bc46b; background: rgba(107, 196, 107, 0.12); }
.voice-button.is-speaking { box-shadow: 0 0 0 2px rgba(107, 196, 107, 0.55), 0 0 12px rgba(107, 196, 107, 0.6); }
.voice-button.is-muted { border-color: #c23b3b; background: rgba(194, 59, 59, 0.12); }
.voice-button.is-muted::after { content: ''; position: absolute; left: 20%; right: 20%; top: 50%; height: 2px;
  background: #e05a4f; transform: rotate(-35deg); }
.voice-count { position: absolute; top: -6px; right: -6px; min-width: 16px; height: 16px; padding: 0 3px;
  border-radius: 8px; background: #6bc46b; color: #10170f; font: 700 10px/16px 'Manrope', sans-serif; }
.voice-panel { position: absolute; top: calc(100% + 6px); right: 0; z-index: 70; width: 260px;
  display: flex; flex-direction: column; gap: 10px; padding: 12px 14px; background: var(--card-espresso);
  border: 1.5px solid rgba(184, 147, 90, 0.7); border-radius: 6px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
  color: var(--card-cream); font: 600 13px 'Manrope', sans-serif; text-align: left; }
.voice-panel[hidden] { display: none; }
.voice-title { font-weight: 800; letter-spacing: 0.04em; }
.voice-members { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.voice-member { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 2px 8px;
  padding: 4px 6px; border-radius: 4px; border: 1px solid transparent; }
.voice-member.is-speaking { border-color: #6bc46b; background: rgba(107, 196, 107, 0.12); }
.voice-member.is-muted .voice-name { opacity: 0.6; }
.voice-member input[type="range"] { grid-column: 1 / -1; width: 100%; accent-color: #f5c542; padding: 0; border: 0;
  background: transparent; }
.voice-empty { font-weight: 400; font-style: italic; opacity: 0.75; }
.voice-actions { display: flex; gap: 8px; }
.voice-actions button { flex: 1; padding: 6px 10px; }
.voice-join { border-color: #6bc46b; }
.voice-leave { border-color: #c23b3b; }
.voice-note { margin: 0; font-weight: 400; color: #f0a8a0; }
/* On the candlelit menu: in its colours. */
.table-menu .voice-button { height: 44px; padding: 0 12px; margin: 0; border-color: var(--tm-line-warm);
  color: #b6a585; font-size: 16px; border-radius: 0; }
.table-menu .voice-button.is-joined { border-color: #4f8f3a; }
.table-menu .voice-button.is-muted { border-color: var(--tm-blood); }
.table-menu .voice-panel { background: rgba(20, 13, 10, 0.97); border: 1px solid var(--tm-line); border-radius: 0;
  font-family: 'IM Fell English', Georgia, serif; font-size: 15px; font-weight: 400; color: var(--tm-ink); }
.table-menu .voice-title { font-family: var(--tm-sc); color: var(--tm-gold); font-weight: 400; }
.table-menu .voice-actions button { font-family: var(--tm-sc); background: transparent; color: #b6a585; }
.online-player-voice { flex: none; font-size: 12px; margin-left: auto; }
.online-player-voice + .online-player-status, .online-player-voice + .online-player-bell { margin-left: 0; }
body.is-compact .table-menu .voice-button { height: 38px; }
.table-menu .voice-member input[type="range"] { accent-color: var(--tm-gold); }

/* Lane letters at the top of each board (board.js): A-D, E-H, I-L across the
   table, to name a lane in chat. Padded like the peek row below (4px + its
   1px border) so each letter sits over its lane. */
.card-row.lane-letters { min-height: 0; padding: 0 5px; margin-bottom: 2px; }
.lane-letter { text-align: center; font: 800 12px/16px 'Manrope', sans-serif; letter-spacing: 0.08em;
  color: rgba(233, 223, 201, 0.75); }
body.is-compact .lane-letter { font-size: 10px; line-height: 13px; }

/* Player colours (tester 2026-10-02): Player 1 green, Player 2 orange,
   Player 3 purple — a teammate's card on your board is outlined in theirs
   (Ultracoop), and each player's avatar is ringed in it. */
:root { --seat-0: #4fbf5f; --seat-1: #f0923a; --seat-2: #a875e0; }
.seat-0 { --seat: var(--seat-0); }
.seat-1 { --seat: var(--seat-1); }
.seat-2 { --seat: var(--seat-2); }
.card.slot.is-teammates-card, .sacrifice-pick .card.is-teammates-card { outline: 3px solid var(--seat); outline-offset: -1px;
  box-shadow: 0 0 10px color-mix(in srgb, var(--seat) 55%, transparent), 0 3px 8px var(--card-shadow); }
.avatar[class*="seat-"], .turn-avatar[class*="seat-"] { border-color: var(--seat); box-shadow: 0 0 0 1px var(--seat); }
/* The topbar's "whose hand you're looking at" ring stays, the colour outside it. */
.turn-avatar.is-viewed[class*="seat-"] { box-shadow: 0 0 0 2px var(--card-cream), 0 0 0 4px var(--seat); }

/* Beside the scale: the damage still needed to win (board.js damageToWin). */
.to-win { display: inline-flex; align-items: baseline; gap: 2px; margin-left: 8px; padding: 2px 8px;
  border: 1px solid rgba(240, 200, 80, 0.55); border-radius: 10px; background: rgba(240, 200, 80, 0.1);
  color: var(--card-cream); font: 600 12px 'Manrope', sans-serif; white-space: nowrap; cursor: help; }
.to-win strong { color: #f5c542; font-weight: 800; font-size: 13px; }
body.is-compact .to-win-label { display: none; }
body.is-compact .to-win { margin-left: 4px; padding: 1px 6px; }

/* Multiplayer: a hand in its player's colour (board.js renderHandStrip); a
   teammate's says whose it is. */
.hand-dock.is-seat-coloured .card--hand { outline: 3px solid var(--seat); outline-offset: -1px; }
.hand-dock.is-theirs .card--hand { opacity: 0.85; }
.hand-dock.is-theirs .hand-strip-cards { position: relative; }
/* Just above the cards' visible tops: they hang half a card (0.7 x the width
   of a 5:7 card) below the strip's 60px slide-up room. */
.hand-owner { position: absolute; left: 50%; top: calc(60px + var(--card-w) * 0.7 - 30px); transform: translateX(-50%);
  padding: 3px 12px; border-radius: 12px; white-space: nowrap; pointer-events: none;
  background: var(--card-espresso); border: 2px solid var(--seat); color: var(--card-cream);
  font: 700 12px 'Manrope', sans-serif; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); }
body.is-compact .hand-owner { font-size: 11px; padding: 2px 8px; }

/* The sacrifice picker: each card with its lane's letter on top. */
.sacrifice-pick { position: relative; display: flex; flex-direction: column; align-items: center; padding-top: 16px; }
/* The lane's letter as a badge on top of the card, ringed in the owner's colour for a teammate's card. */
.sacrifice-lane { position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 3; min-width: 22px; padding: 1px 7px;
  border-radius: 11px; text-align: center; background: var(--card-espresso, #1c1410); border: 2px solid var(--seat, var(--card-cream, #e9dfc9));
  color: var(--card-cream, #e9dfc9); font: 800 13px/16px 'Manrope', sans-serif; letter-spacing: 0.04em; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.55); }
.sacrifice-pick .card.is-teammates-card { outline-width: 4px; }

/* The Hall of Fame: the won runs, and the Arena champions on the right. */
.hof-layout { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 24px; align-items: start;
  width: 100%; max-width: 1400px; margin: 0 auto; }
.hof-layout .hof-list { max-width: none; margin: 0; }
.arena-champions { position: sticky; top: 16px; padding: 18px 20px; background: rgba(20, 13, 10, 0.75);
  border: 1px solid var(--tm-line); box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.6); }
.arena-champion-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px;
  counter-reset: champ; }
.arena-champion { display: flex; align-items: baseline; gap: 10px; counter-increment: champ; font-size: 16px; }
.arena-champion::before { content: counter(champ) "."; min-width: 1.6em; color: var(--tm-gold); font-family: var(--tm-sc); }
.arena-champion-name { color: var(--tm-ink-bright); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arena-champion-kills { margin-left: auto; font-style: italic; color: var(--tm-muted); white-space: nowrap; }
@media (max-width: 900px) { .hof-layout { grid-template-columns: 1fr; } .arena-champions { position: static; } }
body.is-compact .hof-layout { grid-template-columns: 1fr; }
body.is-compact .arena-champions { position: static; }

/* The Arena page (lobby.js renderArena): a tier, one of your decks, then play. */
.arena-body { display: flex; flex-direction: column; gap: 20px; width: 100%; max-width: 1100px; margin: 0 auto; }
.arena-tiers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.table-menu .arena-tier { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 16px 14px;
  background: rgba(20, 13, 10, 0.75); border: 1px solid var(--tm-line); color: var(--tm-ink); cursor: pointer;
  font-family: inherit; letter-spacing: normal; text-align: center; transition: border-color .2s, box-shadow .2s; }
.table-menu .arena-tier:hover { background: rgba(20, 13, 10, 0.9); color: var(--tm-ink); border-color: var(--tm-line-warm); }
.table-menu .arena-tier[aria-pressed="true"] { border-color: var(--tm-gold); box-shadow: 0 0 0 1px var(--tm-gold), 0 0 18px rgba(201, 164, 106, 0.35); }
.arena-medal { font-size: 30px; line-height: 1; }
.arena-tier-name { font-family: var(--tm-sc); font-size: 20px; color: var(--tm-gold); }
.arena-tier-about { font-style: italic; font-size: 14px; color: var(--tm-muted); }
.arena-tier-candles { font-size: 13px; color: var(--tm-ink); }
.arena-decks { display: flex; flex-direction: column; gap: 10px; }
.table-menu .arena-deck { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding: 12px 14px;
  background: rgba(20, 13, 10, 0.75); border: 1px solid var(--tm-line); color: var(--tm-ink); cursor: pointer;
  font-family: inherit; letter-spacing: normal; text-align: left; }
.table-menu .arena-deck:hover { background: rgba(20, 13, 10, 0.9); color: var(--tm-ink); border-color: var(--tm-line-warm); }
.table-menu .arena-deck[aria-pressed="true"] { border-color: var(--tm-gold); box-shadow: 0 0 0 1px var(--tm-gold); }
.arena-deck .hof-cards { gap: 4px; }
.arena-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
.table-menu .arena-actions .tm-button { min-width: 180px; }
body.is-compact .arena-tiers { grid-template-columns: 1fr; }

/* Versus (static/duel.js): one board, the opponent across the table. */
#board.is-duel .peek-row, #board.is-puzzle .peek-row { display: none; }
.duel-opponent { display: flex; align-items: center; gap: 10px; padding: 4px 8px; margin: 4px 0 2px;
  font-family: 'Manrope', sans-serif; color: var(--card-cream); border: 1px solid rgba(233, 223, 201, 0.12);
  border-radius: 6px; background: rgba(0, 0, 0, 0.18); }
.duel-opponent.is-active { border-color: rgba(201, 164, 106, 0.6); box-shadow: 0 0 10px rgba(201, 164, 106, 0.25); }
.duel-opponent .player-name { font-weight: 600; }
.duel-hand-count, .duel-wins { font-size: 13px; opacity: 0.9; }
.duel-wins { margin-left: auto; letter-spacing: 2px; color: #e2b867; }
.duel-round-note { font-weight: 600; }
.duel-over-box.is-won h3 { color: #e2b867; }
.versus-modes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.table-menu .versus-mode[disabled] { opacity: 0.5; cursor: not-allowed; }
body.is-compact .versus-modes { grid-template-columns: 1fr; }
.card-cost { font-weight: 600; white-space: nowrap; margin-left: 0.35em; }

/* Full screen (static/fullscreen.js). */
.fullscreen-button.header-fullscreen { background: transparent; border: 1px solid rgba(184, 147, 90, 0.6); border-radius: 6px;
  color: var(--card-cream); font-size: 15px; line-height: 1; padding: 6px 8px; cursor: pointer; margin-right: 6px; }
.fullscreen-button.header-fullscreen:hover { border-color: #f5c542; }
.fullscreen-button[aria-pressed="true"] { border-color: #f5c542; }
.table-menu .menu-fullscreen { font-size: 18px; line-height: 1; padding-left: 12px; padding-right: 12px; }
.table-menu .tm-icon-button { display: inline-flex; align-items: center; justify-content: center; padding-left: 12px; padding-right: 12px; }
/* Upright phones: one row of icon buttons; your name is in "At the table". */
body.is-compact.is-portrait .tm-seated { display: none; }
/* Phones: when the whole hand fits below the board (app.js fitHand — a tall
   phone in Single Player or Versus, tester 2026-10-03), it shows whole and a
   selected card lifts a little; else it stays half-hidden. */
body.is-compact.hand-fits .hand-dock .card--hand { transform: none; }
body.is-compact.hand-fits .hand-dock .card--hand.is-selected { transform: translateY(-10px); }
body.is-compact.hand-fits .hand-strip-cards { padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px)); }
/* The monster's back row (the peek row): a big faint ▼▼ in every empty slot,
   so it reads as "comes down next" (tester 2026-10-03); a queued card covers it. */
.peek-row .slot.card--empty { position: relative; }
.peek-row .slot.card--empty::after {
  content: ''; position: absolute; inset: 14% 18%; pointer-events: none; opacity: 0.14;
  background: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64' fill='none' stroke='%23b88a3e' stroke-width='7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 12 L32 30 L52 12'/%3E%3Cpath d='M12 34 L32 52 L52 34'/%3E%3C/svg%3E");
}
/* Phones: the draw piles slide in just above the hand (app.js sets --hand-h),
   smaller, so your hand stays in view while you pick a pile (tester 2026-10-03). */
body.is-compact .deck-flyout { bottom: calc(var(--hand-h, 0px) + 6px); padding: 6px 8px;
  border-bottom: 1.5px solid var(--accent); border-radius: 8px; }
body.is-compact .deck { width: auto; min-width: 56px; }
body.is-compact .deck-label { white-space: nowrap; font-size: 9px; }
body.is-compact .deck-art { width: 52px; height: 70px; }
/* A hand card's printed sigils, as badges, only while a phone shows the hand
   half-hidden (the art's sigils are on the hidden half) — in a row along the
   top (tester 2026-10-03). */
.card-extra-sigils .is-printed { display: none; }
body.is-compact:not(.hand-fits) .hand-dock .card-extra-sigils .is-printed { display: inline-flex; }
body.is-compact:not(.hand-fits) .hand-dock .card-extra-sigils { flex-direction: row; top: 18%; right: 3px; }
/* Phones: a hand too big for one row fans out like cards held in a hand
   (static/handfan.js): overlapping on a gentle arc, one card at the front —
   raised, upright, whole. A swipe across the hand changes the front card. */
body.is-compact .hand-strip-cards.is-fanned { position: relative; display: block; box-sizing: content-box;
  width: var(--fan-w); height: calc(var(--fan-card-w) * 1.4); padding-top: 0; padding-left: 0; padding-right: 0;
  margin: 0 auto; }
body.is-compact .hand-strip-cards.is-fanned > .card--hand {
  position: absolute; top: 0; left: var(--x); width: var(--fan-card-w); min-width: 0; flex: none;
  transform-origin: 50% 130%; touch-action: pan-y;
  transform: translateY(calc(var(--drop, 0px) + var(--arc, 0px))) rotate(var(--rot, 0deg));
  transition: transform 140ms ease, box-shadow 140ms ease;
  box-shadow: -3px 0 8px rgba(0, 0, 0, 0.45);
}
body.is-compact .hand-strip-cards.is-fanned > .card--hand.is-front {
  transform: translateY(calc(var(--drop, 0px) - 8px)) scale(1.05);
  box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.6);
}
body.is-compact .hand-strip-cards.is-fanned > .card--hand.is-selected { box-shadow: 0 0 0 3px #5dcaa5, 0 6px 14px rgba(0, 0, 0, 0.5); }
/* Half-hidden (no room for the whole hand): the cards hang half below the
   edge; the selected one comes up. */
body.is-compact:not(.hand-fits) .hand-strip-cards.is-fanned > .card--hand { --drop: calc(var(--fan-card-w) * 0.7); }
body.is-compact:not(.hand-fits) .hand-strip-cards.is-fanned > .card--hand.is-selected { --drop: 0px; }
body.is-compact.is-landscape:not(.hand-fits) .hand-strip-cards.is-fanned > .card--hand.is-selected { --drop: calc(var(--fan-card-w) * 0.42); }
/* The game socket dropped (a phone back from the background): reconnecting. */
.reconnecting-pill { position: fixed; top: 10px; left: 50%; transform: translateX(-50%); z-index: 120;
  display: flex; align-items: center; gap: 8px; padding: 6px 14px; border-radius: 999px;
  background: var(--card-espresso); color: var(--card-cream); border: 1.5px solid var(--accent);
  font: 600 13px 'Manrope', sans-serif; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5); }

/* The card pick's hidden setups (a cost or a tribe): face-down cards. */
.hidden-pick-note { margin: -4px 0 10px; font: italic 14px 'Manrope', sans-serif; color: var(--card-cream); opacity: 0.8; text-align: center; }
.card.hidden-pick { width: var(--card-w-reward); aspect-ratio: 5 / 7; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 8px; position: relative; color: var(--card-cream);
  border: 2px solid var(--accent); border-radius: 8px; cursor: pointer;
  background: repeating-linear-gradient(45deg, #2a1d14 0 6px, #22170f 6px 12px); }
.card.hidden-pick:hover { border-color: #f5c542; box-shadow: 0 0 14px rgba(245, 197, 66, 0.35); }
.hidden-pick-icon { font-size: 26px; letter-spacing: -2px; line-height: 1; }
.hidden-pick-name { font: 700 15px 'Manrope', sans-serif; letter-spacing: 0.04em; text-transform: uppercase; }
.hidden-pick-mark { position: absolute; bottom: 8px; font: 700 20px 'Cinzel', serif; color: var(--accent); opacity: 0.7; }
.reward-reveal-box { align-items: center; text-align: center; }
.reward-reveal-box .card.is-revealed { animation: reveal-flip 0.5s ease-out; }
@keyframes reveal-flip { from { transform: rotateY(90deg); } to { transform: rotateY(0); } }
@media (prefers-reduced-motion: reduce) { .reward-reveal-box .card.is-revealed { animation: none; } }
.reward-reveal-box .card { width: var(--card-w-reward); flex: none; }
body.is-compact .reward-reveal .modal-box.reward-reveal-box { width: auto; max-width: 90vw; height: auto; max-height: none;
  align-self: center; margin: auto; border-radius: 8px; border: 1.5px solid var(--accent); }
/* Voice: your microphone's live level and the microphone picker. */
.voice-mic { display: flex; flex-direction: column; gap: 6px; }
.voice-level { height: 6px; border-radius: 3px; background: rgba(233, 223, 201, 0.15); overflow: hidden; }
.voice-level-fill { display: block; height: 100%; width: 0; background: #6bc46b; transition: width 120ms linear; }
.voice-mic-select { width: 100%; font: 12px 'Manrope', sans-serif; background: var(--card-espresso); color: var(--card-cream);
  border: 1px solid rgba(184, 147, 90, 0.5); border-radius: 4px; padding: 4px; }
.voice-mic-select[hidden] { display: none; }
.voice-mic-label { font-size: 11px; opacity: 0.75; }

/* Desktop: the header in one line — the fight's name shortens (…) before
   the phase, the boss's candles and the difficulty wrap under it; the
   "coscryption" title gives way on narrower screens (tester 2026-10-04:
   the hand covered the board). */
body:not(.is-compact) .run-header-left { flex-wrap: nowrap; align-items: center; min-width: 0; overflow: hidden; }
body:not(.is-compact) .run-header-left > * { flex: none; }
body:not(.is-compact) .run-header-left > .run-encounter { flex: 0 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 1700px) { body:not(.is-compact) .run-header .run-title { display: none; } }
.turn-indicator.is-solo:empty { display: none; }
/* The monster is deciding: your hand waits (no taps, dimmed). */
body.monster-thinking .hand-dock .card--hand { pointer-events: none; filter: grayscale(0.5) brightness(0.75); }
.connection-latency { margin: 6px 0 0; font: 12px 'Manrope', sans-serif; opacity: 0.7; }
.connection-latency.is-slow { color: #f0997b; opacity: 1; }

/* Puzzle mode: the result popup. */
.puzzle-result-box { align-items: center; text-align: center; }
.puzzle-result-box.is-solved h3 { color: #e2b867; font-size: 20px; }
.puzzle-solution { text-align: left; margin: 0; padding-left: 20px; font-size: 14px; line-height: 1.5; }
.puzzle-solution[hidden] { display: none; }
.puzzle-note { margin: 0; font-size: 13px; opacity: 0.8; }
.puzzle-buttons { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.puzzle-buttons .is-primary { border-color: #f5c542; }
body.is-compact .puzzle-result .modal-box.puzzle-result-box { width: auto; max-width: 92vw; height: auto; max-height: none;
  align-self: center; margin: auto; border-radius: 8px; border: 1.5px solid var(--accent); }

/* Sandbox: the lab screen. */
.sandbox-body { display: flex; flex-direction: column; gap: 22px; }
.sandbox-section { display: flex; flex-direction: column; gap: 8px; }
.sandbox-note { margin: 0; font-style: italic; color: var(--tm-muted); }
.sandbox-all { display: flex; gap: 8px; }
.table-menu .sandbox-all .tm-button { padding: 4px 12px; font-size: 13px; }
.sandbox-option { display: flex; align-items: center; gap: 10px; padding: 8px 10px; cursor: pointer;
  background: rgba(20, 13, 10, 0.6); border: 1px solid var(--tm-line); }
.sandbox-option:hover { border-color: var(--tm-line-warm); }
.sandbox-option input { width: 18px; height: 18px; accent-color: #c9a46a; flex: none; }
.sandbox-icon { width: 30px; height: 30px; flex: none; }
.sandbox-option-text { display: flex; flex-direction: column; gap: 2px; }
.sandbox-option-text strong { color: var(--tm-gold); font-weight: 600; }
.sandbox-option-text span { font-size: 14px; color: var(--tm-ink); opacity: 0.85; }
.lobby-sandbox { margin: 0 0 12px; font-style: italic; opacity: 0.85; text-transform: capitalize; }

/* Sandbox status effects (rules/sandbox.py): badges on the card. */
.card-statuses { position: absolute; top: 3px; left: 3px; display: flex; flex-direction: column; gap: 2px; pointer-events: none; z-index: 2; }
.card-status { font: 700 11px/1 'Manrope', sans-serif; color: #fff; background: rgba(16, 12, 9, 0.88);
  border: 1px solid #b8935a; border-radius: 9px; padding: 2px 5px; white-space: nowrap; }
.card-status--burn { border-color: #f0784a; }
.card-status--poison { border-color: #8fd36b; }
.card-status--frozen { border-color: #8cc8f0; }
.card.is-frozen .card-art img { filter: saturate(0.4) brightness(1.05) hue-rotate(170deg); }
/* Sandbox: picking the teammate's card a Command card gives orders to. */
.command-pick { position: fixed; top: 64px; left: 50%; transform: translateX(-50%); z-index: 60; display: flex; align-items: center;
  gap: 12px; padding: 8px 14px; background: var(--card-espresso); color: var(--card-cream); border: 1.5px solid #f5c542;
  border-radius: 8px; font: 600 14px 'Manrope', sans-serif; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.6); }
.command-pick button { padding: 4px 12px; }
.sandbox-levels { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
body.is-compact .sandbox-levels { grid-template-columns: 1fr; }

/* Loose Tail: the tail pops into the lane the creature slipped out of. */
.slot.is-tail-dropped { animation: tail-drop 0.25s ease-out; }
@keyframes tail-drop { from { transform: scale(0.6); opacity: 0; } to { transform: none; opacity: 1; } }

/* ---- Themed cards (static/theme.js; the Sandbox's Theme option) ----
   The themed art is a framed card with a blank name strip on top and a blank
   panel below the picture: the name, power, health, cost and sigils are
   printed here, sized from the card's width (cqw; .card-art is the container)
   and placed as a share of the card (the art is stretched to fill it, so a
   percentage lands on the same spot of the picture at every card size). */
.card--themed { --ink: #2a1a0c; }
.card--themed.card--alien { --ink: #0c1d12; }
.card--themed .card-art img,
.card--themed .card-art.is-folding > img { object-fit: fill; filter: none; }
.card-name-plate {
  position: absolute; left: 7%; right: 7%; top: 3.4%; height: 11.4%;
  display: flex; align-items: center; justify-content: center;
  font-family: 'Anton', Impact, 'Arial Narrow', 'Helvetica Neue', sans-serif;
  font-size: calc(var(--fit, 10) * 1cqw);
  line-height: 1; letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap;
  color: var(--ink); pointer-events: none;
  z-index: 2;   /* a long title runs over the tribe emblem, not under it */
  text-shadow: 0 0 2px rgba(255, 244, 220, 0.95), 0 0 4px rgba(255, 244, 220, 0.8);
}
.card-stat-themed {
  position: absolute; top: 69%; height: 25%; width: 27%;
  display: flex; align-items: center; justify-content: center;
  font-family: 'Anton', Impact, 'Arial Narrow', 'Helvetica Neue', sans-serif;
  font-size: 27cqw; line-height: 1; color: var(--ink); pointer-events: none;
}
.card-stat-themed.card-power { left: 6%; }
.card-stat-themed.card-toughness { right: 6%; }
.card-stat-themed.is-damaged { color: #a1261b; }
.card-stat-themed.is-boosted { color: #1d6b2e; }
.card-cost-themed {
  position: absolute; right: 7%; top: 18%;
  padding: 0.5cqw 2cqw; border-radius: 3cqw;
  background: rgba(16, 12, 9, 0.78); border: 1px solid rgba(255, 255, 255, 0.25);
  font-size: 8cqw; line-height: 1.2; letter-spacing: 0.1em; white-space: nowrap; pointer-events: none;
}
.card--themed .card-extra-sigils {
  top: 70%; right: auto; left: 34%; width: 32%; height: 23%;
  flex-direction: row; flex-wrap: wrap; justify-content: center; align-content: center; align-items: center;
  gap: 1.5cqw;
}
.card--themed .card-extra-sigils img {
  width: 14cqw; height: 14cqw; padding: 1.4cqw; box-sizing: border-box;
}
.card--themed .card-extra-sigils .sigil-chip { width: 14cqw; height: 14cqw; font-size: 8cqw; }
.sandbox-levels--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
body.is-compact .sandbox-levels--two { grid-template-columns: 1fr; }

/* Sandbox poison: a green skull in the middle of the card, and a green glow. */
.card-poison-mark {
  position: absolute; left: 50%; top: 50%; width: 34cqw; aspect-ratio: 40 / 44;
  transform: translate(-50%, -50%); z-index: 2; pointer-events: none;
  filter: drop-shadow(0 0 5px rgba(120, 220, 80, 0.9)) drop-shadow(0 1px 1px rgba(0, 0, 0, 0.7));
  animation: poison-pulse 1.8s ease-in-out infinite;
}
.card-poison-mark svg { display: block; width: 100%; height: 100%; }
.card-poison-mark .pm-skull { fill: #8fe05a; stroke: #1c3d12; stroke-width: 1.6; }
.card-poison-mark .pm-hole { fill: #14280d; }
.card-poison-mark .pm-teeth { stroke: #1c3d12; stroke-width: 1.8; fill: none; }
.card.is-poisoned { box-shadow: 0 0 0 2px rgba(143, 211, 107, 0.85), 0 0 10px 2px rgba(120, 220, 80, 0.55); }
@keyframes poison-pulse { 0%, 100% { opacity: 0.9; } 50% { opacity: 0.6; } }
.card--themed .card-statuses { top: 35%; left: 7%; }      /* below the tribe icon */

/* Sign-up approval (static/lobby.js setupAdminButton / renderAdmin). */
.table-menu .admin-button { font-size: 13px; padding: 6px 12px; }
.table-menu .admin-button[hidden] { display: none; }
.table-menu .admin-button.has-pending { border-color: #f5c542; color: #f5c542; box-shadow: 0 0 10px rgba(245, 197, 66, 0.35); }
.login-note { color: #c9d8a8; margin-top: 12px; max-width: 360px; line-height: 1.4; }
.admin-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; max-width: 640px; }
.admin-row { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; padding: 12px 16px;
  background: rgba(16, 12, 9, 0.7); border: 1px solid #4a3a2a; border-radius: 6px; }
.admin-name { font-weight: 700; font-size: 17px; flex: 1 1 140px; }
.admin-when { font-size: 13px; opacity: 0.7; flex: 1 1 180px; }
.admin-account { align-items: baseline; }
.admin-tag { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: #f5c542; border: 1px solid #f5c542; border-radius: 3px; padding: 1px 6px; }
.admin-tag[hidden] { display: none; }
.admin-facts { font-size: 13px; opacity: 0.8; flex: 1 1 100%; order: 3; }
.admin-delete { margin-left: auto; border-color: #a1261b; color: #e8a198; }
.admin-delete:hover:not(:disabled) { background: rgba(161, 38, 27, 0.35); }
.admin-list { max-width: 820px; }
.admin-list .hof-empty { text-align: left; padding: 6px 0; margin: 0; }

/* Change password dialog (static/lobby.js showPasswordDialog). */
.password-dialog { display: flex; flex-direction: column; gap: 10px; }
.password-dialog label { display: flex; flex-direction: column; gap: 4px; font: 600 12px 'Manrope', sans-serif; color: var(--card-cream); }
.password-dialog input { padding: 8px 10px; border-radius: 4px; border: 1px solid #6b5a43; background: rgba(0, 0, 0, 0.35);
  color: var(--card-cream); font: 14px 'Manrope', sans-serif; }
.password-dialog .password-note { min-height: 1.2em; color: #e8a198; opacity: 1; }
.password-dialog .password-note.is-ok { color: #9fd38f; }

/* Forgotten passwords and the temporary password box (static/lobby.js). */
.menu-button--link { background: none; border: none; text-decoration: underline; opacity: 0.8; font-size: 13px; padding: 4px; }
.menu-button--link:hover { opacity: 1; }
.table-menu .tm-button .tm-button-label { margin-left: 6px; font-size: 13px; vertical-align: middle; }
body.is-compact .table-menu .tm-button .tm-button-label { display: none; }
.table-menu #change-password { display: inline-flex; align-items: center; }
.admin-account .admin-reset-account { margin-left: auto; }
.admin-account .admin-reset-account + .admin-delete { margin-left: 0; }
.admin-temp { max-width: 820px; margin: 0 0 18px; padding: 14px 18px; border: 1px solid #f5c542; border-radius: 6px; background: rgba(245, 197, 66, 0.08); }
.admin-temp[hidden] { display: none; }
.admin-temp-line { margin: 0 0 6px; font-size: 16px; }
.admin-temp-password { font: 700 20px ui-monospace, Menlo, monospace; letter-spacing: 0.08em; color: #f5c542; padding: 0 8px; }
.admin-temp-note { margin: 0; font-size: 13px; opacity: 0.75; }
.password-dialog .password-hint { margin: 0; }
.admin-page #admin-change-password { width: auto; display: inline-block; align-self: flex-start; }

/* The in-page "are you sure?" (static/confirm.js). */
.ask-confirm { display: flex; flex-direction: column; gap: 10px; }
.ask-confirm p { margin: 0; }
.ask-confirm-type { display: flex; flex-direction: column; gap: 4px; font: 600 12px 'Manrope', sans-serif; color: var(--card-cream); }
.ask-confirm-type input { padding: 8px 10px; border-radius: 4px; border: 1px solid #6b5a43; background: rgba(0, 0, 0, 0.35);
  color: var(--card-cream); font: 14px 'Manrope', sans-serif; }
.ask-confirm .menu-button:disabled { opacity: 0.4; cursor: not-allowed; }

/* The tribe icon on a themed card (static/theme.js tribeIcons): top-left of the art. */
.card-tribe-box { position: absolute; left: 3%; top: 3.5%; width: 25.5cqw; z-index: 1; pointer-events: none;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.45)); }
.card-tribe-box .card-tribe { display: block; width: 100%; height: auto; }
.card-tribe-box.is-all { width: 37.5cqw; display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.4cqw; }

/* Alien-mode pictures standing in for the emoji (static/theme.js). */
.cur-icon { height: 1.15em; width: 1.15em; vertical-align: -0.22em; object-fit: contain; }
.card-cost-themed .cur-icon { height: 1.5em; width: 1.5em; vertical-align: middle; margin: 0 -0.12em; }
.hidden-pick-icon .cur-icon { height: 36px; width: 36px; margin: 0 -3px; }
.hidden-pick-icon .tribe-glyph, .tribe-glyph { height: 1.25em; width: 1.25em; vertical-align: -0.3em; object-fit: contain; }
.hidden-pick-icon .tribe-glyph { height: 44px; width: 44px; }
.totem-reward .item-reward-icon .tribe-glyph { height: 64px; width: 64px; }
.item-row .item-icon .item-img, .item-row .item-icon .tribe-glyph { height: 1.4em; width: 1.4em; object-fit: contain; }
.item-reward-icon .item-img { height: 64px; width: 64px; object-fit: contain; }
.pool-bones .cur-icon { height: 1.3em; width: 1.3em; }
.card-details .cur-icon, .sigil-tooltip .cur-icon { height: 1.1em; width: 1.1em; }

/* A card that dies burns like paper (static/burn.js): a copy laid over the table,
   eaten away by masked holes with a glowing rim, and embers on a canvas. */
.burn-ghost { position: fixed; z-index: 60; pointer-events: none; overflow: hidden; margin: 0; }
.burn-ghost .burn-glow { position: absolute; inset: 0; pointer-events: none; mix-blend-mode: normal; }
.burn-embers { position: fixed; left: 0; top: 0; z-index: 61; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .burn-ghost, .burn-embers { display: none; } }

/* ---- The Card Market (static/market.js) ---- */
.market { --card-w-reward: min(132px, 38vw); }
.market-credits { display: inline-flex; align-items: center; padding: 6px 14px; margin-right: 8px; border: 1px solid var(--tm-line-warm, rgba(184, 147, 90, 0.5));
  background: rgba(11, 8, 7, 0.6); color: var(--tm-gold, #e0a458); font-family: var(--tm-sc, inherit); font-size: 18px; letter-spacing: 0.06em; }
.market-tabs { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin: 4px 0 14px; }
.market-tab { padding: 8px 18px; background: rgba(20, 13, 10, 0.75); color: var(--tm-muted, #b9a98a); border: 1px solid var(--tm-line, rgba(184, 147, 90, 0.3));
  font-family: var(--tm-sc, inherit); font-size: 16px; letter-spacing: 0.08em; cursor: pointer; }
.market-tab[aria-pressed="true"] { color: var(--tm-gold, #e0a458); border-color: var(--tm-gold, #e0a458); background: rgba(60, 38, 20, 0.7); }
.market-body { width: 100%; max-width: 1100px; margin: 0 auto; display: flex; flex-direction: column; gap: 14px; }
.market-decks { display: flex; flex-wrap: wrap; gap: 8px; }
.market-deck { padding: 8px 12px; background: rgba(20, 13, 10, 0.75); color: var(--tm-muted, #b9a98a); border: 1px solid var(--tm-line, rgba(184, 147, 90, 0.3));
  font-size: 14px; cursor: pointer; }
.market-deck[aria-pressed="true"] { color: var(--tm-gold, #e0a458); border-color: var(--tm-gold, #e0a458); }
.market-note { margin: 0; font-style: italic; font-size: 14px; color: var(--tm-muted, #b9a98a); }
.market-sort { font-size: 14px; color: var(--tm-muted, #b9a98a); }
.market-sort select { margin-left: 6px; padding: 4px 8px; background: rgba(20, 13, 10, 0.9); color: inherit; border: 1px solid var(--tm-line, rgba(184, 147, 90, 0.3)); }
.market-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--card-w-reward), 1fr)); gap: 18px 12px; justify-items: center; }
.market-card { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 6px; }
.market-card .card { flex: none; }
.market-card-caption { display: flex; flex-direction: column; align-items: center; gap: 2px; font-size: 13px; text-align: center; }
.market-worth { color: var(--tm-gold, #e0a458); font-weight: 700; }
.market-actions { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; }
.market-actions .tm-button { padding: 4px 8px; font-size: 12px; }
.market-actions .tm-button:disabled { opacity: 0.45; cursor: not-allowed; }
.market-modal { display: flex; flex-direction: column; gap: 12px; align-items: center; max-width: min(640px, 100vw); max-height: 100dvh; overflow-y: auto; }
.market-customise-box { align-items: stretch; }
.market-preview { display: flex; justify-content: center; }
.market-controls { display: flex; flex-direction: column; gap: 10px; }
.market-stat { display: flex; align-items: center; gap: 10px; }
.market-stat-label { min-width: 70px; font-size: 14px; color: var(--tm-muted, #b9a98a); }
.market-stat-value { min-width: 24px; text-align: center; font-size: 20px; font-weight: 700; }
.market-step { width: 34px; height: 34px; font-size: 20px; line-height: 1; border: 1px solid var(--tm-line-warm, rgba(184, 147, 90, 0.5)); background: rgba(20, 13, 10, 0.9); color: inherit; cursor: pointer; }
.market-step:disabled { opacity: 0.35; cursor: not-allowed; }
.market-sigils { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.market-sigil { padding: 4px 10px; font-size: 13px; border: 1px solid var(--tm-line, rgba(184, 147, 90, 0.3)); background: rgba(20, 13, 10, 0.9); color: inherit; cursor: pointer; }
.market-sigil.is-on { border-color: var(--tm-gold, #e0a458); color: var(--tm-gold, #e0a458); }
.market-sigils--add { max-height: 150px; overflow-y: auto; }
.market-quote { min-height: 2.6em; margin: 0; font-size: 14px; text-align: center; }
.market-quote.is-short { color: #e07a6a; }
.market-modal-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.market-price-label { display: flex; flex-direction: column; gap: 6px; font-size: 14px; align-items: center; }
.market-price { width: 8em; padding: 6px 8px; font-size: 18px; text-align: center; background: rgba(20, 13, 10, 0.9); color: inherit; border: 1px solid var(--tm-line-warm, rgba(184, 147, 90, 0.5)); }
body.is-compact .market-cards { gap: 14px 8px; }

/* The Mirror Tentacle's power is a mirror away from the board (static/board.js MIRROR_GLYPH). */
.card-stat .card-stat-symbol .mirror-glyph, .card-stat-symbol .mirror-glyph { position: static; inset: auto; width: 1.05em; height: 1.05em; vertical-align: -0.15em; }
.card-stat-themed.card-stat-symbol { display: flex; align-items: center; justify-content: center; }
.card-stat-themed.card-stat-symbol .mirror-glyph { width: 0.85em; height: 0.85em; }

/* The Arena page's wins: a name, then bronze, silver and gold counts (static/lobby.js paintArenaMedals). */
.arena-medals { width: 100%; border-collapse: collapse; }
.arena-medals th, .arena-medals td { padding: 5px 4px; font-size: 16px; }
.arena-medal-col { width: 2.4em; text-align: center; font-weight: 400; }
.arena-medal-name { text-align: left; font-weight: 400; color: var(--tm-ink-bright); max-width: 9em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arena-medal-count { text-align: center; font-family: var(--tm-sc); color: var(--tm-gold); }
.arena-medal-count.is-zero { color: var(--tm-muted); opacity: 0.55; }
.arena-medal-row + .arena-medal-row th, .arena-medal-row + .arena-medal-row td { border-top: 1px solid var(--tm-line, rgba(184, 147, 90, 0.2)); }

/* The dealer (static/dealer.js): their status line under the header, and the role choice on the Dealer page. */
.dealer-status { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; margin: 6px auto 0; padding: 6px 14px; max-width: 1100px;
  border: 1px solid rgba(184, 147, 90, 0.35); background: rgba(20, 13, 10, 0.7); color: var(--card-cream, #e9dfc9); font: 600 14px 'Manrope', sans-serif; }
.dealer-status.is-my-turn { border-color: #6bc46b; box-shadow: 0 0 10px rgba(107, 196, 107, 0.3); }
.dealer-pools { margin-left: auto; color: #f5c542; }
.dealer-roles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-bottom: 8px; }
.dealer-role { text-align: left; }
@media (max-width: 700px) { .dealer-roles { grid-template-columns: 1fr; } }
