/* ─── Stellar Atlas, runtime tier — red star ──────────────────────────
   /runtime is the red point of the trinity (Lib blue, Lang yellow,
   Runtime red). The dark bg is tinted warm so the page temperature
   matches the runtime star instead of the cooler blue used on /libs. */
:root,
:root[data-theme="dark"] {
  --bg: #0e0506;
  --bg-elev: #170809;
  --bg-code: #1a090b;
  --rule: #2e181c;
  --rule-strong: #422530;
  --text: #efdcd8;
  --text-dim: #a4837f;
  --text-faint: #553f3c;
  /* Stellar-spectrum trinity. /runtime is the red star — matches
     the umbrella's Runtime card. */
  --star-lib: #6db4ff;
  --star-lang: #ffd54a;
  --star-runtime: #ff5c4a;
  --accent: var(--star-runtime);
  --accent-dim: #ff8a78;
  --accent-soft: #3a1410;
  --add: #6dd49a;
  --rem: #ff7a7a;
  --shadow-strong: rgba(0, 0, 0, 0.6);
  --mono: "JetBrains Mono", "SF Mono", Menlo, Consolas, ui-monospace, monospace;
  --sans: "Spectral", Georgia, "Times New Roman", serif;
  --serif: "Fraunces", Georgia, "Times New Roman", serif;
}

:root[data-theme="light"] {
  --bg: #f3efe3;
  --bg-elev: #f8f5ec;
  --bg-code: #ebe5d4;
  --rule: #d4cdba;
  --rule-strong: #b8af9a;
  --text: #1a1812;
  --text-dim: #5e574a;
  --text-faint: #94897a;
  --star-lib: #1a4880;
  --star-lang: #a07a18;
  --star-runtime: #b94a1f;
  --accent: var(--star-runtime);
  --accent-dim: #8a3614;
  --accent-soft: #f4d4cf;
  --add: #4d7c4f;
  --rem: #b94a1f;
  --shadow-strong: rgba(26, 24, 18, 0.1);
}

* {
  box-sizing: border-box;
}

html {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  font-variant-numeric: tabular-nums;
  text-rendering: optimizeLegibility;
  /* Smooth-scroll the page for anchor jumps. The h2[id] scroll-margin-top
   * already keeps targets clear of the sticky header. */
  scroll-behavior: smooth;
}

/* Respect the system "reduce motion" preference — disable smooth-scroll
 * (and any future motion) for users who've opted out at the OS level. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body {
  margin: 0;
  padding: 0;
  position: relative;
}

/* ─── Atmospheric backgrounds — three parallax layers ─────────────────
   Three-layer parallax starfield. Red dominates (this is the runtime
   tier of the trinity — see Stellar Atlas comment up top).

   Layer 1 (html::before, deepest): soft nebulae backdrop, slowest.
   Layer 2 (body::before, middle): dense starfield, medium.
   Layer 3 (body::after, closest): bright foreground stars + near dust,
   fastest. */

html::before,
body::before,
body::after {
  content: "";
  position: fixed;
  /* Extend 60vh below viewport so when the layer translates up to -38vh
     (max parallax) there's still content covering the viewport bottom. */
  inset: 0 0 -60vh 0;
  pointer-events: none;
  will-change: transform;
  transform: translate3d(0, 0, 0);
}

/* ─── Layer 1: deep nebulae (html::before) — red-led ───────────────── */
:root[data-theme="dark"]::before,
:root:not([data-theme="light"])::before {
  z-index: 0;
  background-image:
    radial-gradient(ellipse 80vw 65vh at 78% 18%, rgba(255, 92, 74, 0.13), transparent 65%),
    radial-gradient(ellipse 65vw 50vh at 12% 75%, rgba(109, 180, 255, 0.08), transparent 65%),
    radial-gradient(ellipse 55vw 60vh at 55% 110%, rgba(180, 110, 220, 0.06), transparent 60%);
  animation: layer-deep linear;
  animation-timeline: scroll(root);
}

