/* ==========================================================================
   GB27 shared stylesheet

   The palette is the game's own UI, not a website's idea of one: every
   neutral below is a colour from Assets/Scripts/GoreUI/Core/GoreUITheme.cs
   (panel, field, button, divider, text, textDim, header, accent). Change one
   there and it is worth changing here, or the site drifts away from the menu
   it is advertising.

   Shapes follow the game too. GoreUI buttons and panels are square-cornered
   9-slices, the dolls are boxes, and the icons are flat two-tone glyphs, so
   nothing here is rounded, frosted or glowing.

   No external requests: Cloudflare Pages serves this whole site cold, fonts
   included, and the CSP in _headers forbids anything else.
   ========================================================================== */

/* -------------------------------------------------------------- fonts -- */
/* Kenyan Coffee (Typodermic), the game's UI face.

   THESE MUST BE THE WEBFONT LICENCE FILES, not the .otf sitting in the Unity
   project at Assets/Scripts/GoreUI/Fonts/. The free desktop licence bundled
   there lists "web page (embedded)" and "web template" under Not allowed, so
   converting those .otf files to .woff2 would put the site in breach. Use the
   files Typodermic delivers with the web licence, drop them in assets/fonts/,
   and this picks them up with no other change. */

/* ===================================================================== *
 * THE @font-face BLOCK IS DISABLED ON PURPOSE. TURN IT BACK ON THE DAY
 * THE LICENSED WEBFONT FILES LAND IN assets/fonts/ — AND NOT BEFORE.
 *
 * Why it is off (2026-08-16): assets/fonts/ does not exist, so all four
 * faces 404. That is not free. Measured in Chrome on this page:
 *
 *   1. font-display: swap opens a ~100ms block period, during which every
 *      heading paints in the fallback.
 *   2. The four requests fail.
 *   3. Font matching then falls through to a "Kenyan Coffee" installed on
 *      the local machine, if there is one, and the page REPAINTS.
 *
 * That repaint is the flash of wrong-font text people were seeing. It is
 * caused entirely by asking for files that are not there. With these rules
 * commented out there is no request, no block period, and no swap: a
 * machine with the font installed paints it immediately, and a machine
 * without it paints the condensed fallback immediately. Either way, once.
 *
 * The dev machine HAS the font installed, so a preview there shows Kenyan
 * Coffee while visitors get the fallback stack below. Check the fallback
 * on a machine without it before judging the headings.
 *
 * Re-enabling is: drop the delivered .woff2/.woff into assets/fonts/,
 * uncomment below, and add a <link rel="preload"> for the 700 cut, which is
 * what headings use. See README, "Design system".
 * ===================================================================== */

/*
@font-face {
  font-family: "Kenyan Coffee";
  src: url("../fonts/kenyan-coffee-rg.woff2") format("woff2"),
       url("../fonts/kenyan-coffee-rg.woff")  format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Kenyan Coffee";
  src: url("../fonts/kenyan-coffee-bd.woff2") format("woff2"),
       url("../fonts/kenyan-coffee-bd.woff")  format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Kenyan Coffee";
  src: url("../fonts/kenyan-coffee-rg-it.woff2") format("woff2"),
       url("../fonts/kenyan-coffee-rg-it.woff")  format("woff");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: "Kenyan Coffee";
  src: url("../fonts/kenyan-coffee-bd-it.woff2") format("woff2"),
       url("../fonts/kenyan-coffee-bd-it.woff")  format("woff");
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}
*/

:root {
  /* Ground. The page sits a step darker than a GoreUI panel so panels read
     as panels on it, the way they do over the menu scene in game. */
  --bg:         #0c0d10;
  --bg-2:       #121418;
  --panel:      #1c1f24;   /* GoreUITheme.panel   (0.11, 0.12, 0.14) */
  --field:      #292c33;   /* GoreUITheme.field   (0.16, 0.17, 0.20) */
  --button:     #2b2e36;   /* GoreUITheme.button  (0.17, 0.18, 0.21) */
  --button-hi:  #3d424d;   /* GoreUITheme.buttonHi                   */
  --line:       rgba(255, 255, 255, 0.10);   /* GoreUITheme.divider */
  --line-2:     rgba(255, 255, 255, 0.18);

  --text:       #dbdee3;   /* GoreUITheme.text    */
  --text-dim:   #8f949e;   /* GoreUITheme.textDim */
  --head:       #f2f5fa;   /* GoreUITheme.header  */

  /* GoreBox red (GoreUITheme.accent), plus a lighter cut for red TEXT: the
     accent itself only reaches about 4.3:1 on --bg, short of AA for body
     size, so links and small labels use --red-text instead. */
  --red:        #db2e2e;
  --red-hi:     #e84343;
  --red-text:   #f2605c;
  --red-deep:   #5e1113;

  --gutter:     clamp(1rem, 4.5vw, 3.5rem);
  --measure:    72ch;
  --nav-h:      64px;

  /* Headings, labels and buttons wear the game's face. Visitors without it
     (everyone, until the web licence lands) get a condensed system face,
     not Arial: Bahnschrift ships with Windows 10+ and has a real condensed
     width, which font-stretch below selects; Apple and Android have their
     own condensed families. Impact is the last resort, not the plan. */
  --font-display: "Kenyan Coffee", "Bahnschrift", "Avenir Next Condensed",
                  "DIN Condensed", "Roboto Condensed", "Arial Narrow",
                  sans-serif-condensed, Impact, sans-serif;
  /* Body copy is NOT the display face. A condensed caps-first face at 17px
     over a paragraph reads as signage, and the privacy policy is long. */
  --font-text:    system-ui, -apple-system, "Segoe UI", Roboto,
                  "Helvetica Neue", Arial, sans-serif;
}

