/* ============================================================
   GhostCloud — Game Library Site

   Visual language: flat Discord-style app chrome.
   Stacked opaque greys, hairline borders, one flat accent,
   small radii, hover = a background change. No gradients, no
   glow, no backdrop blur anywhere except where it is load
   bearing.

   Structure: a fixed left rail (nav + genres) with a slim top
   bar over the content column. The page still scrolls on the
   window, so every existing scroll/hero behaviour is intact.

   Every class name and design token from the previous build is
   preserved, so JS continues to work unchanged. Themes only
   swap accent values — a Pro gradient theme overrides --grad
   and adds a background wash, and the grey surfaces never move.
   ============================================================ */

/* ── Design tokens ─────────────────────────────────────── */
:root {
  /* surfaces — Discord's grey ramp, darkest to lightest */
  --bg: #1e1f22;             /* app frame: rail, rail-foot, deep wells */
  --surface: #2b2d31;        /* panels, sidebar-adjacent, cards */
  --surface2: #313338;       /* primary content background */
  --surface3: #383a40;       /* hover / raised */
  --panel: #2b2d31;
  --panel-2: #35373c;
  --glass: #1e1f22;          /* opaque now — kept as a token alias */
  --glass-strong: #1e1f22;
  --border: #3f4147;
  --border-light: #2a2c31;

  /* brand (overridden per theme below) */
  --accent: #D4F000;
  --accent-2: #D4F000;
  --accent-rgb: 212,240,0;
  --accent-dim: rgba(212,240,0,0.12);
  --accent-glow: rgba(212,240,0,0.22);
  /* Flat by default. A Pro gradient theme is the only thing that
     replaces this with a real gradient. */
  --grad: var(--accent);
  --on-accent: #0B0D00;      /* text on an accent-filled surface */

  /* text */
  --text: #f2f3f5;
  --text-muted: #b5bac1;
  --text-dim: #949ba4;
  --text-faint: #80848e;

  --red: #f23f43;
  --green: #23a55a;
  --gold: #f0b232;

  /* geometry */
  --radius: 8px;
  --radius-sm: 4px;
  --radius-lg: 12px;
  --radius-pill: 99px;

  /* type */
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --display: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, monospace;

  /* layout */
  --rail-w: 260px;           /* space reserved for the rail by the page */
  --rail-w-panel: 260px;     /* the rail's own width — never collapses, so
                                translateX(-100%) always slides it fully off */
  --header-h: 48px;          /* slim top bar */
  --header-h-tight: 48px;
  --content-top: 48px;       /* space reserved for the fixed top bar */
  --gutter: 32px;            /* page side padding */
  --maxw: 1560px;            /* content width */

  --transition: 0.15s ease;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --shadow-lg: 0 8px 32px rgba(0,0,0,0.5);
  --shadow-card: none;
  --shadow-hover: 0 8px 20px rgba(0,0,0,0.32);
}

/* ── Free themes ────────────────────────────────────────
   A free theme sets the accent quartet only. Everything else (the greys,
   borders, radii) stays put, which is what keeps every free theme looking
   like the same app. */
