/*
  Qualiacology hub styles — "the index, in three rooms".
  Discipline budget (do not exceed): one aura per view, resting wing alpha <= 8%,
  glow shadows only on the scroll filament, the lit nav pilot light, and the playing
  audio LED. Hero art gave its glow back on 2026-08-27 — the picture is the light now.
  Wing colors mean rooms — amber = psychopharmacology, cyan = games, pink = Doopliss.
  Amber focus outlines are accessibility, not theming; they never change.

  Motion law - three lanes, so entrance, pointer and press can never write the same
  property and fight each other:
    translate = entrance   (scroll timelines only: `rise`, `settle`)
    scale     = pointer    (hover/focus, always inside
                            @media (hover: hover) and (prefers-reduced-motion: no-preference))
    transform = press      (:active, 45ms attack)
  Entrance animations are transform-family ONLY, never opacity: an opacity keyframe
  makes axe fail colour-contrast on below-fold content mid-animation. This was learned
  the hard way; do not re-learn it.

  Not doing, on purpose: card tilt, parallax, magnetic buttons, a cursor follower, a
  cursor-driven torch, a fixed scroll-driven lamp, per-card stagger beyond four
  nth-child buckets, `will-change` on card images. All are desktop-only by nature,
  invisible inside a stranger's first ten seconds, absent on the phones this site is
  mostly read on, and untestable by a QA suite that runs at 390px and 1440px with no
  hover emulation.
*/

@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/inter-latin-v20.woff2") format("woff2");
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
}

@font-face {
  font-family: "Space Grotesk";
  src: url("/assets/fonts/space-grotesk-latin-v22.woff2") format("woff2");
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
}

:root {
  color-scheme: dark;
  --ink: #060a0f;
  --ink-soft: #091018;
  --surface: #0d151d;
  --surface-2: #121e28;
  --line: #263846;
  --line-bright: #3d5968;
  /* The lit edge of a shelf the art sits ON. 3.2:1 against --surface, so the frame
     is a component boundary you can actually see. */
  --rule-lit: #4a6577;
  --text: #f4f7f8;
  --text-soft: #c9d4d9;
  --muted: #9eb0b9;
  --cyan: #40eee1;
  --cyan-dark: #0baaa4;
  --pink: #ff5a9e;
  --violet: #a386ff;
  --amber: #ffc16b;
  --danger: #ff796e;
  --font-body: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-display: "Space Grotesk", "Inter", system-ui, sans-serif;
  --content: 76rem;
  --gutter: clamp(1rem, 3vw, 2rem);
  /* The seam between chapters. Asymmetric on purpose: a heading needs less air above
     it than the content it closes needs below. It is also the numeral's height budget. */
  --seam-top: clamp(3rem, 6vw, 5rem);
  --seam-bot: clamp(3.5rem, 7vw, 6rem);
  --header-height: 4.5rem;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  /* The room you are standing in. */
  --wing: var(--cyan);
  --wing-dim: rgba(64, 238, 225, 0.06);
  --wing-ink: #001312;
}

/* Rooms: hub pages. */
body[data-page="psych"] {
  --wing: var(--amber);
  --wing-dim: rgba(255, 193, 107, 0.06);
  --wing-ink: #1a1206;
}

body[data-page="music"] {
  --wing: var(--pink);
  --wing-dim: rgba(255, 90, 158, 0.06);
  --wing-ink: #1c0410;
}

/* Rooms: homepage hallway, one doorway per section. */
#community {
  --wing: var(--amber);
  --wing-dim: rgba(255, 193, 107, 0.05);
  --wing-ink: #1a1206;
}

#game {
  --wing: var(--cyan);
  --wing-dim: rgba(64, 238, 225, 0.05);
  --wing-ink: #001312;
}

#music {
  --wing: var(--pink);
  --wing-dim: rgba(255, 90, 158, 0.05);
  --wing-ink: #1c0410;
}

#connect {
  --wing: var(--violet);
  --wing-dim: rgba(163, 134, 255, 0.05);
  --wing-ink: #120a24;
}

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

html {
  min-width: 20rem;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-height) + 1rem);
  background: var(--ink);
}

body {
  min-width: 20rem;
  margin: 0;
  overflow-x: clip;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.65;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* Film grain: the paper the index is printed on. Under the header and chrome. */
body::after {
  position: fixed;
  z-index: 60;
  inset: -2%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  content: "";
  opacity: 0.045;
  pointer-events: none;
}

body.menu-open {
  overflow: hidden;
}

::selection {
  background: var(--cyan);
  color: #001312;
}

/* Selecting text tells you which room you are in. */
body[data-page="psych"] ::selection {
  background: var(--amber);
  color: #1a1206;
}

body[data-page="music"] ::selection {
  background: var(--pink);
  color: #1c0410;
}

#community ::selection {
  background: var(--amber);
  color: #1a1206;
}

#music ::selection {
  background: var(--pink);
  color: #1c0410;
}

#connect ::selection {
  background: var(--violet);
  color: #120a24;
}

/* Every hover affordance below carries a :focus-visible twin. The amber outline says
   WHERE you are; the twin says what the thing DOES. Keyboard users were getting only
   the first half. The outline itself never changes — see the law at the top. */
a {
  color: inherit;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 0.08em;
}

button,
input,
textarea,
select {
  font: inherit;
}

button,
a {
  -webkit-tap-highlight-color: transparent;
}

img,
picture,
svg {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
}

h1,
h2,
h3,
p,
dl,
dd,
figure,
blockquote {
  margin-top: 0;
}

h1,
h2,
h3 {
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 1.02;
  text-wrap: balance;
}

p {
  color: var(--text-soft);
}

[hidden] {
  display: none !important;
}

:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 4px;
}

/* The locator register: tiny, tracked, printed. It names a place — a door, a room,
   a kind, a count. The counterweight to the display scale. */