/* -------------------------------------------------------------- reset -- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* In-page anchors land below the sticky nav instead of under it. */
  scroll-padding-top: calc(var(--nav-h) + 1rem);
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-text);
  font-size: 1.0625rem;
  line-height: 1.6;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; height: auto; }
figure, blockquote, dl, dd { margin: 0; }

a { color: var(--red-text); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--head); }

:focus-visible {
  outline: 2px solid var(--red-text);
  outline-offset: 3px;
}

::selection { background: var(--red); color: #fff; }

/* --------------------------------------------------------------- type -- */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-stretch: condensed;
  /* 700 is the heaviest cut Kenyan Coffee ships. Asking for 900 makes the
     browser synthesise a bolder weight, which smears a condensed face. */
  font-weight: 700;
  text-transform: uppercase;
  /* Positive tracking on purpose: set in caps, a condensed face runs its
     verticals together at heading sizes. Small enough not to read as
     letterspaced, and it helps the fallback faces just as much. */
  letter-spacing: 0.015em;
  line-height: 0.95;
  color: var(--head);
  margin: 0 0 0.5em;
}

h1 { font-size: clamp(2.6rem, 7vw, 4.75rem); }
h2 { font-size: clamp(2rem, 4.4vw, 3.1rem); }
h3 { font-size: clamp(1.35rem, 2.4vw, 1.75rem); line-height: 1; }

p  { margin: 0 0 1rem; max-width: var(--measure); }

.lede {
  font-size: clamp(1.08rem, 1.7vw, 1.25rem);
  line-height: 1.55;
  color: var(--text);
  max-width: 58ch;
}

.dim { color: var(--text-dim); }

/* Small caps label in the game's face: stat names, table heads, card tags.
   This replaces the monospace "dev template" labels the site used to have. */
.label {
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* ------------------------------------------------------------- layout -- */

.wrap {
  width: 100%;
  max-width: 1200px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: clamp(3.5rem, 8vw, 6.5rem); }
.section + .section { padding-top: 0; }
.section-alt {
  background: var(--bg-2);
  border-block: 1px solid var(--line);
}
.section-alt + .section,
.section + .section-alt { padding-top: clamp(3.5rem, 8vw, 6.5rem); }

.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.stack > * + * { margin-top: 1rem; }
.center { text-align: center; }
.center p, .center .lede { margin-inline: auto; }

/* ------------------------------------------------- section heading -- */
/* A GoreUI settings header: glyph, then the title. The glyphs are the
   game's own menu icons (scripts/import_game_art.py), drawn at 48px from
   96px sources. */

.sec-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.5rem 1.25rem;
  margin-bottom: clamp(1.75rem, 4vw, 2.75rem);
}
.sec-head-title {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-right: auto;
}
.sec-head h2 { margin: 0; }
.sec-head .glyph { height: 44px; width: auto; flex: none; }
.sec-head > p { margin: 0; color: var(--text-dim); flex-basis: 100%; }
.sec-head-link {
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 1rem;
  text-decoration: none;
  color: var(--text-dim);
  padding-bottom: 0.3rem;
}
.sec-head-link:hover { color: var(--head); }

/* ------------------------------------------------------- studio bar -- */
/* Thin bar above the nav: which studio this is and which of its games you
   are looking at. The site is both the F2Games studio site and the GB27
   site, and before this bar the only way to learn that was the footer. */

.studio-bar {
  background: #07080a;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.studio-bar-inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: 34px;
}
.studio-bar a { color: var(--text-dim); text-decoration: none; }
.studio-bar a:hover,
.studio-bar a[aria-current="page"] { color: var(--head); }
.studio-name { color: var(--text) !important; }
.studio-games,
.studio-links {
  display: flex;
  gap: 1.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.studio-games { padding-left: 1.5rem; border-left: 1px solid var(--line); }
.studio-games a.is-here { color: var(--head); }
.studio-links { margin-left: auto; }

/* ---------------------------------------------------------------- nav -- */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(12, 13, 16, 0.94);
  border-bottom: 1px solid var(--line);
}
@supports (backdrop-filter: blur(1px)) {
  .nav { background: rgba(12, 13, 16, 0.82); backdrop-filter: blur(12px); }
}

.nav-inner {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: var(--nav-h);
}

.nav-mark {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 1.4rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--head);
  text-decoration: none;
  flex: none;
}
.nav-mark img { width: 34px; height: 34px; }
.nav-mark:hover { color: var(--head); }

