/* Snake Arcade — global styles.
   Theme colors live in js/themes.js; applyTheme() writes them into the
   CSS custom properties below, so the page chrome always matches the canvas. */

:root {
  --bg: #0b1020;
  --panel: #131a33;
  --text: #e2e8f0;
  --muted: #64748b;
  --accent: #4ade80;
  --danger: #ef4444;
  /* colour of every neon text-shadow. Themes that shouldn't glow
     (paper) set this to transparent instead of dropping the shadows. */
  --glow: var(--accent);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  display: grid;
  place-items: center;
  min-height: 100dvh;
  /* stop pull-to-refresh / overscroll glow interfering with swipes */
  overscroll-behavior: none;
}

.app {
  /* fill the screen: capped by height so HUD + board + footer always
     fit, and by 840px so cells don't get comically large on a 27" */
  width: min(94vw, 76vh, 840px);
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 0;
}

/* ---------- HUD ---------- */

.hud {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
}

.hud-item {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}

.hud-right { margin-left: auto; text-align: right; }

.hud-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
}

.hud-value {
  font-size: clamp(22px, 3vh, 30px);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.hud-combo {
  font-size: 18px;
  font-weight: 700;
  color: var(--accent);
  text-shadow: 0 0 12px var(--glow);
  animation: combo-pop 0.25s ease-out;
}

@keyframes combo-pop {
  from { transform: scale(1.6); }
  to   { transform: scale(1); }
}

/* Rush countdown: grows and turns red for the final seconds.
   The [hidden] rule is load-bearing: .hud-item sets display:flex, which
   outranks the browser's [hidden] { display: none }, so without this
   the Rush timer stays on screen during a Classic run. */
#timer-box[hidden] { display: none; }
#timer-box { text-align: center; }
#timer-box .hud-value {
  transition: font-size 0.25s ease, color 0.25s ease;
}
#timer-box.urgent .hud-value {
  color: var(--danger);
  font-size: clamp(30px, 4.2vh, 42px);
  text-shadow: 0 0 14px var(--danger);
}

.pause-btn {
  background: var(--panel);
  color: var(--text);
  border: 1px solid color-mix(in srgb, var(--muted) 40%, transparent);
  border-radius: 8px;
  font-size: 12px;
  padding: 8px 12px;
  cursor: pointer;
}
.pause-btn:hover { border-color: var(--accent); }

/* ---------- Stage / canvas ---------- */

.stage {
  position: relative;
  aspect-ratio: 1;
  /* the game handles all touch gestures itself */
  touch-action: none;
}

/* ...but overlays are menus, not a board: they need normal touch
   behaviour or their sliders can't be dragged and their panels can't
   be scrolled. touch-action inherits, so this has to be re-enabled. */
.overlay { touch-action: auto; }

#game-canvas {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 14px;
  background: var(--panel);
}

/* ---------- Overlay (start / pause / game over) ---------- */

.overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 14px;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(4px);
  text-align: center;
}

.overlay[hidden] { display: none; }

.panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 20px;
}

.title {
  margin: 0;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: clamp(28px, 9vw, 40px);
  letter-spacing: 0.18em;
  line-height: 1.1;
  text-shadow: 0 0 18px var(--glow);
}

.title span {
  display: block;
  font-size: 0.45em;
  letter-spacing: 0.6em;
  color: var(--accent);
}

.overlay h2 {
  margin: 0;
  font-size: 26px;
  letter-spacing: 0.08em;
}

.hint { margin: 0; color: var(--muted); font-size: 14px; }

.final-score {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 40px;
  font-weight: 700;
  margin: 0;
}

.new-best {
  color: var(--accent);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-shadow: 0 0 12px var(--glow);
}

.btn {
  background: var(--accent);
  color: var(--bg);
  border: none;
  border-radius: 10px;
  font-size: 16px;
  font-weight: 700;
  padding: 12px 28px;
  cursor: pointer;
  transition: transform 0.1s ease;
}
.btn:hover { transform: scale(1.05); }
.btn:active { transform: scale(0.97); }

.btn--ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid color-mix(in srgb, var(--muted) 45%, transparent);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}

/* ---------- fullscreen fx layer ---------- */

/* Intro cinematics and confetti bursts. Above everything visual
   (including Zig, so confetti falls over him) but never interactive. */