.eyebrow,
.nav-link,
.card-kicker,
.dialog-label,
.proof-list dd,
.route-card small,
.subhero-stat span,
.filter-shell legend,
.filter-button {
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

/* The data register: the same small size, said instead of shouted. These carry values
   that were already cased on purpose — WASD, LMB, RMB, Esc, TikTok — and uppercasing
   them threw that signal away. Set them, don't transform them. */
.card-meta li,
.footer-links a {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.005em;
}

.skip-link {
  position: fixed;
  z-index: 200;
  top: 0.75rem;
  left: 0.75rem;
  min-height: 2.75rem;
  padding: 0.65rem 1rem;
  transform: translateY(-180%);
  background: var(--text);
  color: var(--ink);
  font-weight: 800;
  text-decoration: none;
  transition: transform 160ms ease;
}

.skip-link:focus {
  transform: translateY(0);
}

/* The one glowing line on the site: the filament that measures your descent.
   Where scroll-driven animations exist the browser drives the scale itself, which
   removes one inline-style write and one style recalc per scroll frame; the JS stays
   as the fallback for engines without them. */
.scroll-progress {
  position: fixed;
  z-index: 150;
  inset: 0 0 auto;
  height: 3px;
  transform: scaleX(0);
  transform-origin: left center;
  color: var(--wing);
  background: currentColor;
  box-shadow: 0 0 12px -2px currentColor;
  pointer-events: none;
}

/* Must come AFTER the base rule: same specificity, so source order decides, and
   `transform: none` here is load-bearing. The base parks the filament at scaleX(0)
   for the JS fallback; left in place it multiplies against the animated `scale` and
   the filament is zero-width at every scroll position, forever. */
@supports (animation-timeline: scroll(root)) {
  .scroll-progress {
    animation: filament linear both;
    animation-timeline: scroll(root);
    transform: none;
  }
}

@keyframes filament {
  from {
    scale: 0 1;
  }

  to {
    scale: 1 1;
  }
}

/* The filament used to walk the hallway on a fixed set of percentage stops, which
   were 10-20% out of phase with the real sections (pink arrived at 80% for a section
   that ended at 78%) and never showed violet at all. Re-tuning the stops would only
   have drifted again the moment the seams changed. `data-here` is set by an
   IntersectionObserver over the four homepage rooms, so the light is a report rather
   than a guess, and it cannot go stale when the layout moves. */
.home-page[data-here="community"] .scroll-progress { color: var(--amber); }
.home-page[data-here="game"] .scroll-progress { color: var(--cyan); }
.home-page[data-here="music"] .scroll-progress { color: var(--pink); }
.home-page[data-here="connect"] .scroll-progress { color: var(--violet); }

.scroll-progress {
  transition: color 420ms var(--ease);
}

/* The stylesheet has always claimed the lit pilot light is "the room you are standing
   in". On the homepage it was never lit, because there was no page to be current. */
.home-page[data-here="community"] .nav-link[href="/psychopharmacology/"]::before,
.home-page[data-here="game"] .nav-link[href="/games/"]::before,
.home-page[data-here="music"] .nav-link[href="/music/"]::before,
.home-page[data-here="connect"] .nav-link[href="/#connect"]::before {
  opacity: 1;
  box-shadow: 0 0 8px 1px var(--dot, transparent);
}

.container {
  width: min(calc(100% - (var(--gutter) * 2)), var(--content));
  margin-inline: auto;
}

/* The plate rail. Words stay in the 76rem measure where they read; pictures get their
   own wider one, because a shelf of browser worlds is not a column of prose. At 1440
   that is 1376px against the text column's 1216px, so a 3-up card goes 386 -> 440px:
   29% more picture area at the same scan rate and no extra page height.
   rem and percentages ONLY - never 100vw. The Windows scrollbar is outside the
   viewport width, and browser-qa asserts pageWidth <= width + 1. */
.plate-bleed {
  width: 100%;
  padding-inline: var(--gutter);
}

.plate-bleed > * {
  max-width: 100rem;
  margin-inline: auto;
}

/* The sticky filter rail lives in the plate rail with the grid it filters - it can only
   stick inside its own parent's box, and leaving it behind in .container meant that box
   ended immediately below it. It gets the plate gutter back so its rule spans the shelf. */
.plate-bleed > .filter-shell {
  width: 100%;
}

.site-header {
  position: fixed;
  z-index: 100;
  inset: 0 0 auto;
  min-height: var(--header-height);
  border-bottom: 1px solid rgba(61, 89, 104, 0.55);
  background: rgba(6, 10, 15, 0.92);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  transition: background 240ms ease, border-color 240ms ease;
}

/* At the top of the page the header dissolves into the poster. JS-gated. */
.site-header.is-top {
  border-bottom-color: transparent;
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.header-row {
  display: flex;
  min-height: var(--header-height);
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.brand {
  display: inline-flex;
  min-height: 2.75rem;
  align-items: center;
  gap: 0.7rem;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: -0.025em;
  text-decoration: none;
}

.brand-mark {
  color: var(--cyan);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.08em;
}

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

.desktop-nav {
  display: none;
  align-items: center;
  gap: 0.15rem;
}

.nav-link {
  display: inline-flex;
  min-height: 2.75rem;
  align-items: center;
  padding: 0.45rem 0.7rem;
  color: var(--muted);
  text-decoration: none;
  transition: color 160ms ease;
}

/* Pilot lights: each wing's door carries a dot in its room's color.
   The lit one is the room you are standing in. */
.nav-link::before {
  width: 5px;
  height: 5px;
  margin-right: 0.5rem;
  border-radius: 50%;
  background: var(--dot, transparent);
  content: "";
  opacity: 0.45;
  transition: opacity 160ms ease, box-shadow 160ms ease;
}

.nav-link[href="/psychopharmacology/"] { --dot: var(--amber); }
.nav-link[href="/games/"] { --dot: var(--cyan); }
.nav-link[href="/music/"] { --dot: var(--pink); }
.nav-link[href="/#connect"] { --dot: var(--violet); }

.nav-link:hover,
.nav-link[aria-current="page"] {
  color: var(--text);
}

.nav-link:hover::before,
.nav-link:focus-visible::before {
  opacity: 1;
}

.nav-link[aria-current="page"]::before {
  opacity: 1;
  box-shadow: 0 0 8px 1px var(--dot, transparent);
}

.menu-button,
.dialog-close {
  display: inline-flex;
  min-width: 2.75rem;
  min-height: 2.75rem;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--text);
  cursor: pointer;
  font-weight: 750;
}

.mobile-dialog {
  width: min(100%, 31rem);
  max-width: none;
  height: 100vh;
  height: 100dvh;
  max-height: none;
  margin: 0 0 0 auto;
  padding: 0;
  border: 0;
  background: var(--ink-soft);
  color: var(--text);
}

.mobile-dialog::backdrop {
  background: rgba(0, 0, 0, 0.78);
}

.dialog-shell {
  display: flex;
  min-height: 100%;
  flex-direction: column;
  padding: 1rem var(--gutter) 2rem;
}

.dialog-head {
  display: flex;
  min-height: var(--header-height);
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border-bottom: 1px solid var(--line);
}

.dialog-label {
  color: var(--muted);
}

.dialog-nav {
  display: grid;
  margin-top: 2rem;
  gap: 0.4rem;
}

.dialog-nav a {
  display: flex;
  min-height: 3.5rem;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--line);
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 6vw, 2rem);
  font-weight: 600;
  text-decoration: none;
}

