/*
 * The base layer, moved out of a <style> block in index.html.
 *
 * It was not dead: nineteen selectors live only here — `*` box-sizing, `html`,
 * `a`, `.wrap`, `.shot`, `.lede`, `.thumb img`, `:focus-visible`, the footer
 * nav — and the page falls apart without them. The two were layered, with the
 * <link> after the <style> so the rules below won.
 *
 * Moved rather than merged, and placed ABOVE the home rules, so the cascade is
 * byte-for-byte what it was with two stylesheets: base first, overrides after.
 * One file to edit, 21KB off every HTML response, and it caches separately from
 * the document now.
 *
 * Relative urls still resolve the same way: index.html sits at the site root
 * and so does this file, so `assets/fonts/...` means the same thing in both.
 */

/*
 * Everything is inline and hand-written, on purpose.
 *
 * This page exists to be found and to load before somebody gives up. It
 * is the opposite problem from the games it links to: those are 1.8MB of
 * Babylon and a WASM physics runtime and they can afford a loading
 * screen, because by then a player has chosen to be there. A visitor
 * arriving cold from a search result has not chosen anything yet, and a
 * framework, a font request and a stylesheet round-trip are three
 * reasons to leave before the first paint.
 *
 * So: no build step, no dependencies, one request for the document and
 * one per thumbnail. It is also why the ad slots below reserve their
 * height — see the note there.
 */

/* ---- Tokens -------------------------------------------------------
 * This is the shared arcade language used by Nightline, Chainshift and
 * Ghost Circuit: deep green-black structure, warm bone type, hard
 * outlines and one clear accent per action or game.
 */
/*
 * Self-hosted, and a variable file so one 34KB download covers every
 * weight on the page.
 *
 * The display type used to be `Impact, Haettenschweiler, "Arial Narrow
 * Bold", sans-serif` — an unhosted system stack, so the wordmark was a
 * different typeface on Windows, macOS and Linux and the page had no say
 * in it. Replacing it with `system-ui` fixed the arcade-poster look and
 * kept the inconsistency.
 *
 * Self-hosted rather than linked from Google Fonts, which is how
 * apps/client still loads Inter: a font request to a third party hands
 * them the visitor's IP and user agent on every page load, and this
 * arcade's privacy page says there are no third-party identifiers.
 *
 * `font-display: swap` so the text is readable while it arrives — the
 * fallback below is metrically close enough that the swap does not
 * reflow the hero.
 */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("assets/fonts/archivo-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
    U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/*
 * Paper, not a void.
 *
 * The arcade's six oldest games all ground out between 0.002 and 0.007
 * relative luminance, and this page sat at 0.007 with them — so the shelf
 * looked like a thirteenth game, and twelve thumbnails with twelve
 * different palettes all arrived glowing on the same black. Shunt and
 * Marginalia were deliberately lightened to break that family; the
 * catalogue had not been.
 *
 * The hero keeps a dark veil, because it is a photograph and wants one.
 * Everything below it is paper, and the games are the only saturated
 * things on the page.
 */
:root {
  color-scheme: light;
  --ground: #f4efe4;
  --surface: #fffdf8;
  --surface-lift: #e9e1d2;
  --line: #d8cfbc;
  --rule: #2b2824;
  --text: #1c1a16;
  --text-dim: #6a6456;
  --on-accent: #fffdf8;
  --bar: rgba(244, 239, 228, 0.94);
  --hero-veil: rgba(16, 20, 18, 0.5);
  /* Darkened from the old neon set, which was mixed for black and is
     unreadable on paper: #ffd247 on #f4efe4 is a 1.3:1 contrast. */
  --accent: #a8740a;
  --accent-warm: #c2402b;
  --live: #0d7260;
  --violet: #6244c4;
  --blue: #2b65bd;
  --display: "Archivo", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --body: ui-monospace, "SFMono-Regular", Consolas, monospace;
  --text-face: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* How dark a game's accent is mixed. The hue belongs to the game and
     is set on the card; only the lightness is a property of the page, so
     one number re-mixes all twelve for the other scheme. Fixed hexes mixed
     for paper came out at 1.6:1 to 2.3:1 on the dark surface. */
  --card-l: 31%;
  --radius: 6px;
  --gutter: clamp(16px, 4vw, 40px);
  --maxw: 1240px;
}

/* Dark stays available for anyone who asks for it at the OS level — the
   arcade bar already defined light tokens while this page had none, so the
   two disagreed. */
/*
 * No dark scheme, deliberately.
 *
 * There was one here, and it stopped working the moment the home rules below
 * landed: they set `:root` unconditionally and later in the cascade, so every
 * token this block set was overridden and the page stayed light under
 * `prefers-color-scheme: dark` while still carrying ~40 lines that looked like
 * they did something.
 *
 * Removed rather than resurrected, because the home layer declares
 * `color-scheme: light` on purpose and its palette IS a light ground with dark
 * cards — restoring the old dark tokens would have mixed two designs. If a dark
 * scheme is wanted it needs its own pass over the home palette, after this
 * comment, not before it.
 */

*,
*::before,
*::after {
  box-sizing: border-box;
  letter-spacing: 0;
}

html {
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

.wrap {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: var(--gutter);
}

a {
  color: inherit;
  text-decoration: none;
}
:focus-visible {
  outline: 3px solid var(--live);
  outline-offset: 4px;
}

/* ---- Header ------------------------------------------------------ */
header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--bar);
  border-bottom: 1px solid var(--line);
}
.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  height: 68px;
}
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--display);
  font-weight: 900;
  font-size: 1.35rem;
  text-transform: uppercase;
}
.brand .mark {
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--accent-warm);
  display: grid;
  place-items: center;
  color: var(--on-accent);
  font-size: 18px;
  font-weight: 900;
  flex: none;
}
.brand-accent {
  color: var(--accent);
}
nav.links {
  display: flex;
  align-items: stretch;
  gap: 26px;
  color: var(--text-dim);
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
}
nav.links a {
  display: grid;
  align-items: center;
  border-bottom: 3px solid transparent;
}
nav.links a:hover {
  color: var(--text);
  border-color: var(--live);
}
@media (max-width: 640px) {
  nav.links {
    display: none;
  }
}

