/* ==========================================================================
   TwicePlay — Page layouts + responsive rules
   Breakpoints: 1180 (laptop) · 900 (tablet) · 640 (mobile) · 420 (small)
   ========================================================================== */

/* ==========================================================================
   1. Home
   ========================================================================== */

.home-hero-wrap { padding-top: var(--sp-6); }

.stat-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--surface) 65%, transparent);
}
.stat__n {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 800;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.stat__l { font-size: var(--fs-xs); color: var(--text-dim); }

/* Category filter bar sticks under the header while browsing the grid */
.filter-bar {
  position: sticky;
  top: var(--header-h);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding-block: var(--sp-3);
  margin-bottom: var(--sp-5);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
}

.filter-bar__label {
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
  flex: 0 0 auto;
}

.sort-select {
  flex: 0 0 auto;
  padding: 0.5rem 2rem 0.5rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: var(--surface) url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238494a8' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 0.6rem center / 14px;
  color: var(--text-soft);
  font-size: var(--fs-sm);
  font-weight: 600;
  appearance: none;
  cursor: pointer;
  transition: all var(--t-fast) var(--ease);
}
.sort-select:hover { border-color: var(--border-strong); background-color: var(--surface-2); }

/* Horizontal scrolling rail for New / Trending rows */
.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(200px, 1fr);
  gap: var(--sp-5);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  padding-bottom: var(--sp-3);
  scrollbar-width: thin;
}
.rail > * { scroll-snap-align: start; }

/* ==========================================================================
   2. Page header block (categories / search / legal pages)
   ========================================================================== */

.page-head {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding-block: var(--sp-6) var(--sp-8);
}

.page-head__title { font-size: var(--fs-3xl); }
.page-head__lead { color: var(--text-soft); font-size: var(--fs-md); max-width: 62ch; }

.page-head--split {
  flex-direction: row;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-5);
}

/* ==========================================================================
   3. Search page
   ========================================================================== */

.search-hero {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: var(--sh-md);
}

.search-big {
  position: relative;
  display: flex;
  align-items: center;
}
.search-big .search-field__icon { left: 18px; width: 19px; height: 19px; }
.search-big input {
  width: 100%;
  padding: 1rem 3.2rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  background: var(--bg-elevated);
  color: var(--text);
  font-size: var(--fs-md);
  transition: all var(--t-fast) var(--ease);
}
.search-big input::placeholder { color: var(--text-faint); }
.search-big input:focus {
  outline: none;
  border-color: rgba(34, 203, 180, 0.6);
  box-shadow: 0 0 0 5px var(--accent-soft);
}
.search-big .search-field__clear { right: 14px; width: 30px; height: 30px; }
.search-big .search-field__clear svg { width: 15px; height: 15px; }

.search-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
  font-size: var(--fs-sm);
  color: var(--text-dim);
}

.term-row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }

/* ==========================================================================
   4. Game page
   ========================================================================== */

/* The detail page is a vertical journey now — hero, gameplay, related, info —
   so the page itself stacks and only the information block is two-column. */
.game-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-8);
  align-items: start;
}
.game-layout > * { min-width: 0; }

.game-main { min-width: 0; display: grid; gap: var(--sp-6); }
/* Grid items default to min-width:auto, which lets long metadata rows force
   the whole page wider than the viewport. */
.game-main > * { min-width: 0; }
.game-side > * { min-width: 0; }

.game-title-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-5);
  flex-wrap: wrap;
}
.game-title-row > * { min-width: 0; }
.game-title-row .row { flex-wrap: wrap; }

.game-title { font-size: var(--fs-2xl); }

.game-title-meta {
  display: flex;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  flex-wrap: wrap;
  margin-top: var(--sp-2);
}

/* --- The player: the drop-in mount point for real games --- */
.player {
  position: relative;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: #05070b;
  overflow: hidden;
  box-shadow: var(--sh-lg);
}

.player__stage {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background:
    radial-gradient(600px 300px at 50% 0%, rgba(255, 167, 34, 0.10), transparent 70%),
    linear-gradient(180deg, #0b1017, #05070b);
  display: grid;
  place-items: center;
  overflow: hidden;
}

/* The <iframe> a real game gets injected into fills the stage exactly. */
.player__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  background: #05070b;
  animation: ph-fade var(--t-slow) var(--ease) both;
}

