/* Design tokens. These are the CLASSIC theme's values and the fallback for
   everything; the other three themes redefine them in themes.css, selected by
   [data-theme] on <html>. Anything drawn on canvas instead of by the browser
   goes through Stackzilla.SKIN — see theme.js. Keep the two in step. */
:root {
  --bg: #070811;
  --bg-image: radial-gradient(1200px 800px at 50% -10%, #16193080, transparent);
  --panel: #121527;
  --panel-2: #1b1f38;
  --panel-3: #232842;      /* button hover */
  --well: #0c0f1e;         /* inputs, slots, meter troughs */
  --board: #0b0d17;        /* playfield backdrop behind the canvas */
  --text: #e6e8f0;
  --muted: #8b93b0;
  --accent: #22d3ee;
  --accent-2: #a855f7;
  --accent-3: #ec4899;     /* third stop in the clear-popup gradient */
  --on-accent: #0a0c16;    /* text on an accent-filled surface */
  --danger: #ef4444;
  --danger-2: #7f1d1d;
  --warn: #f59e0b;
  --gold: #facc15;
  --ok: #22c55e;
  --border: rgba(255, 255, 255, 0.08);
  --border-hover: rgba(255, 255, 255, 0.16);
  --scrim: rgba(7, 8, 17, 0.82);       /* over a board: pause, countdown, game over */
  --scrim-modal: rgba(4, 5, 12, 0.72); /* full-screen behind a dialog */
  --shadow-panel: 0 24px 60px rgba(0, 0, 0, 0.45);

  /* Type. Themes swap the display face; the body face stays legible. */
  --font-body: "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --font-display: var(--font-body);
  --display-transform: none;
  --display-weight: 700;
  --display-spacing: 0.5px;

  /* Per-player identity colour. Themes that distinguish the two boards set
     these; app.js does not need to know about it. */
  --side-you: var(--accent);
  --side-them: var(--accent-2);
  --side: var(--side-you);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg-image), var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.hidden { display: none !important; }

h1 {
  font-size: 2.6rem;
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  text-transform: var(--display-transform);
  letter-spacing: var(--display-spacing);
  margin: 0 0 4px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.tagline { color: var(--muted); margin: 0 0 28px; }

/* ---------- Hero: the win condition, shown ----------
   A miniature of the real match HUD — the 2:00 round clock and the five KO
   lamps — so the menu states the rules instead of describing the genre. */
.hero-rules {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: -18px 0 14px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel-2);
  position: relative;
}
.hr-clock {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: 20px;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.hr-sep {
  width: 1px;
  align-self: stretch;
  background: var(--border);
}
.hr-lamps { display: flex; gap: 7px; }
.hr-lamps i {
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--panel-3);
  border: 1px solid var(--border);
  /* Each lamp lights a beat after the one before it, then they all reset —
     five KOs in five seconds, which is the rule the strip exists to teach. */
  animation: hero-lamp 5s linear infinite;
}
.hr-lamps i:nth-child(1) { animation-delay: 0.2s; }
.hr-lamps i:nth-child(2) { animation-delay: 1.0s; }
.hr-lamps i:nth-child(3) { animation-delay: 1.8s; }
.hr-lamps i:nth-child(4) { animation-delay: 2.6s; }
.hr-lamps i:nth-child(5) { animation-delay: 3.4s; }
@keyframes hero-lamp {
  0%, 3%   { background: var(--panel-3); box-shadow: none; }
  6%, 84%  { background: var(--accent-2); box-shadow: 0 0 8px var(--accent-2); }
  88%, 100% { background: var(--panel-3); box-shadow: none; }
}
/* The payoff on the fifth lamp: the strip announces the knockout, which is
   how the match actually ends. */
.hr-ko {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  background: var(--panel-2);
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  letter-spacing: 0.18em;
  font-size: 20px;
  color: var(--accent);
  opacity: 0;
  animation: hero-ko 5s linear infinite;
}
@keyframes hero-ko {
  0%, 80%   { opacity: 0; }
  84%, 94%  { opacity: 1; }
  98%, 100% { opacity: 0; }
}
/* Motion here is decoration on top of text that already reads correctly, so
   the honest reduced-motion answer is to stop it, not to substitute a fade. */
@media (prefers-reduced-motion: reduce) {
  .hr-lamps i, .hr-ko { animation: none; }
  .hr-lamps i:nth-child(1), .hr-lamps i:nth-child(2) {
    background: var(--accent-2);
  }
  .hr-ko { opacity: 0; }
}

.hero-sub {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
  margin: 0 0 22px;
}

/* Sits directly under the primary CTA and answers the question that kills
   every small versus game: "will anyone be there?" */
.cta-note {
  color: var(--muted);
  font-size: 12px;
  margin: -6px 0 2px;
}

/* ---------- Menu ---------- */
#menu {
  width: min(460px, 92vw);
  text-align: center;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 36px 28px;
  box-shadow: var(--shadow-panel);
}