.nav-main { align-self: stretch; display: flex; margin: 0 auto 0 1rem; }
.nav-links {
  display: flex;
  align-items: stretch;
  gap: 0.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links a {
  position: relative;
  display: flex;
  align-items: center;
  height: 100%;
  padding-inline: 0.85rem;
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 1.08rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-decoration: none;
}
.nav-links a:hover { color: var(--head); }
/* The selected tab in GoreUI is a solid bar, not an underline. */
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0.85rem;
  right: 0.85rem;
  bottom: -1px;
  height: 3px;
  background: var(--red);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 0.15s ease;
}
.nav-links a:hover::after { transform: scaleY(0.67); }
.nav-links a[aria-current="page"] { color: var(--head); }
.nav-links a[aria-current="page"]::after { transform: none; }

.nav-cta { flex: none; }

/* -- mobile menu ------------------------------------------------------ */
/* A <details>, so the menu opens with no script at all (the CSP bans inline
   script, so there is no way to hide a JS-only menu before first paint).
   site.js only adds the niceties: Escape and outside clicks close it. */

.menu { display: none; position: relative; }
.menu > summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  height: 44px;
  padding-inline: 0.85rem;
  cursor: pointer;
  background: var(--button);
  border: 1px solid var(--line-2);
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--head);
  user-select: none;
}
.menu > summary::-webkit-details-marker { display: none; }
/* Three bars that become a cross, drawn with borders so there is no icon
   file to load. */
.menu-bars {
  position: relative;
  width: 18px;
  height: 12px;
  border-block: 2px solid currentColor;
}
.menu-bars::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 3px;
  border-top: 2px solid currentColor;
}
.menu[open] .menu-bars { border-color: transparent; }
.menu[open] .menu-bars::before,
.menu[open] .menu-bars::after {
  content: "";
  position: absolute;
  left: -1px; right: -1px; top: 3px;
  border-top: 2px solid var(--head);
}
.menu[open] .menu-bars::before { transform: rotate(45deg); }
.menu[open] .menu-bars::after  { transform: rotate(-45deg); }

.menu-panel {
  position: fixed;
  left: 0;
  right: 0;
  top: var(--nav-h);
  max-height: calc(100dvh - var(--nav-h));
  overflow-y: auto;
  background: var(--panel);
  border-bottom: 1px solid var(--line-2);
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.6);
  padding: 0.5rem var(--gutter) 1.5rem;
}
.menu-group + .menu-group { margin-top: 1rem; }
.menu-group .label { display: block; padding: 0.75rem 0 0.35rem; }
.menu-group ul { list-style: none; margin: 0; padding: 0; }
.menu-group a {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding-inline: 0.75rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 1.3rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text);
  text-decoration: none;
}
.menu-group a[aria-current="page"] {
  color: var(--head);
  background: var(--field);
  box-shadow: inset 3px 0 0 var(--red);
}

@media (max-width: 60rem) {
  .studio-bar, .nav-links { display: none; }
  .menu { display: block; }
  .nav-inner { gap: 0.75rem; }
  .nav-mark { margin-right: auto; }
}
@media (max-width: 26rem) {
  .nav-mark span { display: none; }
}

/* ------------------------------------------------------------ buttons -- */
/* A GoreUI button: square, flat, with a darker lip along the bottom edge so
   it reads as something to press rather than a coloured rectangle. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 48px;
  padding: 0.55rem 1.5rem 0.65rem;
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 1.2rem;
  line-height: 1;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-decoration: none;
  color: #fff;
  background: var(--red);
  border: 0;
  border-radius: 0;
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.3);
  cursor: pointer;
  transition: background-color 0.12s ease, transform 0.08s ease;
}
.btn:hover { background: var(--red-hi); color: #fff; }
.btn:active { transform: translateY(1px); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3); }
.btn img { height: 1em; width: auto; }

.btn-ghost {
  background: var(--button);
  color: var(--head);
  box-shadow: inset 0 0 0 1px var(--line-2), inset 0 -3px 0 rgba(0, 0, 0, 0.3);
}
.btn-ghost:hover { background: var(--button-hi); color: var(--head); }

.btn-sm { min-height: 40px; font-size: 1.05rem; padding-inline: 1.1rem; }

.btn[aria-disabled="true"] {
  background: var(--button);
  color: var(--text-dim);
  pointer-events: none;
}

.btn-row { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.75rem; }

/* External links carry a small arrow so leaving the site is never a surprise. */
.ext::after {
  content: "\00a0\2197";
  font-family: var(--font-text);
  font-weight: 400;
  font-size: 0.85em;
  color: var(--red-text);
}
.btn.ext::after { color: inherit; }

/* --------------------------------------------------------------- chip -- */
/* Status tag. Square, with a red tab on its left edge like a GoreUI row. */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.7rem 0.3rem 0.6rem;
  background: rgba(12, 13, 16, 0.72);
  box-shadow: inset 3px 0 0 var(--red), inset 0 0 0 1px var(--line-2);
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 0.9rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--head);
  font-variant-numeric: tabular-nums;
  /* Never stretch to fill a flex column (platform cards, tiles). */
  width: fit-content;
}
.chip-dim { box-shadow: inset 3px 0 0 var(--button-hi), inset 0 0 0 1px var(--line); color: var(--text); }

/* --------------------------------------------------------------- hero -- */

.hero {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  min-height: min(80vh, 700px);
  padding-block: clamp(3rem, 8vw, 6rem);
  overflow: hidden;
  border-bottom: 1px solid var(--line);
}

/* The Steam library hero, full bleed. The dolls stand on the left third, so
   the copy takes the right half and a gradient buys it contrast. */