/* ---- Hero -------------------------------------------------------- */
/*
 * The hero is the page's one dark band, because it is a photograph and a
 * photograph wants a dark mount. So it carries its own tokens rather than
 * inheriting the paper ones — without this the wordmark, the lede and the
 * stat strip all went ink-on-night and vanished the moment the page was
 * lightened. The accents go back to their bright mixes here too: the
 * darkened amber is for paper, and on a night scene it reads as mud.
 */
.hero {
  --text: #f7f2e7;
  --text-dim: #ddd5c3;
  --line: rgba(247, 242, 231, 0.42);
  --surface: rgba(16, 20, 18, 0.46);
  --surface-lift: rgba(16, 20, 18, 0.66);
  --accent: #ffd247;
  --accent-warm: #ff6652;
  --live: #57dfca;
  --on-accent: #101412;
  color: var(--text);
  position: relative;
  isolation: isolate;
  width: 100vw;
  min-height: 500px;
  margin-left: calc(50% - 50vw);
  padding: 62px max(var(--gutter), calc((100vw - var(--maxw)) / 2 + var(--gutter))) 52px;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  border-bottom: 1px solid var(--rule);
  background: #101412;
}
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--hero-veil);
}
.hero > div:first-child {
  position: relative;
  z-index: 2;
  width: min(760px, 100%);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--live);
  color: var(--on-accent);
  font-size: 0.7rem;
  font-weight: 900;
  text-transform: uppercase;
}
.dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--on-accent);
  animation: pulse 1.5s steps(2, end) infinite;
}
@keyframes pulse {
  50% {
    opacity: 0.25;
  }
}

h1 {
  max-width: 720px;
  margin: 20px 0 16px;
  font-family: var(--display);
  font-size: 5.25rem;
  line-height: 0.86;
  font-weight: 900;
  text-transform: uppercase;
  text-shadow: 5px 5px 0 rgba(16, 20, 18, 0.9);
}
h1 .grad {
  color: var(--accent);
}
.lede {
  font-family: var(--text-face);
  margin: 0 0 24px;
  color: var(--text);
  font-size: 1rem;
  max-width: 58ch;
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 46px;
  padding: 11px 18px;
  border: 1px solid var(--line);
  border-radius: 3px;
  font-family: var(--display);
  font-weight: 900;
  font-size: 1rem;
  line-height: 1;
  text-transform: uppercase;
  transition:
    transform 80ms ease,
    box-shadow 80ms ease,
    background 120ms ease;
}
.btn-primary {
  background: var(--accent-warm);
  color: var(--on-accent);
}
.btn-primary:hover {
  transform: translate(-2px, -2px);
}
.btn-ghost {
  background: var(--surface);
  color: var(--text);
}
.btn-ghost:hover {
  background: var(--surface-lift);
  transform: translate(-2px, -2px);
}
.btn:active {
  transform: translate(3px, 3px);
}