.menu-btns { display: flex; flex-direction: column; gap: 12px; }

button {
  font: inherit;
  font-weight: 600;
  color: var(--text);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 18px;
  cursor: pointer;
  transition: transform 0.08s ease, background 0.15s ease, border 0.15s ease;
}
button:hover { background: var(--panel-3); border-color: var(--border-hover); }
button:active { transform: translateY(1px); }

button.primary {
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  color: var(--on-accent);
  border: none;
}
button.primary:hover { filter: brightness(1.07); }

.join-row { display: flex; gap: 8px; }
.join-row input {
  flex: 1;
  font: inherit;
  text-transform: uppercase;
  letter-spacing: 2px;
  text-align: center;
  color: var(--text);
  background: var(--well);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0 14px;
}

.divider {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--muted);
  font-size: 0.8rem;
  margin: 8px 0;
}
.divider::before, .divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ---------- Playfield layout ---------- */
.screen { width: 100%; display: flex; flex-direction: column; align-items: center; }
/* The game screen starts at the top of the window rather than being centred in
   it: a playfield is tall, and vertical centring spends the difference on empty
   space above the board. */
body:has(#game:not(.hidden)) { align-items: flex-start; }

.arena {
  display: flex;
  gap: 22px;
  align-items: flex-start;
  justify-content: center;
  flex-wrap: wrap;
}
/* Two boards side by side have to be the same object twice over. Your side
   column has one more line in it than the opponent's mirror, which left the
   shells 18px different and their bottom edges visibly out of step. */
.arena.versus { align-items: stretch; }

.player {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 16px;
}
.player.dead { opacity: 0.5; filter: grayscale(0.5); }

.board-wrap { position: relative; }
canvas.board {
  display: block;
  border-radius: 8px;
  background: var(--board);
  box-shadow: inset 0 0 0 1px var(--border);
}

.side { display: flex; flex-direction: column; gap: 14px; min-width: 96px; }
.side h3 {
  margin: 0 0 6px;
  font-size: 0.72rem;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--muted);
}
.slot {
  background: var(--well);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px;
}
canvas.mini { display: block; width: 100%; height: auto; }

.stat { margin-bottom: 8px; }
.stat .label {
  font-size: 0.68rem;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--muted);
}
.stat .value { font-size: 1.4rem; font-weight: 700; }

.name-tag {
  font-weight: 700;
  /* Above the board overlay (.overlay is inset:0 over the same wrapper) so the
     name stays readable behind WAITING… and the game-over card. */
  position: relative;
  z-index: 8;
  margin-bottom: 8px;
  min-height: 26px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
/* No badge text means no badge — otherwise an empty pill floats beside the
   name looking like a rendering fault. */
.badge:empty { display: none; }
.badge {
  font-size: 0.7rem;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--muted);
}

/* garbage meter */
.garbage-meter {
  width: 10px;
  align-self: stretch;
  border-radius: 6px;
  background: var(--well);
  border: 1px solid var(--border);
  position: relative;
  overflow: hidden;
}
.garbage-meter .fill {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: linear-gradient(180deg, var(--danger), var(--danger-2));
  transition: height 0.12s ease;
}

.status-bar {
  margin-top: 18px;
  min-height: 26px;
  color: var(--muted);
  text-align: center;
}
.status-bar b { color: var(--text); }

