/* ==========================================================================
   Telecine — design system
   Built on Apple's interface foundations: size-specific tracking, translucent
   chrome with content scrolling under it, instant press feedback and
   critically-damped motion. The face is Plus Jakarta Sans.

   Dark only, by design: the brand is dark and the backdrop heroes are arbitrary
   photographs, which read reliably under a dark wash and poorly under a light
   one. Accessibility preferences (reduced motion, reduced transparency, higher
   contrast) are still honoured — see the bottom of this file.
   ========================================================================== */

/* ==========================================================================
   Plus Jakarta Sans — self-hosted
   Served from this repo rather than hotlinked from Google, so no visitor IP
   is handed to a third party (LG Munchen I, 3 O 17493/20). One variable file
   per subset covers every weight the page uses.
   ========================================================================== */

@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/plus-jakarta-sans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/plus-jakarta-sans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  color-scheme: dark;

  /* ---- Brand: the logo's two lenses and the frame between them ---- */
  --brand-blue: #3b9af7;
  --brand-teal: #1893a0;
  --star: #efb73e;

  /* ---- Type ----
     Plus Jakarta Sans (self-hosted, see @font-face above), with the platform
     faces behind it: they cover scripts outside the latin subsets and keep the
     page readable while the webfont loads. */
  --font: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "SF Pro Text",
    "Helvetica Neue", "Segoe UI Variable Text", "Segoe UI", Roboto, system-ui,
    sans-serif;

  /* ---- Motion: critically damped by default, no overshoot ---- */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --t-press: 100ms;
  --t-fast: 180ms;
  --t-base: 320ms;

  /* ---- Metrics in rem, so a larger text setting scales the layout too ---- */
  --wrap: 46rem;
  --wrap-wide: 74rem; /* artwork grids need more room than prose */
  --gutter: 1.5rem;
  --r-sm: 0.625rem;
  --r-md: 0.875rem;
  --r-lg: 1.25rem;
  --r-pill: 62.5rem;

  /* Height of the sticky chrome, so overlays can clear it. Updated at the
     breakpoint where the bar becomes two rows. */
  --chrome-h: 3.5rem;

  /* ---- Palette ----
     Taken from the iOS app: a neutral near-black page, black chrome, slate
     controls, blue-grey secondary text. Blue is for things you press, teal for
     things that are selected or measured. */
  --bg: #1a1c1e;
  /* Same colour at zero alpha. Fading to `transparent` instead would
     interpolate through grey and muddy the backdrop edges. */
  --bg-fade: rgba(26, 28, 30, 0);
  --bg-sunken: #121314;
  --surface: #2a313b;
  --surface-hover: #333b47;
  --material: rgba(0, 0, 0, 0.78);
  --material-edge: rgba(255, 255, 255, 0.07);
  --text: #f3f6f8;
  --text-2: #a3b6c0;
  --text-3: #869aa6;
  --hairline: rgba(255, 255, 255, 0.1);
  --hairline-strong: rgba(255, 255, 255, 0.22);
  --accent: var(--brand-blue);
  --tint: rgba(59, 154, 247, 0.14);
  /* The logo teal carries white text at 3.7:1 — fine for the logo, short of
     AA for labels. Fills with text on them use this deeper step (4.7:1). */
  --teal-fill: #16808c;
  --teal-track: rgba(255, 255, 255, 0.28);
  --shadow-md: 0 0.25rem 1.25rem rgba(0, 0, 0, 0.4);
  --shadow-lg: 0 0.75rem 2.5rem rgba(0, 0, 0, 0.5);

  /* Hero type sits on a photo whose lower edge dissolves into the page colour,
     so it needs its own wash to stay legible over any image. */
  --hero-ink: #ffffff;
  --hero-ink-2: rgba(255, 255, 255, 0.88);
  --hero-shadow: 0 0.0625rem 0.25rem rgba(0, 0, 0, 0.55);
  --hero-wash-near: rgba(0, 0, 0, 0.66);
  --hero-wash-mid: rgba(0, 0, 0, 0.25);
  --hero-wash-far: rgba(0, 0, 0, 0);
}

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

/* The UA's [hidden] rule loses to any class that sets display, and several
   elements here are toggled via the hidden attribute. Make it win. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  background-color: var(--bg);
}

body {
  margin: 0;
  background-color: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 1.0625rem; /* 17px — Apple's body size */
  line-height: 1.647;
  letter-spacing: -0.003em; /* body sits near zero */
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

/* Phosphor glyphs shouldn't inherit body leading */
[class*="ph-"] {
  line-height: 1;
  display: inline-block;
  vertical-align: middle;
}

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

.wrap-wide {
  max-width: var(--wrap-wide);
}

/* Labels that a screen reader needs but the design does not show. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ==========================================================================
   Chrome — translucent, sticky, content scrolls underneath
   ========================================================================== */

.chrome {
  position: sticky;
  top: 0;
  z-index: 100;
}

/* The colour bar rides along at the very top: lens, frame, lens. */
.color-bar {
  height: 0.25rem;
  background: linear-gradient(
    90deg,
    var(--brand-blue) 0 33.333%,
    var(--brand-teal) 33.333% 66.667%,
    var(--brand-blue) 66.667% 100%
  );
}