.hero-art {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 28% 30%;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(90deg, transparent 20%, rgba(12, 13, 16, 0.7) 52%, rgba(12, 13, 16, 0.94) 75%),
    linear-gradient(180deg, transparent 60%, var(--bg) 100%);
}

.hero-inner { display: flex; justify-content: flex-end; }
.hero-copy { width: min(100%, 36rem); }

.hero h1 { margin: 1.1rem 0 1.5rem; }
.hero-logo {
  width: min(100%, 560px);
  height: auto;
  filter: drop-shadow(0 10px 24px rgba(0, 0, 0, 0.65));
}
.hero .lede { font-size: clamp(1.1rem, 1.9vw, 1.35rem); color: var(--head); }
.hero-note { margin: 1.25rem 0 0; font-size: 0.95rem; color: var(--text-dim); }

/* On a phone the art becomes a banner above the copy instead of a backdrop
   under it: at 375px the dolls and the copy cannot share the frame. */
@media (max-width: 48rem) {
  .hero { display: block; min-height: 0; padding-top: 0; }
  .hero-art { position: relative; height: 62vw; object-position: 22% 30%; }
  .hero::before {
    background: linear-gradient(180deg, transparent 20vw, var(--bg) 62vw);
  }
  .hero-inner { display: block; margin-top: -9vw; }
  .hero h1 { margin-top: 1rem; }
}

/* --------------------------------------------------------------- facts -- */
/* The four things people ask first, as a readout under the hero. */

.facts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background: var(--panel);
  border: 1px solid var(--line);
  margin-top: -1px;
}
.fact {
  padding: 1.1rem 1.25rem 1.2rem;
  border-left: 1px solid var(--line);
}
.fact:first-child { border-left: 0; }
.fact .label { display: block; margin-bottom: 0.35rem; }
.fact-value {
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 1.3rem;
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--head);
}
@media (max-width: 48rem) {
  .facts { grid-template-columns: 1fr 1fr; }
  .fact:nth-child(3) { border-left: 0; }
  .fact:nth-child(n + 3) { border-top: 1px solid var(--line); }
}

/* -------------------------------------------------------------- media -- */

.frame {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--panel);
  border: 1px solid var(--line);
  overflow: hidden;
}
.frame img, .frame video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.frame-cap {
  margin-top: 0.6rem;
  font-size: 0.88rem;
  color: var(--text-dim);
}