.controls-hint {
  margin-top: 8px;
  color: var(--muted);
  font-size: 0.8rem;
  text-align: center;
}
.controls-hint kbd {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: 5px;
  padding: 1px 6px;
  font-family: inherit;
  font-size: 0.78rem;
}

/* Burger hard left, Rematch hard right, nothing in between — the room code
   moved into the pause menu, which is the only place you need to read it. */
.topbar {
  position: relative;
  width: 100%;
  max-width: 900px;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
.topbar .burger { justify-self: start; }
.topbar #btn-rematch { justify-self: end; }

/* Wide enough that nothing can collide with the centred arena: lift the burger
   and Rematch out of the flow so the row costs no height of its own. The clock
   still sits in flow above the boards, which is where it belongs; in solo and
   puzzles the row then collapses and the playfield starts level with the
   burger instead of a band of empty background. */
@media (min-width: 820px) {
  /* .arena-wrap is also position:relative, so as the later positioned sibling it
     would paint — and hit-test — over the now out-of-flow burger, leaving the
     menu button visible but unclickable. The countdown (z-index 30) still wins
     over it, which is what we want. */
  .topbar { align-items: start; z-index: 20; }
  .topbar .burger,
  .topbar #btn-rematch { position: absolute; top: 0; }
  .topbar .burger { left: 0; }
  .topbar #btn-rematch { right: 0; }
  .topbar:not(:has(#match-clock:not(.hidden))) { margin-bottom: 0; }
}

/* ---------- Battle round clock ---------- */
.match-clock {
  grid-column: 2;
  display: grid;
  justify-items: center;
  gap: 1px;
  line-height: 1;
}
.mc-label {
  font-size: 0.6rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
}
.mc-time {
  font-size: 2rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
/* The last ten seconds are the whole point of a two-minute round. */
.match-clock.urgent .mc-time { color: var(--danger); animation: mc-tick 1s steps(2) infinite; }
@keyframes mc-tick { 0%, 60% { opacity: 1; } 61%, 100% { opacity: 0.55; } }

/* ---------- KO counter (five to win a Battle round) ---------- */
.ko-row { display: grid; gap: 5px; }
.ko-pips { display: flex; gap: 4px; }
.ko-pip {
  flex: 1;
  height: 10px;
  border-radius: 999px;
  background: var(--well);
  border: 1px solid var(--border);
}
.ko-pip.on {
  background: var(--danger);
  border-color: var(--danger);
  box-shadow: 0 0 8px color-mix(in srgb, var(--danger) 65%, transparent);
}
/* A knockout landing is the loudest thing that happens in a round. */
.ko-flash {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  z-index: 9;
  font-size: 3rem;
  font-weight: 900;
  letter-spacing: 2px;
  color: var(--danger);
  opacity: 0;
}
.ko-flash.show { animation: ko-hit 0.9s ease-out forwards; }
@keyframes ko-hit {
  0%   { opacity: 0; transform: scale(0.5); }
  18%  { opacity: 1; transform: scale(1.25); }
  60%  { opacity: 1; transform: scale(1.05); }
  100% { opacity: 0; transform: scale(1.05); }
}

/* Room code, now shown inside the pause menu. */
.modal-card .room-code {
  font-weight: 700;
  letter-spacing: 3px;
  color: var(--accent);
  font-size: 1.1rem;
}
.modal-card .room-code:empty { display: none; }

/* combo / back-to-back indicator */
.combo-indicator {
  position: absolute;
  left: 8px;
  top: 46px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  pointer-events: none;
}
.combo-indicator .c {
  font-weight: 900;
  font-size: 1.5rem;
  letter-spacing: 1px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 10px rgba(34, 211, 238, 0.7));
  animation: comboPulse 0.6s ease-in-out infinite alternate;
}
.combo-indicator .b {
  font-weight: 900;
  font-size: 1.2rem;
  letter-spacing: 2px;
  background: linear-gradient(90deg, var(--warn), var(--danger));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 10px rgba(245, 158, 11, 0.75));
}
@keyframes comboPulse {
  from { transform: scale(1); }
  to { transform: scale(1.12); }
}

/* transient clear popup (Quad / Combo / B2B + points) */
.clear-popup {
  position: absolute;
  left: 0;
  right: 0;
  top: 38%;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  padding: 0 6px;
  line-height: 1.05;
}
.clear-popup.show { animation: popup 1.2s ease-out forwards; }
.clear-popup .pl {
  font-weight: 900;
  font-size: 2.1rem;
  letter-spacing: 1px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2), var(--accent-3));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 18px rgba(168, 85, 247, 0.95));
}
.clear-popup .pp {
  font-weight: 800;
  font-size: 1.6rem;
  color: var(--gold);
  text-shadow: 0 0 16px rgba(250, 204, 21, 0.85);
}
@keyframes popup {
  0% { opacity: 0; transform: translateY(26px) scale(0.75); }
  15% { opacity: 1; transform: translateY(0) scale(1.28); }
  30% { transform: translateY(0) scale(1.08); }
  70% { opacity: 1; transform: translateY(0) scale(1.08); }
  100% { opacity: 0; transform: translateY(-32px) scale(1.08); }
}