.facts {
  display: inline-grid;
  grid-auto-flow: column;
  margin-top: 28px;
  border-block: 1px solid var(--line);
  color: var(--text);
  font-size: 0.68rem;
  text-transform: uppercase;
}
.facts div {
  min-width: 118px;
  padding: 8px 18px 9px;
  border-right: 1px solid var(--line);
}
.facts div:first-child {
  border-left: 1px solid var(--line);
}
.facts b {
  display: block;
  color: var(--accent);
  font-family: var(--display);
  font-size: 1.45rem;
  line-height: 1;
  font-weight: 900;
}

/* The hero art. A real frame from the game, not a mockup — a portal
   that illustrates itself with stock art tells a visitor the games are
   somebody else's. */
.shot {
  position: absolute;
  inset: 0;
  z-index: -2;
  background: var(--surface);
}
.shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 52%;
  display: block;
  filter: saturate(1.08) contrast(1.08) brightness(0.72);
}
.shot::after {
  display: none;
}

/* ---- Sections ---------------------------------------------------- */
section {
  padding-block: 58px;
  scroll-margin-top: 78px;
}
.sec-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 26px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
h2 {
  margin: 0;
  font-family: var(--display);
  font-size: 2.4rem;
  line-height: 0.95;
  font-weight: 900;
  text-transform: uppercase;
}
.sec-head p {
  margin: 0;
  color: var(--live);
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
}

/* ---- Game grid ---------------------------------------------------- */
.grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 30px 28px;
}
.card {
  grid-column: span 2;
}
@media (max-width: 900px) {
  .grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .card {
    grid-column: auto;
  }
}
@media (max-width: 680px) {
  .grid {
    grid-template-columns: 1fr;
  }
}

.card {
  --card-accent: hsl(var(--card-hue, 8) 62% var(--card-l));
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  /* The one saturated thing on a card, and it belongs to the game rather
     than to the card's position in the grid — which is what the old
     `nth-child(4n + 2)` rotation gave it. */
  border-top: 3px solid var(--card-accent);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(28, 26, 22, 0.05);
  transition:
    transform 100ms ease,
    box-shadow 100ms ease;
}
.card:hover {
  transform: translate(-3px, -3px);
  box-shadow: 0 10px 26px rgba(28, 26, 22, 0.13);
}
.thumb {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--surface-lift);
  border-bottom: 1px solid var(--line);
}
.thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 180ms ease;
}
.card:hover .thumb img {
  transform: scale(1.025);
}
.badge {
  position: absolute;
  top: 10px;
  left: 10px;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-radius: 2px;
  font-size: 0.64rem;
  font-weight: 900;
  text-transform: uppercase;
  background: var(--card-accent);
  color: var(--on-accent);
}
.body {
  padding: 17px 18px 19px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}
.body h3 {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--display);
  font-size: 1.65rem;
  line-height: 1;
  font-weight: 900;
  text-transform: uppercase;
}
.body h3::after {
  content: "→";
  color: var(--card-accent);
  font-family: var(--body);
  font-size: 1.25rem;
}
.body p {
  margin: 0;
  font-family: var(--text-face);
  color: var(--text-dim);
  font-size: 0.8rem;
  flex: 1;
}
.tags {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 4px;
}
.tag {
  padding: 3px 7px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: transparent;
  color: var(--text-dim);
  font-size: 0.62rem;
  font-weight: 800;
  text-transform: uppercase;
}

/* The whole card is the link, but only the title is the accessible
   name — a screen reader announcing the description as the link text
   makes a list of games unusable to navigate. */
.card .stretch::after {
  content: "";
  position: absolute;
  inset: 0;
}

/*
 * Pinned to the hero's bottom corner, not left in the flow.
 *
 * `.shot` is an absolutely positioned full-bleed layer at `z-index: -2`,
 * so a paragraph dropped inside it lands behind the veil and off the
 * bottom of the image — present in the DOM, invisible on the page. This
 * sits in the hero's own stacking context instead.
 */