.nav-bar {
  background: var(--material);
  backdrop-filter: blur(1.25rem) saturate(180%);
  -webkit-backdrop-filter: blur(1.25rem) saturate(180%);
  box-shadow: inset 0 1px 0 var(--material-edge);
}

.nav-bar .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 3.25rem;
}

/* Scroll edge effect: a soft fade where content passes under the bar,
   rather than a permanent hard divider. */
.chrome::after {
  content: "";
  position: absolute;
  inset: 100% 0 auto 0;
  height: 1.25rem;
  background: linear-gradient(to bottom, var(--bg), transparent);
  opacity: 0;
  transition: opacity var(--t-base) var(--ease-out);
  pointer-events: none;
}

.chrome[data-scrolled="true"]::after {
  opacity: 1;
}

/* When a backdrop runs up behind the chrome, that fade would smear a band of
   page colour across the image. The glass already separates them there. */
body:has(main.flush) .chrome::after {
  display: none;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  text-decoration: none;
  color: var(--text);
  border-radius: var(--r-sm);
  transition: opacity var(--t-fast) var(--ease-out);
}

.brand img {
  height: 1.625rem;
  width: auto;
  display: block;
}

.brand span {
  font-weight: 700;
  font-size: 0.9375rem;
  /* The app's wordmark: capitals, set wide. Open tracking is what makes small
     caps read as a mark rather than a shouted word. */
  text-transform: uppercase;
  letter-spacing: 0.22em;
  margin-right: -0.22em; /* tracking trails the last letter; take it back */
}

.brand:hover {
  opacity: 0.72;
}

.nav {
  display: flex;
  align-items: center;
  gap: 0.125rem;
  font-size: 0.875rem;
  min-width: 0;
}

.nav a {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.375rem 0.6875rem;
  border-radius: var(--r-pill);
  color: var(--text-2);
  text-decoration: none;
  white-space: nowrap;
  letter-spacing: -0.006em;
  transition: color var(--t-fast) var(--ease-out),
    background-color var(--t-fast) var(--ease-out);
}

.nav a i {
  font-size: 1rem;
  opacity: 0.75;
}

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

/* Vibrancy: over translucent chrome the current page needs weight, not grey. */
.nav a[aria-current="page"] {
  color: var(--accent);
  background: var(--tint);
  font-weight: 590;
}

.nav a[aria-current="page"] i {
  opacity: 1;
}

main {
  flex: 1 0 auto;
  padding: 3.5rem 0 4.5rem;
}

/* ==========================================================================
   Cards
   ========================================================================== */

/* The app's section header: small capitals set wide, then a hairline that
   runs out to the edge. */
.section-label {
  display: flex;
  align-items: center;
  gap: 1rem;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-3);
  margin: 0 0 1rem;
}

.section-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

.docs-section {
  margin-top: 3.5rem;
}

.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}

@media (min-width: 40rem) {
  .cards {
    grid-template-columns: 1fr 1fr;
  }
}

.card {
  display: flex;
  align-items: flex-start;
  gap: 0.875rem;
  text-decoration: none;
  color: inherit;
  background: var(--surface);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  padding: 1.125rem 1.25rem;
  transition: background-color var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out),
    transform var(--t-base) var(--ease-out),
    box-shadow var(--t-base) var(--ease-out);
}

.card:hover {
  background: var(--surface-hover);
  transform: translateY(-0.125rem);
  box-shadow: var(--shadow-md);
}

/* Feedback on press, not on release. */
.card:active {
  transform: scale(0.985);
  transition-duration: var(--t-press);
  transition-timing-function: ease-out;
}

.icon-tile {
  flex: none;
  display: grid;
  place-items: center;
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 0.75rem;
  background: var(--tint);
  color: var(--accent);
  font-size: 1.25rem;
}

.card-body {
  min-width: 0;
  flex: 1;
}

.card h3 {
  margin: 0.0625rem 0 0.25rem;
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.012em;
}

.card p {
  margin: 0;
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.5;
}

.card .chevron {
  flex: none;
  align-self: center;
  color: var(--text-3);
  font-size: 1.125rem;
  transition: transform var(--t-base) var(--ease-out),
    color var(--t-fast) var(--ease-out);
}

.card:hover .chevron {
  color: var(--accent);
  transform: translateX(0.1875rem);
}

/* ==========================================================================
   Document pages
   ========================================================================== */

.backlink {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--text-2);
  text-decoration: none;
  font-size: 0.9375rem;
  letter-spacing: -0.004em;
  margin-bottom: 2rem;
  transition: color var(--t-fast) var(--ease-out);
}

.backlink i {
  font-size: 1rem;
  transition: transform var(--t-base) var(--ease-out);
}

.backlink:hover {
  color: var(--text);
}

/* Exits the way it entered — the arrow leads back to where you came from. */
.backlink:hover i {
  transform: translateX(-0.1875rem);
}

.doc-head {
  margin-bottom: 2.5rem;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 0.625rem;
}

.doc h1 {
  font-size: clamp(2rem, 6vw, 2.875rem);
  line-height: 1.08;
  letter-spacing: -0.022em;
  font-weight: 700;
  margin: 0 0 0.75rem;
  text-wrap: balance;
}

.doc .lede {
  font-size: 1.1875rem;
  line-height: 1.45;
  letter-spacing: -0.012em;
  color: var(--text-2);
  margin: 0 0 1.125rem;
  max-width: 34rem;
}