/* ---------- Debug overlay (toggle with F2) ---------- */
#dbg-flash {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-weight: 900;
  font-size: 5rem;
  color: #fff;
  text-shadow: 0 0 24px rgba(34, 211, 238, 0.9), 0 0 8px #000;
  pointer-events: none;
  opacity: 0;
  z-index: 9999;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
#dbg-flash.hit { animation: dbgFlash 0.5s ease-out; }
@keyframes dbgFlash {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.6); }
  20% { opacity: 1; transform: translate(-50%, -50%) scale(1.15); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
}
#dbg-panel {
  position: fixed;
  left: 10px;
  top: 10px;
  z-index: 9999;
  background: rgba(7, 8, 17, 0.86);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  line-height: 1.5;
  color: #cdd3e6;
  pointer-events: none;
  min-width: 220px;
}
#dbg-panel .hl { color: #22d3ee; font-weight: 700; }
#dbg-panel .warn { color: #f59e0b; }
#dbg-panel .bad { color: #ef4444; }
#dbg-panel hr { border: none; border-top: 1px solid var(--border); margin: 6px 0; }
#dbg-panel .row { display: flex; justify-content: space-between; gap: 12px; }
#dbg-panel .log { white-space: pre; color: #8b93b0; }

/* game over overlay */
.overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: var(--scrim);
  border-radius: 8px;
  text-align: center;
}
.overlay .big { font-size: 1.6rem; font-weight: 800; }
.overlay.win .big { color: var(--ok); }
.overlay.lose .big { color: var(--danger); }
.overlay .ov-btn {
  margin-top: 16px;
  pointer-events: auto;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  color: var(--on-accent);
  border: none;
  font-weight: 700;
}
.overlay .ov-btn:hover { filter: brightness(1.08); }
/* Secondary actions (Menu, Sign in to save) sit under the primary one and
   read as secondary, but stay unmistakably clickable — at game over these are
   the only controls on screen. */
.overlay .ov-btn-extra {
  margin-top: 8px;
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
  font-weight: 600;
}

/* ---------- online count + latency chips ---------- */
.netline {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 14px;
}
.netline .chip {
  font-size: 0.78rem;
  color: var(--muted);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 11px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.netline .chip.good { color: var(--ok); }
.netline .chip.warn { color: var(--warn); }
.netline .chip.bad { color: var(--danger); }
.netline .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 6px var(--ok);
}