.shot-credit {
  position: absolute;
  right: max(var(--gutter), calc((100vw - var(--maxw)) / 2 + var(--gutter)));
  bottom: 16px;
  z-index: 1;
  margin: 0;
  /* Not --text-dim: this sits over a photograph behind a veil rather than
     on the page ground, and the shared dim measured 3.90:1 at 10px. */
  color: #c3ccda;
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
@media (max-width: 680px) {
  .shot-credit { display: none; }
}

footer {
  border-top: 1px solid var(--line);
  margin-top: 54px;
  padding-block: 30px 40px;
  background: var(--surface-lift);
  color: var(--text-dim);
  font-size: 0.72rem;
  text-transform: uppercase;
}
.foot {
  display: flex;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}
.foot nav {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
}
.foot a:hover {
  color: var(--accent);
}

#multiplayer,
#about {
  max-width: 900px;
  padding-left: 24px;
  border-left: 8px solid var(--accent-warm);
}
#about {
  border-color: var(--live);
}
#multiplayer .sec-head,
#about .sec-head {
  border-bottom: 0;
  margin-bottom: 12px;
  padding-bottom: 0;
}
#multiplayer .lede,
#about .lede {
  color: var(--text-dim);
  margin-bottom: 0;
}

@media (max-width: 860px) {
  .hero {
    min-height: 520px;
    padding-block: 54px 46px;
  }
  h1 {
    font-size: 4rem;
  }
}

@media (max-width: 680px) {
  .bar {
    height: 62px;
  }
  .brand {
    font-size: 1.08rem;
  }
  .brand .mark {
    width: 30px;
    height: 30px;
    font-size: 16px;
  }
  .bar > .btn {
    min-height: 38px;
    padding: 8px 10px;
    font-size: 0.78rem;
    }
  .hero {
    min-height: 540px;
    padding: 46px var(--gutter) 38px;
  }
  .hero::after {
    background: var(--hero-veil);
  }
  .shot img {
    object-position: 57% center;
  }
  h1 {
    font-size: 3.2rem;
    text-shadow: 2px 2px 0 var(--hero-veil);
  }
  .lede {
  font-family: var(--text-face);
    font-size: 0.88rem;
  }
  .cta-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
  }
  .btn {
    min-width: 0;
    padding-inline: 10px;
    font-size: 0.85rem;
  }
  .facts {
    width: 100%;
    grid-auto-flow: initial;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .facts div {
    min-width: 0;
    padding-inline: 9px;
  }
  section {
    padding-block: 44px;
  }
  .sec-head {
    align-items: flex-start;
    flex-direction: column;
    gap: 7px;
  }
  h2 {
    font-size: 2rem;
  }
  .grid {
    gap: 25px;
  }
  #multiplayer,
  #about {
    padding-left: 16px;
    border-left-width: 6px;
  }
}

@media (max-width: 420px) {
  .hero {
    min-height: 520px;
  }
  h1 {
    font-size: 2.7rem;
  }
  .eyebrow {
    font-size: 0.61rem;
  }
  .cta-row {
    grid-template-columns: 1fr;
  }
  .facts {
    font-size: 0.56rem;
  }
  .facts b {
    font-size: 1.2rem;
  }
}

/* ---- home: the current look, overriding the base above ---- */

/*
 * Caper Arcade home: the games light the room.
 *
 * This was a near-white page with dark cards on it, and the verdict on it was
 * "doesn't look like a gaming website", which is exactly right. A cream ground
 * with earth-toned accents and a column of body copy is the visual language of
 * a magazine or a product page; it was calm, and calm is not what an arcade is
 * selling.
 *
 * The irony worth recording: it was light because an earlier round of feedback
 * said the site was too dark, and that correction went too far. The answer is
 * not the old near-black — `arcade-visual-dna` has near-black ground as the
 * first of the traits that made six games look like one game. This is a deep
 * blue-black with the card surface LIFTED off it rather than dropped into it,
 * so twelve screenshots are the brightest things on the page and the chrome
 * gets out of their way.
 *
 * Contrast is checked against the new ground below, not inherited: every value
 * in the old palette was mixed for paper and most of them invert badly.
 */
:root {
  color-scheme: dark;
  --ground: #0b0e15;
  /* Lifted off the ground, not dropped into it: a card has to read as an
     object sitting on the page, and at the old #101311 it was darker than
     the ground it sat on and the whole grid disappeared. */
  --surface: #161c27;
  --surface-lift: #1f2634;
  --line: #2b3444;
  --rule: #e9eef7;
  --text: #eef2f9;
  --text-dim: #9aa6bb;
  --on-accent: #0a0d13;
  --bar: rgba(11, 14, 21, 0.86);
  --hero-veil: rgba(5, 7, 11, 0.52);
  /* The lime survives the flip and is the one thing worth keeping from the
     light palette: 13.4:1 on this ground, and it already read as the brand. */
  --accent: #b9ef3d;
  --accent-warm: #ff7a62;
  /* Both of these were mixed dark for paper and vanish here. */
  --live: #3ddc97;
  --violet: #a78bfa;
  --blue: #5aa2ff;
  /* Card hues are mixed light now rather than dark: the comment on the base
     palette said one number re-mixes all twelve for the other scheme, and
     this is that number doing it. */
  --card-l: 63%;
  --radius: 8px;
  --gutter: clamp(18px, 3vw, 42px);
  --maxw: 1240px;
}