.updated {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.6875rem;
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  color: var(--text-3);
  font-size: 0.8125rem;
  letter-spacing: 0.008em;
  margin: 0;
}

.updated i {
  font-size: 0.9375rem;
}

/* ---- On this page ---- */

.toc {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: 1.125rem 1.25rem;
  margin: 0 0 2.75rem;
}

.toc h2 {
  margin: 0 0 0.625rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-3);
}

.toc ol {
  margin: 0;
  padding: 0;
  list-style: none;
  columns: 2;
  column-gap: 1.5rem;
  font-size: 0.9375rem;
}

@media (max-width: 34rem) {
  .toc ol {
    columns: 1;
  }
}

.toc a {
  display: block;
  padding: 0.1875rem 0;
  color: var(--text-2);
  text-decoration: none;
  letter-spacing: -0.004em;
  transition: color var(--t-fast) var(--ease-out);
}

.toc a:hover,
.toc a[data-active="true"] {
  color: var(--accent);
}

.doc section {
  margin-bottom: 2rem;
  scroll-margin-top: 5rem; /* clears the sticky chrome */
}

.doc h2 {
  display: flex;
  gap: 0.5rem;
  font-size: 1.3125rem;
  line-height: 1.3;
  letter-spacing: -0.015em;
  font-weight: 600;
  margin: 0 0 0.5rem;
  color: var(--text);
}

.doc h2 .num {
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.doc p {
  margin: 0 0 0.75rem;
  color: var(--text-2);
}

.doc p:last-child {
  margin-bottom: 0;
}

.doc strong {
  color: var(--text);
  font-weight: 590;
}

/* Inline links only — buttons, backlink and doc-nav style themselves. */
.doc p a {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px solid var(--tint);
  transition: border-color var(--t-fast) var(--ease-out);
}

.doc p a:hover {
  border-bottom-color: var(--accent);
}

/* ---- Inline "do it in the app" rows ---- */

.steps {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.steps li {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 0.875rem 1rem;
}

.steps i {
  flex: none;
  margin-top: 0.1875rem;
  color: var(--accent);
  font-size: 1.125rem;
}

.steps strong {
  display: block;
  font-size: 0.9375rem;
}

.steps span {
  display: block;
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.5;
}

.steps code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.875em;
  color: var(--text);
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.75rem 1.375rem;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-size: 1rem;
  font-weight: 590;
  letter-spacing: -0.008em;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out),
    transform var(--t-base) var(--ease-out),
    box-shadow var(--t-base) var(--ease-out);
}

.btn i {
  font-size: 1.1875rem;
}

.btn-primary {
  background: var(--teal-fill);
  color: #fff;
}

.btn-primary:hover {
  transform: translateY(-0.0625rem);
  box-shadow: var(--shadow-md);
  filter: brightness(1.06);
}

.btn-secondary {
  background: var(--surface);
  color: var(--text);
}

.btn-secondary:hover {
  background: var(--surface-hover);
}

/* The app's TRAILER button: slate, small capitals set wide, square-ish. */
.btn-caps {
  padding: 0.5rem 1rem 0.5rem 0.875rem;
  border-radius: var(--r-sm);
  font-size: 0.875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-2);
}

.btn-caps i {
  font-size: 1rem;
}

.btn-caps:hover {
  color: var(--text);
}

.btn:active {
  transform: scale(0.97);
  transition-duration: var(--t-press);
  transition-timing-function: ease-out;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  margin: 1.25rem 0 0.25rem;
}

/* ---- App Store badge ----
   The two-line lockup Apple uses, set in the site's own system face so it
   matches the surrounding type. Solid white, because it sits on an arbitrary
   photograph and has to hold its own against any of them. */
.btn-appstore {
  padding: 0.5rem 1.25rem 0.5rem 1.0625rem;
  background: var(--hero-ink);
  color: #000;
  gap: 0.5625rem;
  box-shadow: var(--shadow-md);
}

.btn-appstore i {
  font-size: 1.75rem;
  /* The glyph's optical centre sits low; nudge it back onto the baseline. */
  margin-top: -0.125rem;
}

.btn-appstore:hover {
  transform: translateY(-0.0625rem);
  box-shadow: var(--shadow-lg);
}

.btn-appstore-text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
}

.btn-appstore-kicker {
  font-size: 0.6875rem;
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: 0.005em;
}

.btn-appstore-title {
  font-size: 1.1875rem;
  font-weight: 590;
  line-height: 1.15;
  letter-spacing: -0.021em;
}

/* ==========================================================================
   Next-document navigation
   ========================================================================== */

.doc-nav {
  display: grid;
  gap: 0.75rem;
  margin-top: 3.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--hairline);
}

@media (min-width: 40rem) {
  .doc-nav {
    grid-template-columns: 1fr 1fr;
  }
}

.doc-nav a {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.9375rem 1.125rem;
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  text-decoration: none;
  color: var(--text);
  transition: background-color var(--t-fast) var(--ease-out),
    border-color var(--t-fast) var(--ease-out),
    transform var(--t-base) var(--ease-out);
}

.doc-nav a:hover {
  background: var(--surface);
  border-color: var(--hairline-strong);
  transform: translateY(-0.125rem);
}

.doc-nav a:active {
  transform: scale(0.985);
  transition-duration: var(--t-press);
}