/* While the stage holds the placeholder or the click-to-play poster there is
   no game dictating an aspect ratio, so the stage sizes to its content
   instead of clipping it (which it did at 16:9 on narrow screens). */
.player.is-placeholder .player__stage {
  aspect-ratio: auto;
  min-height: min(56.25vw, 460px);
  padding-block: var(--sp-6);
}

/* Placeholder shown while gamePath is null (no game wired up yet) */
.player__placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-8);
  text-align: center;
  max-width: 54ch;
}

.player__grid-fx {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.028) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(circle at 50% 45%, #000 10%, transparent 72%);
  pointer-events: none;
}

.player__logo {
  width: 64px;
  height: 64px;
  opacity: 0.9;
  animation: ph-pop 500ms var(--ease-out) both;
}

.player__title {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  color: #eef3fa;
}
.player__hint { font-size: var(--fs-sm); color: #8494a8; }

/* Loading veil over the stage while an iframe boots */
.player__loading {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  gap: var(--sp-3);
  background: rgba(5, 7, 11, 0.9);
  z-index: 4;
}
.player__loading > div { display: grid; justify-items: center; gap: var(--sp-3); }
.player__loading p { font-size: var(--fs-sm); color: #8494a8; }

/* Player toolbar */
.player__bar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: #0a0e14;
  border-top: 1px solid var(--border);
}

.player__bar-title {
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: 650;
  color: #dfe7f1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.player__bar-actions { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; }

.player-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.45rem 0.8rem;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.05);
  color: #cfd8e4;
  font-size: var(--fs-xs);
  font-weight: 600;
  transition: all var(--t-fast) var(--ease);
}
.player-btn:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
.player-btn svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; }
.player-btn[aria-pressed="true"] { background: var(--primary-soft); color: var(--brand-amber-300); border-color: rgba(255, 167, 34, 0.35); }

/* Native fullscreen: the player element itself becomes the fullscreen root */
.player:fullscreen { border: 0; border-radius: 0; display: flex; flex-direction: column; background: #05070b; }
.player:fullscreen .player__stage { flex: 1 1 auto; aspect-ratio: auto; height: auto; }
.player:fullscreen .player__bar { border-top-color: rgba(255, 255, 255, 0.08); }

/* Fallback "maximised" mode for browsers that refuse the Fullscreen API */
.player.is-faux-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 300;
  border-radius: 0;
  border: 0;
  display: flex;
  flex-direction: column;
}
.player.is-faux-fullscreen .player__stage { flex: 1 1 auto; aspect-ratio: auto; }

.game-tabs {
  display: flex;
  gap: var(--sp-1);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}
.game-tabs::-webkit-scrollbar { display: none; }

.game-tab {
  padding: 0.7rem 1rem;
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-dim);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: all var(--t-fast) var(--ease);
}
.game-tab:hover { color: var(--text); }
.game-tab.is-active { color: var(--primary); border-bottom-color: var(--primary); }

.prose { color: var(--text-soft); font-size: var(--fs-base); }
.prose > * + * { margin-top: var(--sp-4); }
.prose h3 { font-size: var(--fs-md); color: var(--text); }
.prose ul { display: grid; gap: var(--sp-2); }
.prose ul li { padding-left: 1.2rem; position: relative; }
.prose ul li::before {
  content: "";
  position: absolute;
  left: 0; top: 0.62em;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--primary);
}
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

/* Controls list */
.controls-list { display: grid; gap: var(--sp-2); }

.control-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--r-md);
  background: var(--surface-2);
  font-size: var(--fs-sm);
}

.keycap {
  display: inline-grid;
  place-items: center;
  min-width: 30px;
  padding: 0.25rem 0.5rem;
  border-radius: var(--r-xs);
  border: 1px solid var(--border-strong);
  border-bottom-width: 3px;
  background: var(--surface-3);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--text);
  flex: 0 0 auto;
}

.control-row__keys { display: flex; gap: var(--sp-2); flex-wrap: wrap; flex: 0 0 auto; }
.control-row__what { color: var(--text-soft); }

