/* Samsung's Worst Day — design tokens and layout.
   Recreated from the design handoff (docs/design-handoff.md). The handoff
   prototype used inline styles; the security headers this repo ships forbid
   them, so every value lives here instead. The token table below is the
   source of truth — docs/design-handoff.md § "Design tokens". */

:root {
  --parchment: #f4efe6;
  --card: #fbf8f2;
  --ink: #1d1a15;
  --body-ink: #211d17;
  --muted-ink: #6b6355;
  --terracotta: #a85a3c;
  --sea: #2f6f74;
  --hero-ground: #0b0d0c;
  --plate-a: #e7e1d5;
  --plate-b: #ddd6c8;
  --hairline: rgba(29, 26, 21, 0.14);
  --border: rgba(29, 26, 21, 0.16);

  --serif: 'Newsreader', Georgia, serif;
  --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --pad-major: clamp(56px, 9vw, 120px);
  --pad-minor: clamp(24px, 4vw, 48px);
  --gutter: 24px;

  /* --measure is the text column, and every text column on the page resolves
     to it, so the left edge never steps in and out as the reader scrolls.
     --spread is the wider container the pictures and the footer sit in. */
  --measure: 660px;
  --spread: 1100px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--parchment);
  color: var(--ink);
  font-family: var(--serif);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--terracotta); text-decoration: none; }
a:hover { color: var(--ink); }
p {
  text-wrap: pretty;
  hanging-punctuation: first last;
}

/* ── Animations ─────────────────────────────────────────── */

@keyframes icarusPulse {
  0%, 100% { opacity: .25; transform: translate(-50%, -50%) scale(.82); }
  50% { opacity: .85; transform: translate(-50%, -50%) scale(1.06); }
}