.dialog-nav a::after {
  color: var(--dot, var(--cyan));
  content: "→";
  font-family: var(--font-body);
  font-size: 1rem;
}

.dialog-nav a[href="/psychopharmacology/"] { --dot: var(--amber); }
.dialog-nav a[href="/games/"] { --dot: var(--cyan); }
.dialog-nav a[href="/music/"] { --dot: var(--pink); }
.dialog-nav a[href="/#connect"] { --dot: var(--violet); }

.dialog-note {
  margin: auto 0 0;
  padding-top: 2rem;
  color: var(--muted);
  font-size: 0.9rem;
}

.button {
  display: inline-flex;
  min-height: 2.875rem;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 0.7rem 1.1rem;
  border: 1px solid var(--line-bright);
  background: transparent;
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  transition: border-color 180ms ease, background 180ms ease, color 180ms ease, transform 180ms var(--ease);
}

.button:hover {
  border-color: var(--wing);
  color: var(--text);
}

/* Game-feel: fast attack, unhurried release. */
.button:active,
.filter-button:active,
.menu-button:active,
.audio-toggle:active {
  transform: translateY(1px);
  transition-duration: 45ms;
}

/* The two biggest targets on the site — the card art and the card title, 24 of each on
   /games/ — acknowledged nothing at all. With the resting filter gone there is now no
   other feedback before navigation, least of all on a phone. `transform` is the press
   lane; the entrance animation owns `translate` on the ancestor, so they never contend. */
.card-media:active,
.card-title a:active {
  transform: translateY(1px);
  transition: transform 45ms ease;
}

.button-primary {
  border-color: var(--cyan);
  background: var(--cyan);
  color: #001312;
}

.button-primary:hover {
  border-color: #73fff5;
  background: #73fff5;
  color: #001312;
}

.button-pink {
  border-color: var(--pink);
  color: #ffd9e8;
}

.button-pink:hover {
  border-color: var(--pink);
  background: rgba(255, 90, 158, 0.12);
}

.button-quiet {
  border-color: transparent;
  background: transparent;
  color: var(--text-soft);
}

.button-arrow::after {
  content: "→";
  transition: transform 180ms var(--ease);
}

.button-arrow:hover::after,
.button-arrow:focus-visible::after {
  transform: translateX(0.25em);
}

.external-link::after {
  content: "↗";
  font-size: 0.92em;
}

.eyebrow {
  display: flex;
  align-items: center;
  margin-bottom: 1.1rem;
  color: var(--wing);
  line-height: 1.4;
}

/* Chapter numbers, promoted to architecture. aria-hidden in the templates. */
.ghost-index {
  position: absolute;
  z-index: -1;
  top: -0.16em;
  right: -0.04em;
  color: transparent;
  font-family: var(--font-display);
  font-size: clamp(7rem, 21vw, 17rem);
  font-weight: 700;
  letter-spacing: -0.05em;
  line-height: 0.75;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-text-stroke: 1px rgba(158, 176, 185, 0.2);
  -webkit-text-stroke-color: color-mix(in srgb, var(--wing) 30%, transparent);
}

@supports not (-webkit-text-stroke: 1px red) {
  .ghost-index {
    color: rgba(158, 176, 185, 0.07);
    color: color-mix(in srgb, var(--wing) 7%, transparent);
  }
}

/* The numeral was absolutely positioned INSIDE the text column, so on the one page
   with a heading long enough to reach it ("Psychopharmacology" is a single unbreakable
   726px word) it struck straight through the words. It now hangs in the seam above the
   chapter rule, sized off the seam tokens rather than a viewport clamp — so it cannot
   grow into the text at any width, by construction rather than by tuning. */
@media (min-width: 45rem) {
  .section-heading .ghost-index {
    top: auto;
    right: -0.04em;
    bottom: calc(100% - 0.06em);
    font-size: calc((var(--seam-top) + var(--seam-bot)) * 0.86);
  }
}

.hero {
  position: relative;
  padding: calc(var(--header-height) + clamp(2rem, 5vw, 4.5rem)) 0 clamp(2.5rem, 5vw, 4rem);
  border-bottom: 1px solid transparent;
  border-image: linear-gradient(90deg, transparent, var(--line-bright) 12% 88%, transparent) 1;
}

/* The hallway: three rooms leak their light into the entry. Total alpha stays low. */
.hero::before {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(42rem 26rem at 8% 108%, rgba(255, 193, 107, 0.05), transparent 65%),
    radial-gradient(48rem 30rem at 55% -25%, rgba(64, 238, 225, 0.06), transparent 65%),
    radial-gradient(40rem 26rem at 100% 75%, rgba(255, 90, 158, 0.05), transparent 65%);
  content: "";
  pointer-events: none;
}