.game-side { display: grid; gap: var(--sp-5); position: sticky; top: calc(var(--header-h) + var(--sp-5)); }

/* Interactive star rating widget */
.rate-widget { display: flex; align-items: center; gap: var(--sp-1); }
.rate-widget button {
  padding: 2px;
  line-height: 0;
  border-radius: var(--r-xs);
  transition: transform var(--t-fast) var(--ease);
}
.rate-widget button:hover { transform: scale(1.2); }
.rate-widget svg { width: 20px; height: 20px; fill: var(--surface-3); transition: fill var(--t-fast) var(--ease); }
.rate-widget button.is-on svg { fill: var(--star); }

/* ==========================================================================
   5. Legal / content pages
   ========================================================================== */

.doc-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 240px;
  gap: var(--sp-10);
  align-items: start;
}
.doc-body { max-width: 74ch; }
.doc-body h2 { font-size: var(--fs-xl); margin-top: var(--sp-10); }
.doc-body h2:first-child { margin-top: 0; }
.doc-body h3 { margin-top: var(--sp-6); }
.doc-toc { position: sticky; top: calc(var(--header-h) + var(--sp-5)); display: grid; gap: var(--sp-2); }
.doc-toc a { font-size: var(--fs-sm); color: var(--text-dim); padding: var(--sp-1) 0; }
.doc-toc a:hover { color: var(--primary); }

.form-grid { display: grid; gap: var(--sp-4); }
.field { display: grid; gap: var(--sp-2); }
.field label { font-size: var(--fs-sm); font-weight: 600; color: var(--text-soft); }
.field input,
.field textarea,
.field select {
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg-elevated);
  color: var(--text);
  font-size: var(--fs-sm);
  transition: all var(--t-fast) var(--ease);
  width: 100%;
}
.field textarea { min-height: 140px; resize: vertical; }
.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: rgba(34, 203, 180, 0.55);
  box-shadow: 0 0 0 4px var(--accent-soft);
}

/* ==========================================================================
   6. Responsive
   ========================================================================== */

/* Laptop */
@media (max-width: 1180px) {
  :root { --card-min: 178px; }
  .game-layout { grid-template-columns: minmax(0, 1fr) 280px; gap: var(--sp-6); }
  .footer-top { grid-template-columns: 1fr 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; max-width: none; }
}

/* Navigation lives in the rail now; see PORTAL SHELL at the end of this file
   for the three states it moves through. */

/* Tablet */
@media (max-width: 980px) {
  .search-field { width: clamp(150px, 30vw, 260px); }
  .hero { grid-template-columns: 1fr; min-height: 0; }
  .hero__media { order: -1; aspect-ratio: 16 / 9; position: relative; }
  .hero__media img { position: absolute; }
  .hero__media::after { background: linear-gradient(to top, var(--surface) 4%, transparent 70%); }
  .hero__dots { position: static; margin-top: var(--sp-2); }
  .game-layout { grid-template-columns: minmax(0, 1fr); }
  .player__stage { aspect-ratio: 4 / 3; }
  .game-side { position: static; }
  .doc-layout { grid-template-columns: 1fr; }
  .doc-toc { display: none; }
}