@keyframes ringIn {
  from { opacity: 0; transform: translate(-50%, -50%) scale(1.6); }
  to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ── Shared ─────────────────────────────────────────────── */

.article {
  max-width: var(--spread);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* Every run of text, at one measure and one left edge.

   Columns were tried on 2026-09-18 and rejected — see docs/STATUS.md. The
   measure itself is the design's, restored on Ed's instruction after the
   typography pass. */
.prose {
  max-width: var(--measure);
  margin: 0 auto;
}

.col-wide {
  max-width: var(--spread);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.mono {
  font-family: var(--mono);
  text-transform: uppercase;
}

.lede {
  margin: 0 0 1.7rem;
  font-size: clamp(1.3rem, 2vw, 1.62rem);
  line-height: 1.42;
  letter-spacing: -0.008em;
  color: var(--ink);
}

/* Sized in em so it keeps its relationship to the lede across the whole
   clamp range — a viewport unit here is correct at exactly one width. Cap
   top flush with the first line, baseline sitting on the second. */
.dropcap {
  float: left;
  font-size: 2.72em;
  line-height: 0.86;
  padding: 0.08em 0.075em 0 0;
  color: var(--ink);
}

.body-p {
  margin: 0 0 1.55rem;
  font-size: 1.2rem;
  line-height: 1.74;
  color: var(--body-ink);
}

/* The essay is full of numbers in running text — 2019, 50MP vs 48MP, 8K,
   $2,000, +2MP. Lining figures shout at this size. */
.body-p,
.lede,
.internal-line,
.aside-p,
.display-answer {
  font-variant-numeric: oldstyle-num;
}

/* The first paragraph of each answer steps up, so the reader lands on the
   argument before settling into the body rhythm. */
.body-p.answer-open {
  font-size: clamp(1.14rem, 1.6vw, 1.34rem);
  line-height: 1.6;
  color: var(--ink);
}

/* Q2's answer is two words, so it is set as a display line instead. */
.display-answer {
  margin: 0 0 1.8rem;
  font-size: clamp(1.5rem, 2.8vw, 2.05rem);
  line-height: 1.2;
  letter-spacing: -0.012em;
  color: var(--ink);
}

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

/* The global `a` rule drops underlines, which is right for the nav rows and
   the hero links but wrong inside running prose — a link in a paragraph has
   to look like one. */
.prose-link {
  color: var(--terracotta);
  text-decoration: underline;
  text-decoration-thickness: 0.055em;
  text-underline-offset: 0.16em;
  text-decoration-color: color-mix(in srgb, var(--terracotta) 45%, transparent);
  transition: text-decoration-color 0.18s ease;
}

.prose-link:hover,
.prose-link:focus-visible { text-decoration-color: var(--terracotta); }

/* `hidden` alone is inert when a rule sets display, so state classes carry
   their own display:none rather than relying on the attribute. */
.is-hidden { display: none !important; }

/* ── 1. Hero — Find Icarus ──────────────────────────────── */

.hero {
  position: relative;
  height: 100svh;
  min-height: 560px;
  overflow: hidden;
  background: var(--hero-ground);
  cursor: crosshair;
}

.hero.is-found { cursor: default; }

.hero-layer {
  position: absolute;
  inset: 0;
  background-image: url('../images/icarus.jpg');
  background-size: cover;
  /* Centred unless centring would crop Icarus out of the hero, which it does
     on every phone in portrait. js/hit.js computes the anchor and the hit
     point from the same numbers; the fallback here is the desktop case. */
  background-position: var(--bgx, 50%) var(--bgy, 50%);
}

.hero-dim { filter: brightness(0.3) saturate(0.5); }

/* The spotlight. Both fallbacks resolve to a zero-radius circle, so nothing
   is revealed before the script runs and the painting never flashes bright. */
.hero-bright {
  mask-image: radial-gradient(circle var(--r, 0px) at var(--x, 50%) var(--y, 50%), #000 0%, rgba(0, 0, 0, 0.8) 54%, transparent 100%);
  -webkit-mask-image: radial-gradient(circle var(--r, 0px) at var(--x, 50%) var(--y, 50%), #000 0%, rgba(0, 0, 0, 0.8) 54%, transparent 100%);
}

.hero-vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 90% at 50% 40%, transparent 30%, rgba(8, 9, 8, 0.55) 100%);
  pointer-events: none;
}

.hero-marker {
  position: absolute;
  left: var(--ix, 85.8%);
  top: var(--iy, 75.6%);
  transform: translate(-50%, -50%);
  border-radius: 999px;
  pointer-events: none;
}

.hero-pulse {
  width: 150px;
  height: 150px;
  background: radial-gradient(circle, rgba(244, 239, 230, 0.5) 0%, rgba(244, 239, 230, 0.07) 55%, transparent 72%);
  animation: icarusPulse 2.6s ease-in-out infinite;
}

.hero-ring {
  width: 220px;
  height: 220px;
  border: 1px solid rgba(244, 239, 230, 0.5);
  animation: ringIn .9s cubic-bezier(.2, .7, .2, 1) both;
}

.hero-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: clamp(20px, 3.5vw, 44px);
  pointer-events: none;
}

.hero-corners {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
}

.hero-stamp {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(244, 239, 230, 0.72);
}

.hero-place {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(244, 239, 230, 0.38);
  text-align: right;
}

.hero-text { max-width: 940px; }

.hero-title {
  margin: 0;
  font-weight: 400;
  font-size: clamp(2.9rem, 8.4vw, 7.2rem);
  line-height: 0.94;
  letter-spacing: -0.025em;
  color: var(--parchment);
  text-shadow: 0 2px 40px rgba(0, 0, 0, 0.55);
}

.hero-byline {
  margin-top: clamp(14px, 2vw, 22px);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: rgba(244, 239, 230, 0.66);
}

.hero-prompt {
  margin-top: clamp(22px, 3.4vw, 40px);
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: auto;
}

.hero-prompt-line {
  font-size: clamp(1.3rem, 2.4vw, 1.9rem);
  font-style: italic;
  color: var(--parchment);
}

.hero-hint {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .08em;
  color: rgba(244, 239, 230, 0.5);
  max-width: 34ch;
  line-height: 1.7;
}

.hero-skip {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(244, 239, 230, 0.45);
  border-bottom: 1px solid rgba(244, 239, 230, 0.2);
  align-self: flex-start;
  padding-bottom: 3px;
  margin-top: 6px;
}

.hero-skip:hover {
  color: var(--parchment);
  border-bottom-color: rgba(244, 239, 230, 0.6);
}

.hero-reveal {
  margin-top: clamp(22px, 3.4vw, 40px);
  max-width: 30ch;
  animation: fadeUp .9s cubic-bezier(.2, .7, .2, 1) both;
  pointer-events: auto;
}

.hero-reveal p {
  margin: 0;
  font-size: clamp(1.25rem, 2.1vw, 1.7rem);
  line-height: 1.42;
  font-style: italic;
  color: var(--parchment);
}

.hero-read {
  display: inline-block;
  margin-top: 20px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(244, 239, 230, 0.8);
  border-bottom: 1px solid rgba(244, 239, 230, 0.45);
  padding-bottom: 3px;
}

.hero-read:hover { color: #fff; border-bottom-color: #fff; }

/* ── 2. Lede ────────────────────────────────────────────── */

.essay { padding: var(--pad-major) var(--gutter) 0; }

/* ── 3. Numbered nav ────────────────────────────────────── */

.nav { padding: clamp(48px, 7vw, 96px) var(--gutter); }

.nav-prompt {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted-ink);
  padding-bottom: 14px;
  border-bottom: 1px solid var(--hairline);
}

.nav-row {
  display: flex;
  gap: clamp(16px, 3vw, 32px);
  align-items: baseline;
  padding: 22px 0;
  border-bottom: 1px solid var(--hairline);
  color: var(--ink);
}

.nav-row:hover { color: var(--terracotta); }

.nav-num {
  font-size: 1.15rem;
  font-style: italic;
  color: var(--terracotta);
  min-width: 2.2em;
}

.nav-label {
  font-size: clamp(1.15rem, 2vw, 1.5rem);
  line-height: 1.3;
}

/* ── 4. Section dividers ────────────────────────────────── */

.question { padding: var(--pad-major) 0 0; }
.question:first-of-type { padding-top: clamp(28px, 5vw, 64px); }

.divider {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(16px, 3vw, 36px);
  align-items: flex-end;
}

/* Each detail is a real file cut from the painting by tools/build-crops.py,
   so the framing cannot drift with the container the way a tuned
   background-position did. */
.divider-crop {
  flex: 1 1 460px;
  min-width: 0;
  max-width: 100%;
  width: 100%;
  height: auto;
  display: block;
  filter: saturate(0.82);
}

.divider-label {
  flex: 1 1 240px;
  min-width: 0;
  padding-bottom: 6px;
}

.divider-eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--terracotta);
}