body {
  min-height: 100%;
  background: var(--ground);
  color: var(--text);
  font-family: var(--display);
}

header {
  background: var(--bar);
  border-bottom: 1px solid rgba(23, 26, 24, 0.1);
  backdrop-filter: blur(18px) saturate(125%);
}

.bar { height: 66px; }

.brand {
  gap: 10px;
  color: var(--text);
  font-size: 1rem;
}

.brand .mark {
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 5px;
  background: var(--accent);
  color: var(--on-accent);
}

/* 4.81:1 on the bar. The lime this came from (#618f00) is 3.72:1 at 16px/900,
   which is under AA because 16px bold is not "large text" — that needs 18.66px. */
.brand-accent { color: var(--accent); }

nav.links {
  height: 66px;
  gap: 28px;
  color: #9aa6bb;
  font-size: 0.68rem;
  letter-spacing: 0;
}

nav.links a:hover {
  color: var(--text);
  border-color: var(--accent-warm);
}

.btn {
  min-height: 42px;
  border: 1px solid rgba(23, 26, 24, 0.14);
  border-radius: 6px;
  box-shadow: none;
  font-size: 0.8rem;
  letter-spacing: 0;
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.btn-primary:hover,
.btn-ghost:hover { transform: translateY(-2px); }

.btn-ghost {
  background: rgba(255, 255, 255, 0.1);
  border-color: rgba(255, 255, 255, 0.22);
  color: #f6f8f4;
}

main > .wrap { padding-top: 34px; }

/*
 * The one sentence that says what this site is.
 *
 * It was 0.93rem of #656c67 sitting above a hero with 80px type in it, which
 * made the only positioning statement on the page the least visible thing on
 * it: a first-time visitor met a giant VAULT RUSH and had to squint at grey
 * 15px text to learn this is an arcade of twelve games rather than one game's
 * site. Sized and coloured to be read before the hero rather than after it.
 */
.library-intro {
  margin: 0 0 22px;
  max-width: 54ch;
  color: var(--text);
  font-family: var(--text-face);
  font-size: 1.22rem;
  line-height: 1.45;
  letter-spacing: -0.01em;
}

@media (max-width: 680px) {
  .library-intro {
    font-size: 1.05rem;
    margin-bottom: 16px;
  }
}

.hero {
  width: 100%;
  min-height: clamp(420px, 52vh, 540px);
  margin: 0;
  padding: clamp(34px, 5vw, 68px);
  align-items: center;
  border: 0;
  border-radius: var(--radius);
  background: #0d100e;
  box-shadow: 0 22px 50px rgba(20, 24, 21, 0.16);
}

.hero::after {
  background:
    linear-gradient(90deg, rgba(6, 8, 7, 0.97) 0%, rgba(6, 8, 7, 0.82) 39%, rgba(6, 8, 7, 0.08) 76%),
    linear-gradient(0deg, rgba(6, 8, 7, 0.34), transparent 56%);
}

.hero > div:first-child { width: min(610px, 100%); }

.eyebrow {
  padding: 0;
  border: 0;
  background: transparent;
  color: rgba(246, 248, 244, 0.72);
  font-size: 0.67rem;
  letter-spacing: 0;
}

.dot {
  background: var(--accent);
  box-shadow: 0 0 14px rgba(185, 239, 61, 0.78);
}

h1 {
  max-width: 600px;
  margin: 18px 0;
  color: #f7f9f5;
  font-size: clamp(4.4rem, 8vw, 7.5rem);
  line-height: 0.77;
  letter-spacing: 0;
  text-shadow: none;
}

h1 .grad {
  display: block;
  color: #ffd35a;
}

.hero .lede {
  max-width: 56ch;
  margin-bottom: 26px;
  color: rgba(246, 248, 244, 0.78);
  font-size: 0.95rem;
  line-height: 1.56;
}

.facts {
  margin-top: 28px;
  border-color: rgba(246, 248, 244, 0.18);
  color: rgba(246, 248, 244, 0.53);
  letter-spacing: 0;
}

.facts div,
.facts div:first-child { border-color: rgba(246, 248, 244, 0.15); }
.facts b { color: #f7f9f5; }

.shot img {
  object-position: center;
  filter: saturate(1.08) contrast(1.08) brightness(0.8);
}

.shot-credit {
  /* 0.42 alpha measured 3.90:1 at 10px over the hero. It also sits over a
     photograph, so the real figure moves with whatever is behind it — worth
     more headroom than the 4.5 line, not less. */
  color: rgba(246, 248, 244, 0.72);
  letter-spacing: 0;
}

section { padding-block: 48px; }

.sec-head {
  align-items: center;
  margin-bottom: 22px;
  padding-bottom: 14px;
  border-bottom: 1px solid rgba(23, 26, 24, 0.13);
}

h2 {
  font-size: clamp(2rem, 3.5vw, 3rem);
  letter-spacing: 0;
}

/* 4.62:1 on the ground, up from 3.62:1. Same hue, darker: the lime reads as the
   brand at display sizes and fails AA the moment it carries 12px copy. */
.sec-head p {
  color: var(--accent);
  letter-spacing: 0;
}

.grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 22px;
}

.card {
  grid-column: auto;
  min-width: 0;
  background: #101311;
  border: 0;
  border-radius: var(--radius);
  box-shadow: 0 10px 28px rgba(20, 24, 21, 0.11);
}

.card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  width: 36%;
  height: 4px;
  background: var(--card-accent);
}