/* ─── Layer 2: dense starfield (body::before) ───────────────────────── */
:root[data-theme="dark"] body::before,
:root:not([data-theme="light"]) body::before {
  z-index: 0;
  background-image:
    radial-gradient(2px 2px at 8% 18%, rgba(255, 255, 255, 0.85) 25%, rgba(255, 255, 255, 0.18) 55%, transparent 100%),
    radial-gradient(2px 2px at 23% 76%, rgba(255, 255, 255, 0.8) 25%, rgba(255, 255, 255, 0.15) 55%, transparent 100%),
    radial-gradient(2px 2px at 67% 12%, rgba(255, 255, 255, 0.85) 25%, rgba(255, 255, 255, 0.18) 55%, transparent 100%),
    radial-gradient(2px 2px at 23% 22%, rgba(255, 255, 255, 0.8) 25%, rgba(255, 255, 255, 0.15) 55%, transparent 100%),
    radial-gradient(2px 2px at 49% 53%, rgba(255, 170, 150, 0.85) 25%, rgba(255, 92, 74, 0.18) 55%, transparent 100%),
    radial-gradient(2px 2px at 38% 4%, rgba(255, 255, 255, 0.75) 25%, rgba(255, 255, 255, 0.14) 55%, transparent 100%),
    radial-gradient(1px 1px at 14% 52%, rgba(255, 255, 255, 0.55) 50%, transparent 100%),
    radial-gradient(1px 1px at 35% 7%, rgba(255, 255, 255, 0.45) 50%, transparent 100%),
    radial-gradient(1px 1px at 49% 88%, rgba(255, 255, 255, 0.5) 50%, transparent 100%),
    radial-gradient(1px 1px at 73% 53%, rgba(255, 255, 255, 0.45) 50%, transparent 100%),
    radial-gradient(1px 1px at 18% 38%, rgba(255, 255, 255, 0.4) 50%, transparent 100%),
    radial-gradient(1px 1px at 31% 65%, rgba(255, 255, 255, 0.5) 50%, transparent 100%),
    radial-gradient(1px 1px at 47% 28%, rgba(255, 255, 255, 0.45) 50%, transparent 100%),
    radial-gradient(1px 1px at 58% 82%, rgba(255, 255, 255, 0.4) 50%, transparent 100%),
    radial-gradient(1px 1px at 64% 38%, rgba(255, 255, 255, 0.5) 50%, transparent 100%),
    radial-gradient(1px 1px at 76% 18%, rgba(255, 255, 255, 0.45) 50%, transparent 100%),
    radial-gradient(1px 1px at 84% 62%, rgba(255, 255, 255, 0.4) 50%, transparent 100%),
    radial-gradient(1px 1px at 95% 28%, rgba(255, 255, 255, 0.5) 50%, transparent 100%),
    radial-gradient(1px 1px at 4% 84%, rgba(255, 255, 255, 0.4) 50%, transparent 100%),
    radial-gradient(1px 1px at 28% 92%, rgba(255, 255, 255, 0.45) 50%, transparent 100%),
    radial-gradient(1px 1px at 41% 18%, rgba(255, 92, 74, 0.5) 50%, transparent 100%),
    radial-gradient(1px 1px at 87% 8%, rgba(109, 180, 255, 0.4) 50%, transparent 100%),
    radial-gradient(1px 1px at 12% 5%, rgba(255, 255, 255, 0.4) 50%, transparent 100%),
    radial-gradient(1px 1px at 52% 96%, rgba(255, 255, 255, 0.4) 50%, transparent 100%);
  background-size: 100vw 160vh;
  animation: layer-mid linear;
  animation-timeline: scroll(root);
}

/* ─── Layer 3: foreground bright stars + near-dust (body::after) ─────── */
:root[data-theme="dark"] body::after,
:root:not([data-theme="light"]) body::after {
  z-index: 0;
  background-image:
    /* Brightest foreground star is red on this site (runtime lead). */
    radial-gradient(3px 3px at 88% 38%, rgba(255, 170, 150, 1) 16%, rgba(255, 92, 74, 0.5) 38%, transparent 90%),
    radial-gradient(4px 4px at 41% 31%, rgba(255, 255, 255, 1) 14%, rgba(255, 255, 255, 0.35) 38%, transparent 90%),
    radial-gradient(3px 3px at 14% 78%, rgba(180, 210, 255, 0.95) 16%, rgba(109, 180, 255, 0.35) 38%, transparent 90%),
    radial-gradient(3px 3px at 71% 88%, rgba(255, 255, 255, 0.95) 16%, rgba(255, 255, 255, 0.3) 38%, transparent 90%),
    radial-gradient(ellipse 35vw 28vh at 85% 8%, rgba(255, 92, 74, 0.07), transparent 70%),
    radial-gradient(ellipse 30vw 25vh at 15% 92%, rgba(109, 180, 255, 0.05), transparent 70%);
  animation: layer-near linear;
  animation-timeline: scroll(root);
}