.watch {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  align-items: start;
}
.watch-side .frame + p { margin-top: 0.6rem; }
@media (max-width: 52rem) { .watch { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------ features -- */
/* Feature rows alternate image and copy. Each is one system of the game,
   with the Play listing's bullet points underneath it. */

.feature {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: center;
}
.feature + .feature { margin-top: clamp(3rem, 7vw, 5.5rem); }
.feature-flip > .frame { order: 2; }
.feature h3 { font-size: clamp(1.8rem, 3.4vw, 2.5rem); margin-bottom: 0.9rem; }
.feature .glyph { height: 40px; width: auto; margin-bottom: 1rem; }
@media (max-width: 52rem) {
  .feature { grid-template-columns: 1fr; }
  .feature-flip > .frame { order: 0; }
}

/* Bullets are small red squares. Blocky, like everything else in the game. */
.ticks { list-style: none; margin: 0; padding: 0; }
.ticks li {
  position: relative;
  padding-left: 1.3rem;
  margin-bottom: 0.55rem;
  color: var(--text);
}
.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 7px;
  height: 7px;
  background: var(--red);
}

/* ------------------------------------------------------------- arsenal -- */
/* The weapon cards are the game's own item icons (Textures/WeaponIcons),
   untouched. The label strip under each is an in-game item tile. */

.arsenal {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  list-style: none;
  margin: 2rem 0 0;
  padding: 0;
}
.arm {
  background: var(--panel);
  border: 1px solid var(--line);
}
.arm img { width: 100%; aspect-ratio: 1; object-fit: cover; }
.arm .label {
  display: block;
  padding: 0.5rem 0.65rem 0.55rem;
  color: var(--head);
  border-top: 1px solid var(--line);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 64rem) { .arsenal { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 36rem) { .arsenal { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* The Reality Crusher gets a card of its own: it is the game's signature
   item and it is not a gun. */
.crusher {
  display: grid;
  grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
  background: var(--panel);
  border: 1px solid var(--line);
  box-shadow: inset 3px 0 0 var(--red);
}
.crusher img { width: 100%; height: 100%; aspect-ratio: 1; object-fit: cover; }
.crusher-body { padding: 1.4rem 1.5rem; align-self: center; }
.crusher-body h3 { margin-bottom: 0.5rem; }
.crusher-body p { margin: 0; color: var(--text); }
@media (max-width: 30rem) {
  .crusher { grid-template-columns: 1fr; }
  .crusher img { aspect-ratio: 2 / 1; }
}

/* --------------------------------------------------------------- modes -- */
/* Menu rows, as the in-game mode picker draws them: GoreUITheme.menuRow at
   rest, menuRowActive under the cursor. */

.rows { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.row {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr);
  gap: 0 1rem;
  align-items: center;
  padding: 0.95rem 1rem;
  background: rgba(26, 28, 33, 0.55);
  border-bottom: 1px solid var(--line);
  transition: background-color 0.12s ease, box-shadow 0.12s ease;
}
.row img { width: 2.4rem; height: auto; justify-self: center; opacity: 0.9; }
.row h3 { font-size: 1.4rem; margin: 0 0 0.2rem; }
.row p { margin: 0; color: var(--text-dim); font-size: 0.97rem; }
@media (hover: hover) {
  .row:hover { background: rgba(41, 43, 51, 0.94); box-shadow: inset 3px 0 0 var(--red); }
}

.split {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
}
.split > * > h3 { margin-bottom: 1rem; }
@media (max-width: 52rem) { .split { grid-template-columns: 1fr; } }

/* -------------------------------------------------------------- panels -- */

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  padding: clamp(1.25rem, 3vw, 1.75rem);
}
.panel h3 { margin-bottom: 0.6rem; }
.panel > p:last-child { margin-bottom: 0; }
.panel .glyph { height: 40px; width: auto; margin-bottom: 1rem; }
.panel-sub { margin-top: 1.75rem; padding-top: 1.5rem; border-top: 1px solid var(--line); }

/* A panel with a picture on top: map-select tile. */
.tile {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--line);
}
.tile > img { width: 100%; aspect-ratio: 2 / 1; object-fit: cover; border-bottom: 1px solid var(--line); }
.tile-body { padding: 1.25rem 1.4rem 1.4rem; }
.tile-body h3 { margin-bottom: 0.75rem; }

/* Cards that are also links. An <a> is inline by default, which breaks the
   panel's padding and border anywhere it is not already a grid item. */
a.panel { display: block; }
a.panel, a.tile { color: inherit; text-decoration: none; transition: border-color 0.12s ease, background-color 0.12s ease; }
a.panel:hover, a.tile:hover { border-color: var(--line-2); background: #20242a; }
a.panel:hover h3, a.tile:hover h3 { color: #fff; }

/* ------------------------------------------------------------- gallery -- */

.gallery {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
/* The lead shot spans two rows; its height comes from the rows, not from
   its own 16:9, or it overshoots the two stacked shots beside it. */
.gallery .frame:nth-child(1) { grid-column: span 2; grid-row: span 2; aspect-ratio: auto; }
@media (max-width: 52rem) {
  .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .gallery .frame:nth-child(1) { grid-row: auto; aspect-ratio: 16 / 9; }
}

/* ------------------------------------------------------------ CTA band -- */
/* The red menu scene the game opens on, with the loading screen's blood
   streaks at the edges. */

.band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: clamp(4rem, 10vw, 7.5rem);
  border-block: 1px solid var(--line);
  background: #2a0808 url("../media/menu-backdrop.jpg") center 60% / cover no-repeat;
}
.band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(70% 90% at 50% 50%, rgba(12, 13, 16, 0.55), rgba(12, 13, 16, 0.9));
}
.band-edge {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: -1;
  width: auto;
  height: 100%;
  opacity: 0.8;
  pointer-events: none;
}
.band-edge-l { left: 0; }
.band-edge-r { right: 0; }
.band h2 { font-size: clamp(2.4rem, 6vw, 4rem); }
.band .btn-row { justify-content: center; }
@media (max-width: 40rem) { .band-edge { opacity: 0.4; } }

/* ------------------------------------------------------ inner page head -- */
/* Inner pages open on the same red scene, pushed much further back. */

.page-head {
  position: relative;
  isolation: isolate;
  padding-block: clamp(2.75rem, 7vw, 4.75rem) clamp(2.25rem, 5vw, 3.5rem);
  border-bottom: 1px solid var(--line);
  background: #1a0606 url("../media/menu-backdrop.jpg") center 55% / cover no-repeat;
}
.page-head::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(90deg, rgba(12, 13, 16, 0.96) 30%, rgba(12, 13, 16, 0.78) 100%);
}
.page-head h1 { margin: 0.9rem 0 0.9rem; }
.page-head .lede { margin: 0; color: var(--text); }
.page-head-plain { background: var(--bg-2); }
.page-head-plain::before { display: none; }

/* On-page contents: jump links under a long page's heading. */
.toc {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  margin: 1.75rem 0 0;
  padding: 0;
}
.toc a {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0.25rem 0.8rem;
  background: rgba(28, 31, 36, 0.85);
  border: 1px solid var(--line-2);
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
  text-decoration: none;
}
.toc a:hover { background: var(--button-hi); color: var(--head); }

/* "Back to X" link above a heading. */
.back {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-decoration: none;
}
.back::before {
  content: "";
  width: 0.45em;
  height: 0.45em;
  border-left: 2px solid var(--red-text);
  border-bottom: 2px solid var(--red-text);
  transform: rotate(45deg);
}
.back:hover { color: var(--head); }

/* ----------------------------------------------------------- platforms -- */

.platform {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--line);
  box-shadow: inset 0 3px 0 var(--red);
  padding: clamp(1.4rem, 3vw, 2rem);
}
.platform h2 { font-size: clamp(2rem, 4vw, 2.6rem); margin: 0.85rem 0 0.75rem; }
.platform .ticks { margin: 0.25rem 0 1.5rem; }
.platform .btn-row { margin-top: auto; }

/* -------------------------------------------------------- fact sheet -- */

.kv-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-top: 1px solid var(--line);
  margin: 0;
}
.kv-grid > div {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr);
  gap: 1rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--line);
}
.kv-grid > div:nth-child(odd) { padding-right: 1.5rem; }
.kv-grid > div:nth-child(even) { padding-left: 1.5rem; border-left: 1px solid var(--line); }
.kv-grid dt { margin: 0; padding-top: 0.12rem; }
.kv-grid dd { margin: 0; color: var(--head); }
@media (max-width: 52rem) {
  .kv-grid { grid-template-columns: 1fr; }
  .kv-grid > div:nth-child(n) { padding-inline: 0; border-left: 0; }
}
@media (max-width: 30rem) {
  .kv-grid > div { grid-template-columns: 1fr; gap: 0.15rem; }
}