.card:hover {
  transform: translateY(-5px);
  box-shadow: 0 20px 42px rgba(20, 24, 21, 0.2);
}

.thumb {
  aspect-ratio: 16 / 9;
  border-bottom: 1px solid rgba(246, 248, 244, 0.1);
  background: #171b19;
}

.thumb::after {
  content: "";
  position: absolute;
  inset: auto 0 0;
  height: 36%;
  background: linear-gradient(transparent, rgba(9, 12, 10, 0.45));
  pointer-events: none;
}

.badge {
  top: 12px;
  left: 12px;
  z-index: 2;
  border: 0;
  border-radius: 4px;
  background: var(--card-accent);
  color: #0b0e0c;
  letter-spacing: 0;
}

.body {
  gap: 9px;
  min-height: 184px;
  padding: 17px 18px 18px;
}

.body h3 {
  color: #f7f9f5;
  font-size: 1.48rem;
  letter-spacing: 0;
}

.body h3::after {
  display: grid;
  width: 29px;
  height: 29px;
  place-items: center;
  border: 1px solid color-mix(in srgb, var(--card-accent), transparent 36%);
  border-radius: 50%;
  font-size: 1rem;
}

.body p {
  display: -webkit-box;
  overflow: hidden;
  color: #a6b2c6;
  font-size: 0.79rem;
  line-height: 1.48;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}

.tags {
  gap: 5px;
  margin-top: auto;
}

.tag {
  padding: 3px 6px;
  border-color: rgba(246, 248, 244, 0.14);
  border-radius: 3px;
  color: #94a2b8;
  font-size: 0.58rem;
  letter-spacing: 0;
}

#multiplayer,
#about {
  max-width: none;
  padding: 34px 0;
  border-top: 1px solid rgba(23, 26, 24, 0.12);
  border-left: 0;
}

#multiplayer .sec-head,
#about .sec-head { margin-bottom: 10px; }

#multiplayer .lede,
#about .lede { color: var(--text-dim); }

footer {
  margin-top: 18px;
  border-top: 1px solid rgba(23, 26, 24, 0.1);
  background: #121823;
  /* Lifted off the page ground so the footer reads as a slab, the same
     relationship the cards have. Dim text re-measured against it below. */

  color: #94a0b4;
  letter-spacing: 0;
}

@media (max-width: 920px) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hero::after {
    background:
      linear-gradient(90deg, rgba(6, 8, 7, 0.95), rgba(6, 8, 7, 0.48)),
      linear-gradient(0deg, rgba(6, 8, 7, 0.58), transparent);
  }
}

@media (max-width: 600px) {
  .bar { height: 58px; }
  .brand { font-size: 0.9rem; }
  .bar > .btn {
    min-height: 36px;
    padding-inline: 10px;
    font-size: 0.67rem;
  }
  main > .wrap { padding-top: 20px; }
  .library-intro { font-size: 0.79rem; }
  .hero {
    min-height: 600px;
    padding: 28px 20px;
    align-items: flex-end;
  }
  .hero::after {
    background: linear-gradient(0deg, rgba(6, 8, 7, 0.98) 5%, rgba(6, 8, 7, 0.6) 58%, rgba(6, 8, 7, 0.05));
  }
  .shot img { object-position: 58% center; }
  h1 {
    margin-block: 14px;
    font-size: clamp(4rem, 22vw, 5.8rem);
  }
  .hero .lede { font-size: 0.84rem; }
  .cta-row { grid-template-columns: 1fr; }
  .facts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .facts div { padding-inline: 8px; }
  section { padding-block: 38px; }
  .grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  .body { min-height: 0; }
  .sec-head { align-items: flex-start; }
}