/* Mobile */
@media (max-width: 640px) {
  :root {
    --card-min: 150px;
    --header-h: 60px;
    --gutter: 1rem;
  }

  .header-inner { gap: var(--sp-3); }
  .logo__word { font-size: 1.12rem; }
  .logo__mark { width: 30px; height: 30px; }

  .card-grid { gap: var(--sp-3); }
  .game-card__body { padding: var(--sp-2) var(--sp-3) var(--sp-3); }
  .game-card__title { font-size: var(--fs-sm); }

  .section { margin-top: var(--sp-10); }
  .section-title { font-size: var(--fs-lg); }

  /* Keep the hero from consuming an entire phone screen on its own. */
  .hero__media { aspect-ratio: 16 / 10; }
  .hero__content { padding: var(--sp-5) var(--sp-4) var(--sp-6); gap: var(--sp-3); }
  .hero__desc { -webkit-line-clamp: 2; font-size: var(--fs-base); }
  .hero__cta { margin-top: 0; }
  .hero__cta .btn { flex: 1 1 auto; }

  .filter-bar { gap: var(--sp-2); }
  .filter-bar__label { display: none; }

  .footer-top { grid-template-columns: 1fr 1fr; gap: var(--sp-6); padding-block: var(--sp-8) var(--sp-6); }
  .footer-bottom { flex-direction: column; align-items: flex-start; }

  .rail { grid-auto-columns: minmax(158px, 1fr); gap: var(--sp-3); }

  /* 16:9 on a portrait phone leaves a ~220px-tall stage — too short for most
     games' own menus. Give the running game a taller box instead; games size
     themselves to whatever they are handed. */
  .player__stage { aspect-ratio: 3 / 4; }
  .player.is-placeholder .player__stage { aspect-ratio: auto; min-height: 0; padding-block: var(--sp-5); }
  .player__placeholder { padding: var(--sp-4) var(--sp-3); gap: var(--sp-3); }
  .player__logo { width: 48px; height: 48px; }
  .player__title { font-size: var(--fs-md); }
  .player__bar { padding: var(--sp-2) var(--sp-3); }
  .player-btn span { display: none; }
  .player-btn { padding: 0.45rem 0.6rem; }
  .player-btn.player-btn--wide span { display: inline; }

  .empty { padding: var(--sp-10) var(--sp-4); }
  .stat { padding: var(--sp-3); }

  /* Long button labels wrap rather than widen the page on narrow screens. */
  .btn-lg { white-space: normal; }
}

/* Small phones */
@media (max-width: 400px) {
  :root { --card-min: 140px; }

  /* A fourth icon button pushes the header past the viewport at 320px.
     My Library stays one tap away in the drawer. */
  .library-btn { display: none; }
  .cat-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .game-strip-item__thumb { width: 62px; }
}

/* The search toggle only exists on small screens */
.search-toggle { display: none; }

/* ==========================================================================
   7. Print — a courtesy, keeps game info readable on paper
   ========================================================================== */
@media print {
  .site-header, .site-footer, .player__bar, .game-side, .scrim, .drawer { display: none !important; }
  body { background: #fff; color: #000; }
}

/* ==========================================================================
   GAME DETAIL PAGE — hero · focused gameplay · related · information
   ========================================================================== */

/* --- 1. Hero -------------------------------------------------------------
   The game shown large enough to judge, beside the one decision the page is
   asking for. Deliberately not the game embedded small. */
.gd-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: var(--sp-8);
  align-items: center;
  padding: var(--sp-6) 0 var(--sp-2);
}

.gd-hero__media {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-xl);
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--border);
  box-shadow: var(--sh-lg);
}

.gd-hero__art { width: 100%; height: 100%; object-fit: cover; }

/* The clip layers over the art, exactly as on a card: if it never arrives,
   the art is what stays. */
.gd-hero__media video.preview {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 260ms var(--ease);
  pointer-events: none;
}
.gd-hero__media.is-previewing video.preview { opacity: 1; }

.gd-hero__live {
  position: absolute;
  left: var(--sp-4);
  bottom: var(--sp-4);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0.25rem 0.6rem;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--bg) 70%, transparent);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border-strong);
  color: var(--text-soft);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.gd-hero__soon { position: absolute; top: var(--sp-4); left: var(--sp-4); }

.gd-hero__body { min-width: 0; display: grid; gap: var(--sp-4); align-content: center; }
.gd-hero__back { justify-self: start; }
.gd-hero__title { font-size: var(--fs-3xl); line-height: var(--lh-tight); }
.gd-hero__meta { margin-top: 0; }

.gd-hero__desc {
  color: var(--text-soft);
  font-size: var(--fs-md);
  line-height: 1.65;
  max-width: 56ch;
}

.gd-hero__actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: center; }
.gd-hero__actions > * { min-width: 0; }
.gd-hero__play { box-shadow: var(--sh-glow); }
.gd-hero__note { color: var(--text-faint); font-size: var(--fs-xs); }
.btn.is-disabled { opacity: 0.55; pointer-events: none; }

/* --- 2. Focused gameplay -------------------------------------------------
   PLAY NOW promotes the player to the whole viewport. The game gets every
   pixel that is not a control, and the page behind it cannot scroll. */