.doc-nav i:first-child {
  color: var(--accent);
  font-size: 1.1875rem;
}

.doc-nav span {
  flex: 1;
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: -0.006em;
}

.doc-nav .chevron {
  color: var(--text-3);
  font-size: 1rem;
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  flex-shrink: 0;
  background: var(--bg-sunken);
  border-top: 1px solid var(--hairline);
  padding: 2.25rem 0 2.75rem;
}

.site-footer .wrap {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.footer-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.375rem;
  font-size: 0.875rem;
}

.footer-links a {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--text-2);
  text-decoration: none;
  letter-spacing: -0.004em;
  transition: color var(--t-fast) var(--ease-out);
}

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

.footer-links i {
  font-size: 0.9375rem;
  opacity: 0.7;
}

.footer-meta {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.6;
  letter-spacing: 0.004em;
  color: var(--text-3);
  max-width: 40rem;
}

.footer-meta a {
  color: var(--text-2);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
}

.footer-meta a:hover {
  color: var(--text);
  border-bottom-color: var(--hairline-strong);
}

/* ==========================================================================
   Backdrop — a contained image card, not a full-bleed band
   ========================================================================== */

/* The sticky chrome sits in normal flow and so occupies the top of the page.
   Pages that open with a backdrop are pulled up behind it, so the image really
   does reach the top edge and the glass floats on top of it. app.js publishes
   the measured height into --chrome-h; the token is the pre-script fallback. */
main.flush {
  padding-top: 0;
  margin-top: calc(var(--chrome-h) * -1);
}

.backdrop {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  min-height: min(84svh, 44rem);
  margin-bottom: 3rem;
}

/* Identical treatment to the home hero — only the spacing differs, because the
   poster laps up into it from below. */
.backdrop--show {
  margin-bottom: 0;
}

/* Sits over the backdrop, clearing the sticky chrome above it. */
/* It sits high on the backdrop, where no wash reaches, so neither light nor
   dark ink is safe over an arbitrary photo. It carries its own material
   instead — which also makes it theme-independent. */
.backlink--over {
  position: absolute;
  top: calc(var(--chrome-h) + 0.875rem);
  left: 2rem;
  z-index: 1;
  margin: 0;
  padding: 0.375rem 0.8125rem 0.375rem 0.6875rem;
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(0.625rem);
  -webkit-backdrop-filter: blur(0.625rem);
  color: #fff;
  transition: background-color var(--t-fast) var(--ease-out);
}

.backlink--over:hover {
  background: rgba(0, 0, 0, 0.62);
  color: #fff;
}

.backdrop-media {
  position: absolute;
  inset: 0;
  z-index: -2;
}

.backdrop-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 22%;
}

/* Three stacked layers, painted in this order:
   1. left and right feathers, dissolving the sides into the page
   2. a bottom feather, so there is no hard lower boundary
   3. a legibility wash, only across the lower part where the text sits

   Deliberately no top fade: the image meets the top edge of the page at full
   strength and the glass chrome floats on it. */
.backdrop-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
      to right,
      var(--bg) 0%,
      var(--bg-fade) 15%,
      var(--bg-fade) 85%,
      var(--bg) 100%
    ),
    linear-gradient(to bottom, var(--bg-fade) 62%, var(--bg) 100%),
    linear-gradient(
      to top,
      var(--hero-wash-near) 0%,
      var(--hero-wash-mid) 34%,
      var(--hero-wash-far) 62%
    );
}

.backdrop-inner {
  padding: 2.5rem 2rem 4.5rem;
  width: 100%;
  max-width: 44rem;
  text-align: center;
}

/* Vibrancy over a changing photo: high contrast against the wash, a little
   extra weight, never a flat grey token. */
.backdrop h1 {
  color: var(--hero-ink);
  font-size: clamp(2.25rem, 6.5vw, 3.75rem);
  line-height: 1.03;
  letter-spacing: -0.03em;
  font-weight: 700;
  margin: 0 0 0.625rem;
  text-shadow: var(--hero-shadow);
  text-wrap: balance;
}

/* Hero call to action, below the tagline. */
.hero-cta {
  display: flex;
  justify-content: center;
  margin: 1.625rem 0 0;
}

.backdrop .tagline {
  color: var(--hero-ink-2);
  font-size: clamp(1.0625rem, 2.4vw, 1.375rem);
  line-height: 1.35;
  letter-spacing: -0.012em;
  margin: 0 auto;
  max-width: 32rem;
  text-shadow: var(--hero-shadow);
}

/* ---- Title header: below the backdrop, artwork lapping up into it ----
   The app's arrangement: title, meta and actions on the left, artwork on the
   right. It stays side by side even on a phone, as it does in the app. */

/* A grid rather than a row: text, rating and actions stack in the first
   column beside the artwork, and on a phone the rating and actions can take
   the full width under it. */
.title-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  column-gap: 1.5rem;
  margin-bottom: 2rem;
  position: relative;
}

.title-text {
  grid-column: 1;
  min-width: 0;
}

.title-head > .rating,
.title-head > .title-actions {
  grid-column: 1;
}

/* On the page background now, so it uses normal text tokens — no white ink and
   no shadow needed. */
.title-text h1 {
  font-size: clamp(1.625rem, 5vw, 2.75rem);
  line-height: 1.08;
  letter-spacing: -0.022em;
  font-weight: 700;
  margin: 0 0 0.5rem;
  color: var(--text);
  text-wrap: balance;
}