/*
 * The questions.
 *
 * A description list rather than headings-and-paragraphs: the question/answer
 * relationship is the actual semantics, and it is what a screen reader and an
 * extractor both get for free from <dl>. Answers are set at body width rather
 * than full bleed because an answer that runs the width of a desktop monitor is
 * one nobody finishes.
 */
.faq {
  margin: 0;
  display: grid;
  gap: 22px;
  max-width: 68ch;
}
.faq dt {
  font-family: var(--display);
  font-weight: 800;
  font-size: 1.02rem;
  letter-spacing: -0.01em;
  color: var(--text);
}
.faq dd {
  margin: 7px 0 0;
  color: var(--text-dim);
  font-size: 0.92rem;
  line-height: 1.6;
}
/*
 * One column, on purpose.
 *
 * A two-column grid over a <dl> does not pair questions with answers side by
 * side — it puts the dt in the first column and the dd in the second, which
 * narrows both and leaves a third of the row empty. Reading order is what
 * matters here anyway: question, then answer, down the page.
 */

/* ---- cards: posters, not catalogue rows ---- */

/*
 * The palette flip alone did not change what this page IS, and the verdict on
 * it — "you just made it darker, what's new?" — was right. Every card was art,
 * then a title, then a three-line paragraph, then three chips: a catalogue row
 * with a picture at the top, twelve times down the page. Reading all of it is
 * work, and none of it is the thing being sold.
 *
 * The art carries the card now. The title sits ON it, the description is one
 * line under it, and the whole thing answers to the pointer. Nothing is removed
 * from the document — the copy is all still there for a reader or a crawler,
 * it is just no longer the first thing competing with the screenshot.
 */
.grid {
  gap: 18px;
  /* Cards size to their own content. Stretched to a shared row height they
     inherited `flex-grow: 1` on the body from the old layout, which inflated
     a 84px stack of text into a 184px box and left 100px of air under every
     card in the grid. The descriptions are clamped to two lines now, so the
     natural heights are near enough equal that nothing looks ragged. */
  align-items: start;
}