.divider-name {
  font-size: clamp(1.6rem, 3vw, 2.3rem);
  line-height: 1.1;
  margin-top: 8px;
}

.divider-caption {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .06em;
  color: var(--muted-ink);
  margin-top: 10px;
}

/* Width comes from .article, which this sits on. Setting max-width here too
   is what kept the question sections narrow while the rest of the page
   widened — see the same-property guard in test/css.test.js. */
.question-body {
  padding-top: clamp(40px, 6vw, 72px);
}

/* The numeral sits above the heading rather than beside it. In a row it
   indents the heading off the body text edge, and the box has to be resized
   for III. */
.q-head {
  margin: 0 0 1.5rem;
  padding-bottom: 1.4rem;
  border-bottom: 1px solid var(--hairline);
}

.q-numeral {
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  line-height: 1;
  font-style: italic;
  color: var(--terracotta);
  margin-bottom: clamp(10px, 1.4vw, 16px);
}

.question-heading {
  margin: 0;
  font-weight: 400;
  font-size: clamp(1.7rem, 3.2vw, 2.45rem);
  line-height: 1.1;
  letter-spacing: -0.018em;
  text-wrap: balance;
}

/* The short measure signals "not the main voice" before a word is parsed. */
.aside-p {
  margin: 0 0 1.9rem;
  padding-left: clamp(14px, 2vw, 20px);
  border-left: 1px solid rgba(168, 90, 60, 0.4);
  font-size: 1.02rem;
  line-height: 1.66;
  color: var(--muted-ink);
  font-style: italic;
  max-width: 54ch;
}