:root[data-theme="light"] body::before {
  z-index: 0;
  background-image: radial-gradient(circle at 1px 1px, rgba(26, 24, 18, 0.08) 1px, transparent 1px);
  background-size: 24px 24px;
}
:root[data-theme="light"]::before,
:root[data-theme="light"] body::after {
  display: none;
}

@keyframes layer-deep {
  to {
    transform: translate3d(0, -6vh, 0);
  }
}
@keyframes layer-mid {
  to {
    transform: translate3d(0, -22vh, 0);
  }
}
@keyframes layer-near {
  to {
    transform: translate3d(0, -38vh, 0);
  }
}

.page,
header {
  position: relative;
  z-index: 1;
}

/* ─── Binary star brand mark ──────────────────────────────────────────
   Same shape and animation as para.script.dev's .binary. The two stars
   share an `orbit` keyframe so they always stay 180° apart on the same
   circular path; each also has its own opacity twinkle so they shimmer
   asynchronously. transform-box: view-box puts transform-origin in
   viewport coords; 50% 50% is (16, 16) = the system's barycenter.

   Companion (amber) is the heavier body on this site — it orbits closer
   to the barycenter (cx=21, distance 5) while the lighter primary
   (blue-white) traces the wider arc (cx=5, distance 11). On para.script.dev
   the relationship inverts. */
.binary {
  display: inline-block;
  width: 32px;
  height: 32px;
  vertical-align: -10px;
  margin-right: 10px;
  /* drop-shadow extends past the viewBox; let it paint into adjacent
     flex gap instead of clipping at the SVG box. */
  overflow: visible;
}
.binary circle {
  transform-box: view-box;
  transform-origin: 50% 50%;
}
.binary .star-a {
  fill: var(--star-lib);
  /* Single shadow — stacked drop-shadows recompute every animation
     frame on the rotating star and spike scroll latency. */
  filter: drop-shadow(0 0 4px var(--star-lib));
  animation:
    orbit 14s linear infinite,
    twinkle-a 4.2s ease-in-out infinite;
}
.binary .star-b {
  fill: var(--star-runtime);
  filter: drop-shadow(0 0 4px var(--star-runtime));
  animation:
    orbit 14s linear infinite,
    twinkle-b 5.7s ease-in-out infinite;
}
@keyframes orbit {
  to {
    transform: rotate(360deg);
  }
}
@keyframes twinkle-a {
  0%,
  100% {
    opacity: 1;
  }
  45% {
    opacity: 0.78;
  }
}
@keyframes twinkle-b {
  0%,
  100% {
    opacity: 1;
  }
  60% {
    opacity: 0.7;
  }
}
@media (prefers-reduced-motion: reduce) {
  .binary .star-a,
  .binary .star-b {
    animation: none;
  }
}

.page {
  max-width: 900px;
  margin: 0 auto;
  padding: 72px 32px 128px;
}

header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  margin-top: 14px;
  margin-bottom: 56px;
  /* Floating pill — same shape as para.script.dev. Smaller surface +
     GPU-promoted nebulae underneath keep the backdrop-filter cost
     bounded enough to bring the blur back in. */
  position: sticky;
  top: 14px;
  z-index: 10;
  padding: 14px;
  background: color-mix(in srgb, var(--bg) 55%, transparent);
  backdrop-filter: blur(10px) saturate(150%);
  -webkit-backdrop-filter: blur(10px) saturate(150%);
  border: 1px solid var(--rule);
  border-radius: 14px;
}

/* Once anchor-linked, the heading would otherwise scroll under the sticky
 * header — pad the scroll target so it lands below the bar. */