.hero .container {
  position: relative;
}

.role-line {
  max-width: none;
  margin-bottom: 1.4rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line-bright);
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.hero h1 {
  max-width: none;
  margin-bottom: 1.4rem;
  font-size: clamp(3rem, 11vw, 9.5rem);
  letter-spacing: -0.065em;
  line-height: 0.95;
}

/* One word carries the three rooms — the same seam the footer wears.
   The padding/margin pair widens the background-clip paint box so the
   negative letter-spacing can't shave the last glyph's edge. */
.alive-word {
  padding-inline: 0.08em;
  margin-inline: -0.08em;
  color: var(--cyan);
}

@supports (-webkit-background-clip: text) or (background-clip: text) {
  .alive-word {
    background: linear-gradient(97deg, var(--amber) 8%, var(--cyan) 52%, var(--pink) 94%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.hero-deck {
  max-width: 44rem;
  margin-bottom: 1.9rem;
  color: var(--text-soft);
  font-size: clamp(1.02rem, 2.6vw, 1.22rem);
}

.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* The one painting on the site. It used to sit in a lit box with a bracket and a
   label, which is how you present a specimen; a picture only needs somewhere to hang.
   The mat stays as a load-in ground so the plate never flashes page-black. */
.hero-art {
  position: relative;
  margin: clamp(2rem, 5vw, 3rem) 0 0;
  background: #05080d;
}

/* Phones now get a real 3:2 cut of the plate rather than a 16:9 punch-out of it, so
   the ratio here has to match the source heroPicture() actually serves them - and the
   switch has to happen at the SAME breakpoint (45rem) as that <source media> query, or
   there is a band of widths where the CSS box and the downloaded image disagree.
   At 390w this is 239px tall, against 201px for the old punch-out and 140px if the
   authored 41:16 simply carried down. */
.hero-art img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  object-position: center;
}

/* The stats ruler: three numbers, three rooms, three ticks. */
.proof-list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: clamp(2rem, 4vw, 3rem) 0 0;
  padding: 0;
}

.proof-list > div {
  position: relative;
  min-width: 0;
  padding: 1.1rem 0.65rem 0 0;
  border-top: 1px solid var(--line);
}

.proof-list > div::before {
  position: absolute;
  top: -1px;
  left: 0;
  width: 2rem;
  height: 2px;
  background: var(--tick, var(--cyan));
  content: "";
}

.proof-list > div:nth-child(1) { --tick: var(--amber); }
.proof-list > div:nth-child(2) { --tick: var(--cyan); }
.proof-list > div:nth-child(3) { --tick: var(--pink); }

/* The three numbers are the three doors. They already carried amber / cyan / pink
   ticks - the same three rooms the nav uses - so this needed no new colour and no new
   word, only the ability to press them.
   The anchor sits INSIDE the <dd> and stretches over the whole cell with a positioned
   ::after. Wrapping <dt> + <dd> in an <a> looks tidier and is invalid: a <dl>'s <div>
   may contain only <dt> and <dd>, and axe fails it on definition-list + dlitem. */
.proof-list a {
  display: inline-flex;
  min-height: 2.75rem;
  align-items: center;
  color: inherit;
  text-decoration: none;
}

.proof-list a::after {
  position: absolute;
  inset: 0;
  content: "";
}

.proof-list > div:hover dt,
.proof-list > div:focus-within dt {
  color: var(--tick, var(--cyan));
}

.proof-list > div:hover dd,
.proof-list > div:focus-within dd {
  color: var(--text-soft);
}

.proof-list dt,
.proof-list dd {
  transition: color 160ms ease;
}

.proof-list dt {
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 5vw, 3.2rem);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
}

.proof-list dd {
  margin: 0;
  color: var(--muted);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  line-height: 1.35;
}

.section {
  padding-block: var(--seam-top) var(--seam-bot);
}

/* Every section opens like a chapter: a full rule, a wing eyebrow, a ghost numeral. */
.section-heading {
  position: relative;
  z-index: 0;
  display: grid;
  margin-bottom: clamp(1.5rem, 3.5vw, 2.5rem);
  padding-top: 1.3rem;
  border-top: 1px solid var(--line-bright);
  gap: 1.25rem;
}

.section-heading h2 {
  max-width: 15ch;
  margin-bottom: 0;
  font-size: clamp(2.3rem, 7.4vw, 5rem);
  letter-spacing: -0.055em;
  line-height: 0.98;
}

#community .section-heading h2 {
  max-width: 19ch;
  font-size: clamp(2rem, 5.6vw, 4rem);
}

body[data-page="psych"] .subhero h1 {
  max-width: 14ch;
  font-size: clamp(2rem, 6vw, 5rem);
}

.section-heading-copy {
  max-width: 40rem;
}

.section-heading-copy p:last-child {
  margin-bottom: 0;
}

.section-action {
  margin-top: 1.25rem;
}

/* The server is translated from the tall community poster into native,
   responsive site elements instead of squeezing the poster into a side rail. */
.server-pillars {
  display: grid;
  gap: 1.75rem;
}

.server-pillar {
  position: relative;
  min-width: 0;
  padding: 1.45rem 0 0;
  border-top: 1px solid var(--line);
}

.pillar-number {
  display: block;
  margin-bottom: 1.2rem;
  color: var(--wing);
  font-family: var(--font-display);
  font-size: 1.45rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
}

.server-pillar h3 {
  margin-bottom: 0.75rem;
  font-size: clamp(1.35rem, 4vw, 1.9rem);
}

.server-pillar p {
  max-width: 34rem;
  margin-bottom: 0;
  color: var(--muted);
  font-size: 0.94rem;
}