/* Leads into the synopsis below it: same measure, a step up in size and
   weight so it reads as the opening line rather than another paragraph. */
.show-tagline {
  max-width: 44rem;
  margin: 0 0 0.5rem;
  color: var(--text);
  font-size: 1.1875rem;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.012em;
  text-wrap: balance;
}

/* "Miami Vice · S1E3" under an episode title. Inline, so a long show name
   wraps like a sentence with the code trailing it. */
.title-kicker {
  margin: 0 0 0.375rem;
  color: var(--text-2);
  font-size: 1.0625rem;
  letter-spacing: -0.006em;
}

.title-kicker a {
  color: var(--accent);
  text-decoration: none;
}

.title-kicker a:hover {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.title-kicker span::before {
  content: "·";
  margin: 0 0.5rem;
  color: var(--text-3);
}

/* "2024 • 2 Seasons" — the dots come from here, not the markup. */
.title-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.625rem;
  margin: 0;
  color: var(--text-3);
  font-size: 1rem;
  letter-spacing: 0.004em;
}

.title-meta span + span::before {
  content: "•";
  margin-right: 0.625rem;
  font-size: 0.75em;
  vertical-align: 0.1em;
}

.title-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.625rem;
  margin-top: 1.125rem;
}

.show-poster {
  display: block; /* a link on the episode page */
  grid-column: 2;
  grid-row: 1 / span 3;
  align-self: end;
  width: 11rem;
  aspect-ratio: 2 / 3;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--bg-sunken);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-lg);
  margin-top: -4.5rem; /* the lap into the backdrop */
  transition: transform var(--t-base) var(--ease-out),
    border-color var(--t-fast) var(--ease-out);
}

a.show-poster:hover {
  border-color: var(--hairline-strong);
  transform: translateY(-0.125rem);
}

a.show-poster:active {
  transform: scale(0.97);
  transition-duration: var(--t-press);
}

.show-poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

@media (max-width: 40rem) {
  .title-head {
    column-gap: 1rem;
    margin-bottom: 1.5rem;
  }

  .title-head > .rating,
  .title-head > .title-actions {
    grid-column: 1 / -1;
  }

  .title-head > .rating {
    max-width: none;
  }

  /* Proportions from the app's show screen: the poster takes about a third. */
  .show-poster {
    grid-row: 1;
    width: 7.5rem;
    margin-top: -3rem;
  }

  .backdrop-inner {
    padding: 1.75rem 1.25rem 1.5rem;
  }
}

/* ---- Rating: the app's teal score bar ---- */

.rating {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  width: 100%;
  max-width: 22rem;
  margin-top: 1rem;
  padding: 0.5rem 1rem;
  border-radius: var(--r-sm);
  background: var(--teal-fill);
  color: #fff;
}

.rating-value {
  flex: none;
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

.rating-track {
  flex: 1;
  min-width: 3rem;
  height: 0.3125rem;
  border-radius: var(--r-pill);
  background: var(--teal-track);
  overflow: hidden;
}

.rating-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: #fff;
}

.rating-count {
  flex: none;
  font-size: 0.875rem;
  letter-spacing: 0.004em;
  color: rgba(255, 255, 255, 0.86);
  font-variant-numeric: tabular-nums;
}

/* ---- Where to watch: one slate row, logos on the right ---- */

.where {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  max-width: 44rem;
  margin: 0 0 2.75rem;
  padding: 0.875rem 1rem 0.875rem 1.25rem;
  border-radius: var(--r-sm);
  background: var(--surface);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--t-fast) var(--ease-out),
    transform var(--t-base) var(--ease-out);
}

a.where:hover {
  background: var(--surface-hover);
}

a.where:active {
  transform: scale(0.985);
  transition-duration: var(--t-press);
}

.where-text {
  min-width: 0;
}

.where-text h2 {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: -0.012em;
  color: var(--text-2);
}

.where-text p {
  margin: 0;
  font-size: 0.8125rem;
  letter-spacing: 0.008em;
  color: var(--text-3);
}

.providers {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.providers img {
  display: block;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 0.5rem;
  object-fit: cover;
}

/* ---- Blocks: a labelled section on a detail page ---- */

.block {
  margin: 0 0 2.75rem;
}

/* ---- People: a sideways row of faces ---- */

.people {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  list-style: none;
  margin: 0 calc(var(--gutter) * -1);
  padding: 0.125rem var(--gutter) 0.25rem;
  scroll-padding-inline: var(--gutter);
}

.people::-webkit-scrollbar {
  display: none;
}

.person {
  flex: none;
  width: 6rem;
  scroll-snap-align: start;
  text-align: center;
}

.person-photo {
  width: 5rem;
  height: 5rem;
  margin: 0 auto 0.5rem;
  /* Square with the buttons' corner radius, so faces sit in the same shape
     language as the controls around them. */
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--surface);
}

.person-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 20%; /* headshots: keep the face, lose the collar */
  display: block;
}

.person-photo .art-fallback {
  background: var(--surface);
  font-size: 1.5rem;
}