h2[id] {
  scroll-margin-top: 64px;
}

.brand {
  display: inline-flex;
  align-items: center;
  font-family: var(--serif);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.018em;
  color: var(--text);
  font-variation-settings:
    "opsz" 144,
    "SOFT" 30;
}

/* Dual brand pair (see para-site for shared meaning). */
.brand-pair {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4ch;
}
.brand-active {
  color: var(--text);
  font-weight: 700;
}
.brand-sep {
  color: var(--text-faint);
  font-weight: 300;
  font-style: italic;
}
.brand-link {
  color: var(--text-faint);
  font-weight: 400;
  text-decoration: none;
  border-bottom: none;
  transition: color 160ms ease;
}
.brand-link:hover,
.brand-link:focus-visible {
  color: var(--accent);
  outline: none;
}

.nav {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--text-dim);
  display: flex;
  gap: 20px;
}

.nav a {
  color: inherit;
}
.nav a:hover {
  color: var(--text);
}

/* External-link glyph — anything pointing off-site (http/https in the nav)
 * gets a trailing ↗ so users can tell it leaves the page before clicking. */
.nav a[href^="http"]::after {
  content: " ↗";
  font-size: 0.85em;
  color: var(--text-faint);
  margin-left: 1px;
}
.nav a[href^="http"]:hover::after {
  color: var(--text-dim);
}

/* Floating action button — pinned to the bottom-right of the viewport,
   flips contextual ligatures site-wide. Stays inside a safe-area inset
   on iOS so it doesn't collide with the home indicator. */
.fab {
  position: fixed;
  right: max(16px, env(safe-area-inset-right, 16px));
  bottom: max(16px, env(safe-area-inset-bottom, 16px));
  z-index: 10;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  background: var(--bg-elev);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 8px 14px;
  cursor: pointer;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
  transition:
    color 80ms,
    border-color 80ms,
    background 80ms;
}
.fab:hover {
  color: var(--text);
  border-color: var(--text-dim);
  background: var(--bg-code);
}
.fab[aria-pressed="true"] {
  color: var(--accent);
  border-color: var(--accent-dim);
}

/* Ligature toggle: default off (no font-variant-ligatures hint), `on` when
   html.ligatures is present. JetBrains Mono (loaded as a webfont) ships
   with contextual ligatures for `=>`, `|>`, `!=`, `..=`, etc.; the toggle
   is a no-op if the user blocks the font. */
code,
pre,
.install,
.prompt,
.brand {
  font-variant-ligatures: none;
  font-feature-settings: "calt" 0;
}
html.ligatures code,
html.ligatures pre,
html.ligatures .install,
html.ligatures .prompt,
html.ligatures .brand {
  font-variant-ligatures: contextual;
  font-feature-settings: "calt" 1;
}

h1 {
  font-family: var(--serif);
  font-size: clamp(2.6rem, 1.9rem + 2.8vw, 4.2rem);
  line-height: 1.04;
  letter-spacing: -0.022em;
  font-weight: 600;
  margin: 0 0 24px;
  color: var(--text);
  font-variation-settings:
    "opsz" 144,
    "SOFT" 30;
}

h1 em {
  font-style: italic;
  font-weight: 400;
  color: var(--accent);
  font-variation-settings:
    "opsz" 144,
    "SOFT" 50;
}

.lede {
  font-family: var(--serif);
  font-size: 1.32rem;
  font-weight: 400;
  font-style: italic;
  line-height: 1.45;
  letter-spacing: -0.005em;
  color: var(--text-dim);
  margin: 0 0 28px;
  max-width: 38em;
  font-variation-settings:
    "opsz" 36,
    "SOFT" 50;
}

h2 {
  font-family: var(--serif);
  font-size: clamp(1.6rem, 1.4rem + 0.8vw, 2.1rem);
  font-weight: 500;
  letter-spacing: -0.012em;
  color: var(--text);
  margin: 80px 0 20px;
  padding-top: 28px;
  border-top: 1px solid var(--rule);
  font-variation-settings:
    "opsz" 144,
    "SOFT" 30;
}