.community-facts {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.community-facts-inline {
  margin-top: clamp(2.4rem, 5vw, 3.5rem);
  border-top: 1px solid var(--line-bright);
}

.community-facts li {
  min-width: 0;
  padding: 1.25rem 1.5rem 1.25rem 0;
  border-bottom: 1px solid var(--line);
}

.community-facts li:last-child {
  border-bottom: 0;
}

.community-facts strong,
.community-facts span {
  display: block;
}

.community-facts strong {
  margin-bottom: 0.15rem;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 1.05rem;
}

.community-facts span {
  color: var(--muted);
  font-size: 0.9rem;
}

.community-actions {
  margin-top: 1.4rem;
}

.community-cta {
  display: grid;
  margin-top: clamp(2.75rem, 6vw, 4.5rem);
  padding-top: clamp(1.5rem, 4vw, 2.25rem);
  border-top: 1px solid var(--line-bright);
  gap: 1.4rem;
}

.community-cta h2 {
  max-width: 18ch;
  margin-bottom: 0;
  font-size: clamp(2rem, 6vw, 3.8rem);
  letter-spacing: -0.055em;
}

.card-list,
.catalog-grid,
.route-grid,
.method-grid,
.community-grid {
  margin: 0;
  padding: 0;
  list-style: none;
}

.card-list,
.catalog-grid {
  display: grid;
  counter-reset: plate;
  gap: 2.25rem 1.75rem;
}

/* Index cards: entries in a catalog, not floating boxes. */
.card-list > li,
.catalog-grid > li {
  border-top: 1px solid var(--line);
  counter-increment: plate;
  padding-top: 1.35rem;
}

.work-card,
.album-card {
  display: flex;
  height: 100%;
  flex-direction: column;
}

/* The index counts what is ON THE SHELF, not what exists in the data. Filtering to
   Horror used to render 01, 09, 15, 16, 17 underneath a line reading "Showing 5
   worlds." A counter cannot do that: `[hidden] { display: none }` means a filtered-out
   item generates no box, so it never increments. */
.card-index::before {
  content: counter(plate, decimal-leading-zero);
}

.card-index {
  display: block;
  margin-bottom: 0.9rem;
  color: var(--wing);
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
}

/* The mat sits ABOVE the page, not below it. It used to paint #04070b — darker than
   the page itself — which is exactly what let the nine near-black cards dissolve into
   the background instead of reading as objects on a shelf. */
.card-media {
  position: relative;
  display: block;
  overflow: hidden;
  border: 1px solid var(--line);
  border-top-color: var(--rule-lit);
  background: var(--surface);
  transition: border-color 240ms ease;
}

/* Every card image is authored 16:9 — the frame matches it, so nothing crops. */
.work-card .card-media {
  aspect-ratio: 16 / 9;
}

.album-card .card-media {
  aspect-ratio: 1 / 1;
}

/* <picture> is only a source-picker; it must fill the frame so the image can. */
.card-media picture {
  display: block;
  width: 100%;
  height: 100%;
}

/* The art rests resolved, always, on every device. It used to sit at 55% chroma until
   you hovered it — a darkroom develop, which is a lovely idea on a page with one image
   and costs you the whole shelf on a page with twenty-four. */
.card-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The develop survives as a push toward you instead of a change of exposure.
   `scale` is the pointer lane: the entrance animation owns `transform` and the press
   owns nothing here, so the two can never write the same property. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .card-media img {
    transition: scale 520ms var(--ease);
  }
}

.work-card:hover .card-media,
.album-card:hover .card-media,
.work-card:focus-within .card-media,
.album-card:focus-within .card-media {
  border-color: var(--wing);
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .work-card:hover .card-media img,
  .album-card:hover .card-media img,
  .work-card:focus-within .card-media img,
  .album-card:focus-within .card-media img {
    scale: 1.03;
  }
}

.card-body {
  display: flex;
  min-height: 0;
  flex: 1;
  flex-direction: column;
  padding: 1.2rem 0 0;
}

.card-kicker {
  margin-bottom: 0.55rem;
  color: var(--wing);
  font-size: 0.68rem;
  line-height: 1.35;
}

.card-title {
  margin-bottom: 0.8rem;
  font-size: clamp(1.45rem, 4vw, 1.95rem);
  letter-spacing: -0.035em;
  line-height: 1.05;
}

.card-title a {
  display: inline-flex;
  min-height: 2.75rem;
  align-items: center;
  text-decoration: none;
  transition: color 180ms ease;
}

.card-title a:hover,
.card-title a:focus-visible {
  color: var(--wing);
}

.card-summary {
  margin-bottom: 1.2rem;
  color: var(--text-soft);
  font-size: 0.95rem;
  line-height: 1.62;
}

/* Metadata is printed, not chipped, and it stays attached to the summary it describes.
   The row's slack used to sit HERE (`margin-top: auto`), tearing a ~138px hole between
   a short summary and its own metadata. It now sits below, above the action. */
.card-meta {
  display: flex;
  flex-wrap: wrap;
  margin: 0 0 1.2rem;
  padding: 0;
  gap: 0.3rem 0;
  list-style: none;
}

.card-meta li {
  display: inline-flex;
  align-items: baseline;
  color: var(--muted);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  line-height: 1.6;
}

/* Hung off the item it FOLLOWS, not the one it precedes. As a ::before on the next
   item it was physically incapable of staying on the line it terminates, so a wrapping
   row started line two with an orphaned slash. */
.card-meta li:not(:last-child)::after {
  margin: 0 0.55rem;
  color: var(--line-bright);
  content: "/";
  font-weight: 400;
}

/* The action sits on the floor of the card, so every button in a row shares one
   baseline. This is where a row's uneven slack goes: as breathing room in front of
   the press, never as a gap inside the reading. */
.card-actions {
  display: flex;
  flex-wrap: wrap;
  margin-top: auto;
  gap: 0.55rem;
}

/* The action sits at its own width instead of stretching the whole column. A
   full-bleed 386px slab of solid cyan out-emitted the picture above it - 24 of them
   on /games/ were the brightest thing on a page whose subject is the artwork. */
.card-actions .button {
  flex: 0 0 auto;
}