/* ---------- in-match chat ---------- */
.chat {
  position: fixed;
  right: 16px;
  bottom: 16px;
  width: 300px;
  max-width: calc(100vw - 32px);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
  z-index: 50;
  overflow: hidden;
  font-size: 0.85rem;
}
.chat-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  cursor: pointer;
  user-select: none;
  background: var(--panel-2);
  border-bottom: 1px solid var(--border);
}
.chat-title { font-weight: 600; }
.chat-unread {
  background: var(--accent-2);
  color: var(--on-accent);
  font-weight: 700;
  font-size: 0.72rem;
  border-radius: 999px;
  padding: 1px 7px;
}
.chat-toggle {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1rem;
  cursor: pointer;
  padding: 0 2px;
}
.chat.collapsed .chat-body { display: none; }
.chat-body { display: flex; flex-direction: column; }
.chat-log {
  height: 160px;
  overflow-y: auto;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.chat-msg { display: flex; flex-direction: column; max-width: 85%; }
.chat-msg .who { font-size: 0.66rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.chat-msg .txt {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 5px 9px;
  word-break: break-word;
  line-height: 1.35;
}
.chat-msg.mine { align-self: flex-end; align-items: flex-end; }
.chat-msg.mine .txt {
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  color: var(--on-accent);
  border: none;
}
.chat-msg.theirs { align-self: flex-start; }
.chat-msg.theirs .txt { animation: chatIn 0.18s ease; }
@keyframes chatIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.chat-sys { color: var(--muted); font-size: 0.75rem; text-align: center; font-style: italic; }
.chat-emotes {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 12px;
  border-top: 1px solid var(--border);
}
.chat-emotes .emote {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text);
  font: inherit;
  font-size: 0.78rem;
  padding: 3px 10px;
  cursor: pointer;
}
.chat-emotes .emote:hover { border-color: var(--accent); color: var(--accent); }
.chat-input-row { display: flex; gap: 6px; padding: 8px 12px 12px; }
.chat-input-row input {
  flex: 1;
  min-width: 0;
  background: var(--well);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font: inherit;
  padding: 7px 10px;
}
.chat-input-row input:focus { outline: none; border-color: var(--accent); }
.chat-input-row button {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font: inherit;
  font-weight: 600;
  padding: 7px 12px;
  cursor: pointer;
}
.chat-input-row button:hover { border-color: var(--accent); }
@media (max-width: 640px) {
  .chat { right: 8px; left: 8px; bottom: 8px; width: auto; }
  .chat-log { height: 120px; }
}

/* ---------- name input + leaderboard ---------- */
.name-input {
  background: var(--well);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  font: inherit;
  padding: 11px 14px;
  text-align: center;
  margin-bottom: 4px;
}
.name-input:focus { outline: none; border-color: var(--accent); }
.leaderboard {
  display: flex;
  gap: 14px;
  margin-top: 18px;
  flex-wrap: wrap;
  justify-content: center;
}
.lb-col {
  flex: 1;
  min-width: 130px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
}
.lb-col h4 {
  margin: 0 0 8px;
  font-size: 0.78rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
}
.lb-col ol { margin: 0; padding: 0; list-style: none; counter-reset: rank; }
.lb-col li {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 3px 0;
  font-size: 0.82rem;
  counter-increment: rank;
}
.lb-col li::before {
  content: counter(rank) ".";
  color: var(--muted);
  width: 1.4em;
  flex: none;
}
.lb-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-val { color: var(--accent); font-variant-numeric: tabular-nums; font-weight: 600; }
.lb-empty { color: var(--muted); font-size: 0.8rem; font-style: italic; }

/* ---------- specials (Mario-Kart-style power-ups) ---------- */
.specials { max-width: 520px; margin: 12px auto 0; }
.spec-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 8px 12px;
}
.spec-meter {
  flex: 1;
  height: 10px;
  background: var(--well);
  border-radius: 999px;
  overflow: hidden;
  border: 1px solid var(--border);
}
.spec-meter-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width 0.15s linear;
}
.spec-slots { display: flex; gap: 6px; }
.spec-slot {
  position: relative;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text);
  cursor: default;
  display: flex;
  align-items: center;
  justify-content: center;
}
.spec-slot .si { font-size: 1.15rem; opacity: 0.35; }
.spec-slot .sk {
  position: absolute;
  bottom: 1px;
  right: 4px;
  font-size: 0.6rem;
  color: var(--muted);
}
.spec-slot.ready {
  cursor: pointer;
  border-color: var(--accent);
  background: linear-gradient(160deg, var(--panel-2), rgba(34, 211, 238, 0.14));
  animation: specPop 0.2s ease;
}
.spec-slot.ready .si { opacity: 1; }
.spec-slot.ready:hover { border-color: var(--accent-2); }
@keyframes specPop { from { transform: scale(0.8); } to { transform: scale(1); } }
.spec-toast {
  text-align: center;
  font-weight: 700;
  font-size: 0.9rem;
  color: var(--accent);
  height: 20px;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.15s, transform 0.15s;
}
.spec-toast.show { opacity: 1; transform: none; }