.person-name,
.person-role {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.person-name {
  color: var(--text);
  font-weight: 590;
  letter-spacing: -0.004em;
}

.person-role {
  margin-top: 0.125rem;
  color: var(--text-3);
  letter-spacing: 0.004em;
}

/* ---- Facts: the app's settings list — label left, value right ---- */

.facts {
  max-width: 44rem;
  margin: 0;
}

.fact {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 0.875rem 0;
  border-bottom: 1px solid var(--hairline);
}

.fact:first-child {
  padding-top: 0.25rem;
}

.fact dt {
  flex: none;
  color: var(--text);
}

.fact dd {
  margin: 0;
  color: var(--text-2);
  text-align: right;
}

/* ---- Previous / next episode ---- */

.steps-nav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  max-width: 44rem;
  margin-top: 1rem;
}

.step {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-width: 0;
  padding: 0.875rem 1rem;
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  transition: background-color var(--t-fast) var(--ease-out),
    transform var(--t-base) var(--ease-out);
}

.step--next {
  grid-column: 2;
  justify-content: flex-end;
  text-align: right;
}

.step:hover {
  background: var(--surface-hover);
}

.step:active {
  transform: scale(0.985);
  transition-duration: var(--t-press);
}

.step i {
  flex: none;
  color: var(--accent);
  font-size: 1.125rem;
  transition: transform var(--t-base) var(--ease-out);
}

.step--prev:hover i {
  transform: translateX(-0.1875rem);
}

.step--next:hover i {
  transform: translateX(0.1875rem);
}

.step-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.step-label {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-3);
}

.step-title {
  font-size: 0.9375rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 34rem) {
  .steps-nav {
    grid-template-columns: 1fr;
  }

  .step--next {
    grid-column: auto;
  }

  /* The spacer that holds "next" in the right column when there is no
     "previous" is only needed in two columns. */
  .steps-nav > span {
    display: none;
  }
}

/* ==========================================================================
   Search
   ========================================================================== */

.search {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  max-width: 30rem;
  margin: 0 0 2.5rem;
  padding: 0.625rem 1rem;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  transition: border-color var(--t-fast) var(--ease-out),
    background-color var(--t-fast) var(--ease-out),
    width var(--t-base) var(--ease-out);
}

/* In the chrome it is compact, and widens when it has focus so a long query
   stays readable without permanently crowding the nav. */
.search--nav {
  flex: none;
  width: 11.5rem;
  max-width: none;
  margin: 0;
  padding: 0.3125rem 0.75rem;
  gap: 0.4375rem;
  background: var(--surface);
}

.search--nav:focus-within {
  width: 16rem;
}

.search--nav > i {
  font-size: 1rem;
}

.search--nav input {
  font-size: 0.9375rem;
}

.search:focus-within {
  border-color: var(--accent);
  background: var(--surface-hover);
}

.search > i {
  flex: none;
  color: var(--text-3);
  font-size: 1.125rem;
}

.search:focus-within > i {
  color: var(--accent);
}

.search input {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  padding: 0;
  color: var(--text);
  font: inherit;
  font-size: 1rem;
  letter-spacing: -0.006em;
}

.search input:focus {
  outline: none;
}

.search input::placeholder {
  color: var(--text-3);
}

/* The native clear affordance is inconsistent across browsers; ours is a real
   button that also works on touch. */
.search input::-webkit-search-cancel-button,
.search input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}

.search-clear {
  flex: none;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--surface-hover);
  color: var(--text-2);
  font-size: 0.875rem;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease-out),
    color var(--t-fast) var(--ease-out),
    transform var(--t-base) var(--ease-out);
}

.search-clear:hover {
  color: var(--text);
  background: var(--hairline-strong);
}

.search-clear:active {
  transform: scale(0.92);
  transition-duration: var(--t-press);
}

.show-overview {
  max-width: 44rem;
  margin: 0 0 1.25rem;
  color: var(--text-2);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4375rem;
  list-style: none;
  margin: 0 0 2.75rem;
  padding: 0;
}

.chip {
  padding: 0.25rem 0.6875rem;
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  color: var(--text-3);
  font-size: 0.8125rem;
  letter-spacing: 0.008em;
}

/* ==========================================================================
   Poster grid
   ========================================================================== */

/* Same voice as .section-label — capitals set wide, a hairline running out —
   with room for a short note at the end of the line. */
.row-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.125rem;
}

.row-head::after {
  content: "";
  order: 1;
  flex: 1;
  min-width: 1.5rem;
  height: 1px;
  background: var(--hairline);
}

.row-head h2 {
  margin: 0;
  flex: none;
  white-space: nowrap;
  font-size: 0.8125rem;
  line-height: 1.4;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-3);
}

.row-head p {
  order: 2;
  margin: 0;
  min-width: 0;
  text-align: right;
  color: var(--text-3);
  font-size: 0.8125rem;
}

.poster-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 1.5rem 1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (min-width: 48rem) {
  .poster-grid {
    grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  }
}

.poster-card {
  display: block;
  text-decoration: none;
  color: inherit;
}

.poster-frame {
  position: relative;
  aspect-ratio: 2 / 3;
  border-radius: var(--r-sm); /* the app's posters are nearly square-cornered */
  overflow: hidden;
  background: var(--bg-sunken);
  border: 1px solid var(--hairline);
  margin-bottom: 0.625rem;
  transition: transform var(--t-base) var(--ease-out),
    box-shadow var(--t-base) var(--ease-out),
    border-color var(--t-fast) var(--ease-out);
}