/* On the shelf the action is an outline: the picture is the thing being chosen, and
   every card is equally choosable, so nothing here earns a filled slab. The homepage
   featured three keep theirs - that is where "featured" means something, and it keeps
   one unambiguous press-shaped object in a stranger's path. Alex's verb is untouched. */
.catalog-grid .card-actions .button-primary {
  border-color: var(--line-bright);
  background: transparent;
  color: var(--text);
}

.catalog-grid .card-actions .button-primary:hover,
.catalog-grid .card-actions .button-primary:focus-visible {
  border-color: var(--wing);
  background: transparent;
  color: var(--text);
}

/* Three doorways, set as ledger rows. Each door is lit in its own wing. */
.route-grid {
  display: grid;
  border-bottom: 1px solid var(--line-bright);
  gap: 0;
}

.route-card {
  position: relative;
  display: grid;
  min-height: 0;
  padding: clamp(1.5rem, 4vw, 2.1rem) 0;
  border-top: 1px solid var(--line-bright);
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  column-gap: 1.5rem;
  text-decoration: none;
}

.route-card:nth-child(1) { --wing: var(--amber); }
.route-card:nth-child(2) { --wing: var(--pink); }
.route-card:nth-child(3) { --wing: var(--cyan); }

.route-card small {
  grid-column: 1;
  margin-bottom: 0.65rem;
  color: var(--wing);
  font-size: 0.7rem;
}

.route-card strong {
  grid-column: 1;
  max-width: none;
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(1.8rem, 5.5vw, 3.4rem);
  font-weight: 700;
  letter-spacing: -0.05em;
  line-height: 1.02;
  transition: color 180ms ease;
}

.route-card span {
  grid-column: 1;
  margin: 0;
  padding: 0.75rem 0 0;
  color: var(--wing);
  font-weight: 850;
  transition: transform 180ms var(--ease);
}

@media (min-width: 45rem) {
  .route-card {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .route-card span {
    grid-column: 2;
    grid-row: 1 / 3;
    padding: 0;
  }
}

.route-card:hover strong,
.route-card:focus-visible strong {
  color: var(--wing);
}

.route-card:hover span,
.route-card:focus-visible span {
  transform: translateX(0.4em);
}

.route-card:active {
  transform: translateY(1px);
  transition: transform 45ms ease;
}

/* The footer: the house seen from the street after dark.
   The seam is the whole site in one 2px line. */
.site-footer {
  padding: clamp(3rem, 7vw, 5rem) 0 6rem;
  border-top: 2px solid transparent;
  border-image: linear-gradient(90deg, rgba(255, 193, 107, 0.5) 0 33.4%, rgba(64, 238, 225, 0.5) 33.4% 66.7%, rgba(255, 90, 158, 0.5) 66.7% 100%) 1;
  background: #04070b;
}

.footer-wordmark {
  margin: 0 0 clamp(1.5rem, 4vw, 2.5rem);
  color: transparent;
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 10vw, 7.8rem);
  font-weight: 700;
  letter-spacing: -0.055em;
  line-height: 0.95;
  user-select: none;
  -webkit-user-select: none;
  -webkit-text-stroke: 1px var(--line-bright);
}

@supports not (-webkit-text-stroke: 1px red) {
  .footer-wordmark {
    color: var(--surface-2);
  }
}

.footer-grid {
  display: grid;
  gap: 1.5rem;
}

.footer-name {
  margin-bottom: 0.35rem;
  color: var(--text);
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 700;
}

.footer-copy {
  max-width: 34rem;
  margin-bottom: 0;
  color: var(--muted);
  font-size: 0.86rem;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem 1.4rem;
}

.footer-links a {
  display: inline-flex;
  min-height: 2.75rem;
  align-items: center;
  color: var(--text-soft);
  transition: color 160ms ease;
}

.footer-links a:hover,
.footer-links a:focus-visible {
  color: var(--cyan);
}

.footer-links a[href*="discord"]:hover {
  color: var(--amber);
}

.footer-links a[href*="suno"]:hover,
.footer-links a[href*="tiktok"]:hover {
  color: var(--pink);
}

.audio-toggle {
  position: fixed;
  z-index: 90;
  right: max(0.75rem, env(safe-area-inset-right));
  bottom: max(0.75rem, env(safe-area-inset-bottom));
  display: inline-flex;
  min-height: 2.75rem;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.75rem;
  border: 1px solid var(--line-bright);
  background: #080e14;
  color: var(--text);
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 800;
  box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.35);
  transition: transform 180ms var(--ease);
}

.audio-toggle::before {
  color: var(--cyan);
  content: "♪";
  font-size: 1rem;
}

/* The LED: the one idle animation on the site, and only while music is playing. */
.audio-toggle[aria-pressed="true"]::before {
  color: var(--pink);
  content: "♫";
  animation: led-breathe 3s ease-in-out infinite;
  text-shadow: 0 0 8px currentColor;
}

@keyframes led-breathe {
  50% { opacity: 0.55; }
}

.subhero {
  position: relative;
  padding: calc(var(--header-height) + clamp(3.5rem, 9vw, 6.5rem)) 0 clamp(2.5rem, 6vw, 4.5rem);
  border-bottom: 1px solid transparent;
  border-image: linear-gradient(90deg, transparent, var(--line-bright) 12% 88%, transparent) 1;
}

/* One aura per room, from above, in the room's color. */
.subhero::before {
  position: absolute;
  inset: 0;
  background: radial-gradient(58rem 32rem at 80% -22%, var(--wing-dim), transparent 68%);
  content: "";
  pointer-events: none;
}

.subhero-grid {
  position: relative;
  display: grid;
  align-items: end;
  gap: 2rem;
}

.subhero-grid > div:first-child {
  position: relative;
  z-index: 0;
}

/* Making the inner column static hands the containing block to .subhero-grid, which is
   already relative; its z-index: 0 still creates the stacking context, so paint order
   is unchanged. The numeral lands in the empty air over the stat plate instead of on
   top of the headline. */