/* Copy block with a copy button (press descriptions). */
.copy-block { position: relative; }
.copy-block .label { display: block; margin-bottom: 0.75rem; }
.copy-block blockquote { margin: 0; font-size: 1.05rem; color: var(--head); max-width: 70ch; }
.copy-btn { position: absolute; top: 1rem; right: 1rem; }
.copy-btn[hidden] { display: none; }

/* ------------------------------------------------------------- tables -- */

/* container-type makes this element queryable, so the table below can react to
   the width IT actually gets rather than the width of the window. Those are not
   the same number inside .prose, which is the whole reason this was broken. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  container-type: inline-size;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
  min-width: 30rem;
}
th, td {
  text-align: left;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
th {
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-dim);
  white-space: nowrap;
}
td:first-child { color: var(--text-dim); white-space: nowrap; }
thead th { border-bottom-color: var(--line-2); }

/* -------------------------------------------------------------- notes -- */

.note {
  background: var(--panel);
  box-shadow: inset 3px 0 0 var(--red);
  border: 1px solid var(--line);
  padding: 1rem 1.25rem;
}
.note p { margin-bottom: 0.6rem; color: var(--text); font-size: 0.97rem; }
.note p:last-child { margin-bottom: 0; }
.note strong { color: var(--head); }

.warn {
  font-size: 0.85rem;
  color: var(--text-dim);
  max-width: var(--measure);
}

/* --------------------------------------------------------------- prose -- */

/* Long-form documents: legal pages, about. Headings here drop the display
   treatment. A condensed uppercase slab over a privacy policy reads as
   shouting. */
/* The reading measure belongs on the TEXT, not on the wrapper. Capping .prose
   itself capped everything inside it, including things that are not prose: the
   GB27 privacy table was pinned to 488px on a 1280px screen with 692px of
   column going spare, and clipped because it needs 667px. No viewport media
   query can see that — the constraint was the measure, not the window. */
.prose > p,
.prose > ul,
.prose > ol,
.prose > h2,
.prose > h3,
.prose > .note { max-width: var(--measure); }

.prose h2 {
  font-family: var(--font-text);
  font-stretch: normal;
  font-size: 1.2rem;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  line-height: 1.4;
  color: var(--head);
  margin: 2.5rem 0 0.6rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}
.prose h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.prose .note + h2 { margin-top: 2rem; }
.prose h3 {
  font-family: var(--font-text);
  font-stretch: normal;
  font-size: 1.05rem;
  text-transform: none;
  letter-spacing: 0;
  line-height: 1.4;
  margin: 1.5rem 0 0.5rem;
}

.prose p, .prose li { color: var(--text); }
.prose ul, .prose ol { padding-left: 1.2rem; margin: 0 0 1.25rem; }
.prose li { margin-bottom: 0.4rem; }
.prose li::marker { color: var(--red); }
.prose .note { margin-bottom: 1.5rem; }

.prose-meta {
  font-size: 0.9rem;
  color: var(--text-dim);
  margin-bottom: 2rem;
}

/* The legal documents sit in a single column with the reading measure, on a
   page head of their own. */
.doc { padding-block: clamp(2.5rem, 6vw, 4rem) clamp(3.5rem, 8vw, 6rem); }

/* An unresolved placeholder inside a published document. Deliberately loud:
   nobody should be able to read past one of these by accident. Grep the site
   for "todo" before calling a document final. */
.todo {
  font-size: 0.9em;
  color: var(--head);
  background: var(--red-deep);
  border-bottom: 1px dashed var(--red-text);
  padding: 0.1em 0.35em;
}

/* ------------------------------------------------------------- footer -- */

.footer {
  background: #07080a;
  border-top: 1px solid var(--line);
  padding-block: clamp(2.5rem, 6vw, 3.75rem) 2rem;
  font-size: 0.93rem;
  color: var(--text-dim);
}
.footer a { color: var(--text); text-decoration: none; }
.footer a:hover { color: #fff; text-decoration: underline; }

.footer-grid {
  display: grid;
  gap: 2rem 1.5rem;
  grid-template-columns: minmax(0, 1.6fr) repeat(4, minmax(0, 1fr));
  margin-bottom: 2.5rem;
}
@media (max-width: 60rem) {
  .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer-brand { grid-column: 1 / -1; }
}

.footer-brand img { width: 40px; height: 40px; margin-bottom: 0.9rem; }
.footer-brand p { margin: 0 0 0.4rem; max-width: 30ch; }
.footer-brand strong {
  font-family: var(--font-display);
  font-stretch: condensed;
  font-size: 1.3rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--head);
}