h3 {
  font-family: var(--serif);
  font-size: 1.32rem;
  font-weight: 600;
  letter-spacing: -0.008em;
  color: var(--text);
  margin: 36px 0 10px;
  font-variation-settings:
    "opsz" 36,
    "SOFT" 50;
}

h3 .import {
  font-family: var(--mono);
  font-size: 0.78em;
  color: var(--accent);
  font-weight: 500;
}

p {
  margin: 0 0 14px;
}

a {
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  transition: border-color 120ms ease;
}
a:hover {
  border-bottom-color: var(--accent);
}

code {
  font-family: var(--mono);
  font-size: 0.92em;
  background: var(--bg-elev);
  padding: 1px 6px;
  border-radius: 3px;
  color: var(--text);
}

pre {
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.55;
  background: var(--bg-code);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 16px 18px;
  margin: 12px 0 20px;
  overflow-x: auto;
  -webkit-text-size-adjust: 100%;
}

pre code {
  background: none;
  padding: 0;
  color: inherit;
  font-size: inherit;
}

/* Code blocks inside <pre data-lang="…"> are rendered through Shiki at
   build time (scripts/highlight-landing.ts) with dual-theme output: each
   span carries `--shiki-light` and `--shiki-dark` CSS variables but no
   default `color`. The rules below pick the active theme based on
   [data-theme] on <html>. Inline-prose token classes (.pb-kw / .pb-op)
   still use the accent variable directly. */
:root[data-theme="light"] pre[data-lang] span,
:root[data-theme="light"] pre[data-lang] {
  color: var(--shiki-light);
  font-style: var(--shiki-light-font-style, inherit);
  font-weight: var(--shiki-light-font-weight, inherit);
  text-decoration: var(--shiki-light-text-decoration, inherit);
}
:root[data-theme="dark"] pre[data-lang] span,
:root[data-theme="dark"] pre[data-lang],
:root:not([data-theme="light"]) pre[data-lang] span,
:root:not([data-theme="light"]) pre[data-lang] {
  color: var(--shiki-dark);
  font-style: var(--shiki-dark-font-style, inherit);
  font-weight: var(--shiki-dark-font-weight, inherit);
  text-decoration: var(--shiki-dark-text-decoration, inherit);
}
.pure,
.pb-kw {
  color: var(--accent);
  font-weight: 500;
  font-style: italic;
}
.op,
.pb-op {
  color: var(--accent-dim);
  font-weight: 600;
}
.pb-signal-ref {
  color: var(--accent);
}

.install {
  background: var(--bg-code);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 14px 18px;
  font-family: var(--mono);
  font-size: 14px;
  color: var(--text);
  margin-bottom: 14px;
  white-space: nowrap;
  overflow-x: auto;
}
.install .prompt {
  color: var(--text-faint);
  user-select: none;
  margin-right: 10px;
}

.caveat {
  font-size: 13px;
  color: var(--text-faint);
  font-family: var(--mono);
  margin-bottom: 28px;
}

.modules h3 {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-top: 36px;
}

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

.diff {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin: 16px 0 20px;
}

.diff figure.code {
  margin: 0;
}

/* Per-block code wrapper. <figure> groups the language label
   (in <figcaption>) with the <pre> body so the two stay visually
   adjacent with no gap between them. The page-wide ligature FAB
   handles toggling, so the header is label-only. */
figure.code {
  margin: 12px 0 20px;
}
.code-header {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-faint);
  background: var(--bg-code);
  border: 1px solid var(--rule);
  border-bottom: none;
  border-top-left-radius: 6px;
  border-top-right-radius: 6px;
  padding: 6px 12px;
}
figure.code > pre {
  margin: 0;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
.code-lang {
  letter-spacing: 0.02em;
}

/* Tabbed code blocks — TypeScript / ParaBun toggle. Same .figure.code shell;
   the lang-toggle row replaces the static .code-header. JS in fx.js syncs
   all tabs on the page (and persists choice via localStorage). */
figure.code.lang-tabs .code-header {
  display: flex;
  gap: 0;
  padding: 0;
}
figure.code.lang-tabs .lang-toggle {
  display: flex;
  width: 100%;
}
figure.code.lang-tabs .lang-toggle button {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--text-faint);
  background: transparent;
  border: none;
  padding: 6px 14px;
  cursor: pointer;
  border-right: 1px solid var(--rule);
  transition: color 80ms ease;
}
figure.code.lang-tabs .lang-toggle button:hover {
  color: var(--text);
}
figure.code.lang-tabs .lang-toggle button.active {
  color: var(--text);
  background: var(--bg-code);
  position: relative;
}
figure.code.lang-tabs .lang-toggle button.active::after {
  content: "";
  position: absolute;
  inset: auto 0 -1px 0;
  height: 1px;
  background: var(--bg-code);
}
figure.code.lang-tabs pre[hidden] {
  display: none;
}