/* ── The two films ──────────────────────────────────────── */

/* Named "film", not "ad". A class of `ads` or `ad-play`, or a file called
   ad-samsung.jpg, is hidden outright by standard blocker filter lists — the
   two spots would simply be missing for a large share of readers, with
   nothing in the page to show why. Guarded in test/page.test.js.

   Wider than the text measure: two videos at the reading width would be
   postage stamps. */
.films {
  padding: clamp(28px, 4vw, 52px) var(--gutter) clamp(8px, 2vw, 20px);
}

.films-inner {
  max-width: 880px;
  margin: 0 auto;
}

.films-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: clamp(16px, 2.5vw, 28px);
}

.film {
  margin: 0;
}

/* A still, not a player. Nothing is requested from the video host until the
   reader asks for it — the page stays light and sets no third-party cookie
   on someone who only ever scrolls past. */
.film-play {
  display: block;
  position: relative;
  width: 100%;
  padding: 0;
  border: 1px solid var(--border);
  background: var(--ink);
  cursor: pointer;
  overflow: hidden;
}

.film-play img {
  display: block;
  width: 100%;
  height: auto;
  filter: saturate(0.92);
  transition: filter .5s ease, transform .9s cubic-bezier(.2, .7, .2, 1);
}

.film-play:hover img,
.film-play:focus-visible img {
  filter: saturate(1);
  transform: scale(1.02);
}

.film-mark {
  position: absolute;
  left: 50%;
  top: 50%;
  width: clamp(48px, 7vw, 64px);
  height: clamp(48px, 7vw, 64px);
  transform: translate(-50%, -50%);
  border-radius: 999px;
  border: 1px solid rgba(244, 239, 230, 0.75);
  background: rgba(11, 13, 12, 0.35);
  backdrop-filter: blur(2px);
  transition: background .4s ease, border-color .4s ease;
}

/* The triangle, drawn rather than shipped as an asset. */
.film-mark::after {
  content: '';
  position: absolute;
  left: 54%;
  top: 50%;
  transform: translate(-50%, -50%);
  border-left: clamp(13px, 1.8vw, 17px) solid var(--parchment);
  border-top: clamp(8px, 1.1vw, 10px) solid transparent;
  border-bottom: clamp(8px, 1.1vw, 10px) solid transparent;
}

.film-play:hover .film-mark,
.film-play:focus-visible .film-mark {
  background: rgba(11, 13, 12, 0.5);
  border-color: var(--parchment);
}

.film-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--border);
  background: var(--ink);
}

.film-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.film-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin-top: 12px;
}

.film-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--parchment);
  background: var(--ink);
  padding: 4px 8px;
}

.film-credit {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .06em;
  color: var(--muted-ink);
}

/* ── 5. Ambiance strip ──────────────────────────────────── */

.ambiance { padding: clamp(20px, 3vw, 36px) 0; }

/* The 1px gap over a bordered ground draws the rules between tiles. */
.ambiance-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr));
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
}

.ambiance-tile {
  background: var(--card);
  padding: clamp(20px, 2.6vw, 30px);
  min-height: 190px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* The still sits above the line, and the line stays pinned to the bottom of
   the tile so the three read as a set however tall the images crop. */
.ambiance-line {
  margin-top: auto;
}

.ambiance-still {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  filter: saturate(0.88);
}

.ambiance-eyebrow {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--terracotta);
}