html.is-playing, html.is-playing body { overflow: hidden; }

/* `main` carries the page-entry animation, and its `both` fill-mode leaves an
   IDENTITY transform behind when the animation ends. Any transform — even
   `matrix(1,0,0,1,0,0)` — makes that element the containing block for every
   `position: fixed` descendant, which pinned the focused player to the page
   box (starting below the sticky header, 2800px tall) instead of the viewport.
   Clearing it while playing gives the player the viewport it asks for. */
body.is-playing #main {
  animation: none !important;
  transform: none !important;
  will-change: auto !important;
}

body.is-playing #player {
  position: fixed;
  inset: 0;
  z-index: 120;
  border: 0;
  border-radius: 0;
  display: flex;
  flex-direction: column;
  background: #05070b;
  animation: ph-fade var(--t-mid) var(--ease) both;
}

/* The toolbar sits above the game, so the game is never under a floating bar. */
body.is-playing .player__bar--top {
  flex: 0 0 auto;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  border-top: 0;
  padding-block: var(--sp-2);
}

/* The stage takes the rest, and the iframe is centred inside it at the game's
   own aspect ratio — filling the space without stretching the game. */
body.is-playing .player__stage {
  flex: 1 1 auto;
  aspect-ratio: auto;
  min-height: 0;
  height: auto;
  padding: 0;
  display: grid;
  place-items: center;
}

body.is-playing .player__frame {
  position: relative;
  inset: auto;
  width: min(100%, calc((100vh - 56px) * var(--game-aspect, 16 / 9)));
  height: min(100%, calc(100vw / (var(--game-aspect, 16 / 9))));
  max-width: 100%;
  max-height: 100%;
  border-radius: 0;
}

/* Portrait games get a portrait box rather than a letterboxed landscape one. */
body.is-playing #player[data-orientation="portrait"] { --game-aspect: 3 / 4; }

.player-btn--exit { margin-left: var(--sp-2); }
.player-btn--exit:hover { background: color-mix(in srgb, var(--danger) 30%, transparent); }

/* --- 4. Information ------------------------------------------------------ */
.gd-info {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
  gap: var(--sp-8);
  align-items: start;
}
.gd-info__main { min-width: 0; display: grid; gap: var(--sp-5); align-content: start; }
.gd-info__main > * { min-width: 0; }

/* ---------------------------------------------------------------- tablet -- */
@media (max-width: 1080px) {
  .gd-hero { grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); }
  .gd-hero__title { font-size: var(--fs-2xl); }
  .gd-info { grid-template-columns: minmax(0, 1fr); }
  .game-side { position: static; }
}

/* ----------------------------------------------------------------- phone -- */
@media (max-width: 720px) {
  .gd-hero { padding-top: var(--sp-4); gap: var(--sp-5); }
  .gd-hero__desc { font-size: var(--fs-base); }
  .gd-hero__actions .gd-hero__play { flex: 1 1 100%; justify-content: center; }

  /* A phone is portrait, so a 16:9 box would hand the game a third of the
     screen and leave the rest black — the "tiny rectangle" this view exists
     to avoid. Both games lay themselves out responsively, and an iframe is a
     viewport rather than an image, so filling it stretches nothing: the game
     simply gets the whole screen and reflows into it. */
  body.is-playing .player__frame {
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
  }

  /* On a phone the toolbar is compact so the game keeps the screen. */
  body.is-playing .player__bar--top { padding: var(--sp-2) var(--sp-3); }
  body.is-playing .player__bar-title { font-size: var(--fs-xs); }
  body.is-playing .player-btn span { display: none; }
  body.is-playing .player-btn { padding: 0.5rem 0.6rem; }
  body.is-playing .player-btn--wide span { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  body.is-playing #player { animation: none; }
  .gd-hero__media video.preview { transition: none; }
}

/* ==========================================================================
   PORTAL SHELL — top bar · permanent sidebar · content column
   --------------------------------------------------------------------------
   The whole platform now sits in one application layout. It is assembled by
   js/app.js from the two slots every page already had, so no page owns a
   copy of it.

       ≥1200px   full rail, labels and counts
       900–1199  compact icon rail — labels are clipped, not removed, so the
                 links keep their accessible names
       <900px    the rail becomes the slide-out drawer, opened by the burger

   THE RAIL IS ONE ELEMENT IN ALL THREE STATES. There is no second mobile
   menu to fall out of step with it.
   ========================================================================== */

:root {
  --rail-w: 246px;
  --rail-w-compact: 76px;
}

/* --- Top bar -------------------------------------------------------------- */

.header-inner {
  padding-inline: var(--gutter);
  gap: var(--sp-4);
}

/* The search field is the widest thing in the bar, because searching is the
   fastest way to find a game and the bar exists mostly to hold it. */
.search-field--wide {
  flex: 1 1 auto;
  width: auto;
  max-width: 560px;
  margin-right: auto;
}

.search-field--wide .search-field__input {
  padding-block: 0.7rem;
  font-size: var(--fs-base);
}

.menu-wrap { position: relative; }

.menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  z-index: 120;
  width: 244px;
  padding: var(--sp-2);
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  box-shadow: var(--sh-lg);
  display: grid;
  gap: 2px;
  animation: ph-fade var(--t-fast) var(--ease) both;
}
.menu[hidden] { display: none; }