#fx-canvas {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  z-index: 60;
}

/* ---------- start gate ---------- */

/* Shown before anything else: the first key/tap here is the user
   gesture that unlocks audio, so the intro that follows is fully
   scored instead of silent (and that gesture no longer skips it). */
.overlay--gate {
  position: fixed;
  inset: 0;
  z-index: 65;
  border-radius: 0;
  background: var(--bg);
  display: grid;
  place-items: center;
}

.gate-hint {
  margin: 0;
  color: var(--muted);
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 14px;
  letter-spacing: 0.16em;
  animation: gate-pulse 1.6s ease-in-out infinite;
}

@keyframes gate-pulse {
  50% { opacity: 0.35; }
}

/* ---------- intro ---------- */

/* The logo is drawn fullscreen on #fx-canvas; this overlay only
   carries the loading bar, rotating message and skip hint — promoted
   to fullscreen and above the fx layer. */
.overlay--intro {
  position: fixed;
  inset: 0;
  z-index: 65;
  border-radius: 0;
  background: transparent;
  backdrop-filter: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  padding-bottom: 26vh;
}

.intro-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* our display:flex would override the hidden attribute's display:none */
.intro-loading[hidden] { display: none; }

.loading-track {
  width: 180px;
  max-width: 60%;
  height: 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--muted) 25%, transparent);
  overflow: hidden;
}

.loading-fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: inherit;
  box-shadow: 0 0 10px var(--glow);
  transition: width 0.15s linear;
}

.loading-msg {
  margin: 10px 0 0;
  color: var(--muted);
  font-size: 12.5px;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
}

.skip-hint {
  position: absolute;
  bottom: 10px;
  right: 14px;
  margin: 0;
  color: var(--muted);
  font-size: 11.5px;
  letter-spacing: 0.08em;
}

/* ---------- menu ---------- */

/* Lighter veil than the other overlays so the idle AI snake shows
   through; the renderer dims the board as well for readability. */
.overlay--menu {
  background: color-mix(in srgb, var(--bg) 30%, transparent);
  backdrop-filter: none;
  animation: overlay-in 0.3s ease-out;
}

@keyframes overlay-in {
  from { opacity: 0; }
}

.title--small { font-size: clamp(22px, 6vw, 30px); }

.menu {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 200px;
}

.menu-item {
  position: relative;
  background: none;
  border: none;
  color: var(--text);
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: clamp(17px, 2.4vh, 22px);
  letter-spacing: 0.06em;
  padding: 10px 26px;
  border-radius: 10px;
  cursor: pointer;
}

.menu-item.selected {
  color: var(--accent);
  text-shadow: 0 0 14px var(--glow);
}

.menu-item.selected::before {
  content: '▸';
  position: absolute;
  left: 8px;
}

/* ---------- about: the cabinet card ---------- */

.about-card { width: min(420px, 100%); gap: 12px; }

/* Small Zig, cloned from the mascot; the zig-idle bob/nod/pupil
   animations are class-driven CSS, so the clone idles for free. */
.about-zig {
  width: clamp(76px, 22vw, 112px);
  aspect-ratio: 1;
}
.about-zig svg { display: block; width: 100%; height: 100%; }

.about-title {
  margin: 0;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 22px;
  letter-spacing: 0.14em;
  text-shadow: 0 0 14px var(--glow);
}

.about-story {
  margin: 0;
  max-width: 36ch;
  font-size: 13.5px;
  line-height: 1.55;
}

/* The three outbound links, dressed like the arcade menu: mono type,
   generous tap targets, accent glow on hover. */
.about-links {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  max-width: 300px;
}

.about-link-btn {
  display: flex;
  align-items: center;
  gap: 11px;
  min-height: 44px;
  padding: 10px 16px;
  border: 1px solid color-mix(in srgb, var(--muted) 35%, transparent);
  border-radius: 10px;
  color: var(--text);
  text-decoration: none;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 14px;
  letter-spacing: 0.05em;
}

.about-link-btn svg { width: 16px; height: 16px; flex: none; }

.about-link-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  text-shadow: 0 0 12px var(--glow);
}