/* effect overlays applied to a player's own board */
.board-wrap.fx-fog::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  background: radial-gradient(120% 80% at 50% 40%, rgba(180, 200, 230, 0.28), rgba(120, 140, 170, 0.55));
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border-radius: inherit;
}
.board-wrap.fx-glue::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: rgba(234, 179, 8, 0.16);
  border-radius: inherit;
}
.board-wrap.fx-flip .board { transform: scaleX(-1); }
.board-wrap.fx-shield {
  box-shadow: 0 0 0 2px var(--accent), 0 0 26px rgba(34, 211, 238, 0.6);
  border-radius: 8px;
}

/* ===================================================================
   Stackzilla shell: mascot, countdown, pause menu, shared modals.
   The .modal / .modal-card pair is the house dialog style — feature
   modules (settings, account, ads) build their dialogs from it so
   everything stays visually consistent.
   =================================================================== */

#mascot {
  display: block;
  margin: 0 auto 4px;
  image-rendering: auto;
  filter: drop-shadow(0 6px 18px rgba(34, 211, 238, 0.25));
  animation: mascot-bob 3.2s ease-in-out infinite;
}
@keyframes mascot-bob {
  0%, 100% { transform: translateY(0) rotate(-1deg); }
  50%      { transform: translateY(-6px) rotate(1deg); }
}

.menu-footer {
  display: flex;
  gap: 8px;
  margin-top: 14px;
}
.menu-footer button { flex: 1; padding: 10px 12px; font-size: 14px; }

/* Account button doubles as the signed-in indicator: an unlit dot next to
   "Sign in", a lit one next to your name. The name can be long, so it
   truncates rather than wrapping the footer onto two lines. */
.acct-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-width: 0;
}
.acct-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--panel-3);
  border: 1px solid var(--border);
}
.acct-btn.signed-in { color: var(--text); }
.acct-btn.signed-in .acct-dot {
  background: var(--ok);
  border-color: transparent;
  box-shadow: 0 0 6px var(--ok);
}
#acct-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
button.ghost {
  background: transparent;
  color: var(--muted);
}
button.ghost:hover { background: var(--panel-2); color: var(--text); }

/* Burger: the single in-game control (pause + menu), pinned left. */
.burger {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 44px;
  height: 40px;
  padding: 0 12px;
}
.burger span {
  display: block;
  height: 2px;
  width: 100%;
  border-radius: 2px;
  background: var(--text);
  transition: background 0.15s ease;
}
.burger:hover span { background: var(--accent); }

/* ---------------------------------------------------- countdown (3-2-1-GO) */
.arena-wrap { position: relative; }