.menu__head {
  display: grid;
  gap: 2px;
  padding: var(--sp-3) var(--sp-3) var(--sp-3);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--sp-2);
}
.menu__head strong { font-size: var(--fs-sm); }
.menu__head span { font-size: var(--fs-2xs); color: var(--text-faint); }

.menu__item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: 0.55rem var(--sp-3);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  color: var(--text-soft);
  text-align: left;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.menu__item:hover { background: var(--surface-2); color: var(--text); }
.menu__item .ico { color: var(--text-faint); flex: 0 0 auto; }
.menu__item:hover .ico { color: var(--primary); }
.menu__sep { height: 1px; background: var(--border); margin: var(--sp-2) 0; }

/* --- The grid ------------------------------------------------------------- */

.app-shell {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  align-items: start;
}

.app-col { min-width: 0; }

/* Inside the shell the content column has its own gutter, so `.shell` only
   has to cap the line length of very wide screens. */
body.has-shell .app-col .shell { max-width: 1480px; }

/* --- The rail ------------------------------------------------------------- */

.sidenav {
  position: sticky;
  top: var(--header-h);
  height: calc(100vh - var(--header-h));
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  background: var(--bg-elevated);
}

/* The logo and close button are the drawer's header — on desktop the logo is
   already in the top bar, so this is hidden. */
.sidenav__head { display: none; }

.sidenav__scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-4) var(--sp-3) var(--sp-6);
  display: flex;
  flex-direction: column;
  gap: 2px;
  scrollbar-width: thin;
}

.sidenav__heading {
  padding: var(--sp-4) var(--sp-3) var(--sp-2);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}

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

.sidenav__link {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 0.58rem var(--sp-3);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-dim);
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}

.sidenav__link:hover { background: var(--surface-2); color: var(--text); }

.sidenav__link.is-active {
  background: var(--primary-soft);
  color: var(--primary);
}

/* The active marker is a bar on the rail edge, not a coloured pill alone —
   it reads at a glance from anywhere in the list. */
.sidenav__link.is-active::before {
  content: "";
  position: absolute;
  left: calc(var(--sp-3) * -1 + 2px);
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 18px;
  border-radius: 0 3px 3px 0;
  background: var(--primary);
}

.sidenav__ico {
  flex: 0 0 auto;
  width: 22px;
  display: grid;
  place-items: center;
  color: inherit;
}
.sidenav__emoji { font-size: 15px; line-height: 1; filter: saturate(1.1); }
.sidenav__label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; }

.sidenav__count {
  flex: 0 0 auto;
  min-width: 20px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--surface-3);
  color: var(--text-faint);
  font-size: var(--fs-2xs);
  font-weight: 700;
  text-align: center;
  line-height: 18px;
}
.sidenav__link.is-active .sidenav__count { background: var(--primary-soft); color: var(--primary); }
.sidenav__count:empty { display: none; }

.sidenav__cat .sidenav__label { font-weight: 500; }