.about-me {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

/* The spec plate: reads like the label riveted to a cabinet's back,
   not a CV line. */
.spec-plate {
  margin: 2px 0 0;
  padding: 7px 14px;
  border: 1px solid color-mix(in srgb, var(--muted) 40%, transparent);
  border-radius: 6px;
  color: var(--muted);
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 10.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* ---------- creator moment: buttons in Zig's bubble ---------- */

.bubble-links {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.bubble-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 14px;
  font-size: 14px;
  text-decoration: none;
}

.bubble-btn svg { width: 15px; height: 15px; flex: none; }

/* ---------- customise screen ---------- */

/* Fullscreen rather than inside the square stage: on a 380px phone the
   stage is ~360px tall, which can't hold a preview plus five rows.
   Same pattern as the gate and intro overlays. */
.overlay--customise {
  position: fixed;
  inset: 0;
  z-index: 65;
  border-radius: 0;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(6px);
  padding: 12px;
}

/* Capped at the grid area's height, not the viewport: a centred grid
   item taller than its area gets its top clipped with no way to scroll
   back up. Capping first means it can never get there. */
.panel--scroll {
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain; /* don't scroll the page behind it */
}

.customise {
  width: min(440px, 100%);
  gap: 16px;
  padding: 16px 14px calc(16px + env(safe-area-inset-bottom));
}

.customise .opt-row { max-width: none; }

.customise h2 { font-size: 22px; }

/* Square so it matches the real board's aspect; capped by height so it
   can't push the controls off a short screen. */
.preview {
  width: min(200px, 46vw, 26vh);
  aspect-ratio: 1;
}

#preview-canvas {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 12px;
  background: var(--panel);
}

.swatches {
  display: grid;
  grid-template-columns: repeat(3, 1fr); /* 3x2 fits 380px comfortably */
  gap: 6px;
}

.swatch {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  height: 52px;
  padding: 6px 4px 5px;
  border: 2px solid transparent;
  border-radius: 10px;
  cursor: pointer;
  overflow: hidden;
}

/* the snake stripe and food dot preview the palette itself */
.sw-snake {
  position: absolute;
  top: 12px;
  left: 14%;
  width: 52%;
  height: 7px;
  border-radius: 999px;
}

.sw-food {
  position: absolute;
  top: 11px;
  right: 16%;
  width: 9px;
  height: 9px;
  border-radius: 50%;
}

.sw-name {
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: #fff;
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.85);
  mix-blend-mode: normal;
}

/* Paper is light: its label needs dark text instead */
[data-theme] .swatch[data-action='theme:paper'] .sw-name {
  color: #1c1b18;
  text-shadow: 0 1px 3px rgb(255 255 255 / 0.85);
}

.swatch.selected {
  border-color: var(--accent);
  box-shadow: 0 0 12px var(--glow);
}

.swatch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- shared option rows (customise) ---------- */

.opt-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  width: 100%;
  max-width: 34ch;
}

.opt-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--muted);
  text-align: left;
}

.seg {
  display: flex;
  gap: 4px;
  padding: 3px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--muted) 16%, transparent);
}

/* Four options don't fit across 380px — let them wrap to two rows
   instead of shrinking the labels to nothing. */
.seg--wrap { flex-wrap: wrap; }
.seg--wrap .seg-btn { flex: 1 1 44%; }

.seg-btn {
  flex: 1;
  background: none;
  border: none;
  border-radius: 7px;
  padding: 7px 4px;
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
}

.seg-btn:hover { background: color-mix(in srgb, var(--muted) 20%, transparent); }

.seg-btn.selected {
  background: var(--accent);
  color: var(--bg);
  font-weight: 700;
}

/* Range inputs ignore colour inheritance, so the track and thumb are
   styled explicitly per engine to follow the theme. */
.slider {
  width: 100%;
  height: 22px;
  margin: 0;
  background: none;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}

.slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--muted) 35%, transparent);
}
.slider::-moz-range-track {
  height: 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--muted) 35%, transparent);
}

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -5px; /* centre the thumb on a 6px track */
  border: none;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--glow);
}
.slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: none;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--glow);
}

.slider:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ---------- Zig the mascot ---------- */

:root { --zig-size: clamp(240px, 52vh, 560px); }

#zig-layer {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none; /* only the bubble is interactive */
}

#zig-wrap {
  position: absolute;
  left: 0;
  bottom: 0;
  width: var(--zig-size);
  height: var(--zig-size);
  /* Zig is pokeable — clicking him makes him react */
  pointer-events: auto;
  cursor: pointer;
}