body[data-theme="cyan"]    { --accent: #38bdf8; --accent-2: #38bdf8; --accent-rgb: 56,189,248;  --accent-dim: rgba(56,189,248,0.14);  --accent-glow: rgba(56,189,248,0.26); --on-accent: #04121c; }
body[data-theme="magenta"] { --accent: #eb459e; --accent-2: #eb459e; --accent-rgb: 235,69,158;  --accent-dim: rgba(235,69,158,0.14);  --accent-glow: rgba(235,69,158,0.26); --on-accent: #1a0410; }
body[data-theme="green"]   { --accent: #23a55a; --accent-2: #23a55a; --accent-rgb: 35,165,90;   --accent-dim: rgba(35,165,90,0.14);   --accent-glow: rgba(35,165,90,0.26);  --on-accent: #04170c; }
body[data-theme="orange"]  { --accent: #f0883e; --accent-2: #f0883e; --accent-rgb: 240,136,62;  --accent-dim: rgba(240,136,62,0.14);  --accent-glow: rgba(240,136,62,0.26); --on-accent: #1a0b02; }
body[data-theme="black"]   { --bg: #0b0b0c; --surface: #121214; --surface2: #17171a; --surface3: #232327; --panel: #121214; --panel-2: #1c1c20; --glass: #0b0b0c; --glass-strong: #0b0b0c; --border: #2a2a30; --border-light: #1c1c20; }

/* ── Pro themes (full-screen gradients) ─────────────────
   A Pro theme is not an accent swap. Each one defines a --wash that paints the
   entire viewport behind the app, plus a --tint pair that the grey surfaces are
   rebuilt from at low alpha — so the gradient reads through the rail, the
   content column, every card and every modal. It also turns --grad into a real
   gradient, which is what the filled buttons use.
   body.pro-theme::before is the only thing that ever paints behind the surfaces,
   and .pro-theme on <body> is set from the theme registry in app.js. */
body[data-theme="nebula"] {
  --accent: #a78bfa; --accent-2: #7c3aed; --accent-rgb: 167,139,250;
  --accent-dim: rgba(167,139,250,0.18); --accent-glow: rgba(167,139,250,0.34); --on-accent: #14042e;
  --tint: 26,20,52; --tint-2: 58,44,108;
  --wash: radial-gradient(1100px 640px at 10% -8%, #341a6e 0%, transparent 62%),
          radial-gradient(900px 560px at 94% 4%, #4c1d95 0%, transparent 58%),
          linear-gradient(165deg, #171431 0%, #0d0b1c 52%, #1b1038 100%);
}
body[data-theme="aurora"] {
  --accent: #2dd4bf; --accent-2: #0d9488; --accent-rgb: 45,212,191;
  --accent-dim: rgba(45,212,191,0.18); --accent-glow: rgba(45,212,191,0.32); --on-accent: #04211f;
  --tint: 14,38,44; --tint-2: 22,70,74;
  --wash: radial-gradient(1100px 640px at 8% -6%, #0f4f52 0%, transparent 60%),
          radial-gradient(880px 560px at 96% 10%, #134e4a 0%, transparent 56%),
          linear-gradient(160deg, #0c2a2e 0%, #07161a 54%, #0d3437 100%);
}
body[data-theme="sunset"] {
  --accent: #fb7185; --accent-2: #e11d48; --accent-rgb: 251,113,133;
  --accent-dim: rgba(251,113,133,0.18); --accent-glow: rgba(251,113,133,0.34); --on-accent: #2a0512;
  --tint: 46,18,28; --tint-2: 92,38,32;
  --wash: radial-gradient(1100px 640px at 6% -8%, #7c2d12 0%, transparent 58%),
          radial-gradient(920px 560px at 96% 6%, #831843 0%, transparent 58%),
          linear-gradient(158deg, #34131f 0%, #170a12 52%, #3d1a17 100%);
}
body[data-theme="emerald"] {
  --accent: #34d399; --accent-2: #059669; --accent-rgb: 52,211,153;
  --accent-dim: rgba(52,211,153,0.18); --accent-glow: rgba(52,211,153,0.32); --on-accent: #04231a;
  --tint: 14,42,36; --tint-2: 22,78,62;
  --wash: radial-gradient(1100px 640px at 10% -8%, #065f46 0%, transparent 60%),
          radial-gradient(880px 540px at 94% 8%, #0f5132 0%, transparent 56%),
          linear-gradient(162deg, #0b2e26 0%, #06170f 54%, #0d3a2c 100%);
}
body[data-theme="candy"] {
  --accent: #f472b6; --accent-2: #38bdf8; --accent-rgb: 244,114,182;
  --accent-dim: rgba(244,114,182,0.18); --accent-glow: rgba(244,114,182,0.34); --on-accent: #2b0620;
  --tint: 40,18,44; --tint-2: 30,54,96;
  --wash: radial-gradient(1000px 620px at 8% -6%, #831843 0%, transparent 58%),
          radial-gradient(940px 580px at 96% 10%, #0c4a6e 0%, transparent 58%),
          linear-gradient(150deg, #2b1030 0%, #140b1e 50%, #0e2740 100%);
}

/* Rebuild the grey ramp from each Pro theme's tint pair. These five selectors
   are the whole "gradient everywhere" trick: the surfaces stay exactly as
   structured as before, they are simply translucent now, so the wash behind
   them is what you see. */
body[data-theme="nebula"],
body[data-theme="aurora"],
body[data-theme="sunset"],
body[data-theme="emerald"],
body[data-theme="candy"] {
  --grad: linear-gradient(135deg, var(--accent), var(--accent-2));
  --surface:  rgba(var(--tint), 0.74);
  --surface2: rgba(var(--tint), 0.60);
  --surface3: rgba(var(--tint-2), 0.58);
  --bg:       rgba(var(--tint), 0.84);
  --panel:    rgba(var(--tint), 0.74);
  --panel-2:  rgba(var(--tint-2), 0.58);
  --glass:    rgba(var(--tint), 0.84);
  --glass-strong: rgba(var(--tint), 0.92);
  --border:       rgba(var(--accent-rgb), 0.28);
  --border-light: rgba(var(--accent-rgb), 0.15);
}
/* Near-black base under the wash so nothing white can peek through on overscroll. */
body.pro-theme { background: #0a0912; scrollbar-color: rgba(var(--accent-rgb), 0.4) transparent; }
body.pro-theme::before { background: var(--wash); }
body.pro-theme ::-webkit-scrollbar-thumb { background: rgba(var(--accent-rgb), 0.34); }
body.pro-theme ::-webkit-scrollbar-thumb:hover { background: rgba(var(--accent-rgb), 0.55); }

/* ── Reset / base ──────────────────────────────────────── */
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
  font-family: var(--font);
  background: var(--surface2);
  color: var(--text);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  line-height: 1.5;
  text-rendering: optimizeLegibility;
}
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
input, select { font-family: inherit; }
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
.hidden { display: none !important; }
::selection { background: rgba(var(--accent-rgb), 0.3); color: #fff; }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: #1a1b1e; border-radius: 99px; }
::-webkit-scrollbar-thumb:hover { background: #4e5058; }
::-webkit-scrollbar-track { background: transparent; }

/* A gradient theme (Pro) is the only thing that paints behind the
   grey surfaces. Nothing here by default — the app is flat grey. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: none;
}
#particle-canvas { position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: 0.3; }

/* ── App frame: left rail + top bar ────────────────────── */
.sidebar {
  position: fixed; top: 0; left: 0; bottom: 0; z-index: 120;
  width: var(--rail-w-panel);
  display: flex; flex-direction: column;
  background: var(--bg);
  border-right: 1px solid var(--border-light);
  transition: transform 0.2s var(--ease-out);
}
.sidebar-head {
  height: var(--header-h); flex: 0 0 auto;
  display: flex; align-items: center; gap: 10px;
  padding: 0 14px;
  border-bottom: 1px solid var(--border-light);
}
.side-nav {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  padding: 8px; display: flex; flex-direction: column; gap: 2px;
}
.side-label {
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase;
  color: var(--text-faint); padding: 16px 8px 6px;
}
.side-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  height: 34px; padding: 0 8px; flex: 0 0 auto;
  border-radius: var(--radius-sm);
  font-size: 14.5px; font-weight: 500; color: var(--text-muted);
  text-align: left;
  transition: background var(--transition), color var(--transition);
}
.side-item > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-item i { font-size: 15px; width: 16px; text-align: center; flex-shrink: 0; color: var(--text-faint); }
.side-item:hover { background: var(--surface3); color: var(--text); }
.side-item:hover i { color: var(--text-muted); }
.side-item.active { background: var(--surface3); color: #fff; }
.side-item.active i { color: var(--accent); }
.side-item .side-count {
  font-family: var(--mono); font-size: 10.5px; font-weight: 700;
  color: var(--text-faint); background: var(--bg);
  border-radius: var(--radius-sm); padding: 2px 6px;
}
/* While the Library view is open, the browse/genre list stays put but steps
   back — the rail shows one active thing at a time. */
.sidebar.in-library #side-genres,
.sidebar.in-library .side-item[data-tag] { opacity: 0.45; }
.sidebar.in-library #side-genres:hover,
.sidebar.in-library .side-item[data-tag]:hover { opacity: 1; }

.sidebar-foot {
  flex: 0 0 auto; padding: 8px; display: flex; flex-direction: column; gap: 2px;
  border-top: 1px solid var(--border-light);
}
.sidebar-foot .online-badge { margin-top: 4px; justify-content: center; }
.side-backdrop {
  position: fixed; inset: 0; z-index: 110;
  background: rgba(0,0,0,0.6);
  opacity: 0; pointer-events: none; transition: opacity var(--transition);
}
.side-backdrop.show { opacity: 1; pointer-events: auto; }

/* Logo */
.logo { display: flex; align-items: center; gap: 9px; min-width: 0; }
.logo-img { width: 26px; height: 26px; flex-shrink: 0; }
.logo-text { font-size: 16px; font-weight: 700; letter-spacing: -0.3px; color: #fff; white-space: nowrap; }
.logo-text b { color: var(--accent); font-weight: 700; }
.logo-sub {
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-muted); background: var(--surface3);
  border-radius: var(--radius-sm); padding: 2px 6px; flex-shrink: 0;
}

/* Top bar */
.site-header {
  position: fixed; top: 0; left: var(--rail-w); right: 0; z-index: 100;
  height: var(--header-h);
  background: var(--surface2);
  border-bottom: 1px solid var(--border-light);
  transition: background var(--transition), border-color var(--transition), left 0.2s var(--ease-out);
}
.site-header.scrolled { background: var(--bg); border-bottom-color: var(--border); }
.header-inner {
  max-width: var(--maxw); margin: 0 auto; height: 100%;
  padding: 0 var(--gutter);
  display: flex; align-items: center; gap: 10px;
}

/* Search — a wide control, Discord-style */
.search-box { position: relative; display: flex; align-items: center; flex: 1 1 auto; max-width: 620px; }
.search-icon { position: absolute; left: 9px; color: var(--text-faint); font-size: 12px; pointer-events: none; transition: color var(--transition); }
.search-input {
  width: 100%; height: 30px;
  background: var(--bg);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  color: var(--text); font-size: 13.5px; padding: 0 30px 0 30px; outline: none;
  transition: border-color var(--transition), background var(--transition);
}
.search-input::placeholder { color: var(--text-faint); }
.search-input:focus { border-color: var(--border); background: #232428; }
.search-box:focus-within .search-icon { color: var(--text-muted); }
.search-clear {
  position: absolute; right: 6px; width: 20px; height: 20px; border-radius: 50%;
  color: var(--text-faint); font-size: 15px; line-height: 1; display: grid; place-items: center;
  transition: color var(--transition), background var(--transition);
}
.search-clear:hover { color: #fff; background: var(--surface3); }
/* Shortcut hint, in the clear button's corner. It exists so Ctrl+K can be
   discovered; while there is text to clear, clearing wins the corner. */
.search-kbd {
  position: absolute; right: 5px;
  font: 600 10.5px var(--mono); letter-spacing: 0.02em;
  color: var(--text-faint); background: var(--surface3);
  border-radius: 3px; padding: 2px 5px;
  transition: color var(--transition), background var(--transition);
}
.search-kbd:hover { color: var(--text); background: #45474f; }
.search-box.has-text .search-kbd { display: none; }
@media (max-width: 900px) { .search-kbd { display: none; } }

/* Action cluster */
.header-actions { display: flex; align-items: center; gap: 2px; margin-left: auto; flex-shrink: 0; }
.header-actions::before { content: none; }

.online-badge {
  display: flex; align-items: center; gap: 6px; height: 24px;
  padding: 0 9px;
  border-radius: var(--radius-pill);
  background: var(--surface3);
  font-size: 12px; font-weight: 600; color: var(--text-muted);
  cursor: default; user-select: none; white-space: nowrap;
}
.online-badge i { color: var(--green); font-size: 9px; }
.online-badge span { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 12px; text-align: center; color: var(--text); font-size: 11.5px; }
@keyframes livePulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }

.api-status {
  display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 9px;
  border-radius: var(--radius-sm); font-size: 11px; font-weight: 700;
  color: var(--text-faint); cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.api-status:hover { background: var(--surface3); color: var(--text-muted); }
.status-label { font-size: 10px; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; }
.status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text-faint); flex-shrink: 0; }
.status-dot.online { background: var(--green); }
.status-dot.connecting { background: var(--gold); }
.status-dot.error { background: var(--red); }
@keyframes pulse { 50% { opacity: 0.3; } }

.btn-icon {
  width: 30px; height: 30px; border-radius: var(--radius-sm); display: grid; place-items: center;
  color: var(--text-muted); font-size: 14px; flex-shrink: 0;
  transition: background var(--transition), color var(--transition);
}
.btn-icon:hover { background: var(--surface3); color: #fff; }
.btn-icon.active { background: var(--accent-dim); color: var(--accent); }
.rail-toggle { display: grid; }

/* ── Main content ──────────────────────────────────────── */
/* margin-left/left follow --rail-w, and both transition for the same 0.2s the
   rail slides — so collapsing on a laptop moves the grid and the rail as one. */
.main-content {
  margin-left: var(--rail-w);
  padding-top: var(--content-top);
  min-height: 100vh; position: relative; z-index: 1;
  transition: margin-left 0.2s var(--ease-out);
}

/* Pages with no rail (pro.html) span the full width. */
body.no-rail .site-header { left: 0; }
body.no-rail .main-content { margin-left: 0; }

/* ── States ────────────────────────────────────────────── */
.loading-state, .error-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 110px 20px; color: var(--text-muted); text-align: center;
}
.loading-state p { font-size: 14px; }
.error-state i { font-size: 34px; color: var(--red); }
.error-state h2 { font-size: 22px; color: var(--text); font-weight: 700; }

.spinner {
  width: 30px; height: 30px; border-radius: 50%;
  border: 2px solid var(--surface3);
  border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Hero — full-bleed, but flat ───────────────────────── */
.hero {
  position: relative; overflow: hidden;
  margin-top: calc(-1 * var(--content-top));
  height: 52vh; min-height: 400px; max-height: 560px;
  display: flex; align-items: flex-end;
  background: var(--bg);
}
.hero-bg {
  position: absolute; inset: 0;
  background-size: cover; background-position: center 26%;
  filter: brightness(0.6) saturate(100%);
}
.hero-gradient {
  position: absolute; inset: 0;
  background:
    linear-gradient(0deg, var(--surface2) 0%, rgba(49,51,56,0.92) 26%, rgba(49,51,56,0.42) 62%, rgba(30,31,34,0.12) 100%),
    linear-gradient(96deg, rgba(30,31,34,0.96) 0%, rgba(30,31,34,0.82) 34%, rgba(30,31,34,0.42) 62%, rgba(30,31,34,0.06) 86%);
}
.hero-info { position: relative; z-index: 2; padding: 0 var(--gutter) 62px; max-width: 640px; }
.hero-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.hero-tags .tag {
  font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: var(--radius-sm);
  letter-spacing: 0.01em;
  background: rgba(0,0,0,0.45); color: var(--text-muted);
}
.hero-tags .tag:first-child { color: var(--accent); background: var(--accent-dim); }
.hero-title {
  font-size: clamp(28px, 3.4vw, 42px); font-weight: 800;
  letter-spacing: -1px; line-height: 1.08; margin-bottom: 12px;
  text-shadow: 0 2px 18px rgba(0,0,0,0.55);
}
.hero-desc {
  font-size: 14px; color: var(--text-muted); line-height: 1.6;
  text-shadow: 0 1px 12px rgba(0,0,0,0.5);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  max-width: 560px; margin-bottom: 20px;
}
.hero-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.hero::after {
  content: ''; position: absolute; left: var(--gutter); bottom: 32px; z-index: 2;
  width: 110px; height: 3px; border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.16);
}
.hero-progress {
  position: absolute; left: var(--gutter); bottom: 32px; z-index: 3;
  width: 110px; height: 3px; border-radius: var(--radius-pill);
  background: var(--accent);
  transform-origin: left center;
  animation: heroProgress 8s linear infinite;
}
@keyframes heroProgress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* The progress bar holds while the banner is held (pointer over it, or the tab
   in the background) instead of promising a change that isn't coming. */
.hero.paused .hero-progress { animation-play-state: paused; }

/* One dot per featured pick. Clicking one is the only way to choose a banner
   without waiting for the rotation to come round. */
.hero-dots {
  position: absolute; right: var(--gutter); bottom: 31px; z-index: 3;
  display: flex; align-items: center; gap: 6px;
}
.hero-dot {
  width: 8px; height: 8px; border-radius: var(--radius-pill);
  background: rgba(255,255,255,0.22);
  transition: width var(--transition), background var(--transition);
}
.hero-dot:hover { background: rgba(255,255,255,0.55); }
.hero-dot[aria-selected="true"] { width: 22px; background: var(--accent); }

/* ── Buttons ───────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 9px 16px; border-radius: var(--radius-sm);
  font-size: 13.5px; font-weight: 600;
  transition: background var(--transition), color var(--transition), filter var(--transition);
  white-space: nowrap;
}
.btn-play {
  background: var(--grad); color: var(--on-accent);
  font-size: 14px; font-weight: 700; padding: 11px 22px;
}
.btn-play:hover { filter: brightness(1.1); }
.btn-play:active { filter: brightness(0.95); }
.btn-ghost { background: var(--surface3); color: var(--text); }
.btn-ghost:hover { background: #404249; }
.btn-danger { background: var(--red); color: #fff; }
.btn-danger:hover { filter: brightness(1.1); }
.btn:disabled { opacity: 0.4; pointer-events: none; }

/* ── Filter bar: segmented control + tag pills ─────────── */
.filters {
  display: flex; align-items: center; gap: 10px;
  max-width: var(--maxw); margin: 0 auto; padding: 18px var(--gutter) 4px;
}
.filter-chips {
  display: flex; align-items: center; gap: 4px; flex-wrap: nowrap;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -ms-overflow-style: none;
  padding: 0; min-width: 0;
}
.filter-chips::-webkit-scrollbar { display: none; }
.chip {
  padding: 6px 12px; border-radius: var(--radius-sm);
  font-size: 13px; font-weight: 600;
  background: transparent; color: var(--text-muted);
  transition: background var(--transition), color var(--transition);
  white-space: nowrap; flex: 0 0 auto;
}
/* .chip.seg = the three view filters (always visible).
   .chip.genre = tag pills (rail lists those on desktop, so hidden there). */
.chip.seg { display: inline-flex; align-items: center; }
.chip:hover { background: var(--surface3); color: var(--text); }
.chip i { font-size: 11px; margin-right: 3px; color: var(--text-faint); }
.chip.active { background: var(--surface3); color: #fff; }
.chip.active i { color: var(--accent); }
.chip[data-tag="favorites"].active i { color: var(--gold); }
.result-count { font-size: 12px; color: var(--text-faint); margin-left: auto; white-space: nowrap; font-family: var(--mono); flex-shrink: 0; }

/* ── Home category rows ────────────────────────────────── */
.home-sections { max-width: var(--maxw); margin: 0 auto; padding: 10px var(--gutter) 20px; }
.home-section { margin-bottom: 34px; }
.home-section-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.home-section-head h2 {
  font-size: 17px; font-weight: 700; letter-spacing: -0.2px;
  display: inline-flex; align-items: center; gap: 9px;
}
.home-section-head h2 i {
  width: 24px; height: 24px; border-radius: var(--radius-sm); flex-shrink: 0;
  display: grid; place-items: center; font-size: 11px;
  color: var(--text-muted); background: var(--surface3);
}
.section-viewall {
  margin-left: auto;
  font-size: 12.5px; font-weight: 600; color: var(--text-muted);
  padding: 6px 12px; border-radius: var(--radius-sm);
  background: transparent;
  transition: background var(--transition), color var(--transition);
}
.section-viewall:hover { color: #fff; background: var(--surface3); }

/* Carousel row + hover edge arrows */
.cat-row-wrap { position: relative; }
.cat-row {
  display: flex; gap: 12px; overflow-x: auto; padding: 2px 0 10px;
  scroll-snap-type: x proximity; scroll-behavior: smooth;
  scrollbar-width: none; -ms-overflow-style: none;
}
.cat-row::-webkit-scrollbar { display: none; }
.cat-row .game-card { flex: 0 0 172px; scroll-snap-align: start; }

.row-nav {
  position: absolute; top: calc(50% - 8px); transform: translateY(-50%);
  z-index: 5; width: 36px; height: 36px; border-radius: var(--radius-sm);
  display: grid; place-items: center; font-size: 13px; color: var(--text);
  background: var(--bg); border: 1px solid var(--border);
  opacity: 0; pointer-events: none;
  transition: opacity var(--transition), background var(--transition);
}
.row-nav.prev { left: -12px; }
.row-nav.next { right: -12px; }
.row-nav:hover { background: var(--surface3); }
.cat-row-wrap:hover .row-nav { opacity: 1; pointer-events: auto; }
.cat-row-wrap.at-start .row-nav.prev,
.cat-row-wrap.at-end .row-nav.next { opacity: 0 !important; pointer-events: none !important; }
@media (hover: none) { .row-nav { display: none; } }

/* ── Game grid ─────────────────────────────────────────── */
.game-grid { max-width: var(--maxw); margin: 0 auto; padding: 14px var(--gutter) 50px; }
.game-grid-inner {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(172px, 1fr));
  gap: 22px 16px;
}
.grid-empty { grid-column: 1 / -1; text-align: center; padding: 70px 0; color: var(--text-faint); }
.grid-empty i { font-size: 32px; margin-bottom: 12px; display: block; }

/* ── Game card ─────────────────────────────────────────── */
.game-card { display: flex; flex-direction: column; cursor: pointer; }
.card-cover-wrap {
  position: relative; width: 100%; aspect-ratio: 2 / 3;
  border-radius: var(--radius); overflow: hidden;
  background: var(--bg);
  transition: box-shadow var(--transition);
}
.card-cover-wrap::before {
  /* bottom anchor so the play badge and hover read on bright art */
  content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(180deg, transparent 62%, rgba(0,0,0,0.4) 100%);
}
.card-cover-wrap::after {
  /* play affordance on hover — the only motion on a card */
  content: '\f04b';
  font-family: 'Font Awesome 6 Free'; font-weight: 900;
  position: absolute; inset: 0; margin: auto; z-index: 3;
  width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 13px; color: var(--on-accent);
  background: var(--accent);
  box-shadow: 0 4px 14px rgba(0,0,0,0.5);
  opacity: 0;
  transition: opacity var(--transition);
  pointer-events: none;
}
.game-card:hover .card-cover-wrap { box-shadow: 0 0 0 1px var(--border); }
.game-card:hover .card-cover-wrap::after { opacity: 1; }
.card-cover {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: filter var(--transition);
}
.game-card:hover .card-cover { filter: brightness(1.12); }
.card-cover-fallback { position: absolute; inset: 0; display: grid; place-items: center; font-size: 26px; color: var(--text-faint); }

.card-fav {
  position: absolute; top: 6px; right: 6px; z-index: 4;
  width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: grid; place-items: center; font-size: 12px;
  background: rgba(0,0,0,0.6); color: var(--text-muted);
  opacity: 0;
  transition: opacity var(--transition), color var(--transition), background var(--transition);
}
.game-card:hover .card-fav, .card-fav.on, .card-fav:focus-visible { opacity: 1; }
.card-fav:hover { background: rgba(0,0,0,0.85); color: #fff; }
.card-fav.on { color: var(--gold); }
@media (hover: none) {
  .card-fav { opacity: 1; }
  .card-cover-wrap::after { opacity: 0.9; }
}

.card-badge {
  position: absolute; top: 6px; left: 6px; z-index: 4;
  font-size: 10px; font-weight: 700; letter-spacing: 0.02em;
  color: var(--on-accent); background: var(--accent);
  border-radius: var(--radius-sm); padding: 2px 7px;
}

.card-body { padding: 8px 2px 0; }
.card-name {
  font-size: 13.5px; font-weight: 600; line-height: 1.35; color: var(--text-muted);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  margin-bottom: 5px;
}
.game-card:hover .card-name { color: #fff; }
.card-tags { display: flex; gap: 4px; overflow: hidden; }
.tag {
  font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: var(--radius-sm);
  background: var(--surface3); color: var(--text-muted);
  white-space: nowrap; flex: 0 0 auto;
}

/* ── Quick find (Ctrl/⌘+K) ───────────────────────────────
   A command palette, not a modal: it hangs from the top of the window the way
   an editor's does, so the list grows downward and the eye never moves. */
.palette-overlay { align-items: flex-start; padding: clamp(52px, 11vh, 132px) 16px 16px; z-index: 210; }
.palette {
  width: min(560px, 100%); max-height: min(72vh, 560px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  animation: slideUp 0.16s var(--ease-out);
}
.palette-search { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--border-light); }
.palette-search > i { color: var(--text-faint); font-size: 14px; }
.palette-input {
  flex: 1; min-width: 0; background: transparent; border: 0; outline: none;
  color: var(--text); font: 600 15px/1.4 var(--font);
}
.palette-input::placeholder { color: var(--text-faint); font-weight: 500; }
.palette-esc {
  font: 700 10px var(--mono); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-faint); border: 1px solid var(--border); border-radius: 3px; padding: 2px 6px;
}
.palette-results { overflow-y: auto; padding: 6px; }
.palette-label {
  font: 700 10.5px var(--font); letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-faint); padding: 8px 10px 4px;
}
.palette-row {
  width: 100%; display: flex; align-items: center; gap: 10px;
  padding: 6px 8px; border-radius: var(--radius-sm); text-align: left;
}
.palette-row:hover, .palette-row.sel { background: var(--surface3); }
.palette-row.sel { box-shadow: inset 2px 0 0 var(--accent); }
.palette-thumb { width: 30px; height: 40px; border-radius: var(--radius-sm); object-fit: cover; background: var(--bg); flex: 0 0 auto; }
.palette-icon {
  width: 30px; height: 40px; border-radius: var(--radius-sm); flex: 0 0 auto;
  display: grid; place-items: center; font-size: 12px;
  color: var(--text-muted); background: var(--bg);
}
.palette-row.sel .palette-icon { color: var(--accent); }
.palette-text { flex: 1; min-width: 0; }
.palette-name { display: block; font-size: 13.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette-meta { display: block; font-size: 11.5px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette-go { font-size: 11px; color: var(--accent); opacity: 0; }
.palette-row.sel .palette-go { opacity: 1; }
.palette-empty { padding: 30px 16px; text-align: center; font-size: 13px; color: var(--text-faint); }
.palette-foot {
  display: flex; gap: 14px; padding: 8px 14px;
  border-top: 1px solid var(--border-light); background: var(--bg);
  font-size: 11.5px; color: var(--text-faint);
}
.palette-foot kbd {
  font: 600 10px var(--mono); color: var(--text-muted);
  border: 1px solid var(--border); border-radius: 3px; padding: 1px 4px; margin-right: 3px;
}
@media (max-width: 768px) {
  .palette-overlay { padding-top: 46px; }
  .palette-foot { display: none; }
  .palette-row { padding: 8px; }
}

/* ── Modals ────────────────────────────────────────────── */
.modal-overlay {
  position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.7);
  animation: fadeIn 0.15s ease;
  padding: 20px;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.modal {
  background: var(--surface);
  border: 1px solid var(--border-light); border-radius: var(--radius-lg);
  padding: 22px; max-width: 800px; width: 92vw; max-height: 88vh; overflow-y: auto;
  position: relative; animation: slideUp 0.18s var(--ease-out);
  box-shadow: var(--shadow-lg);
}
.modal-sm { max-width: 460px; }

/* What's new — the release notes panel. Plain list, one item per change:
   a bold line and one sentence of what it means for the player. */
.whatsnew-btn { position: relative; }
.new-dot {
  position: absolute; top: 3px; right: 3px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 2px var(--surface2);
}
.whatsnew h2 i { color: var(--accent); margin-right: 6px; }
.whatsnew-sub { font-size: 12.5px; color: var(--text-dim); margin: 4px 0 14px; }
.whatsnew-list { display: flex; flex-direction: column; gap: 13px; margin-bottom: 18px; }
.whatsnew-item h4 {
  font-size: 14px; font-weight: 700; color: var(--text);
  margin-bottom: 3px; display: flex; align-items: baseline; gap: 7px;
}
.whatsnew-item h4 i { color: var(--accent); font-size: 12px; }
.whatsnew-item p { font-size: 13px; color: var(--text-muted); line-height: 1.55; }
.modal-md { max-width: 560px; }
.modal h2 { font-weight: 700; letter-spacing: -0.3px; font-size: 20px; }
@keyframes slideUp { from { transform: translateY(10px); opacity: 0; } to { transform: none; opacity: 1; } }

.modal-close {
  position: absolute; top: 10px; right: 12px; width: 28px; height: 28px; border-radius: var(--radius-sm);
  font-size: 19px; color: var(--text-faint); display: grid; place-items: center; z-index: 5;
  transition: background var(--transition), color var(--transition);
}
.modal-close:hover { background: var(--surface3); color: #fff; }

/* Detail */
.detail-layout { display: flex; gap: 22px; }
.detail-cover {
  flex: 0 0 218px; aspect-ratio: 2/3; border-radius: var(--radius); overflow: hidden;
  background: var(--bg);
}
.detail-cover img { width: 100%; height: 100%; object-fit: cover; }
.detail-info { flex: 1; display: flex; flex-direction: column; gap: 13px; min-width: 0; }
.detail-info h2 { font-size: 24px; font-weight: 700; letter-spacing: -0.5px; line-height: 1.15; }
.detail-tags { display: flex; gap: 5px; flex-wrap: wrap; }
.detail-tags .tag {
  font-size: 11.5px; padding: 3px 9px; font-weight: 600;
  background: var(--surface3); color: var(--text-muted);
}
.detail-desc {
  font-size: 14px; color: var(--text-muted); line-height: 1.7;
  overflow-y: auto; max-height: 38vh; padding-right: 6px;
}
.detail-info .btn-play { align-self: flex-start; margin-top: auto; }

.detail-spec { display: flex; gap: 24px; flex-wrap: wrap; padding-top: 2px; }
.detail-spec .spec { display: flex; flex-direction: column; gap: 2px; }
.detail-spec .spec small {
  font-size: 11px; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase;
  color: var(--text-faint);
}
.detail-spec .spec strong { font-size: 15px; font-weight: 700; color: var(--text); }

/* Play + Share sit together at the foot of the detail sheet. */
.detail-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: auto; padding-top: 4px;
}
.detail-info .detail-actions .btn-play { align-self: auto; margin-top: 0; }

/* Share / embed codes. Label and hint share the top line so the code below can
   use the full width — an iframe snippet is long and must not wrap mid-tag. */
.share-game { color: var(--accent); }
.share-field { display: flex; flex-direction: column; gap: 5px; margin-top: 14px; }
.share-field > span {
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--text-faint);
}
.share-rows { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.share-row {
  background: var(--surface2); border: 1px solid var(--border-light);
  border-radius: var(--radius); padding: 10px 11px;
}
.share-row-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.share-row-name { font-size: 13px; font-weight: 700; color: var(--text); }
.share-row-hint { flex: 1; font-size: 11.5px; color: var(--text-dim); min-width: 0; }
.share-copy { padding: 4px 10px; font-size: 12px; }
.share-code {
  display: block; font-family: var(--mono); font-size: 11.5px; line-height: 1.5;
  color: var(--text-muted); background: var(--bg);
  border: 1px solid var(--border-light); border-radius: var(--radius-sm);
  padding: 7px 9px;
  /* Wrap rather than scroll: an iframe snippet is long, and seeing the whole
     thing at once is worth the line break mid-tag. */
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.share-credit { margin-top: 14px; }

/* ── Player ──────────────────────────────────────────────
   The player is the one surface in the app with no radius and no gutter. It is
   not a card, it is a game: a game draws its own buttons into its own corners,
   and a 12px radius plus `overflow: hidden` clipped exactly those corners away,
   while the 16px inset made the window smaller than the browser for nothing.
   Full-bleed and square, so nothing the game draws is cut off and the play
   area is as large as the browser allows. */
.player-container {
  position: fixed; inset: 0; z-index: 210; display: flex; flex-direction: column;
  background: #000; border-radius: 0; overflow: hidden;
  box-shadow: none;
}

.player-bar {
  flex: 0 0 auto; height: 44px; display: flex; align-items: center; justify-content: flex-end; gap: 6px;
  padding: 0 10px; background: var(--bg); border-bottom: 1px solid var(--border-light);
  z-index: 2;
}
/* Title left, every control packed at the right — the status pills used to
   spread across the middle of the bar. */
.player-bar-left { display: flex; align-items: center; gap: 9px; min-width: 0; margin-right: auto; }
.bar-logo { width: 18px; height: 18px; flex-shrink: 0; }
.player-title { font-size: 13px; font-weight: 600; color: var(--text-muted); }

.player-gamepad {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;
  color: var(--text-muted); background: var(--surface3); border-radius: var(--radius-sm); padding: 3px 8px;
  max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.player-gamepad i { font-size: 13px; color: var(--green); }

.player-daily {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;
  color: var(--text-muted); background: var(--surface3);
  border-radius: var(--radius-sm); padding: 3px 8px; white-space: nowrap;
}
.player-daily i { font-size: 12px; }
.player-daily.warn {
  color: #fca5a5; background: rgba(242,63,67,0.16);
  animation: pulse 1.2s infinite;
}

/* Fit / Fill — the second half of "don't crop the game". Fitting is the
   default, so nobody gets a cropped game by surprise; filling is one click
   away for anyone who would rather use the whole window. */
.player-fit {
  display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
  font-size: 12px; font-weight: 600; color: var(--text-muted);
  background: var(--surface3); border: none; cursor: pointer;
  border-radius: var(--radius-sm); padding: 4px 9px; white-space: nowrap;
  transition: background var(--transition), color var(--transition);
}
.player-fit:hover { background: #404249; color: #fff; }
.player-fit.fill { background: var(--accent); color: var(--on-accent); }
.player-fit.fill:hover { background: var(--accent); color: var(--on-accent); }

.player-actions { display: flex; gap: 2px; }
.player-actions .btn-icon { color: var(--text-muted); }
.player-actions .btn-icon:hover { color: #fff; }
.player-actions .btn-close:hover { color: var(--red); }
.player-actions .btn-icon.active { color: var(--accent); background: var(--accent-dim); }

/* Session countdown timer */
.player-timer {
  position: absolute; top: 54px; right: 12px; z-index: 5;
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--mono); font-size: 13px; font-weight: 700;
  color: #fff; background: rgba(0,0,0,0.72);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 5px 10px; pointer-events: none;
}
.player-timer i { color: var(--accent); }
.player-timer.warn { background: var(--red); border-color: var(--red); animation: pulse 1s infinite; }

/* Stream watermark */
.player-watermark {
  position: absolute; right: 14px; bottom: 12px; z-index: 5;
  font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: 0.18em;
  color: rgba(255,255,255,0.4); background: rgba(0,0,0,0.4);
  border: 1px solid rgba(255,255,255,0.1); border-radius: var(--radius-sm); padding: 3px 9px;
  pointer-events: none; user-select: none;
}
.player-watermark span { color: var(--accent); }

/* Autoplay guards. A link opened in a new tab carries no user gesture, so the
   stream starts muted and may need one click before it will paint at all.
   Both are overlays, so they never swallow input meant for the game. */
.player-sound {
  position: absolute; left: 12px; top: 56px; z-index: 6;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 11px; border-radius: var(--radius-sm);
  font-size: 12.5px; font-weight: 600; color: #fff;
  background: rgba(0,0,0,0.72); border: 1px solid var(--border);
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.player-sound:hover { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

.player-tapstart {
  position: absolute; inset: 44px 0 0; z-index: 6;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 11px;
  font-size: 15px; font-weight: 700; color: #fff;
  background: rgba(0,0,0,0.74);
  transition: background var(--transition);
}
.player-tapstart i { font-size: 28px; color: var(--accent); }
.player-tapstart:hover { background: rgba(0,0,0,0.62); }
.player-container:fullscreen .player-tapstart { inset: 0; }

/* Launch status overlay */
.player-status {
  position: absolute; inset: 44px 0 0; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  background: #101012;
}
.player-status-inner {
  text-align: center; display: flex; flex-direction: column; align-items: center; gap: 11px;
  padding: 0 20px; max-width: 420px;
  animation: fadeIn 0.2s ease;
}
.ps-icon {
  width: 76px; height: 76px; border-radius: var(--radius); object-fit: cover;
  background: var(--bg); border: 1px solid var(--border-light);
}
#player-status-title { font-size: 20px; font-weight: 700; letter-spacing: -0.3px; margin-top: 4px; }
#player-status-msg { font-size: 13.5px; color: var(--text-muted); line-height: 1.5; min-height: 20px; }
#player-status-msg.err { color: #fca5a5; }
.ps-dots { display: flex; gap: 5px; margin-top: 4px; }
.ps-dots span { width: 6px; height: 6px; border-radius: 50%; background: var(--surface3); animation: dotBounce 1.2s ease-in-out infinite; }
.ps-dots span:nth-child(2) { background: var(--accent); animation-delay: 0.15s; }
.ps-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes dotBounce {
  0%, 100% { transform: translateY(0); opacity: 0.5; }
  50% { transform: translateY(-4px); opacity: 1; }
}
.player-status .btn-danger {
  margin-top: 12px; background: var(--surface3); color: var(--text);
}
.player-status .btn-danger:hover { background: #404249; color: #fff; }
/* The opt-in unlock. Accent-coloured because it is the one useful action on an
   otherwise dead screen — but still an action, never something that happens on
   its own. */
.player-status .btn-unlock {
  margin-top: 12px; background: var(--accent); color: #101012; font-weight: 700;
}
.player-status .btn-unlock:hover { filter: brightness(1.07); }
.player-status .btn-unlock:disabled { opacity: 0.6; cursor: default; filter: none; }
.ps-sub {
  font-size: 12px; line-height: 1.55; color: var(--text-faint);
  max-width: 360px; margin: 0;
}
/* The second way out of an empty pool: a player's own account skips account
   creation completely, so it works even while the service gates registrations. */
.player-status .ps-link {
  background: none; border: 0; padding: 2px 4px; cursor: pointer;
  font: inherit; font-size: 12.5px; color: var(--text-muted);
  text-decoration: underline; text-underline-offset: 3px;
}
.player-status .ps-link:hover { color: var(--text); }

.player-video {
  position: absolute; top: 44px; left: 0; width: 100%; height: calc(100% - 44px);
  /* contain, not cover: cover cropped the stream to fill the window, which is
     how a game's own buttons ended up off-screen and the only way to reach them
     was fullscreen. The letterboxing is black on black, and it is what
     sendMouseInput() already assumes (it centres a `min()`-scaled frame), so the
     pointer now lands where the player clicks instead of a cover-scaled guess. */
  object-fit: contain; background: #000; z-index: 1; display: none;
}
.player-video.show { display: block; }
/* Fill mode is the old cover behaviour, now opt-in instead of forced. The
   pointer mapping follows the same switch (see sendMouseInput). */
.player-video.fill { object-fit: cover; }

/* Stall / recovery notice — driven by the stream watchdog. A buffer hiccup is
   dismissed on its own; a genuinely dead stream offers a Reconnect button. The
   overlay never swallows input, only the button does. */
.player-recover {
  position: absolute; inset: 44px 0 0; z-index: 5;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.45);
  pointer-events: none; animation: fadeIn 0.15s ease;
}
.player-recover-inner {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 18px 26px; text-align: center;
  background: rgba(0,0,0,0.74); border: 1px solid rgba(255,255,255,0.09);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
}
.pr-text { font-size: 13.5px; font-weight: 600; color: #fff; max-width: 320px; }
.pr-spinner {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.18); border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}
.player-recover .btn { pointer-events: auto; }
.player-container:fullscreen .player-recover { inset: 0; }

/* Connection stats HUD — read-only overlay, bottom-left so it never collides
   with the watermark, the timer or the bar. */
.player-hud {
  position: absolute; left: 12px; bottom: 12px; z-index: 5;
  display: none; grid-template-columns: auto auto; gap: 3px 16px;
  padding: 8px 11px; pointer-events: none;
  font-family: var(--mono); font-size: 11px; line-height: 1.35;
  color: rgba(255,255,255,0.6);
  background: rgba(0,0,0,0.62); border: 1px solid rgba(255,255,255,0.09);
  border-radius: var(--radius);
}
.player-hud:not(.hidden) { display: grid; }
.hud-row { display: flex; gap: 5px; align-items: baseline; white-space: nowrap; }
.hud-row b { color: #fff; font-weight: 600; }
.hud-row.warn, .hud-row.warn b { color: var(--gold); }

/* Fullscreen: same frame, same contain rule — a game is no more croppable on a
   monitor than it is in the window. */
.player-container:fullscreen { inset: 0; border-radius: 0; }
.player-container:fullscreen .player-video { top: 0; height: 100%; }
.player-container:fullscreen .player-status { inset: 0; }
.player-container:fullscreen .player-bar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 4;
  background: linear-gradient(rgba(0,0,0,0.8), rgba(0,0,0,0));
  border-bottom: none;
  opacity: 0; transition: opacity 0.2s; pointer-events: none;
}
.player-container:fullscreen:hover .player-bar,
.player-container:fullscreen .player-bar:focus-within { opacity: 1; pointer-events: auto; }

/* First-play shortcuts overlay */
.player-shortcuts {
  position: absolute; left: 50%; bottom: 52px; transform: translateX(-50%);
  z-index: 6; pointer-events: none; text-align: center;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 15px 20px;
  box-shadow: var(--shadow-lg);
  animation: slideUp 0.2s ease;
}
.player-shortcuts-title { font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 12px; }
.player-shortcuts-grid { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; margin-bottom: 12px; }
.player-shortcuts .sc { font-size: 12px; color: var(--text-muted); display: inline-flex; align-items: center; gap: 6px; }
.player-shortcuts kbd {
  font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--text);
  background: var(--surface3); border-radius: var(--radius-sm); padding: 2px 6px; min-width: 20px;
}
.player-shortcuts .btn { pointer-events: auto; }

/* Queue / launch step markers (only present while a session sets up) */
.step { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--text-faint); }
.step.active { color: var(--text); }
.step.done { color: var(--text-muted); }
.step.done i { color: var(--green); }

/* ── Loading screen discord link ───────────────────────── */
.loading-discord {
  margin-top: 10px; display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; font-weight: 600; color: #b5b8ff;
  padding: 8px 16px; border-radius: var(--radius-sm);
  background: rgba(88,101,242,0.16);
  transition: background var(--transition);
}
.loading-discord:hover { background: rgba(88,101,242,0.28); }

/* Header accent buttons */
.discord-btn { color: #8890f5 !important; font-size: 15px; }
.discord-btn:hover { color: #a5abff !important; background: rgba(88,101,242,0.18) !important; }
.pro-btn { color: var(--gold) !important; font-size: 14px; }
.pro-btn:hover { color: #f8c954 !important; background: rgba(240,178,50,0.16) !important; }
.side-item.pro-btn i { color: var(--gold); }
.install-btn { color: var(--accent) !important; font-size: 14px; }
.install-btn:hover { background: var(--accent-dim) !important; }
.cloak-btn { color: var(--text-muted); }

/* ── Pro page ──────────────────────────────────────────── */
.pro-page {
  min-height: calc(100vh - var(--content-top));
  display: flex; align-items: center; justify-content: center;
  padding: 40px 18px;
}
.pro-card {
  background: var(--surface);
  border: 1px solid var(--border-light); border-radius: var(--radius-lg);
  padding: 32px 28px; max-width: 460px; width: 100%; text-align: center;
  box-shadow: var(--shadow-lg); position: relative; overflow: hidden;
}
.pro-card::before { content: none; }
.pro-crown { font-size: 40px; margin-bottom: 8px; }
.pro-card h1 { font-size: 26px; font-weight: 800; letter-spacing: -0.6px; }
.pro-card h1 span { color: var(--accent); }
.pro-sub { font-size: 13.5px; color: var(--text-muted); margin: 6px 0 20px; }
.pro-benefits { list-style: none; text-align: left; display: flex; flex-direction: column; gap: 6px; margin-bottom: 22px; }
.pro-benefits li {
  display: flex; align-items: center; gap: 10px;
  font-size: 13.5px; color: var(--text);
  background: var(--bg);
  border-radius: var(--radius-sm); padding: 10px 12px;
}
.pro-benefits li i { color: var(--green); font-size: 12px; flex-shrink: 0; }
.pro-benefits li em { margin-left: auto; font-style: normal; font-size: 11.5px; color: var(--text-faint); white-space: nowrap; }
.pro-code-row { display: flex; gap: 8px; margin-bottom: 10px; }
.pro-code-row input {
  flex: 1; height: 40px; background: var(--bg); border: 1px solid var(--border-light);
  border-radius: var(--radius-sm); color: var(--text); padding: 0 12px; font-size: 14px; outline: none;
  font-family: var(--mono); letter-spacing: 0.2em; text-transform: uppercase; text-align: center;
}
.pro-code-row input:focus { border-color: var(--accent); }
.pro-code-row .btn { padding: 10px 16px; }
.pro-buy { font-size: 12.5px; color: var(--text-faint); margin-top: 14px; }
.pro-buy a { color: var(--accent); font-weight: 700; }

/* ── Settings ──────────────────────────────────────────── */
.settings-form { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
/* Tabs: one pane at a time instead of one long scroll. */
.settings-tabs {
  display: flex; gap: 4px; padding: 3px;
  background: var(--bg); border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
}
.settings-tab {
  flex: 1; height: 30px; border: none; background: none; cursor: pointer;
  color: var(--text-muted); font-family: inherit; font-size: 12.5px; font-weight: 700;
  border-radius: var(--radius-sm);
  transition: background var(--transition), color var(--transition);
}
.settings-tab:hover { color: var(--text); }
.settings-tab.active { background: var(--surface3); color: var(--text); }
.settings-pane { display: flex; flex-direction: column; gap: 12px; }
.settings-pane.hidden { display: none; }
/* The tab bar replaces the divider the old first section sat under. */
.settings-pane > .settings-section:first-child { border-top: none; margin-top: 0; padding-top: 0; }
.settings-form label { display: flex; flex-direction: column; gap: 6px; }
.settings-form label > span {
  font-size: 11.5px; font-weight: 700; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.02em;
}
.settings-form input {
  width: 100%; height: 38px; background: var(--bg); border: 1px solid var(--border-light);
  border-radius: var(--radius-sm); color: var(--text); padding: 0 12px; outline: none;
  font-family: var(--mono); font-size: 12.5px;
}
.settings-form input:focus { border-color: var(--accent); }
.settings-form input::placeholder { color: var(--text-faint); }
.settings-form select {
  width: 100%; height: 38px; background: var(--bg); border: 1px solid var(--border-light);
  border-radius: var(--radius-sm); color: var(--text); padding: 0 10px; font-size: 13.5px; outline: none;
}
.settings-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 4px; }
.settings-hint { font-size: 12.5px; color: var(--text-faint); line-height: 1.6; }
.settings-credit { font-size: 12.5px; color: var(--text-muted); line-height: 1.7; margin: 0; }
.settings-section {
  margin-top: 4px; padding-top: 16px; border-top: 1px solid var(--border-light);
  display: flex; flex-direction: column; gap: 12px;
}
.settings-section h3 { font-size: 13.5px; font-weight: 700; margin: 0; display: flex; align-items: center; gap: 8px; }
.settings-tag {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--text-muted); background: var(--surface3);
  border-radius: var(--radius-sm); padding: 2px 6px;
}
.settings-row { display: flex; gap: 8px; }
.settings-row label { flex: 1; }
.code-row { display: flex; gap: 8px; align-items: center; }
.code-row input { flex: 1; }

/* Settings: appearance */
.theme-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.theme-swatch {
  position: relative;
  width: 32px; height: 32px; border-radius: var(--radius-sm);
  background: var(--sw); border: 2px solid transparent;
}
.theme-swatch:hover { border-color: var(--border); }
.theme-swatch.active { border-color: #fff; }
/* Pro-only dots stay visible so they advertise themselves, but a padlock keeps
   it honest that clicking one is a trip to the Pro page. */
.theme-swatch.locked::after {
  content: ''; position: absolute; inset: 0;
  background: rgba(0,0,0,0.45); border-radius: var(--radius-sm);
}
.theme-swatch .sw-lock {
  position: absolute; inset: 0; z-index: 1;
  display: grid; place-items: center;
  font-size: 11px; color: rgba(255,255,255,0.85);
  text-shadow: 0 1px 2px rgba(0,0,0,0.6);
}

.switch-row { flex-direction: row !important; align-items: center; justify-content: space-between; gap: 10px !important; cursor: pointer; }
.switch-row span:first-child { margin-right: auto; }
.switch-input { position: absolute; opacity: 0; pointer-events: none; }
.switch { position: relative; width: 40px; height: 24px; flex-shrink: 0; background: #4e5058; border-radius: var(--radius-pill); transition: background var(--transition); }
.switch::after { content: ''; position: absolute; top: 4px; left: 4px; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform var(--transition); }
.switch-input:checked + .switch { background: var(--accent); }
.switch-input:checked + .switch::after { transform: translateX(16px); }
.settings-form input[type="range"] { width: 100%; accent-color: var(--accent); padding: 0; background: none; border: none; height: 22px; }
.sens-value { font-style: normal; color: var(--accent); font-weight: 700; font-size: 12px; }

/* ── Library ───────────────────────────────────────────── */
.library-view { max-width: var(--maxw); margin: 0 auto; padding: 28px var(--gutter) 50px; }
.library-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 28px; }
.stat-card {
  background: var(--bg);
  border-radius: var(--radius);
  padding: 16px 18px; display: flex; flex-direction: column; gap: 4px;
}
.stat-value { font-size: 22px; font-weight: 700; letter-spacing: -0.5px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stat-label { font-size: 11.5px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.02em; font-weight: 700; }

.library-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 16px; }
.library-head h2 { font-size: 18px; font-weight: 700; letter-spacing: -0.3px; }

.library-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 8px; }

.lib-card {
  display: flex; gap: 12px; align-items: center;
  background: var(--bg); border-radius: var(--radius);
  padding: 8px; transition: background var(--transition);
}
.lib-card:hover { background: var(--surface3); }
.lib-cover-wrap { position: relative; flex: 0 0 44px; aspect-ratio: 2/3; border-radius: var(--radius-sm); overflow: hidden; background: var(--surface); }
.lib-cover-wrap .card-cover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.lib-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.lib-name { font-size: 13.5px; font-weight: 600; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.lib-meta { font-size: 11.5px; color: var(--text-faint); display: flex; gap: 12px; }
.lib-play { padding: 7px 12px; flex-shrink: 0; }
.library-empty { text-align: center; padding: 70px 0; color: var(--text-faint); }
.library-empty i { font-size: 36px; margin-bottom: 14px; display: block; }

/* ── Boot screen — mark, wordmark, bar. Nothing else. ──── */
#boot-screen {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
  transition: opacity 0.3s ease;
  cursor: pointer; overflow: hidden;
}
#boot-screen.done { opacity: 0; pointer-events: none; }
.boot-inner { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.boot-mark { width: 52px; height: 52px; animation: bootSpin 1.6s linear infinite; }
@keyframes bootSpin { to { transform: rotate(360deg); } }
.boot-name { font-size: 24px; font-weight: 700; letter-spacing: -0.6px; }
.boot-name span { color: var(--accent); }
.boot-bar { width: 180px; height: 3px; border-radius: var(--radius-pill); background: var(--surface3); overflow: hidden; margin-top: 2px; }
.boot-bar-fill { height: 100%; width: 0%; border-radius: var(--radius-pill); background: var(--accent); transition: width 0.1s linear; }
.boot-tag { font-size: 11.5px; color: var(--text-faint); min-height: 16px; }
.boot-hint { font-size: 10px; color: var(--text-faint); opacity: 0.6; letter-spacing: 0.06em; text-transform: uppercase; font-family: var(--mono); }

/* ── Toast ─────────────────────────────────────────────── */
.toast {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); z-index: 300;
  padding: 11px 18px; border-radius: var(--radius);
  font-size: 13.5px; font-weight: 500;
  background: var(--bg); border: 1px solid var(--border);
  color: var(--text); box-shadow: var(--shadow-lg);
  animation: toastIn 0.18s var(--ease-out);
}
.toast.error { border-color: var(--red); color: #fca5a5; }
.toast.success { border-color: var(--green); color: #86efac; }
.toast.out { animation: toastOut 0.18s ease forwards; }
@keyframes toastIn { from { opacity: 0; transform: translateX(-50%) translateY(10px); } to { opacity: 1; transform: translateX(-50%); } }
@keyframes toastOut { from { opacity: 1; } to { opacity: 0; transform: translateX(-50%) translateY(-8px); } }

/* ── Footer ────────────────────────────────────────────── */
.site-footer {
  text-align: center; padding: 28px 20px; color: var(--text-faint); font-size: 12.5px;
  border-top: 1px solid var(--border-light); max-width: var(--maxw); margin: 0 auto;
}
/* Upstream credit: deliberately brighter than the disclaimer and link-coloured,
   so it reads as real attribution rather than a buried fine-print line. */
.footer-credit {
  display: block; margin-top: 8px;
  color: var(--text-muted); font-size: 13px;
}
.footer-credit a, .settings-credit a {
  color: var(--accent); font-weight: 600;
  text-decoration: underline; text-underline-offset: 2px;
}
.footer-credit a:hover, .settings-credit a:hover { color: var(--text); }

/* Limit modal */
.limit-msg { font-size: 14px; color: var(--text-muted); line-height: 1.7; margin: 14px 0 8px; }

/* ── Panic mode: fake network-error screen (must look native) ── */
.panic-overlay {
  position: fixed; inset: 0; z-index: 99999;
  background: #fff; color: #202124;
  font-family: 'Segoe UI', Roboto, Arial, sans-serif;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
.panic-box { max-width: 500px; width: 90%; text-align: left; }
.panic-title { font-size: 26px; font-weight: 400; letter-spacing: -.2px; margin-bottom: 10px; }
.panic-sub { font-size: 14px; color: #5f6368; margin: 0 0 8px; }
.panic-tips { margin: 4px 0 22px; padding: 0; font-size: 13px; line-height: 1.9; color: #5f6368; }
.panic-tips p { margin: 0; }
.panic-code { font-size: 11px; color: #9aa0a6; }
.panic-resume { position: fixed; right: 16px; bottom: 14px; background: none; border: none; cursor: pointer; color: #dadce0; font-size: 20px; padding: 6px; }
.panic-resume:hover { color: #9aa0a6; }
.panic-hint { position: fixed; left: 16px; bottom: 12px; margin: 0; font-size: 10px; color: #f1f3f4; user-select: none; }

/* ════════════════════════════════════════════════════════
   RESPONSIVE
   Below 900px the rail becomes a slide-over and the inline
   tag pills take over as navigation.
   ════════════════════════════════════════════════════════ */
@media (min-width: 901px) {
  /* On desktop the rail already lists every genre, so the inline pills would
     just be the same list twice — unless the rail is collapsed, in which case
     they are the only way to reach a genre. */
  .chip.genre { display: none; }

  body.rail-collapsed { --rail-w: 0px; }
  body.rail-collapsed .sidebar { transform: translateX(-100%); }
  body.rail-collapsed .chip.genre { display: inline-flex; }
  /* The backdrop only exists for the mobile drawer — never cover a laptop. */
  .side-backdrop { display: none; }
}

@media (max-width: 1180px) {
  :root { --gutter: 24px; }
}

@media (max-width: 1024px) {
  .search-box { max-width: 420px; }
  .logo-sub { display: none; }
}

@media (max-width: 900px) {
  :root { --rail-w: 0px; --gutter: 18px; }
  .sidebar {
    width: var(--rail-w-panel);
    transform: translateX(-100%);
    transition: transform 0.2s var(--ease-out);
    box-shadow: 0 0 40px rgba(0,0,0,0.5);
  }
  .sidebar.open { transform: none; }
  .site-header { left: 0; }
  .main-content { margin-left: 0; }
}

@media (max-width: 768px) {
  :root { --header-h: 52px; --content-top: 52px; --gutter: 14px; }
  .header-inner { padding: 0 12px; gap: 6px; }
  .search-box { max-width: none; }
  .api-status { display: none; } /* owner-only badge */
  .online-badge { position: static; }

  .hero { height: 46vh; min-height: 340px; }
  .hero-info { padding: 0 var(--gutter) 50px; }
  .hero-title { letter-spacing: -0.6px; }
  .hero-desc { font-size: 13.5px; -webkit-line-clamp: 2; margin-bottom: 16px; }
  .hero::after, .hero-progress { bottom: 26px; width: 96px; }
  .hero-dots { right: var(--gutter); bottom: 25px; }

  .filters { padding: 14px var(--gutter) 2px; }
  .cat-row { gap: 10px; }
  .cat-row .game-card { flex: 0 0 140px; }
  .game-grid-inner { grid-template-columns: repeat(auto-fill, minmax(142px, 1fr)); gap: 16px 10px; }
  .library-grid { grid-template-columns: 1fr; }
  .home-section { margin-bottom: 26px; }
  .home-section-head h2 { font-size: 16px; }
  .home-section-head h2 i { width: 22px; height: 22px; font-size: 10px; }

  .player-actions .btn-icon { width: 38px; height: 38px; }
  .player-bar { padding: 0 6px; }
  .player-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .player-shortcuts { width: 88vw; max-width: 360px; padding: 14px 16px; }
  .player-watermark { bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }
  .player-sound { left: 10px; top: 52px; font-size: 12px; padding: 5px 9px; }
  .detail-layout { flex-direction: column; gap: 16px; }
  .detail-cover { flex: none; width: 150px; }
  .detail-desc { max-height: none; }
  .detail-info .btn-play { align-self: stretch; }
}

@media (max-width: 520px) {
  .player-gamepad span, .player-daily span { display: none; }
  .hero-tags .tag { font-size: 10.5px; }
}

/* Bigger tap targets + no input zoom on iOS */
@media (max-width: 768px) {
  .btn { min-height: 40px; }
  .chip { padding: 8px 13px; font-size: 13px; }
  .search-input { height: 36px; font-size: 16px; } /* ≥16px prevents iOS focus zoom */
  .settings-form input, .settings-form select { font-size: 16px; }
  .toast { width: 92vw; text-align: center; }
  .boot-name { font-size: 21px; }
  .boot-mark { width: 46px; height: 46px; }
  .modal { padding: 18px; }
}

@media (max-width: 430px) {
  .discord-btn, .cloak-btn { display: none; }
  .hero-actions .btn { flex: 1; }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  html { scroll-behavior: auto; }
}

/* ── Analytics consent card ─────────────────────────────────
   Injected by js/analytics.js. Sits below the player and the panic
   screen on purpose — it must never cover a game or the panic fake. */
.gc-consent {
  position: fixed;
  left: 22px;
  bottom: 22px;
  /* The ad tags inject a full-viewport click layer at 2147483647, and the only
     way a required consent dialog can be answered is to match it and win the
     tie by DOM order (see keepCardOnTop in analytics.js). It's removed the
     instant the visitor answers, so nothing else is ever covered by it. */
  z-index: 2147483647;
  width: min(360px, calc(100vw - 44px));
  padding: 16px 18px;
  border-radius: var(--radius-lg);
  background: var(--glass-strong);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  animation: gcConsentIn 0.3s var(--ease-out);
}
.gc-consent.hidden { display: none; }
.gc-consent-head {
  display: flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 700; letter-spacing: -0.2px;
  color: var(--text);
}
.gc-consent-head i { font-size: 13px; color: var(--accent); }
.gc-consent-text {
  margin: 8px 0 14px;
  font-size: 12.5px; line-height: 1.55; color: var(--text-muted);
}
.gc-consent-actions { display: flex; gap: 8px; }
.gc-consent-btn {
  flex: 1;
  padding: 9px 12px;
  border: 0; border-radius: var(--radius-sm);
  font-family: var(--font); font-size: 12.5px; font-weight: 700;
  color: var(--on-accent);
  background: var(--grad);
  cursor: pointer;
  transition: filter var(--transition);
}
.gc-consent-btn:hover { filter: brightness(1.1); }
.gc-consent-btn.ghost {
  color: var(--text-muted);
  background: var(--surface3);
  border: 1px solid var(--border);
}
.gc-consent-btn.ghost:hover { color: #fff; background: #404249; }
@keyframes gcConsentIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
@media (max-width: 560px) {
  .gc-consent { left: 12px; right: 12px; bottom: 12px; width: auto; }
}

/* Belt and braces: the panic screen must always win, and it must look real.
   (analytics.js hides the card too — this covers the instant before it does.) */
body:has(#panic-overlay:not(.hidden)) .gc-consent { display: none !important; }
body:has(.modal-overlay:not(.hidden)) .gc-consent { display: none !important; }

/* ── Registration puzzle (the game service's human check) ─────────────
   Geometry is fixed at 320×160 because that is the size the challenge is
   issued in: the slider maps its position onto that width, so scaling the
   stage with CSS would desync the offset we submit. */
.puzzle-hint { color: var(--text-muted); font-size: 13px; margin: 8px 0 14px; }
.puzzle-stage {
  position: relative; width: 320px; height: 160px; margin: 0 auto;
  border-radius: var(--radius); overflow: hidden; background: var(--bg);
  border: 1px solid var(--border-light); touch-action: none;
}
.puzzle-stage img { position: absolute; left: 0; top: 0; display: block; }
#puzzle-bg { width: 320px; height: 160px; }
#puzzle-piece { pointer-events: none; filter: drop-shadow(0 0 2px rgba(0,0,0,0.65)); }
.puzzle-track {
  position: relative; width: 320px; height: 40px; margin: 12px auto 0;
  border-radius: var(--radius-pill); background: var(--bg);
  border: 1px solid var(--border); touch-action: none;
}
.puzzle-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; border-radius: var(--radius-pill); background: var(--accent-dim); }
.puzzle-thumb {
  position: absolute; left: 0; top: 0; width: 40px; height: 38px; border-radius: var(--radius-pill);
  background: var(--accent); color: var(--on-accent);
  display: grid; place-items: center; font-size: 20px; font-weight: 700;
  cursor: grab; user-select: none;
}
.puzzle-thumb:active { cursor: grabbing; }
.puzzle-thumb.done { background: var(--green); color: #fff; }
.puzzle-track.busy .puzzle-thumb { opacity: 0.55; cursor: progress; }
.puzzle-status { min-height: 18px; margin: 10px 0 0; font-size: 12.5px; color: var(--text-dim); text-align: center; }
.puzzle-status.bad { color: var(--red); }
.puzzle-status.good { color: var(--green); }

/* ── Human-check plumbing ────────────────────────────────────────────────
   The check only exists once *Send code* is pressed — it is not on the account
   screen before that — so these make that step impossible to overlook. A failing
   build check is a plain red line rather than a silent one, and the button that
   starts the flow pulses instead of waiting to be found. */
.api-build { font-size: 12.5px; color: var(--text-dim); }
.api-build.ok { color: var(--green); }
.api-build.bad { color: var(--red); }
.manual-alert { color: var(--red); }
.needs-attention { animation: gcAttention 1.6s ease-in-out infinite; outline: 2px solid var(--accent); }
@keyframes gcAttention {
  0%, 100% { outline-color: var(--accent); }
  50%      { outline-color: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .needs-attention { animation: none; }
}

/* ── Ad slots ────────────────────────────────────────────────────────────
   Every slot reserves its height in CSS *before* the network tag loads. An ad
   that arrives into an unsized box shoves the whole page down as it loads, which
   is worse than the ad itself — this is the single most important rule here.

   A slot is `display:none` until ads.js actually mounts a unit into it, so an
   unfilled or refused slot leaves no gap and no empty frame. `body.ads-off` is
   set for Pro subscribers, and hides them outright.

   Placement rationale and sizes: see Ad-placements.md in the repo root. */
.ad-slot { display: none; }
.ad-slot.mounted { display: flex; align-items: center; justify-content: center; }
.ad-slot iframe { border: 0; display: block; }
body.ads-off .ad-slot { display: none !important; }

/* In-feed native tile, sized like a game card so it reads as one more card in
   the row, not as an ad. 172px matches `.cat-row .game-card`; 140px on mobile. */
.ad-slot.ad-native-row { flex: 0 0 172px; min-height: 258px; align-items: flex-start; }
.ad-slot.ad-native-row > div { width: 100%; }

/* Between home rows. Deliberately NOT full width: right-aligned so it does not
   masquerade as content, and it cannot push a row out of the carousel. */
.ad-slot.ad-home-mid { min-height: 250px; justify-content: flex-end; padding: 4px 0 0; max-width: var(--maxw); margin: 0 auto; }

/* Under the hero. Block-level because it sits between the hero and the rows. */
.ad-slot.ad-home-top { min-height: 90px; padding: 10px var(--gutter) 0; max-width: var(--maxw); margin: 0 auto; }

/* Game detail modal, under the action buttons. */
.ad-slot.ad-detail { min-height: 250px; justify-content: flex-start; }

/* Bottom of the left rail — the rail is a flex column and .side-nav is its
   flexible part, so a slot after it pins to the bottom. */
.ad-slot.ad-rail { flex: 0 0 auto; min-height: 300px; flex-direction: column; gap: 8px; padding: 8px; border-top: 1px solid var(--border-light); }
.ad-slot.ad-rail[data-tall="1"] { min-height: 600px; }

/* Above the footer. */
.ad-slot.ad-footer { min-height: 90px; padding: 8px 0; }

/* Mobile sticky anchor: phones only, dismissible, and it must sit UNDER the
   toast layer (.toast is bottom:24px, z-index 300) so a toast is never covered
   by an ad. `body.ad-anchor-on` reserves the space so the footer is reachable. */
.ad-anchor { display: none; }
body.ad-anchor-on { padding-bottom: 62px; }
body.ad-anchor-on .ad-anchor {
  display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 250;
  align-items: center; justify-content: center; min-height: 50px;
  background: var(--bg); border-top: 1px solid var(--border-light);
}
body.ad-anchor-on .ad-anchor .ad-anchor-close {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--surface2); color: var(--text-muted); cursor: pointer;
  font-size: 15px; line-height: 1; display: grid; place-items: center;
}
.ad-anchor.mounted { padding-right: 40px; }
body.ads-off .ad-anchor { display: none !important; }
/* The player is full-bleed now, so a mounted anchor sits on top of the game's
   own bottom edge — where games put their buttons. Every other ad format here
   already refuses to show while a game is open; this makes the phone anchor do
   the same, whether it mounted before the game started or would mount during
   it. It comes back when the player closes, because the class is only for the
   duration of the game. */
body:has(#player-modal:not(.hidden)) .ad-anchor { display: none !important; }

@media (min-width: 769px) {
  /* The anchor is a phone format — never show it on a desktop. */
  body.ad-anchor-on .ad-anchor { display: none; }
  body.ad-anchor-on { padding-bottom: 0; }
}
@media (max-width: 768px) {
  .ad-slot.ad-native-row { flex: 0 0 140px; min-height: 210px; }
  .ad-slot.ad-home-top { min-height: 50px; }
  .ad-slot.ad-footer { min-height: 50px; }
  .ad-slot.ad-home-mid { min-height: 250px; justify-content: center; }
  .ad-slot.ad-detail { justify-content: center; }
}