.footer-col h4 {
  font-size: 0.95rem;
  letter-spacing: 0.1em;
  color: var(--head);
  margin: 0 0 0.9rem;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col li { margin-bottom: 0.55rem; }

.footer-base {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  justify-content: space-between;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  font-size: 0.85rem;
}
.footer-base p { margin: 0; }

/* ------------------------------------------------------ inner pages -- */

/* Platform cards that are links as a whole (gorebox.html). */
a.platform { color: inherit; text-decoration: none; transition: background-color 0.12s ease; }
a.platform:hover { background: #20242a; }
a.platform:hover .btn { background: var(--red-hi); }

/* Link text inside a card that is itself the link: styled, not nested. */
.contact-link {
  font-weight: 600;
  color: var(--red-text);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
a:hover .contact-link { color: var(--head); }

/* Logo previews on the press page sit on the page black, since that is
   where the marks are designed to live. */
.logo-well {
  position: relative;
  aspect-ratio: 2 / 1;
  background: #07080a;
  border-bottom: 1px solid var(--line);
}
.logo-well img {
  position: absolute;
  inset: 1.5rem;
  width: calc(100% - 3rem);
  height: calc(100% - 3rem);
  object-fit: contain;
}
.tile-body > .btn { margin-top: 0.25rem; }

.gallery-even .frame:nth-child(n) { grid-column: auto; grid-row: auto; aspect-ratio: 16 / 9; }

/* The legal hub: one list per product, each row a document. */
.doc-groups {
  display: grid;
  gap: clamp(2rem, 5vw, 3rem) clamp(1.5rem, 4vw, 3rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}
.doc-group h2 { font-size: clamp(1.6rem, 3vw, 2rem); margin-bottom: 0.9rem; }
.doc-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.doc-list a {
  position: relative;
  display: block;
  padding: 0.9rem 2.5rem 0.95rem 1rem;
  background: rgba(26, 28, 33, 0.55);
  border-bottom: 1px solid var(--line);
  color: var(--text-dim);
  text-decoration: none;
  transition: background-color 0.12s ease, box-shadow 0.12s ease;
}
.doc-list a::after {
  content: "";
  position: absolute;
  right: 1.1rem;
  top: 50%;
  width: 0.5rem;
  height: 0.5rem;
  border-top: 2px solid var(--text-dim);
  border-right: 2px solid var(--text-dim);
  transform: translateY(-50%) rotate(45deg);
}
.doc-list a:hover { background: rgba(41, 43, 51, 0.94); box-shadow: inset 3px 0 0 var(--red); }
.doc-list a:hover::after { border-color: var(--red-text); }
.doc-title {
  display: block;
  font-family: var(--font-display);
  font-stretch: condensed;
  font-weight: 700;
  font-size: 1.3rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--head);
}
.doc-desc { display: block; font-size: 0.95rem; margin-top: 0.15rem; }

/* 404: one of the game's emote faces, looking at you. */
.lost { padding-block: clamp(4rem, 12vw, 8rem); }
.lost-face { width: 128px; height: auto; margin: 0 auto 0.5rem; }
.lost h1 { margin-top: 0.75rem; }
.lost .chip { margin-top: 0; }

/* ------------------------------------------------------------ utility -- */

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--red);
  color: #fff;
  padding: 0.75rem 1.25rem;
}
.skip-link:focus { left: 0; }

.mt-0 { margin-top: 0; }
.mt-2 { margin-top: 2rem; }
.mt-3 { margin-top: 3rem; }

/* ==================================================================== *
 *                              motion
 *
 * All of this is CSS only, on purpose. The production CSP in _headers is
 * script-src 'self' with no 'unsafe-inline', so the usual trick of setting
 * a .js class from an inline <head> script is blocked outright. And a
 * JS-driven reveal that starts at opacity 0 turns any script failure into
 * a blank page. Nothing below needs JavaScript to end up visible.
 *
 * The reduce block near the top of this file already clamps every
 * animation and transition to 0.01ms, so each of these lands on its end
 * state instantly for anyone who asks for less motion. That is why every
 * keyframe here ENDS at the visible, resting state and never starts an
 * infinite loop: no pulsing, no strobing, one pass and done.
 * ==================================================================== */

@keyframes rise-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* -- the hero settling, on load ------------------------------------- */
/* fill-mode both is load-bearing: without it the element sits at full
   opacity during its delay and then snaps to 0 when the animation starts,
   which is a worse flash than the one this replaces. */
@media (prefers-reduced-motion: no-preference) {
  .hero-copy > * {
    animation: rise-in 0.55s cubic-bezier(0.2, 0.6, 0.2, 1) both;
  }
  .hero-copy > *:nth-child(1) { animation-delay: 0.02s; }
  .hero-copy > *:nth-child(2) { animation-delay: 0.10s; }
  .hero-copy > *:nth-child(3) { animation-delay: 0.18s; }
  .hero-copy > *:nth-child(4) { animation-delay: 0.26s; }
  .hero-copy > *:nth-child(n + 5) { animation-delay: 0.32s; }
}

/* -- media frames under the cursor ---------------------------------- */
/* The frame already clips, so the still can push past its edges. */
.frame img { transition: transform 0.4s cubic-bezier(0.2, 0.6, 0.2, 1); }
a.frame:hover img, a.frame:focus-visible img,
.frame .shot-zoomable:hover, .shot-zoomable:hover img { transform: scale(1.035); }
.arm img { transition: transform 0.3s cubic-bezier(0.2, 0.6, 0.2, 1); }
.arm { overflow: hidden; }
@media (hover: hover) {
  .arm:hover { border-color: var(--line-2); }
}

/* -- scroll reveal, where the browser can do it without script ------ */
/* animation-timeline is Chromium-only for now. Guarded, so every other
   browser simply gets the finished page with no reveal at all, which is
   the correct thing to degrade to. Anything already on screen at load
   starts past the end of its own range, so nothing above the fold hides. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .feature,
    .arsenal > *,
    .gallery > *,
    .grid > *,
    .rows > *,
    .watch > * {
      animation: rise-in linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 45%;
    }
  }
}

/* ------------------------------------------------------------ lightbox -- */
/* Built by site.js. Everything here is inert until that script adds the
   dialog, and every screenshot is a plain link to its own file underneath,
   so a blocked script costs the zoom and nothing else. */

.shot-zoomable { cursor: zoom-in; }
.frame img.shot-zoomable:focus-visible { outline: 2px solid var(--red-text); outline-offset: -3px; }

dialog.lightbox {
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: 100vw;
  max-height: 100vh;
  padding: 0;
  border: 0;
  background: transparent;
  overflow: hidden;
}
dialog.lightbox::backdrop { background: rgba(7, 8, 10, 0.94); }

.lightbox-img {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: auto;
  height: auto;
  max-width: min(94vw, 1600px);
  max-height: 82vh;
  border: 1px solid var(--line-2);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.7);
}