.ambiance-line { font-size: 1.22rem; line-height: 1.38; }

.ambiance-note {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .06em;
  color: var(--muted-ink);
  margin-top: 12px;
}

/* ── 6. Pull quote ──────────────────────────────────────── */

.run-on { padding: var(--pad-minor) var(--gutter) 0; }

.pull-quote {
  margin: 2.6rem 0 0;
  font-size: clamp(1.45rem, 2.7vw, 2.05rem);
  line-height: 1.26;
  color: var(--ink);
  border-top: 1px solid var(--hairline);
  padding-top: 2.2rem;
}

/* ── 7. Q3 internal-talk block ──────────────────────────── */

.internal {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0 0 2.2rem;
  padding: clamp(20px, 3vw, 28px);
  background: var(--card);
  border: 1px solid var(--border);
}

.internal-line {
  font-size: clamp(1.14rem, 1.6vw, 1.3rem);
  line-height: 1.5;
  color: var(--body-ink);
}

.answer-samsung { color: var(--terracotta); }
.answer-apple { color: var(--sea); }

/* ── 8. Closing painting ────────────────────────────────── */

.closing-painting { padding: clamp(56px, 9vw, 110px) 0 0; }

.painting-band {
  position: relative;
  width: 100%;
  aspect-ratio: 1920 / 1252;
  overflow: hidden;
}

.painting-full {
  position: absolute;
  inset: 0;
  background-image: url('../images/icarus.jpg');
  background-size: cover;
  background-position: center;
}

.painting-spot {
  position: absolute;
  left: 85.8%;
  top: 75.6%;
  width: clamp(90px, 11vw, 200px);
  height: clamp(90px, 11vw, 200px);
  transform: translate(-50%, -50%);
  border-radius: 999px;
  background: radial-gradient(circle, rgba(255, 252, 242, 0.34) 0%, rgba(255, 252, 242, 0.08) 58%, transparent 72%);
  box-shadow: 0 0 0 1px rgba(255, 252, 242, 0.28) inset;
  pointer-events: none;
}

/* ── 9. Closing line and footer ────────────────────────── */

.closing { padding: clamp(64px, 10vw, 140px) var(--gutter) clamp(48px, 7vw, 90px); }

.closing-inner { max-width: var(--spread); margin: 0 auto; }

.closing-line {
  margin: 0;
  font-weight: 400;
  font-size: clamp(1.9rem, 5vw, 3.6rem);
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.end-mark {
  margin-top: clamp(40px, 6vw, 72px);
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: .3em;
  color: var(--terracotta);
}

.footer { padding: 0 var(--gutter) clamp(64px, 9vw, 120px); }

.footer-inner {
  max-width: var(--spread);
  margin: 0 auto;
  border-top: 1px solid var(--hairline);
  padding-top: clamp(24px, 3.5vw, 40px);
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.disclaimer {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.7;
  color: var(--muted-ink);
  font-style: italic;
}

.footer-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  justify-content: space-between;
  align-items: center;
}

.footer-byline {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--muted-ink);
}

.copy-link {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  border: 1px solid rgba(29, 26, 21, 0.3);
  padding: 9px 14px;
  cursor: pointer;
}

.copy-link:hover { border-color: var(--ink); background: var(--card); }

/* ── Headlines, scaled to the scroll ────────────────────── */

/* Each headline is small and pale as it comes up from the bottom, reaches
   full size while it sits in the middle of the screen, and settles back as
   it leaves — tied to scroll position, not to a timer, so it tracks the
   reader's thumb exactly.

   This runs on the compositor with no JavaScript and no scroll listener.
   Where the browser does not support it, the rule is simply skipped and the
   headline renders at full size, which is the correct fallback. */