/* Pre-paint default. Avoids a flash where both panels are visible until
   fx.js loads. The dataset.lang on <html> is set in head before paint
   (see index.html); we mirror it here to drive initial visibility. */
html[data-lang="ts"] figure.code.lang-tabs pre[data-lang="parabun"],
html[data-lang="parabun"] figure.code.lang-tabs pre[data-lang="ts"] {
  display: none;
}
html[data-lang="ts"] figure.code.lang-tabs button[data-lang="ts"],
html[data-lang="parabun"] figure.code.lang-tabs button[data-lang="parabun"] {
  color: var(--text);
  background: var(--bg-code);
}

@media (max-width: 640px) {
  .diff {
    grid-template-columns: 1fr;
  }
}

table.bench {
  width: 100%;
  border-collapse: collapse;
  margin: 14px 0 8px;
  font-family: var(--mono);
  font-size: 13px;
}

table.bench th,
table.bench td {
  padding: 8px 12px;
  text-align: left;
  border-bottom: 1px solid var(--rule);
}

table.bench th {
  color: var(--text-faint);
  font-weight: 400;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.08em;
}

table.bench td.num-cell {
  text-align: right;
  color: var(--text-dim);
}

table.bench tr.winner td {
  color: var(--text);
}
table.bench tr.winner td.num-cell {
  color: var(--accent);
}

.footnote {
  font-size: 14px;
  color: var(--text-dim);
}

.footnote code {
  background: var(--bg-code);
}

footer {
  margin-top: 96px;
  padding-top: 24px;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 13px;
  color: var(--text-faint);
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  justify-content: space-between;
}

footer a {
  color: var(--text-dim);
}
footer a:hover {
  color: var(--text);
}

.disclaimer {
  margin-top: 32px;
  padding: 14px 16px;
  border-left: 2px solid var(--accent-dim);
  background: rgba(245, 176, 65, 0.04);
  font-size: 14px;
  color: var(--text-dim);
}

/* Tier diagram: hardware → primitives → composed → apps. Vertical stack
   reading bottom-up; each row has a label on the left and a flex of
   module pills on the right. */
.tiers {
  margin: 24px 0 32px;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background: var(--bg-elev);
  overflow: hidden;
}
.tier {
  display: grid;
  grid-template-columns: 160px 1fr;
  align-items: center;
  border-bottom: 1px solid var(--rule);
  padding: 14px 16px;
  gap: 16px;
}
.tier:last-child {
  border-bottom: none;
}
.tier-label {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-dim);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.tier-label em {
  display: block;
  margin-top: 3px;
  font-style: normal;
  font-size: 11px;
  color: var(--text-faint);
  text-transform: none;
  letter-spacing: 0;
}
.tier-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.tier-row .mod-pill {
  font-family: var(--mono);
  font-size: 13px;
  padding: 4px 10px;
  border-radius: 3px;
  background: var(--bg-code);
  border: 1px solid var(--rule);
  color: var(--accent);
  /* Override global a { border-bottom } and underline. */
  text-decoration: none;
  display: inline-block;
  transition:
    border-color 120ms ease,
    background-color 120ms ease;
}
a.mod-pill {
  /* Re-state at higher specificity so the global `a` rule's border-bottom
     doesn't bleed through. */
  border-bottom: 1px solid var(--rule);
}
a.mod-pill:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-code));
}
.tier-row .mod-pill.planned {
  color: var(--text-faint);
  border-style: dashed;
}
a.mod-pill.planned {
  border-bottom-style: dashed;
}
.tier-row .mod-pill.hw {
  color: var(--text-dim);
  background: transparent;
  border-style: dotted;
}
a.mod-pill.hw {
  border-bottom-style: dotted;
}
a.mod-pill.hw:hover {
  color: var(--text);
  border-color: var(--text-dim);
}
@media (max-width: 640px) {
  .tier {
    grid-template-columns: 1fr;
    gap: 8px;
  }
}