.sidenav__all {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
  padding: 0.55rem var(--sp-3);
  border-radius: var(--r-md);
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent);
}
.sidenav__all:hover { background: var(--surface-2); }

.sidenav__foot {
  margin-top: auto;
  padding: var(--sp-5) var(--sp-3) 0;
  font-size: var(--fs-2xs);
  color: var(--text-faint);
}

/* --- Compact rail (small laptops and tablets) ----------------------------- */

@media (max-width: 1199px) and (min-width: 900px) {
  .app-shell { grid-template-columns: var(--rail-w-compact) minmax(0, 1fr); }

  .sidenav__scroll { padding-inline: var(--sp-2); align-items: stretch; }

  /* A group heading becomes a hairline: "YOUR GAMES" wraps to two lines in a
     76px rail and reads as clutter. The text stays in the DOM, pushed out of
     view, so the grouping is still announced. */
  .sidenav__heading {
    height: 1px;
    margin: var(--sp-4) var(--sp-3) var(--sp-2);
    padding: 0;
    background: var(--border);
    overflow: hidden;
    text-indent: -9999px;
    white-space: nowrap;
  }
  .sidenav__link { justify-content: center; padding-inline: 0; }

  /* Clipped, not removed: the link keeps its accessible name. */
  .sidenav__label,
  .sidenav__count,
  .sidenav__all span,
  .sidenav__foot {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .sidenav__all { justify-content: center; }
  .sidenav__link.is-active::before { left: -2px; }
}

/* --- Drawer state (phones and small tablets) ------------------------------ */

@media (max-width: 899px) {
  .app-shell { grid-template-columns: minmax(0, 1fr); }

  .sidenav {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 140;
    width: min(84vw, 300px);
    height: 100%;
    border-right: 1px solid var(--border);
    box-shadow: var(--sh-lg);
    /* Translated off to the LEFT, so unlike a right-hand drawer it cannot
       extend the document's scroll width and reintroduce sideways scrolling.
       `visibility` keeps the closed rail out of the tab order. */
    transform: translateX(-101%);
    visibility: hidden;
    transition: transform var(--t-slow) var(--ease-out), visibility 0s linear var(--t-slow);
  }

  .sidenav.is-open {
    transform: none;
    visibility: visible;
    transition: transform var(--t-slow) var(--ease-out), visibility 0s;
  }

  .sidenav__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-3);
    border-bottom: 1px solid var(--border);
  }

  .burger { display: block; }
}

@media (min-width: 900px) {
  /* The rail is the navigation above this width, so the burger has no job. */
  .burger { display: none !important; }
}

/* --- Playing: the shell gets out of the way ------------------------------- */

/* Same trap as `main`: any transform on an ancestor — including an identity
   one left behind by an animation's `both` fill-mode — would make that
   element the containing block for the fixed-position player and pin the
   focused game inside the content column instead of the viewport. */
body.is-playing .app-shell,
body.is-playing .app-col {
  animation: none !important;
  transform: none !important;
  filter: none !important;
  will-change: auto !important;
}

/* The rail is sticky, which paints above nothing but still occupies its
   column; the focused player is fixed at a higher layer and covers it. This
   simply removes it from the reading order of a screen that is now a game. */
body.is-playing .sidenav,
body.is-playing .site-header { visibility: hidden; }

/* --- The bar below the rail breakpoint ------------------------------------
   The burger, the logo and four actions already fill a narrow bar, so the
   search field collapses to its icon and opens as a full-width row under the
   bar — the same behaviour phones had, starting at the width where it is
   actually needed rather than 260px later. */

@media (max-width: 899px) {
  .search-field--wide { display: none; }
  .search-toggle { display: grid !important; }

  .site-header.is-searching .search-field--wide {
    display: flex;
    position: absolute;
    top: 100%;
    left: var(--gutter);
    right: var(--gutter);
    width: auto;
    max-width: none;
    padding-bottom: var(--sp-3);
  }
  .site-header.is-searching .search-pop { left: var(--gutter); right: var(--gutter); width: auto; }

  /* Switching theme is in the player menu below this width, so the standalone
     button would be a second control doing the same job in a bar with no room
     for it. */
  .theme-toggle { display: none; }
}