@keyframes headline-scale {
  0% {
    transform: scale(0.84);
    opacity: 0.45;
  }
  42%, 58% {
    transform: scale(1);
    opacity: 1;
  }
  100% {
    transform: scale(0.9);
    opacity: 0.55;
  }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .question-heading,
    .q-numeral,
    .display-answer,
    .pull-quote,
    .closing-line,
    .divider-name {
      animation: headline-scale linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
      /* Anchored left so the text edge stays put. Scaling from the centre
         would make the margin breathe against every other line on the page. */
      transform-origin: left center;
    }

    /* Never larger than its natural size, so a headline cannot grow past the
       measure and break the one-left-edge rule. */
    .question-heading,
    .closing-line {
      will-change: transform, opacity;
    }
  }
}

/* ── Reading reveals ────────────────────────────────────── */

/* .js-reveal is added by js/main.js, never by the page. Without it — no
   JavaScript, an old browser, reduced motion — every rule below is inert
   and the essay renders plainly. */

.js-reveal .reveal {
  opacity: 0;
  transform: translateY(34px);
  transition:
    opacity .85s cubic-bezier(.2, .7, .2, 1) calc(var(--reveal-order, 0) * 70ms),
    transform .85s cubic-bezier(.2, .7, .2, 1) calc(var(--reveal-order, 0) * 70ms);
  will-change: opacity, transform;
}

.js-reveal .reveal.is-in {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* The section marks and the closing line carry a little more travel, so a
   new section announces itself rather than simply continuing. */
.js-reveal .q-head.reveal,
.js-reveal .closing-line.reveal {
  transform: translateY(30px);
}

.js-reveal .q-head.reveal,
.js-reveal .closing-line.reveal,
.js-reveal .painting-band.reveal {
  transition-duration: 1.05s;
}

/* The numeral arrives a beat before its heading. */
.js-reveal .q-head.reveal .q-numeral {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .7s ease .1s, transform .7s cubic-bezier(.2, .7, .2, 1) .1s;
}

.js-reveal .q-head.reveal.is-in .q-numeral {
  opacity: 1;
  transform: none;
}

/* ── Reduced motion ─────────────────────────────────────── */

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

  /* Belt and braces: the script already declines to start, but if the
     preference is switched on mid-session nothing should be left hidden. */
  .js-reveal .reveal,
  .js-reveal .reveal.is-in,
  .js-reveal .q-head.reveal .q-numeral {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .question-heading,
  .q-numeral,
  .display-answer,
  .pull-quote,
  .closing-line,
  .divider-name {
    animation: none;
    transform: none;
    opacity: 1;
  }
  .hero-pulse,
  .hero-ring,
  .hero-reveal {
    animation: none;
  }
  .hero-pulse { opacity: .6; }
}

/* ── Phones ─────────────────────────────────────────────── */

@media (max-width: 640px) {
  :root { --gutter: 20px; }

  /* Comfortably tappable. A 35px button is a button you miss. */
  .copy-link {
    min-height: 44px;
    padding: 12px 16px;
  }

  .hero-skip,
  .hero-read {
    padding-top: 10px;
    padding-bottom: 8px;
  }

  .nav-row { padding: 20px 0; }

  .footer-row {
    gap: 20px;
    align-items: flex-start;
  }

  /* The dividers stack, so the detail should not be taller than it is wide. */
  .divider-crop { height: auto; }

  .internal { padding: 18px; }
}

/* Nothing should ever scroll sideways on a phone.

   `clip`, NOT `hidden`. `overflow-x: hidden` forces overflow-y to compute to
   `auto`, which makes <body> a scroll container — and every scroll-driven
   animation on the page then measures itself against body's scrollport
   instead of the window. Body's scrollport is the whole document, so nothing
   ever enters or leaves it and the headline scaling freezes solid. It looks
   exactly like the feature was never built.

   `clip` prevents the sideways scroll without creating a scroll container.
   Guarded in test/css.test.js. */
body { overflow-x: clip; }