#zig-wrap svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* entrances */
.zig-slide { animation: zig-slide 0.7s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
.zig-pop   { animation: zig-pop 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
.zig-bounce { animation: zig-bounce 0.6s ease; }

@keyframes zig-slide {
  from { transform: translateX(-110%); }
}
@keyframes zig-pop {
  from { transform: translateY(25%) scale(0.55); opacity: 0; }
}
@keyframes zig-bounce {
  0%   { transform: translateY(0); }
  30%  { transform: translateY(-9%); }
  55%  { transform: translateY(0); }
  75%  { transform: translateY(-4%); }
  100% { transform: translateY(0); }
}

/* idle: sway the whole snake from the base of his neck */
svg.zig-idle {
  transform-origin: 15% 90%;
  animation: zig-bob 2.6s ease-in-out infinite;
}
@keyframes zig-bob {
  50% { transform: translateY(-4.5%) rotate(-1.4deg); }
}

/* idle: the head nods and tilts independently of the neck (the SVG's
   head elements are wrapped in #zig-head at load time) */
svg.zig-idle #zig-head {
  transform-box: fill-box;
  transform-origin: 30% 85%;
  animation: zig-nod 4.6s ease-in-out infinite;
}
@keyframes zig-nod {
  0%, 100% { transform: rotate(0) translateY(0); }
  30% { transform: rotate(2.6deg) translateY(4px); }
  55% { transform: rotate(-2.2deg) translateY(-3px); }
  75% { transform: rotate(1.2deg) translateY(2px); }
}

/* idle: eyes wander — pupils and glints drift together, with holds
   between glances (units are SVG user-space px on a 320 viewBox) */
svg.zig-idle .zig-pupil { animation: zig-look 7s ease-in-out infinite; }
@keyframes zig-look {
  0%, 20% { transform: translate(0, 0); }
  27%, 42% { transform: translate(7px, 2px); }
  49%, 60% { transform: translate(-6px, 1px); }
  67%, 78% { transform: translate(2px, -4px); }
  85%, 100% { transform: translate(0, 0); }
}
/* while blinking, let the lid-squash win over the look animation */
svg.zig-blinking .zig-pupil { animation: none; }

/* hiss: rear back, puff up, shake it out */
.zig-hiss { animation: zig-hiss 0.55s ease; }
@keyframes zig-hiss {
  0%, 100% { transform: translateX(0) scale(1) rotate(0); }
  18% { transform: translateX(-4%) scale(1.05, 0.97) rotate(-3.5deg); }
  40% { transform: translateX(3%) scale(1.07) rotate(2.6deg); }
  65% { transform: translateX(-2%) scale(1.02) rotate(-1.4deg); }
}

/* idle: blink — eyelids are faked by squashing the eye circles */
.zig-eye {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 0.09s ease;
}
svg.zig-blinking .zig-eye { transform: scaleY(0.08); }

/* idle: tongue flick — hidden until the flick animation runs */
#zig-tongue {
  transform-box: fill-box;
  transform-origin: left center;
  transform: scaleX(0);
}
svg.zig-flick #zig-tongue { animation: zig-flick 0.5s ease; }
@keyframes zig-flick {
  0%, 100% { transform: scaleX(0); }
  35%, 70% { transform: scaleX(1); }
}

/* speech bubble */
#zig-bubble {
  position: absolute;
  left: calc(var(--zig-size) * 0.85);
  bottom: calc(var(--zig-size) * 0.55);
  max-width: min(300px, 55vw);
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: 16px;
  padding: 12px 16px;
  pointer-events: auto;
  box-shadow: 0 6px 24px rgb(0 0 0 / 0.35);
}

#zig-bubble::after {
  /* little tail pointing back toward Zig */
  content: '';
  position: absolute;
  left: -6px;
  bottom: 18px;
  width: 12px;
  height: 12px;
  background: var(--panel);
  border-left: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  transform: rotate(45deg);
}

#zig-text {
  margin: 0;
  font-size: 15px;
  line-height: 1.45;
  min-height: 1.45em; /* no jump while the typewriter fills it */
}

.zig-form {
  display: flex;
  gap: 6px;
  margin-top: 10px;
}