@media (min-width: 60rem) {
  .subhero-grid > div:first-child {
    position: static;
  }

  .subhero .ghost-index {
    top: -0.24em;
    right: -0.04em;
    bottom: auto;
    font-size: clamp(6rem, 11.5vw, 10.5rem);
  }
}

.subhero h1 {
  max-width: none;
  margin-bottom: 1.2rem;
  font-size: clamp(3.1rem, 13vw, 8.5rem);
  letter-spacing: -0.065em;
  line-height: 0.9;
}

.subhero-deck {
  max-width: 43rem;
  margin-bottom: 0;
  font-size: clamp(1.02rem, 2.8vw, 1.25rem);
}

/* Spec plate, not a widget. */
.subhero-stat {
  padding: 1rem 0 0;
  border-top: 1px solid var(--wing);
}

.subhero-stat strong,
.subhero-stat span {
  display: block;
}

.subhero-stat strong {
  color: var(--text);
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 7.5vw, 4.75rem);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.06em;
  line-height: 1;
}

.subhero-stat span {
  margin-top: 0.5rem;
  color: var(--muted);
  font-size: 0.72rem;
}

/* Method and community: ruled entries in the ledger. */
.method-grid,
.community-grid {
  display: grid;
  gap: 1.75rem 1.5rem;
}

.method-card,
.community-card {
  position: relative;
  padding: 1.3rem 0 0;
  border-top: 1px solid var(--line);
}

.method-number {
  margin-bottom: 1.6rem;
  color: var(--wing);
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

.method-card h3,
.community-card h3 {
  margin-bottom: 0.7rem;
  font-size: 1.25rem;
}

.method-card p,
.community-card p {
  margin-bottom: 0;
  color: var(--muted);
  font-size: 0.92rem;
}

/* The Discord welcome, translated. Both plates are the server's own words set in the
   site's own type — not the posters pasted in as pictures, which would be unreadable
   at 390px, invisible to a screen reader, and 1.5MB of text-as-JPEG. Ruled plates, no
   glow: the hero aura is already this view's one aura. */
.server-notice {
  margin-bottom: clamp(3rem, 7vw, 5rem);
  padding: clamp(1.25rem, 3.5vw, 2rem);
  border: 1px solid var(--line);
  border-left: 3px solid var(--wing);
  background: var(--surface);
}

.server-notice .eyebrow {
  margin-bottom: 0.9rem;
}

.notice-body {
  max-width: 44rem;
  margin-bottom: 1.15rem;
  color: var(--text-soft);
  font-size: clamp(1rem, 2.6vw, 1.15rem);
}

.notice-lede {
  max-width: 44rem;
  margin-bottom: 0.55rem;
  color: var(--muted);
  font-size: 0.94rem;
}

/* The poster's punchline, kept as a punchline. Display scale in the room's colour is
   the loudest this page gets; it costs nothing from the discipline budget. */
.notice-answer {
  margin-bottom: 0;
  color: var(--wing);
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 5.5vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.045em;
  line-height: 1;
}

/* House rules: one rule per row, numeral in the room's colour. The numeral is a CSS
   counter, not template output — same reason as the card index: reordering or adding a
   rule in site-data.json can then never leave a stale number behind it. */
.rules-list {
  display: grid;
  margin: 0;
  padding: 0;
  counter-reset: houserule;
  gap: 1.6rem;
  list-style: none;
}

.rules-list > li {
  display: grid;
  min-width: 0;
  padding-top: 1.3rem;
  border-top: 1px solid var(--line);
  counter-increment: houserule;
  gap: 0.6rem 1.5rem;
}

.rules-list > li::before {
  align-self: start;
  color: var(--wing);
  content: counter(houserule, decimal-leading-zero);
  font-family: var(--font-display);
  font-size: 1.45rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
}

.rules-list h3 {
  margin-bottom: 0;
  font-size: clamp(1.15rem, 3.4vw, 1.5rem);
}

.rules-list p {
  max-width: 44rem;
  margin-bottom: 0;
  color: var(--muted);
  font-size: 0.94rem;
}

/* The last rule has no bottom rule under it, so the default section-action gap reads
   as if the buttons belong to rule 05. Give the door its own air. */
.rules-list + .section-action {
  margin-top: clamp(2rem, 4.5vw, 3rem);
}

/* The filter rail: a printed index tab bar, latched in the room's color. */
.filter-shell {
  position: sticky;
  z-index: 20;
  top: var(--header-height);
  margin-bottom: 2.25rem;
  padding: 0.85rem 0 0;
  border-bottom: 1px solid var(--line);
  background: rgba(6, 10, 15, 0.94);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
}

.filter-shell fieldset {
  display: flex;
  margin: 0;
  padding: 0;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 1.1rem;
  border: 0;
}

.filter-shell legend {
  width: 100%;
  margin-bottom: 0.35rem;
  color: var(--muted);
  font-size: 0.68rem;
}

.filter-button {
  min-height: 2.75rem;
  padding: 0.55rem 0.15rem;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color 160ms ease, border-color 160ms ease;
}

.filter-button:hover,
.filter-button:focus-visible {
  color: var(--text);
}

.filter-button[aria-pressed="true"] {
  border-bottom-color: var(--wing);
  color: var(--text);
}

.filter-status {
  margin: 0.55rem 0 0.75rem;
  color: var(--muted);
  font-size: 0.82rem;
}

/* The Doopliss intro: an editorial block, not a callout box. */
.music-intro {
  display: grid;
  margin-bottom: clamp(2.5rem, 6vw, 4rem);
  padding-top: 1.3rem;
  border-top: 1px solid var(--line-bright);
  gap: 1.25rem;
}

.music-intro h2 {
  margin-bottom: 0.75rem;
  font-size: clamp(2rem, 5vw, 3.2rem);
  letter-spacing: -0.05em;
}

.music-intro p {
  max-width: 43rem;
  margin-bottom: 0;
}

.page-note {
  margin-top: 2rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.86rem;
}

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

/* Entrance: entries rise onto the ledger as you scroll. Zero JS; browsers
   without scroll-driven animations (and reduced-motion users) see everything.
   It targets the card INSIDE the <li>, not the <li> itself: the <li> carries the
   ledger's border-top, and animating it dragged the site's own hairlines up to 16px
   out of true mid-scroll. The rule stays nailed; the card rises off it. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .card-list > li > *,
    .catalog-grid > li > *,
    .method-card,
    .community-card,
    .route-card {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 45%;
    }
  }
}

/* The result beat. A new keyframe name on the inner card is required: reusing `rise`
   would collide with the view-timeline binding that owns the <li>'s translate. Four
   nth-child buckets, not a per-card stagger — the shelf settles, it does not cascade. */
@media (prefers-reduced-motion: no-preference) {
  .is-filtering .work-card {
    animation: settle 180ms var(--ease) both;
  }

  .is-filtering > li:nth-child(4n + 2) .work-card { animation-delay: 30ms; }
  .is-filtering > li:nth-child(4n + 3) .work-card { animation-delay: 60ms; }
  .is-filtering > li:nth-child(4n + 4) .work-card { animation-delay: 90ms; }
}

@keyframes settle {
  from {
    translate: 0 6px;
  }

  to {
    translate: none;
  }
}

/* Transform-family only, never opacity: text keeps full contrast at every scroll
   position. `translate` is the entrance lane — see the motion law at the top. */
@keyframes rise {
  from {
    translate: 0 22px;
  }

  to {
    translate: none;
  }
}

@media (min-width: 38rem) {
  .brand-word {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    white-space: normal;
  }

  .community-facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 2rem;
  }

  .community-facts li:nth-last-child(-n + 2) {
    border-bottom: 0;
  }

  .catalog-grid,
  .method-grid,
  .community-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 44.99rem) {
  .audio-toggle {
    width: 2.75rem;
    padding-inline: 0;
    justify-content: center;
  }

  .audio-toggle [data-audio-label] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  .section {
    padding-block: 4rem;
  }

  .ghost-index {
    font-size: clamp(5.5rem, 26vw, 8rem);
    top: -0.1em;
  }

  .homepage-shelf {
    display: flex;
    width: calc(100% + var(--gutter));
    margin-right: calc(var(--gutter) * -1);
    padding-right: var(--gutter);
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scroll-padding-inline: 0;
    scroll-snap-type: inline mandatory;
    scrollbar-width: thin;
  }

  .homepage-shelf > li {
    width: min(84vw, 23rem);
    flex: 0 0 auto;
    scroll-snap-align: start;
  }
}