.lightbox-caption {
  position: absolute;
  left: 50%;
  bottom: 3vh;
  transform: translateX(-50%);
  margin: 0;
  padding: 0 1rem;
  max-width: 70ch;
  text-align: center;
  font-size: 0.93rem;
  color: var(--text);
}

.lightbox-btn {
  position: absolute;
  min-width: 48px;
  min-height: 48px;
  padding: 0.55rem 0.8rem;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--head);
  background: var(--button);
  border: 1px solid var(--line-2);
  cursor: pointer;
  transition: background-color 0.12s ease;
}
.lightbox-btn:hover { background: var(--red); border-color: var(--red); }
.lightbox-close { top: 2.5vh; right: 2.5vw; }
.lightbox-prev  { left: 2.5vw;  top: 50%; transform: translateY(-50%); }
.lightbox-next  { right: 2.5vw; top: 50%; transform: translateY(-50%); }

/* On a phone the side arrows would sit on top of the image, so they drop to
   the bottom corners where the thumb already is. */
@media (max-width: 40rem) {
  .lightbox-img { max-width: 96vw; max-height: 70vh; }
  .lightbox-prev, .lightbox-next { top: auto; bottom: 2.5vh; transform: none; }
  .lightbox-caption { bottom: 12vh; font-size: 0.85rem; }
}

/* --------------------------------------------------- tables on a phone -- */
/* Three columns of prose inside a 30rem-minimum table does not survive a
   375px screen. It technically scrolls, but every row is as tall as its
   tallest cell whether or not that cell is on screen, so the visible column
   is mostly empty space and the reader has to swipe sideways per row to
   assemble one fact. Below the breakpoint the rows stop being rows: each one
   becomes a labelled block, with the column heading carried in data-label.
   The real <thead> stays in the DOM for screen readers rather than being
   deleted, so the table is still a table to anything that reads structure.

   This is a @container query, not @media, and that distinction is the entire
   fix. Two viewport breakpoints were tried and both failed, because the table
   does not live in the viewport — it lives in .prose, which caps at 72ch. On a
   1280px screen its container was 488px while the window said "plenty of room".
   Querying the container asks the only question that matters: does the table
   fit where it actually is. 44rem sits just above the ~667px this table wants.

   Trade-off: no container-query support means no stacking, i.e. exactly the
   behaviour that shipped before any of this. That is an acceptable floor. */

@container (max-width: 44rem) {
  .table-scroll { overflow-x: visible; }
  /* The table box itself has to leave table layout too. Left as display:table
     it keeps computing a layout width from its cells and overflows the wrap by
     a dozen pixels even with min-width gone. */
  .table-scroll table,
  .table-scroll tbody { display: block; width: 100%; min-width: 0; }

  .table-scroll thead {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .table-scroll tr {
    display: block;
    padding: 1rem 0;
    border-bottom: 1px solid var(--line);
  }
  .table-scroll tr:last-child { border-bottom: 0; }

  .table-scroll td {
    display: block;
    border: 0;
    padding: 0.15rem 0;
    max-width: none;
  }

  /* A cell carrying a column name announces it. */
  .table-scroll td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 0.1rem;
    font-family: var(--font-display);
    font-stretch: condensed;
    font-weight: 700;
    font-size: 0.78rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-dim);
  }
  .table-scroll td[data-label] { margin-top: 0.55rem; }

  /* The first cell has no label because it IS the label: it is the subject of
     its row. */
  .table-scroll td:first-child:not([data-label]) {
    font-family: var(--font-display);
    font-stretch: condensed;
    font-weight: 700;
    font-size: 1.1rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--head);
  }
}