.poster-frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Artwork fades in once decoded. Deliberately fail-visible: the default state
   is fully opaque and the animation only *plays* on load, so a missed event or
   absent JS can never leave a tile blank. Backdrops are excluded — they are the
   largest paint on screen and should not be held back. */
@media (prefers-reduced-motion: no-preference) {
  .poster-frame img[data-loaded="true"],
  .episode-still img[data-loaded="true"],
  .person-photo img[data-loaded="true"],
  .show-poster img[data-loaded="true"] {
    animation: fade-in var(--t-base) var(--ease-out);
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.poster-card:hover .poster-frame {
  transform: translateY(-0.25rem);
  box-shadow: var(--shadow-lg);
  border-color: var(--hairline-strong);
}

/* Responds on press, not on release. */
.poster-card:active .poster-frame {
  transform: scale(0.97);
  transition-duration: var(--t-press);
  transition-timing-function: ease-out;
}

.poster-card h3 {
  margin: 0 0 0.125rem;
  font-size: 0.9375rem;
  font-weight: 590;
  line-height: 1.35;
  letter-spacing: -0.006em;
}

.poster-meta {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--text-3);
  font-size: 0.8125rem;
  letter-spacing: 0.008em;
}

/* Placeholder for missing artwork */
.art-fallback {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  color: var(--text-3);
  font-size: 1.75rem;
  background: var(--bg-sunken);
}

/* ==========================================================================
   Season picker — the app's "Season 1" menu button, a native select inside
   ========================================================================== */

.season-bar {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: 0 0 1rem;
}

.select {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.select-control {
  appearance: none;
  -webkit-appearance: none;
  padding: 0.625rem 2.75rem 0.625rem 1.125rem;
  border: 0;
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.008em;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease-out);
}

.select-control:hover {
  background: var(--surface-hover);
}

.select i {
  position: absolute;
  right: 1rem;
  color: var(--text-2);
  font-size: 1rem;
  pointer-events: none;
}

.season-count {
  margin: 0;
  color: var(--text-3);
  font-size: 0.9375rem;
}

/* ==========================================================================
   Episodes — a list with hairlines between rows, as in the app
   ========================================================================== */

/* Capped narrower than the artwork grid — episode summaries are prose, and a
   74rem measure is unreadable. */
.episodes {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 58rem;
}

.episodes > li + li {
  border-top: 1px solid var(--hairline);
}

.episode {
  display: flex;
  /* Not stretch: a stretched cross size is a definite height, which overrides
     the still's aspect-ratio and lets the summary length dictate image shape. */
  align-items: flex-start;
  gap: 1rem;
  padding: 0.875rem 0.75rem;
  margin: 0 -0.75rem;
  border-radius: var(--r-md);
  color: inherit;
  text-decoration: none;
  transition: background-color var(--t-fast) var(--ease-out),
    transform var(--t-base) var(--ease-out);
}

a.episode:hover {
  background: var(--surface);
}

a.episode:active {
  transform: scale(0.99);
  transition-duration: var(--t-press);
}

.episode .chevron {
  flex: none;
  align-self: center;
  color: var(--text-3);
  font-size: 1.125rem;
  transition: transform var(--t-base) var(--ease-out),
    color var(--t-fast) var(--ease-out);
}

a.episode:hover .chevron {
  color: var(--accent);
  transform: translateX(0.1875rem);
}

.episode-still {
  flex: none;
  width: 10.5rem;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--bg-sunken);
}

.episode-still img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.episode-body {
  flex: 1;
  min-width: 0;
}

.episode-eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.625rem;
  margin: 0 0 0.1875rem;
  color: var(--text-3);
  font-size: 0.8125rem;
  letter-spacing: 0.008em;
}