@media (min-width: 45rem) {
  /* Wide enough for a number gutter: the numeral spans both text rows so the rule and
     its note hang off one left edge. Below this it stacks, numeral first. */
  .rules-list > li {
    grid-template-columns: 3.25rem minmax(0, 1fr);
  }

  .rules-list > li::before {
    grid-row: 1 / span 2;
  }

  .hero-art img {
    aspect-ratio: 41 / 16;
  }

  .card-list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  /* One principal plate and two studies. Three identical cards at the same size as the
     full catalog meant "featured" had no visual meaning at all - the row said nothing
     the /games/ grid did not already say. Scoped above the mobile scroll-snap rail
     (max-width: 44.99rem), so the rail's negative margin is never unwound. */
  .homepage-shelf[data-featured-games] {
    grid-template-columns: minmax(0, 1.85fr) minmax(0, 1fr);
  }

  .homepage-shelf[data-featured-games] > li:first-child {
    grid-row: span 2;
  }

  /* The principal spans two rows, so its cell is far taller than its own content. The
     first fix for that was `height: auto` on the card - it stopped stretching, which
     did stop the action floating 380px below the summary, but it paid for it with
     322-373px of dead page under the card at EVERY desktop width (measured 720-2200).
     The card stretches again; the PICTURE absorbs the slack instead of the gap above
     the button, so the action stays welded to the summary and the column bottoms line
     up with the two studies beside it. `flex-shrink: 0` keeps 16/9 as a hard floor -
     the frame can only ever grow. */
  .homepage-shelf[data-featured-games] > li:first-child .work-card {
    height: 100%;
  }

  .homepage-shelf[data-featured-games] > li:first-child .card-media {
    min-height: 0;
    flex: 1 0 auto;
  }

  .homepage-shelf[data-featured-games] > li:first-child .card-body {
    flex: 0 0 auto;
  }

  .section-heading {
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 0.75fr);
    align-items: end;
  }

  .footer-grid {
    grid-template-columns: minmax(0, 1.2fr) minmax(16rem, 0.8fr);
    align-items: end;
  }

  .music-intro {
    grid-template-columns: minmax(0, 1.2fr) minmax(16rem, 0.8fr);
    align-items: end;
  }
}

@media (min-width: 60rem) {
  :root {
    --header-height: 4.75rem;
  }

  .desktop-nav {
    display: flex;
  }

  .menu-button {
    display: none;
  }

  .server-pillars {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: clamp(1.5rem, 3vw, 2.5rem);
  }

  .community-facts-inline {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: clamp(1.5rem, 3vw, 2.5rem);
  }

  .community-facts-inline li {
    border-bottom: 0;
  }

  .community-cta {
    grid-template-columns: minmax(0, 1.2fr) auto;
    align-items: end;
  }

  .subhero-grid {
    grid-template-columns: minmax(0, 1.35fr) minmax(15rem, 0.45fr);
  }


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

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

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

@media (min-width: 72rem) {
  .nav-link {
    padding-inline: 0.85rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
  }
}

@media (prefers-contrast: more) {
  :root {
    --line: #587282;
    --line-bright: #8aa4b2;
    --muted: #c3d0d6;
  }

  body::after {
    display: none;
  }

  .site-header.is-top {
    background: rgba(6, 10, 15, 0.94);
    border-bottom-color: rgba(61, 89, 104, 0.55);
  }

  .ghost-index {
    display: none;
  }
}