.disclaimer strong {
  color: var(--text);
  font-weight: 500;
}

/* One-shot accent pulse on bench .winner rows when the table scrolls
   into view, paired with the count-up in fx.js. */
table.bench tr.winner td {
  transition: background-color 240ms ease;
}
table.bench tr.winner.pulse td {
  background-color: rgba(245, 176, 65, 0.1);
}

/* ─── Docs layout: sidebar + article column ───────────────────────────── */
.docs-body .page.docs-layout {
  max-width: 1180px;
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr;
  gap: 0 56px;
  padding: 72px 32px 128px;
}
.docs-body .page.docs-layout > header {
  grid-column: 1 / -1;
}
.docs-side {
  grid-column: 1;
  grid-row: 2;
  position: sticky;
  top: 88px;
  /* Stay above any default-z-index article content. Wide code blocks
     and tables sometimes establish their own stacking contexts via
     overflow: auto + scroll snapping; without an explicit z-index the
     sticky sidebar can paint behind them. */
  z-index: 2;
  align-self: start;
  max-height: calc(100vh - 120px);
  overflow-y: auto;
  font-size: 14px;
  border-right: 1px solid var(--rule);
  padding-right: 32px;
  /* Opaque background so the sticky sidebar masks article content that
     might horizontally overflow into column 1's track during a sideways
     scroll on a wide pre block. */
  background: var(--bg);
}

/* The mobile-only disclosure trigger. Hidden on desktop; the nav is
   shown directly. On mobile the toggle becomes a tap target labelled
   with the current page title, and the nav is collapsed until tapped. */
.docs-nav-toggle {
  display: none;
}
.docs-nav-panel {
  /* Default: visible on desktop. The mobile breakpoint hides this
     unless the .open class is set by the toggle script. */
  display: block;
}
.docs-side h4 {
  margin: 0 0 8px 0;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 11px;
  color: var(--text-faint);
  font-weight: 500;
}
.docs-side h4:not(:first-of-type) {
  margin-top: 24px;
}
.docs-side ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.docs-side li {
  margin: 0;
}
.docs-side a {
  display: block;
  padding: 4px 0;
  color: var(--text-dim);
  text-decoration: none;
  border-bottom: none;
  font-family: var(--mono);
}
.docs-side a:hover {
  color: var(--text);
}
.docs-side a.active {
  color: var(--accent);
}
.docs-content {
  grid-column: 2;
  grid-row: 2;
  min-width: 0;
}
.docs-content h1 {
  margin-top: 0;
}
.docs-content > .lede {
  margin-top: -8px;
  color: var(--text-dim);
}
.docs-content h2 {
  margin-top: 56px;
  scroll-margin-top: 88px;
}
.docs-content h3 {
  margin-top: 32px;
  scroll-margin-top: 88px;
}
.docs-content table {
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0;
  font-size: 14px;
}
.docs-content table th,
.docs-content table td {
  text-align: left;
  padding: 8px 12px;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
.docs-content table th {
  color: var(--text-dim);
  font-weight: 500;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: transparent;
}
.docs-content blockquote {
  margin: 16px 0;
  padding: 12px 16px;
  border-left: 3px solid var(--accent-dim);
  background: var(--bg-elev);
  color: var(--text-dim);
  font-style: italic;
}
.docs-content blockquote code {
  font-style: normal;
}
.docs-content figure.code {
  margin: 16px 0;
}
.docs-content ul,
.docs-content ol {
  padding-left: 24px;
}
.docs-content ul li,
.docs-content ol li {
  margin: 4px 0;
}
.docs-content hr {
  border: none;
  border-top: 1px solid var(--rule);
  margin: 32px 0;
}

@media (max-width: 880px) {
  .docs-body .page.docs-layout {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  .docs-side {
    /* Drop sticky/scroll on mobile — the disclosure handles overflow
       inside an explicit panel rather than via a 40vh scroll cage. */
    position: static;
    max-height: none;
    overflow: visible;
    border-right: none;
    border-bottom: 1px solid var(--rule);
    padding-right: 0;
    padding-bottom: 8px;
    margin-bottom: 8px;
    z-index: auto;
  }
  .docs-content {
    grid-column: 1;
    /* Sidebar already occupies (1, 2) — without an explicit row override,
       the article would stack into the same cell. Row 3 lays it cleanly
       below the sidebar. */
    grid-row: 3;
  }

  /* Show the disclosure trigger; collapse the nav until the toggle opens it.
     The label is a tap-friendly mono text button — flush with the brand and
     header type to fit the rest of the chrome. */
  .docs-nav-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 10px 0;
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--rule);
    color: var(--text-dim);
    font-family: var(--mono);
    font-size: 13px;
    cursor: pointer;
    text-align: left;
    -webkit-tap-highlight-color: transparent;
  }
  .docs-nav-toggle:hover,
  .docs-nav-toggle:focus-visible {
    color: var(--text);
    outline: none;
  }
  .docs-nav-toggle-label {
    /* Long module titles can elide rather than wrap the toggle. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-right: 12px;
  }
  .docs-nav-toggle-caret {
    color: var(--text-faint);
    font-size: 11px;
    transition: transform 140ms ease;
    flex-shrink: 0;
  }
  .docs-nav-toggle[aria-expanded="true"] .docs-nav-toggle-caret {
    transform: rotate(180deg);
  }

  .docs-nav-panel {
    display: none;
    padding-top: 12px;
    /* Inline expand, capped so 20 modules don't push the article off-screen.
       Internal scroll keeps the article reachable without leaving the page. */
    max-height: 60vh;
    overflow-y: auto;
  }
  .docs-nav-panel.open {
    display: block;
  }
}