.episode-eyebrow .num {
  color: var(--text-2);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.episode-body h3 {
  margin: 0 0 0.3125rem;
  font-size: 1.0625rem;
  font-weight: 590;
  line-height: 1.3;
  letter-spacing: -0.012em;
}

/* Two lines is enough to recognise an episode; the rest is one tap away. */
.episode-overview {
  margin: 0;
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.55;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

@media (max-width: 34rem) {
  .episode {
    gap: 0.875rem;
  }

  .episode-still {
    width: 7.5rem;
  }

  .episode .chevron {
    display: none;
  }
}

/* ==========================================================================
   Notices, skeletons and empty states
   ========================================================================== */

.notice {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: 1.5rem;
}

.notice-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.notice-head i {
  color: var(--accent);
  font-size: 1.5rem;
}

.notice h2 {
  margin: 0;
  font-size: 1.1875rem;
  font-weight: 600;
  letter-spacing: -0.014em;
}

.notice p {
  margin: 0 0 0.75rem;
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.6;
}

.notice p:last-child {
  margin-bottom: 0;
}

.notice code,
.notice kbd {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.875em;
  background: var(--tint);
  color: var(--accent);
  padding: 0.0625rem 0.375rem;
  border-radius: 0.3125rem;
}

.notice ol {
  margin: 0 0 0.75rem;
  padding-left: 1.25rem;
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.7;
}

.notice a {
  color: var(--accent);
}

.skeleton {
  background: var(--surface-hover);
}

.skeleton-text {
  height: 0.75rem;
  border-radius: 0.25rem;
  background: var(--surface-hover);
  margin-bottom: 0.375rem;
}

.skeleton-text.short {
  width: 45%;
}

@media (prefers-reduced-motion: no-preference) {
  .skeleton,
  .skeleton-text {
    animation: pulse 1.6s ease-in-out infinite;
  }
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
}

.empty {
  color: var(--text-3);
  font-size: 0.9375rem;
  padding: 2rem 0;
  text-align: center;
}

/* ==========================================================================
   Focus
   ========================================================================== */

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

.card:focus-visible,
.doc-nav a:focus-visible,
.btn:focus-visible,
.select-control:focus-visible,
.episode:focus-visible,
.step:focus-visible,
.where:focus-visible,
a.show-poster:focus-visible {
  outline-offset: 2px;
}

/* The frame is the visible object, so the ring belongs on it. */
.poster-card:focus-visible {
  outline: none;
}

.poster-card:focus-visible .poster-frame {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ==========================================================================
   Entrance — gentle, and only when motion is welcome
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  .reveal {
    animation: reveal 0.55s var(--ease-out) both;
  }

  .reveal:nth-child(2) {
    animation-delay: 0.05s;
  }
  .reveal:nth-child(3) {
    animation-delay: 0.1s;
  }
  .reveal:nth-child(4) {
    animation-delay: 0.15s;
  }
  .reveal:nth-child(5) {
    animation-delay: 0.2s;
  }
  .reveal:nth-child(6) {
    animation-delay: 0.25s;
  }
}

@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(0.5rem);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ==========================================================================
   Accessibility preferences
   ========================================================================== */

/* Reduced motion: keep colour and opacity feedback, drop the travel. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation: none !important;
    transition-duration: 150ms !important;
  }

  .card:hover,
  .card:active,
  .btn:hover,
  .btn:active,
  .doc-nav a:hover,
  .doc-nav a:active,
  .backlink:hover i,
  .card:hover .chevron,
  .poster-card:hover .poster-frame,
  .poster-card:active .poster-frame,
  a.episode:active,
  a.episode:hover .chevron,
  a.where:active,
  a.show-poster:hover,
  a.show-poster:active,
  .step:active,
  .step:hover i {
    transform: none !important;
  }
}

/* Reduced transparency: frost the chrome solid instead of blurring. */
@media (prefers-reduced-transparency: reduce) {
  .nav-bar {
    background: var(--bg);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom: 1px solid var(--hairline);
  }

  /* A heavier wash keeps hero text legible without relying on blur. */
  .backdrop-scrim {
    background: linear-gradient(
        to right,
        var(--bg) 0%,
        var(--bg-fade) 15%,
        var(--bg-fade) 85%,
        var(--bg) 100%
      ),
      linear-gradient(to bottom, var(--bg-fade) 62%, var(--bg) 100%),
      linear-gradient(
        to top,
        var(--hero-wash-near) 0%,
        var(--hero-wash-mid) 52%,
        var(--hero-wash-far) 78%
      );
  }

  .backlink--over {
    background: rgba(0, 0, 0, 0.78);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* More contrast: defined borders, near-solid surfaces. */
@media (prefers-contrast: more) {
  :root {
    --hairline: rgba(128, 128, 128, 0.55);
    --hairline-strong: rgba(128, 128, 128, 0.9);
    --text-2: var(--text);
    --material: var(--bg);
  }

  .nav-bar {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom: 1px solid var(--hairline);
  }
}

/* ==========================================================================
   Narrow screens
   ========================================================================== */

/* Above this the bar fits one row: brand | nav | search. Below it, brand and
   search share the first row and the links become a scroll strip underneath,
   rather than wrapping into a ragged block. */
@media (max-width: 52rem) {
  :root {
    --chrome-h: 6.5rem; /* two rows now */
  }

  .nav-bar .wrap {
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    padding-top: 0.5rem;
    padding-bottom: 0.4375rem;
  }

  .brand {
    order: 1;
  }

  /* Basis 7rem so it sits beside the brand and then grows. `min-width: 0` is
     required: a flex item's automatic minimum size clamps the basis back up to
     the input's intrinsic width, and lines break on that size before anything
     shrinks — which pushed the field onto a row of its own. */
  .search--nav {
    order: 2;
    flex: 1 1 7rem;
    width: auto;
    min-width: 0;
  }

  /* Already flexible, so widening on focus would only cause a jump. */
  .search--nav:focus-within {
    width: auto;
  }

  .nav {
    order: 3;
    flex-basis: 100%;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    margin: 0 calc(var(--gutter) * -1);
    padding: 0 var(--gutter) 0.125rem;
  }

  .nav::-webkit-scrollbar {
    display: none;
  }

  /* Anchors must clear the taller two-row chrome. */
  .doc section {
    scroll-margin-top: 7.5rem;
  }
}

@media (max-width: 34rem) {
  :root {
    --gutter: 1.25rem;
  }

  body {
    font-size: 1rem;
  }

  main {
    padding: 2.5rem 0 3.5rem;
  }

  /* A landscape still cropped into a tall portrait box is mostly empty space,
     so the hero gets shorter as the viewport narrows. */
  .backdrop {
    min-height: min(62svh, 26rem);
  }

  .backdrop-inner {
    padding: 2rem 1rem 3rem;
  }

  .backlink--over {
    left: 1rem;
  }
}