.zig-input {
  flex: 1;
  min-width: 0;
  background: var(--bg);
  color: var(--text);
  border: 1px solid color-mix(in srgb, var(--muted) 45%, transparent);
  border-radius: 8px;
  padding: 9px 10px;
  font-size: 15px;
  font-family: inherit;
}
.zig-input:focus {
  outline: none;
  border-color: var(--accent);
}

.zig-go { padding: 9px 16px; font-size: 14px; }

.zig-skip {
  background: none;
  border: none;
  margin-top: 8px;
  padding: 0;
  color: var(--muted);
  font-size: 12.5px;
  text-decoration: underline;
  cursor: pointer;
}

/* mobile: smaller Zig, bubble above him instead of beside */
@media (max-width: 520px) {
  :root { --zig-size: clamp(160px, 34vh, 280px); }
  #zig-bubble {
    left: 12px;
    right: 12px;
    max-width: none;
    bottom: calc(var(--zig-size) * 0.92 + 12px);
  }
  #zig-bubble::after {
    left: calc(var(--zig-size) * 0.45);
    bottom: -6px;
    border: none;
    border-right: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .zig-slide, .zig-pop, .zig-bounce, .zig-hiss { animation: none; }
  svg.zig-idle,
  svg.zig-idle #zig-head,
  svg.zig-idle .zig-pupil { animation: none; }
  svg.zig-flick #zig-tongue { animation: none; }
}

/* ---------- panel back button ---------- */

/* Pinned top-left of the panel and always on screen. 44px min is the
   smallest reliable touch target, and this is the only way out of a
   full-screen panel on a phone. */
.back-btn {
  position: sticky;
  top: 0;
  align-self: flex-start;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  min-width: 44px;
  margin: -4px 0 -4px -6px;
  padding: 10px 14px 10px 10px;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  border: 1px solid color-mix(in srgb, var(--muted) 35%, transparent);
  border-radius: 10px;
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  cursor: pointer;
}

.back-btn:hover { border-color: var(--accent); color: var(--accent); }
.back-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- leaderboard ---------- */

.leaderboard { width: min(420px, 100%); gap: 12px; }

.tabs {
  display: flex;
  gap: 4px;
  width: 100%;
  padding: 3px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--muted) 16%, transparent);
}

.tabs--sub { background: none; padding: 0; }

.tab {
  flex: 1;
  background: none;
  border: none;
  border-radius: 7px;
  padding: 8px 6px;
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
}

.tab.selected { background: var(--accent); color: var(--bg); font-weight: 700; }
.tabs--sub .tab.selected {
  background: none;
  color: var(--accent);
  text-shadow: 0 0 12px var(--glow);
  border-bottom: 2px solid var(--accent);
  border-radius: 0;
}

/* Fixed height so switching tabs or hitting a loading state doesn't
   make the whole panel jump around. */
.lb-body {
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 260px;
}

.lb-note { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.6; }

.lb-list {
  list-style: none;
  width: 100%;
  margin: 0;
  padding: 0;
  align-self: start;
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
}

/* The name column is the flexible one and everything else is sized to
   its content, so a 12-character name shrinks the gap rather than
   pushing the score off the edge at 380px. */
.lb-row {
  display: grid;
  grid-template-columns: 2.4em minmax(0, 1fr) auto;
  gap: 10px;
  align-items: baseline;
  padding: 7px 8px;
  border-radius: 6px;
}

.lb-row:nth-child(odd) {
  background: color-mix(in srgb, var(--muted) 12%, transparent);
}