/* ─── Mobile (≤640px) ─────────────────────────────────────────────────── */
@media (max-width: 640px) {
  .page,
  .docs-body .page.docs-layout {
    padding: 32px 18px 96px;
  }

  /* Stack the brand above the nav — 7 nav items don't fit beside the brand
     on a phone. Drop sticky too: a wrapped header would eat too much vertical
     space when pinned. */
  header {
    position: static;
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
    padding: 0;
    margin-top: 0;
    margin-bottom: 36px;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom: none;
  }
  .nav {
    flex-wrap: wrap;
    gap: 14px;
    font-size: 12px;
  }

  /* With sticky off, anchored headings can land right at the top edge. */
  h2[id],
  .docs-content h2,
  .docs-content h3 {
    scroll-margin-top: 16px;
  }

  h1 {
    font-size: 32px;
  }
  h2 {
    margin: 48px 0 16px;
  }

  /* Install commands wrap instead of forcing horizontal scroll. break-all
     is OK here — these are URLs and shell commands, not prose. */
  .install {
    white-space: normal;
    word-break: break-all;
    font-size: 13px;
    padding: 12px 14px;
  }

  pre {
    font-size: 12px;
    padding: 12px 14px;
  }

  /* Bench tables have rigid numeric columns that shouldn't wrap. Let the
     table itself scroll horizontally; display: block keeps the inner
     table semantics intact. */
  table.bench {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
    font-size: 12px;
  }
  table.bench th,
  table.bench td {
    padding: 6px 10px;
  }

  /* Docs API tables (Option / Default / Description shape): descriptions
     can wrap, so just tighten cells — no horizontal scroll needed. */
  .docs-content table {
    font-size: 13px;
  }
  .docs-content table th,
  .docs-content table td {
    padding: 6px 8px;
  }

  footer {
    margin-top: 64px;
    gap: 12px;
  }

  /* Pull the FAB tighter to the corner so it doesn't overlap the last
     content line. */
  .fab {
    right: max(10px, env(safe-area-inset-right, 10px));
    bottom: max(10px, env(safe-area-inset-bottom, 10px));
    font-size: 11px;
    padding: 6px 12px;
  }
}

/* Canvas-based starfield + nebulae (public/space.js) replaces the
   pseudo-element parallax layers. */
html::before,
body::before,
body::after {
  display: none !important;
}