.card {
  overflow: hidden;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.thumb {
  /* Taller than 16/9: the extra band is what the title sits in. */
  aspect-ratio: 16 / 10;
  border-bottom: 0;
  overflow: hidden;
}

.thumb img {
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.card:hover .thumb img,
.card:focus-within .thumb img {
  transform: scale(1.06);
}

/* The gradient the title sits on. Taller and heavier than the old 36% wash,
   because it now has to carry 1.3rem of display type rather than nothing. */
.thumb::after {
  inset: auto 0 0;
  height: 72%;
  background: linear-gradient(
    transparent,
    rgba(7, 9, 14, 0.55) 42%,
    rgba(7, 9, 14, 0.93) 100%
  );
}

/* The title moves out of the body and onto the art. */
.card h3 {
  position: absolute;
  z-index: 2;
  right: 14px;
  bottom: 12px;
  left: 14px;
  margin: 0;
  font-size: 1.32rem;
  line-height: 1.1;
  letter-spacing: -0.015em;
  text-shadow: 0 2px 14px rgba(5, 7, 11, 0.6);
}

/*
 * Block layout, not grid.
 *
 * As a grid container its children become grid items, and a grid item has its
 * `display` blockified — which turns the `-webkit-box` the line clamp needs
 * into `flow-root` and silently drops the clamp. The description rendered at
 * full length with the clamp reported as applied in the computed style, which
 * is a confusing thing to debug and the reason this comment exists.
 */
.card .body {
  display: block;
  /* A flex item's `min-height: auto` is a content-based minimum, and here it
     resolved to 184px against 98px of actual content — 100px of air under
     every card in the grid, which no amount of `align-items` would remove
     because it is a minimum rather than a stretch. */
  min-height: 0;
  padding: 13px 14px 14px;
}

/* One line, not three. The sentence is still complete in the document; this
   is the card face deciding how much of it to spend space on. */
.card .body p {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  margin: 0 0 11px;
  color: var(--text-dim);
  font-size: 0.82rem;
  line-height: 1.45;
}

/* A play affordance, so a card looks like something you press. Hidden from
   assistive tech: the real control is the stretched link over the whole card,
   and announcing a second one would be a second thing to tab past. */
.thumb .play {
  position: absolute;
  z-index: 2;
  top: 50%;
  left: 50%;
  display: grid;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 1.05rem;
  opacity: 0;
  place-items: center;
  transform: translate(-50%, -50%) scale(0.82);
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.card:hover .thumb .play,
.card:focus-within .thumb .play {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.card:hover {
  transform: translateY(-6px);
  box-shadow: 0 22px 46px rgba(0, 0, 0, 0.5);
}

@media (prefers-reduced-motion: reduce) {
  .card,
  .thumb img,
  .thumb .play {
    transition: none;
  }
  .card:hover .thumb img,
  .card:focus-within .thumb img {
    transform: none;
  }
}

@media (max-width: 680px) {
  .card h3 {
    font-size: 1.15rem;
  }
}

/* ---- the page's shape: two rooms, not one list ---- */

/*
 * Twelve identical tiles in one flat grid is a catalogue, and it asks the
 * visitor to read all twelve before choosing anything. The only question a
 * player actually arrives with is "do I want to play with someone or not", so
 * that is the split the page is built on: eight games with people, four solo
 * runs, each in its own room with its own count.
 *
 * Within the first room the newest game leads at double width. A grid where
 * every tile is the same size has no focus; one that is all feature tiles has
 * none either. One lead, then the rest.
 */
.grid-social {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.grid-social > .card:first-child {
  grid-column: span 2;
  grid-row: span 2;
}

/* The lead tile is mostly art: its thumb fills the taller cell and the text
   sits at the bottom rather than taking a band of its own. */
.grid-social > .card:first-child .thumb {
  aspect-ratio: auto;
  height: 100%;
}

.grid-social > .card:first-child .thumb img {
  height: 100%;
  object-fit: cover;
}

.grid-social > .card:first-child h3 {
  bottom: 92px;
  font-size: 2.1rem;
}

.grid-social > .card:first-child .body {
  position: absolute;
  inset: auto 0 0;
  z-index: 2;
  background: none;
}

.grid-social > .card:first-child .body p {
  -webkit-line-clamp: 2;
  max-width: 44ch;
  color: #d4dcea;
  font-size: 0.88rem;
}

.grid-solo {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* Solo runs are short and the art reads at a glance, so these are tighter
   than the social tiles and carry one line instead of two. */
.grid-solo .thumb {
  aspect-ratio: 4 / 3;
}

.grid-solo h3 {
  font-size: 1.12rem;
}

.grid-solo .body p {
  -webkit-line-clamp: 1;
}

@media (max-width: 1080px) {
  .grid-social,
  .grid-solo {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .grid-social > .card:first-child {
    grid-row: span 1;
  }
  .grid-social > .card:first-child .thumb {
    aspect-ratio: 16 / 9;
    height: auto;
  }
  .grid-social > .card:first-child h3 {
    bottom: 12px;
    font-size: 1.5rem;
  }
  .grid-social > .card:first-child .body {
    position: static;
  }
}

@media (max-width: 680px) {
  .grid-social,
  .grid-solo {
    grid-template-columns: minmax(0, 1fr);
  }
  .grid-social > .card:first-child {
    grid-column: span 1;
  }
}

/* ---------------------------------------------------------------------------
   A phone has no hover, so a tapped card must not stay lifted.

   Every `:hover` rule above assumes a pointer that can leave. A touchscreen
   has no such thing: tapping a card applies `:hover` and it STICKS — lifted,
   zoomed, play disc showing — until something else is tapped. On a page whose
   whole job is twelve tappable cards, that leaves a trail of stuck tiles, and
   it is most of the traffic for a browser game.

   `any-hover: none` rather than `hover: none`, so a tablet with a trackpad
   attached keeps the effects; it asks whether ANY input can hover, not whether
   the primary one does.

   `:focus-within` is deliberately left alone. Keyboard focus is real on every
   device and is how the play disc appears for somebody tabbing through.
--------------------------------------------------------------------------- */
@media (any-hover: none) {
  .card:hover {
    transform: none;
    box-shadow: var(--card-shadow, 0 1px 2px rgba(20, 24, 21, 0.06));
  }

  .card:hover .thumb img {
    transform: none;
  }

  .card:hover .thumb .play {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.9);
  }

  .btn-primary:hover,
  .btn-ghost:hover {
    transform: none;
  }
}