.lb-row.is-you {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.lb-rank { color: var(--muted); font-size: 13px; }

/* 12 characters is the cap, but a narrow screen can still run out of
   room; ellipsis beats wrapping a row to two lines. */
.lb-name {
  font-weight: 700;
  letter-spacing: 0.04em;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lb-score { font-variant-numeric: tabular-nums; font-weight: 700; }

/* first place stands out without needing a medal graphic */
.lb-row:nth-child(1) .lb-rank,
.lb-row:nth-child(1) .lb-name,
.lb-row:nth-child(1) .lb-score {
  color: var(--accent);
  text-shadow: 0 0 10px var(--glow);
}

/* ---------- leaderboard name entry ---------- */

.initials { gap: 10px; }

.name-form {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
}

/* Arcade-styled but a real text field: centred, monospace, wide enough
   for the full 12 characters without the caret ever leaving view. */
.name-input {
  width: min(300px, 100%);
  background: var(--bg);
  border: none;
  border-bottom: 3px solid color-mix(in srgb, var(--muted) 45%, transparent);
  border-radius: 8px 8px 0 0;
  color: var(--accent);
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: clamp(24px, 7vw, 34px);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-align: center;
  padding: 10px 12px;
  text-shadow: 0 0 16px var(--glow);
}

.name-input:focus {
  outline: none;
  border-bottom-color: var(--accent);
}

.name-note { margin: 0; color: var(--muted); font-size: 12.5px; }
.name-note.is-error { color: var(--danger); }

/* ---------- per-theme effects ---------- */
/* Colours come from js/themes.js. This is only for the things a
   palette can't express, keyed off the data-theme attribute that
   applyTheme() writes onto <html>. */

/* OLED: the board is the same pure black as the page, so the play area
   would have no edge at all. A hairline of accent gives it back. */
[data-theme='oled'] #game-canvas {
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent),
    0 0 26px color-mix(in srgb, var(--accent) 12%, transparent);
}

/* Paper: the drop shadows are tuned for dark themes and read as dirt
   on cream. --glow already turned the neon off (see themes.js). */
[data-theme='paper'] #zig-bubble { box-shadow: 0 6px 18px rgb(0 0 0 / 0.12); }
[data-theme='paper'] #game-canvas { box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.07); }

/* Zig keeps his own green in every theme — he's a character, not
   themed UI. That costs him his edges on the one light background, so
   paper (and only paper) gives him a contact shadow plus a soft
   ambient one to sit him on the page. drop-shadow follows the alpha
   silhouette, which is why the background rect had to go. */
[data-theme='paper'] #zig-wrap svg {
  filter:
    drop-shadow(0 2px 1px rgb(15 23 20 / 0.22))
    drop-shadow(0 12px 24px rgb(15 23 20 / 0.20));
}

/* CRT: phosphor bloom. Deliberately on the canvas and not on .app —
   a filter makes an element the containing block for fixed-position
   descendants, and .app holds the fullscreen gate and intro overlays,
   which would suddenly be clipped to the board. */
[data-theme='crt'] #game-canvas {
  filter: contrast(1.06) saturate(1.3) brightness(1.05);
}

/* Scanlines over everything, including Zig and the intro. Both layers
   are fixed (so they escape the body grid) and click-through. */
[data-theme='crt'] body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 100;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgb(0 0 0 / 0.32) 0 1px,
    rgb(0 0 0 / 0) 1px 3px
  );
  animation: crt-flicker 5.5s steps(60) infinite;
}

/* Vignette. The big inset shadow on a rounded box is the trick that
   sells "curved glass tube" — CSS can't actually bend the pixels. */
[data-theme='crt'] body::after {
  content: '';
  position: fixed;
  inset: -2px;
  z-index: 99;
  pointer-events: none;
  border-radius: 28px;
  box-shadow:
    inset 0 0 120px 34px rgb(0 0 0 / 0.55),
    inset 0 0 40px 6px color-mix(in srgb, var(--accent) 8%, transparent);
}

@keyframes crt-flicker {
  0%, 100% { opacity: 1; }
  47% { opacity: 0.94; }
  48% { opacity: 1; }
  92% { opacity: 0.96; }
}

/* ---------- toast ---------- */

.toast {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--panel);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--text);
  font-size: 13px;
  padding: 8px 16px;
  border-radius: 999px;
  white-space: nowrap;
  pointer-events: none;
  animation: toast-in 0.2s ease-out;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateX(-50%) translateY(8px);
  }
}

/* ---------- Footer ---------- */

.footer {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-size: 12px;
  color: var(--muted);
}

/* Quiet GitHub star: visible, never shouting. Padding brings the tiny
   glyph up to a usable tap target. */
.footer-star {
  display: inline-flex;
  padding: 6px;
  color: var(--muted);
  border-radius: 6px;
}
.footer-star:hover { color: var(--accent); }
.footer-star:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .hud-combo { animation: none; }
  .btn { transition: none; }
  .toast { animation: none; }
  .overlay--menu { animation: none; }
  .loading-fill { transition: none; }
  .gate-hint { animation: none; }
  /* the scanlines stay, the flicker goes */
  [data-theme='crt'] body::before { animation: none; }
}