.countdown {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 30;
  background: color-mix(in srgb, var(--scrim) 55%, transparent);
  backdrop-filter: blur(2px);
  border-radius: 16px;
}
.countdown span {
  font-size: clamp(64px, 18vw, 160px);
  font-weight: 900;
  letter-spacing: 2px;
  color: var(--text);
  text-shadow: 0 0 30px rgba(34, 211, 238, 0.7), 0 6px 24px rgba(0, 0, 0, 0.6);
}
.countdown span.go {
  background: linear-gradient(90deg, var(--accent), var(--ok));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.countdown span.tick { animation: countdown-pop 0.65s ease-out both; }
@keyframes countdown-pop {
  0%   { transform: scale(2.1); opacity: 0; }
  25%  { transform: scale(1);   opacity: 1; }
  75%  { transform: scale(1);   opacity: 1; }
  100% { transform: scale(0.82); opacity: 0.15; }
}

/* -------------------------------------------------------- shared modals */
.modal {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: var(--scrim-modal);
  backdrop-filter: blur(4px);
  animation: modal-fade 0.16s ease-out;
}
@keyframes modal-fade { from { opacity: 0; } to { opacity: 1; } }

.modal-card {
  width: min(440px, 100%);
  max-height: 88vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 24px;
  border-radius: 18px;
  background: var(--panel);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-panel);
  animation: modal-rise 0.2s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
@keyframes modal-rise {
  from { transform: translateY(14px) scale(0.97); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
/* The card is a scrolling flex column with a max-height. Once its content is
   taller than that, flex-shrink squashes every child — a three-line paragraph
   gets a two-line box and its text spills out below, so whatever follows sits
   almost on top of it. The card scrolls; nothing in it should be compressed. */
.modal-card > * { flex-shrink: 0; }

.modal-card h2 {
  margin: 0 0 2px;
  font-size: 22px;
  letter-spacing: 1px;
}
.modal-card .row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.modal-card label { color: var(--muted); font-size: 14px; }

.pause-budget {
  color: var(--muted);
  font-size: 14px;
  margin-bottom: 6px;
}
.pause-budget.low { color: var(--danger); font-weight: 700; }

/* ===================================================================
   Specials in the side column. The panel used to be a wide horizontal
   bar under the arena; it now lives at the bottom of your own player
   column, so the bar becomes a vertical stack and the toast floats over
   the arena instead of wrapping inside a ~96px column.
   =================================================================== */
.side .specials {
  margin: 0;
  max-width: none;
  width: 100%;
}
.side .spec-bar {
  flex-direction: column;
  align-items: stretch;
  gap: 9px;
  padding: 10px;
}
/* A charge meter, not a progress bar — thin reads as a gauge and stops it
   competing with the slots underneath. */
.side .spec-meter { flex: none; width: 100%; height: 5px; }
.side .spec-slots { justify-content: center; gap: 8px; }
.side .spec-slot { width: 34px; height: 34px; }
.side .spec-slot .si { font-size: 1rem; }

/* Float the toast over the arena — centred, not inside the column. */
.side .spec-toast {
  position: fixed;
  left: 50%;
  top: 84px;
  transform: translateX(-50%) translateY(4px);
  z-index: 40;
  height: auto;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--border);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  white-space: nowrap;
  pointer-events: none;
}
.side .spec-toast.show { transform: translateX(-50%); }

/* ---- versus: board column + stats under the board -------------------- */
.board-col { display: flex; flex-direction: column; gap: 10px; }

/* Score/Lines sit under the board in versus (Level is dropped there — it's
   identical for both players and only cost space). */
.under-stats {
  display: flex;
  justify-content: space-around;
  gap: 10px;
}
.under-stats .stat { margin: 0; text-align: center; }
.under-stats .value { font-size: 1.15rem; }

/* The opponent's specials are a read-only mirror: no pointer affordances. */
.spec-mirror .spec-slot { cursor: default; }
.spec-mirror .spec-slot.ready:hover { border-color: var(--accent); }

/* Display-name input flagged by the local or server-side name rules. */
.name-input.invalid {
  border-color: var(--danger);
  box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.18);
}

/* 3-player free-for-all: three boards share the row. */
.arena.trio { gap: 10px; }
.arena.trio .side { min-width: 78px; }

/* ------------------------------------------------------- puzzle picker */
.puzzle-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 8px;
  margin: 4px 0 12px;
}
.pz-item {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "n name" "n goal" "n par" "n best";
  gap: 0 8px;
  text-align: left;
  padding: 8px 10px;
  font-weight: 500;
}
.pz-n {
  grid-area: n;
  align-self: center;
  font-weight: 800;
  font-size: 1.1rem;
  color: var(--muted);
  min-width: 1.4em;
  text-align: center;
}
.pz-name { grid-area: name; font-weight: 700; font-size: 0.9rem; }
/* The goal is what distinguishes one puzzle from the next, so it leads. */
.pz-goal {
  grid-area: goal;
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--accent, #06b6d4);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.pz-par { grid-area: par; font-size: 0.7rem; color: var(--muted); }
.pz-best { grid-area: best; font-size: 0.7rem; color: var(--ok); }
.pz-item.solved { border-color: rgba(34, 197, 94, 0.45); }
.pz-item.solved .pz-n { color: var(--ok); }
