/*
  Times Checkout — EDITORIAL variant.
  Same content as ../times-checkout/, laid out to let typography carry the
  page: oversized display type, narrow measures for body copy, and hairline
  rules instead of filled cards.
*/

:root {
  /* Colour — near-monochrome; blue accent appears only in small doses */
  --bg: #ffffff;
  --ink: #14181a;
  --muted: #5c666b;
  --faint: #949da1;
  --rule: #e3e1dc;
  --accent: #181bf3;
  --dark-bg: #14181a;
  --dark-text: #ffffffdb;
  --dark-faint: #ffffff8c;
  --wash: #f4f1ec;

  /* Motion */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);

  --font: "Helvetica Neue", Helvetica, Arial, sans-serif;

  /* Layout */
  --gutter: 37.6667px;
  --measure: 560px;       /* comfortable line length for body copy */
  --measure-wide: 860px;  /* for large statement type (verdict) */
  --measure-statement: 658px; /* narrower measure used by chapter statements */
  --page-max: 1180px;

  /* Vertical rhythm */
  --gap-s: 24px;
  --gap-m: 48px;
  --gap-l: 96px;
  --gap-xl: 180px;        /* between chapters */
}

* { box-sizing: border-box; }

/* Cross-document view transition — matches the homepage's project card into
   this page's hero on click. Must be present on both ends of the
   navigation (see the fuller comment in home/styles.css); without it here,
   the homepage's own opt-in is a no-op and navigation just falls back to a
   plain cut, which is the correct degradation, not a bug. */
@view-transition {
  navigation: auto;
}

@media not (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) {
    animation-duration: 500ms;
    animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* Mirrors the identical block in home/styles.css — both sides of a
   cross-document transition need to agree on this timing. Ported from
   Checkout's card (2026-08-23, when this page's own homepage card moved
   from .project to .herocard): the card/background leads (expands first),
   the four content pieces follow on a delay so they read as falling into
   place afterward rather than everything resizing in lockstep. */
@media not (prefers-reduced-motion: reduce) {
  ::view-transition-group(vt-card-quickchecker) {
    animation-duration: 650ms;
    animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
  }

  ::view-transition-group(vt-media-quickchecker) {
    animation-duration: 550ms;
    animation-delay: 300ms;
    animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
  }

  ::view-transition-group(vt-eyebrow-quickchecker) {
    animation-duration: 500ms;
    animation-delay: 350ms;
    animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
  }

  ::view-transition-group(vt-title-quickchecker) {
    animation-duration: 500ms;
    animation-delay: 420ms;
    animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
  }

  ::view-transition-group(vt-meta-quickchecker) {
    animation-duration: 450ms;
    animation-delay: 500ms;
    animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
  }
}

html { scroll-behavior: smooth; }

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

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

/* ── Top nav ─────────────────────────────────────────────────── */

/* .nav is already a direct child of <body> (not nested inside the
   max-width-constrained .page), so it just needs width:100% — no
   full-bleed breakout trick needed, and that trick (100vw + left:50% +
   translateX(-50%)) is unreliable combined with position:sticky once the
   element engages its "stuck" state, so don't reach for it here. */
.nav {
  width: 100%;
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg);
  transition: transform 0.3s var(--ease-out);
}

.nav.is-hidden {
  transform: translateY(-100%);
}

.nav__inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 24px var(--gutter);
}

.nav__logo {
  font: 500 15px/20px var(--font);
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
}

.nav__links {
  display: flex;
  gap: 32px;
}

.nav__links a {
  font: 400 13px/18px var(--font);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  transition: color 150ms ease;
}

.nav__links a:hover { color: var(--ink); }

/* ── Page column ─────────────────────────────────────────────── */

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

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

.prose {
  font: 400 17px/29px var(--font);
  color: var(--muted);
  max-width: var(--measure);
  margin: 0;
}

.prose--lead {
  font-size: 19px;
  line-height: 32px;
  color: var(--ink);
}

/* Big, quiet statement type — the workhorse of this layout */
.statement {
  font: 400 29px/42px var(--font);
  letter-spacing: -0.015em;
  color: var(--ink);
  max-width: var(--measure-statement);
  margin: 0 0 var(--gap-l);
}

.passage {
  margin-bottom: var(--gap-m);
}

.passage__title {
  font: 500 13px/18px var(--font);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 14px;
}

/* Short list that follows a .prose paragraph (e.g. the billing constraints) */
.bullets {
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: var(--measure);
}

.bullets li {
  position: relative;
  padding-left: 18px;
  font: 400 16px/26px var(--font);
  color: var(--muted);
}

.bullets li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 10px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}

/* ── Hero (v2 — full-bleed image + title, greymac.com-style) ──── */

.hero {
  /* Matches the homepage hero-echo card's outer .herocard box by name
     (added 2026-08-23, when that card moved from .project to .herocard) —
     this is what makes the rounded, contained card visually grow into this
     full-bleed section on click, not just its inner image/text. */
  view-transition-name: vt-card-quickchecker;
  width: 100vw;
  position: relative;
  left: 50%;
  /* --parallax-y is driven by Motion.heroParallax (script.js) as the user
     scrolls past the hero, on top of the existing full-bleed centring
     transform — never touched directly by JS so the two never fight.
     Mobile's own .hero rule below sets transform:none outright, which
     naturally disables the effect there without extra JS branching. */
  transform: translateX(-50%) translateY(var(--parallax-y, 0px));
  background: var(--dark-bg);
  overflow: hidden;
  padding-top: 64px;
  /* Gap to the next section lives entirely on .metrics's padding-top below
     (single source, not split across two stacking properties) — see there
     for why. */
  margin-bottom: 0;
}

.hero__art {
  /* Matches the homepage project card's .project__media by name — see the
     view-transition-name comment in home/styles.css. This targets .hero__art
     (the wrapper), not the .ph placeholder inside it, so it'll keep working
     unchanged once a real <img> replaces the placeholder. */
  view-transition-name: vt-media-quickchecker;
  position: absolute;
  /* Vertically centred against the title/meta text block (.hero__top), not
     the nav or the hero as a whole. .hero__top is bottom-anchored inside a
     min-height:78vh box, so its bottom edge always sits at 78vh — hence the
     78vh anchor, which keeps the pair centred as viewport HEIGHT changes.

     Unlike the Checkout/Entitlements pages (which subtract a single tuned
     px constant), the offset here is split in two, because this page's art
     is a placeholder with a fixed 4:3 ratio at a % width — so its height
     tracks viewport WIDTH and a lone constant would only be correct at one
     window width:
       offset = half the text block's height  (209px — fixed; the title's
                line breaks are hard-coded and .hero__inner is capped at
                --page-max, so this doesn't change across desktop widths)
              + half the art's height          (13.875vw = ½ × 37% × ¾)
     Re-derive 209px if the title copy, meta rows, or .hero__top's
     padding/min-height change; re-derive the vw term if the art's width %
     or aspect ratio change.

     Sized up 30% + shifted right (2026-08-23, ported from the Checkout/
     Entitlements pages' same-day fix) — width 37%→48.1%, min-width
     340→442px, vw half-height term recomputed for the new width (½ ×
     48.1% × ¾ = 18.0375vw, was 13.875vw). At the bigger size the art's
     left edge would land under .hero__top's text column (620px max-width)
     at desktop widths, so it's also shifted right off its var(--gutter)
     inset — -75px bleeds it past .hero__inner's right edge into the outer
     margin that appears once the viewport exceeds --page-max (1180px).

     Re-derived again (2026-08-23) after .hero__art moved inside
     .hero__inner (see the comment at the top of the Hero section) — this
     caps the art's own width (and therefore height) at --page-max once the
     viewport exceeds 1180px, instead of growing forever with 100vw. The
     vw term is capped to match via min(): its value at exactly 1180px
     (18.0375% × 1180 = 212.84px) is the ceiling once the raw vw value
     would exceed it. The fixed term also needed re-deriving on top of
     that — moved from 287px to 351px — since this is a different
     containing block/positioning setup than the 287px value was solved
     for. Measured via getBoundingClientRect() at 1440×900 and 1920×900
     (to confirm the cap holds beyond --page-max) and solved for the
     offset that centres the art against .hero__top at both.

     Real hero image swapped in (2026-08-23), replacing the 4:3 placeholder.
     hero-devices.png is 2309×1380 (h/w = 0.5977, not 0.75), so the vw
     half-height coefficient was recomputed: ½ × 48.1% × 0.5977 = 14.374vw
     (was 18.0375vw). Cap recomputed to match: 14.374% × 1180 = 169.61px
     (was 212.84px). The fixed 351px term (half the text block's height)
     is unaffected by the art's aspect ratio, so it's unchanged. Computed,
     not re-measured live — width%/min-width/right are horizontal-only and
     don't depend on aspect ratio, so those also carry over unchanged. */
  top: calc(78vh - 351px - min(14.374vw, 169.61px));
  right: -75px;
  width: 48.1%;
  min-width: 442px;
}

.hero__shot {
  /* The artwork already has its own tilt, shadows and transparent ground —
     no rotate/radius/box-shadow needed here (unlike the old flat-screenshot
     treatment, which relied on this to fake depth). */
  position: relative;
  width: 100%;
  height: auto;
}

.hero__inner {
  position: relative;
  z-index: 1;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* Title block fills roughly the first screen, bottom-anchored — kept at
   78vh (its original position) rather than growing to 100vh, so the
   title/meta don't shift down. Now also holds the description (moved in
   from below the fold, see .hero__description) between the title and the
   rule, so the whole eyebrow→title→description→rule→meta stack reads as
   one composition within the initial viewport — same structure as the
   Checkout page. */
.hero__top {
  min-height: 78vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  max-width: 620px;
  padding-bottom: 64px;
}

.hero__eyebrow {
  view-transition-name: vt-eyebrow-quickchecker;
  display: block;
  font: 500 13px/18px var(--font);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: #fff;
  margin-bottom: 20px;
}

.hero__title {
  view-transition-name: vt-title-quickchecker;
  font: 700 clamp(44px, 6vw, 80px) / 1.04 var(--font);
  letter-spacing: -0.03em;
  color: #fff;
  margin: 0 0 32px;
}

.hero__rule {
  height: 1px;
  background: rgba(255, 255, 255, 0.18);
  margin-bottom: 28px;
}

.hero__meta {
  /* Added 2026-08-23, pairing with the homepage herocard's own
     .herocard__meta name once that card moved from .project to .herocard. */
  view-transition-name: vt-meta-quickchecker;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 32px 48px;
}

.hero__meta-item { display: flex; flex-direction: column; gap: 4px; }

.hero__meta dt {
  font: 500 11px/16px var(--font);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: #fff;
}

.hero__meta dd {
  margin: 0;
  font: 400 14px/21px var(--font);
  color: #fff;
}

/* Intro copy — sits between the title and the hairline rule, inside the
   initial viewport (moved up from a below-the-fold block that used to
   clear the fold with a large margin-top of its own). */
.hero__description {
  font: 400 19px/32px var(--font);
  color: #fff;
  text-align: left;
  max-width: var(--measure);
  margin: 0 0 40px;
}

/* ── Figures ─────────────────────────────────────────────────── */

.figure {
  margin: 0 0 var(--gap-l);
}

/* Plain full-width figure (e.g. workshop board) — no card, just a wash fallback */
.figure__img {
  width: 100%;
  background: var(--wash);
}

.figure__cap {
  margin-top: 14px;
  font: 400 13px/18px var(--font);
  color: var(--faint);
}

/* Grouped/annotated screenshots sit in a light card, sized and centred to
   Paper's exact crop rather than stretched to fill the column. */
.figure__card {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #d3e1e7;
  border-radius: 8px;
}

.figure--audit .figure__card { padding-block: 40px; }
.figure--audit .figure__img { width: 954px; max-width: 100%; height: auto; background: none; }

.figure--variant .figure__card { padding-block: 56px; }
.figure--variant .figure__img { width: 922px; max-width: 100%; height: auto; background: none; }

/* ── Image placeholders ──────────────────────────────────────────
   This page has no artwork yet — every figure is a labelled stand-in,
   carrying the same description the Paper artboard's own placeholder
   used. Dashed rule + "Image to come" tag so it can never be mistaken
   for finished work. Swap a .ph block for an <img> as assets land; the
   surrounding .figure / .hero__art sizing is image-agnostic. */

.ph {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 24px;
  text-align: center;
  background: var(--wash);
  border: 1px dashed #cdc4b6;
  border-radius: 8px;
}

.ph__tag {
  font: 500 11px/16px var(--font);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
}

.ph__label {
  font: 400 15px/23px var(--font);
  color: #8b8375;
  max-width: 340px;
}

/* On the dark hero the light wash would read as a blown-out panel — use a
   lifted charcoal and translucent white rule instead, same as .hero's own
   dark-surface treatments elsewhere. */
.ph--dark {
  background: #1f262a;
  border-color: rgba(255, 255, 255, 0.22);
}

.ph--dark .ph__tag { color: #fff; }
.ph--dark .ph__label { color: var(--dark-faint); }

/* Heights mirror the Paper artboard's own placeholder boxes: hero 560px
   (here as a ratio so it scales with .hero__art's % width), full-width
   research/testing figures 480px, grid cells 320px. */
.ph--hero { aspect-ratio: 4 / 3; }
.ph--tall { height: 480px; }
.ph--grid { height: 320px; }

/* Two-column grid of placeholders (wraps to 2×2 for four items) */
.figure--grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 24px;
}

/* Wide flow diagrams and the concepts matrix — same grouped-card treatment,
   sized to the artwork's own crop rather than stretched to the column. */
.figure--flow .figure__card { padding: 56px 40px; }
.figure--flow .figure__img { width: 1000px; max-width: 100%; height: auto; background: none; }

.figure--matrix .figure__card { padding: 56px 40px; }
.figure--matrix .figure__img { width: 892px; max-width: 100%; height: auto; background: none; }

/* Two annotated screenshots side by side. Fixed card height + max-height on
   the images (rather than a shared width) is what keeps the pair level —
   the two sources have different aspect ratios, so matching widths would
   give mismatched card heights. */
.figure--pair { display: flex; gap: 24px; }
.figure--pair .figure__card { flex: 1; min-width: 0; height: 420px; padding: 40px; }
.figure--pair .figure__img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  background: none;
}

/* Phone screenshots grouped in a card — kept at Paper's own 264px crop
   rather than filling the card, two or four to a row. */
.figure--phones .figure__card { padding: 56px 40px; gap: 64px; }
.figure--phones .figure__img { width: 264px; max-width: 100%; height: auto; background: none; }

/* Four across needs tighter side padding than the two-up card, or the row
   (4×240 + 3×24 gaps) runs past the 1104px content column. */
.figure--phones-4 .figure__card { gap: 24px; padding-inline: 24px; }
.figure--phones-4 .figure__img { width: 240px; }

/* Mobile-next-to-desktop showcase — a taller variant of .figure--pair. The
   mobile screenshot is a full-page scroll capture (extremely tall, narrow
   aspect ratio), so the standard 420px pair height would render it almost
   illegibly thin; 760px gives it enough width to read as a phone next to a
   wider desktop frame, which is the expected shape for this comparison. */
.figure--devices .figure__card { height: 760px; }

/* ── Metrics ─────────────────────────────────────────────────── */

.metrics {
  /* Single source for the hero→Impact gap (.hero's own margin-bottom is
     zeroed above). Superseded the earlier "match the solution-copy gap"
     target (276px) — matching that value made the block look bottom-heavy
     once scrolled past (276px of white space above the copy vs. only this
     rule's own 180px padding-bottom below it). Centering the copy in the
     white space instead means the final (fully-scrolled-past) top gap
     should equal this same padding-bottom (180px = var(--gap-xl)), not the
     solution gap. Motion.heroParallax (script.js) pushes .hero up by its
     own maxOffset (180px here) once fully scrolled past, and since .metrics
     doesn't share that transform, the visible top gap = this padding-top +
     180px. So padding-top is set to 180 − 180 = 0 — at rest (pre-scroll)
     Impact sits flush against the hero, then the gap grows in to exactly
     180px as the parallax engages, matching the 180px below and centering
     the copy in the revealed white space. */
  padding-top: 0;
  padding-bottom: var(--gap-xl);
}

.metrics__title {
  display: block;
  margin: 0 0 32px;
}

.metrics__row {
  display: flex;
  gap: 64px;
}

.metric {
  flex: 1;
  border-top: 1px solid var(--ink);
  padding-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.metric__value {
  font: 700 clamp(44px, 4.6vw, 64px) / 1 var(--font);
  letter-spacing: -0.04em;
  color: var(--ink);
}

.metric__label {
  font: 400 14px/21px var(--font);
  color: var(--muted);
  max-width: 100%;
  white-space: nowrap;
}

/* ── Context block — cascading diagonal layout ──────────────────
   Now lives inside the dark .hero (below the fold, alongside
   .hero__description) — positions/sizes unchanged, only entry__text's
   colour was adjusted so it reads on the dark background. */

.block {
  position: relative;
  height: 741px;
  /* Extra separation from .hero__description above (on top of its own
     96px margin-bottom) so Context/Problem/Solution reads as its own
     moment rather than a continuation of the intro paragraph. */
  margin-top: var(--gap-xl);
  margin-bottom: var(--gap-xl);
}

.entry {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 323px;
}

.entry:nth-child(1) { top: 0;    left: 0px; }
.entry:nth-child(2) { top: 231px; left: 392px; }
.entry:nth-child(3) { top: 462px; left: 783px; }

.entry__label {
  font: 700 11px/22px var(--font);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: #fff;
}

.entry__text {
  margin: 0;
  font: 400 17px/29px var(--font);
  color: #fff;
}

/* ── Chapters ────────────────────────────────────────────────── */

.chapter { padding-bottom: var(--gap-xl); }

/* A chapter that exists only to introduce the section right below it (the
   Launch & Impact head above the dark Result band) — the full inter-chapter
   gap would read as a break between them rather than a lead-in, so the
   statement's own margin-bottom carries the whole gap. */
.chapter--lead-in { padding-bottom: 0; }

/* Last chapter on the page: its findings list already carries a bottom
   margin, so the full inter-chapter gap stacked on top of that over-spaces
   the run-in to the footer. */
.chapter--last { padding-bottom: var(--gap-l); }
.chapter--last .findings { margin-bottom: 0; }

.chapter__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  margin-bottom: 40px;
}

.chapter__num {
  font: 500 16px/22px var(--font);
  color: var(--accent);
}

.chapter__title {
  font: 700 clamp(34px, 3.6vw, 46px) / 1.08 var(--font);
  letter-spacing: -0.025em;
  color: var(--ink);
  margin: 0;
}

/* ── Findings / iteration items — rules, not boxes ───────────── */

.findings { margin-bottom: var(--gap-l); }

.finding {
  display: flex;
  gap: 48px;
  padding: 32px 0;
  border-top: 1px solid var(--rule);
}

.finding__icon {
  flex: 0 0 40px;
  color: var(--accent);
}

.finding__icon svg {
  width: 28px;
  height: 28px;
  margin-top: 2px;
  display: block;
}

/* The icon's stroke-draw is its ONLY motion — .finding itself carries no
   opacity/transform reveal (that lives on .finding__body instead, see below),
   so the icon never fades/slides at the same time it's tracing its outline.
   pathLength="100" on each shape normalizes stroke-dasharray/-offset
   regardless of actual path geometry. */
.icon-draw {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transition: stroke-dashoffset 600ms var(--ease-out);
}

.finding.is-visible .icon-draw {
  stroke-dashoffset: 0;
}

/* Filled dots (no stroke to draw) land just after the line finishes. */
.icon-dot {
  opacity: 0;
  transform-origin: center;
  transform: scale(0.5);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
  transition-delay: 500ms;
}

.finding.is-visible .icon-dot {
  opacity: 1;
  transform: scale(1);
}

/* Missing-trust-signals icon keeps its permanent "3 3" dashed stroke — animating
   its own stroke-dashoffset would just phase-shift the existing dash tiling, not
   reveal it, so the dashes wouldn't visibly draw in. Instead an invisible mask
   path (identical "d", solid, pathLength-normalized) draws in behind the scenes
   and progressively unmasks the real dashed path, so the dashes themselves
   appear to trace on one at a time rather than everything popping in at once. */
.icon-mask-draw {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transition: stroke-dashoffset 1050ms var(--ease-out);
}

.finding.is-visible .icon-mask-draw {
  stroke-dashoffset: 0;
}

@media (prefers-reduced-motion: reduce) {
  .icon-draw,
  .icon-mask-draw { stroke-dashoffset: 0; transition: none; }
  .icon-dot { opacity: 1; transform: none; transition: none; }
}

.finding__body {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.finding__title {
  font: 500 22px/30px var(--font);
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0;
}

.finding__points {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 9px;
  max-width: var(--measure);
}

.finding__points li {
  font: 400 16px/26px var(--font);
  color: var(--muted);
}

/* ── Hypotheses — large pull-quote treatment ─────────────────── */

.hypotheses {
  display: flex;
  gap: 72px;
  margin-bottom: var(--gap-l);
}

.hypothesis {
  flex: 1;
  border-top: 1px solid var(--ink);
  padding-top: 20px;
}

.hypothesis__label {
  display: block;
  font: 500 11px/16px var(--font);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 16px;
}

.hypothesis__text {
  margin: 0;
  font: 400 21px/33px var(--font);
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* ── Measures — success criteria as three ruled columns ──────── */

.measures {
  display: flex;
  gap: 64px;
  margin-bottom: var(--gap-l);
}

.measure {
  flex: 1;
  border-top: 1px solid var(--ink);
  padding-top: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.measure__title {
  margin: 0;
  font: 500 19px/27px var(--font);
  letter-spacing: -0.01em;
  color: var(--ink);
}

.measure__text {
  margin: 0;
  font: 400 15px/25px var(--font);
  color: var(--muted);
}

/* ── Before / after comparisons ──────────────────────────────── */

.compare__row { margin-bottom: var(--gap-l); }

.compare__intro { margin-bottom: 32px; }

.compare__pair {
  display: flex;
  gap: 56px;
  align-items: flex-start;
}

/* Each shot is a fixed-height card; the horizontal gap around the image
   comes from centring, not from explicit side padding (Paper lets a
   fixed-width image sit inside a fixed/flexible card and centres it). */
.shot {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  padding-block: 40px;
  background: #d3e1e7;
  border-radius: 8px;
}

.shot:first-child {
  flex: 0 0 621px;
  max-width: 100%;
}

.shot:last-child {
  flex: 1;
  min-width: 0;
}

.shot img {
  /* width comes from the HTML width attribute (matches Paper's exact crop);
     height follows the source's natural aspect ratio at that width. */
  max-width: 100%;
  height: auto;
  display: block;
}

.shot figcaption {
  font: 400 13px/18px var(--font);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--faint);
  order: -1;
}

/* ── Usability verdict ───────────────────────────────────────── */

.verdict {
  border-top: 1px solid var(--ink);
  padding-top: 24px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.verdict__label {
  font: 500 11px/16px var(--font);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
}

.verdict__words {
  margin: 0;
  font: 400 clamp(28px, 3.2vw, 40px) / 1.2 var(--font);
  letter-spacing: -0.025em;
  color: var(--ink);
  max-width: var(--measure-wide);
}

/* ── Result — the one dark moment ────────────────────────────── */

.result {
  background: var(--dark-bg);
  color: #fff;
  /* Full width of .page's content column (matches every other section),
     not a narrower fixed-width card centred within it. */
  width: 100%;
  margin-bottom: var(--gap-xl);
  padding: 96px 56px;
  display: flex;
  flex-direction: column;
}

.result__label {
  font: 500 11px/16px var(--font);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: #fff;
  margin-bottom: 40px;
}

.result__figure {
  margin: 0;
  font: 700 clamp(64px, 8vw, 112px) / 1 var(--font);
  letter-spacing: -0.045em;
  color: #fff;
}

.result__unit {
  margin: 12px 0 56px;
  font: 400 19px/28px var(--font);
  color: var(--dark-faint);
}

.result__copy {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 620px;
}

.result__copy p {
  margin: 0;
  font: 400 17px/30px var(--font);
  color: var(--dark-text);
}

/* ── What's next ─────────────────────────────────────────────── */

.nexts { display: flex; flex-direction: column; }

.next {
  display: flex;
  gap: 64px;
  align-items: flex-start;
  padding: 48px 0;
  border-top: 1px solid var(--rule);
}

.next__text {
  flex: 0 0 434px;
  max-width: 100%;
}

.next__label {
  display: block;
  font: 500 11px/16px var(--font);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 14px;
}

.next__statement {
  margin: 0 0 20px;
  font: 400 19px/31px var(--font);
  color: var(--ink);
}

.next__lever {
  margin: 0;
  font: 400 15px/24px var(--font);
  color: var(--muted);
}

.next__lever span {
  display: block;
  font: 500 11px/16px var(--font);
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 5px;
}

.next__shots {
  flex: 1;
  min-width: 0;
  height: 571px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: #d3e1e7;
}

.next__shots img {
  width: auto;
  height: 437px;
  max-width: 45%;
  object-fit: cover;
  object-position: top;
  flex-shrink: 0;
}

/* ── Footer ──────────────────────────────────────────────────── */

.foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 40px 0 64px;
  border-top: 1px solid var(--rule);
}

.foot__name {
  font: 400 14px/20px var(--font);
  color: var(--faint);
}

.foot__links { display: flex; gap: 28px; }

.foot__links a {
  font: 400 14px/20px var(--font);
  color: var(--muted);
  text-decoration: none;
  transition: color 150ms ease;
}

.foot__links a:hover { color: var(--ink); }

/* ── Motion ──────────────────────────────────────────────────── */

.js-fade-up,
.js-reveal {
  opacity: 0;
  transform: translateY(16px);
  transition-property: opacity, transform;
  transition-timing-function: var(--ease-out);
  transition-duration: 600ms;
}

.js-fade-up.is-visible,
.js-reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js-fade-up,
  .js-reveal { opacity: 1; transform: none; transition: none; }
}

/* ── Tablet ──────────────────────────────────────────────────── */

@media (max-width: 1080px) {
  :root { --gutter: 48px; --gap-xl: 128px; --gap-l: 72px; }

  /* Own vertical-centring constant — the title wraps differently at this
     width (shorter lines), so the desktop offset doesn't carry over.
     Derived the same way: measured at 1000×900 and solved for the gap. */
  /* Same two-part offset as desktop (see .hero__art above), with this
     breakpoint's own values: the title renders smaller here (clamp resolves
     to ~7.2vw), so the text block is shorter — 178px instead of 209px — and
     the art is narrower, so its half-height term is 9vw (½ × 24% × ¾).

     width is 24%, NOT the 30% inherited from the sibling pages, and
     min-width is cleared: at 30% the 340px min-width won floor-to-ceiling
     across this whole breakpoint, pinning the art at 340px wide and
     overlapping the title by 56px. The title fills its full 620px column at
     this size, so the art has to fit in what's left.

     Scaled with the base rule's 30% size-up (2026-08-23) — width 24%→31.2%,
     vw half-height term recomputed (½ × 31.2% × ¾ = 11.7vw, was 9vw). The
     bigger width reopens the overlap this rule was originally tuned to
     avoid (measured ~55px overlap at this breakpoint's 960px lower bound
     with right still at var(--gutter)), so right is also shifted, same as
     the desktop rule above: -22px restores roughly the same ~14px
     clearance at 960px this rule had pre-resize. Not measured live.

     Fixed term re-derived again (2026-08-23) after .hero__art moved
     inside .hero__inner (see the comment at the top of the Hero section)
     — this breakpoint never exceeds --page-max (1180px), so the art's
     width/height here were never actually affected by that change, but
     the offset still needed re-solving for the new containing block.
     Measured at 1000×900: 287px → 351px. No min() cap needed here (only
     the desktop rule above can exceed --page-max).

     Real hero image swapped in (2026-08-23): vw coefficient recomputed for
     hero-devices.png's 0.5977 h/w ratio (was the 4:3 placeholder's 0.75) —
     ½ × 31.2% × 0.5977 = 9.324vw (was 11.7vw). Computed, not re-measured. */
  .hero__art {
    width: 31.2%;
    min-width: 0;
    right: -22px;
    top: calc(78vh - 351px - 9.324vw);
  }
  .hypotheses { flex-direction: column; gap: 40px; }
  /* .block goes position:static/height:auto at this breakpoint (below), so
     there's no fixed-height dead space below the Solution text any more —
     the solution-to-hero-bottom gap here is just .block's own margin-bottom,
     i.e. --gap-xl at this breakpoint (128px). That's LESS than the 180px
     parallax offset used at wider (>1080px) viewports, so the parallax is
     capped to maxOffsetCompact (128px, see index.html's Motion.heroParallax
     call) at this breakpoint and below — otherwise the hero would overshoot
     past where Impact starts once fully scrolled by, visually sliding over
     it. With the offset capped to exactly the target gap, padding-top can
     be 0 (0 + 128 offset = 128, matching). */
  .metrics { padding-top: 0; }
  .metrics__row { gap: 40px; }
  .measures { gap: 32px; }
  .ph--tall { height: 380px; }
  .ph--grid { height: 260px; }
  .figure--flow .figure__card,
  .figure--matrix .figure__card { padding: 40px 24px; }
  .figure--pair .figure__card { height: 340px; padding: 28px; }
  .figure--devices .figure__card { height: 615px; }
  .figure--phones .figure__card { padding: 40px 24px; gap: 40px; }
  /* Four 240px phones plus gaps overflow the narrower column here. */
  .figure--phones-4 .figure__card { gap: 16px; padding-inline: 20px; }
  .figure--phones-4 .figure__img { width: 200px; }
  .next { flex-direction: column; gap: 32px; }
  .next__text { flex: 0 0 auto; width: 100%; }
  .next__shots { flex: 0 0 auto; width: 100%; max-width: 340px; height: auto; padding-block: 32px; }
  .next__shots img { height: 300px; }

  .block { position: static; height: auto; }
  .entry { position: static; width: 100%; gap: 10px; margin-bottom: 32px; }
  .entry:last-child { margin-bottom: 0; }
}

/* ── Narrow tablet — stack the hero art ───────────────────────────
   The absolutely-positioned art only clears the text column down to
   960px. Below that it starts overlapping .hero__rule and .hero__meta,
   which both run the full 620px of .hero__top's column regardless of how
   short the title's own text is (so this collides well before the title
   text itself looks crowded — measure against the rule/meta, not the
   title, when re-checking this).

   Range-bounded rather than a plain max-width so it can't leak into the
   720px mobile block below, which sets its own hero-art treatment and
   does not reset max-width / margin-inline. */

@media (max-width: 959px) and (min-width: 721px) {
  .hero__art {
    position: static;
    width: auto;
    min-width: 0;
    max-width: 598px; /* scaled with the 30% size-up above (2026-08-23) */
    /* No horizontal margin (2026-08-23, after .hero__art moved inside
       .hero__inner — see the top of the Hero section) — .hero__inner's own
       padding already provides the gutter inset, so this just needs the
       bottom gap. The old `0 var(--gutter)` was for when .hero__art was a
       sibling of .hero__inner with no inset of its own; kept here it would
       double up with .hero__inner's padding. */
    margin: 0 0 48px;
  }

  /* With the art in flow above it, the title block no longer needs to
     fill the first screen — the two together already do. */
  .hero__top { min-height: 0; padding-bottom: 0; }
}

/* ── Mobile ────────────────────────────────────────────────────── */

@media (max-width: 720px) {
  :root {
    --gutter: 20px;
    --gap-l: 56px;
    --gap-xl: 88px;
    --measure: 100%;
  }

  .nav__inner { padding: 16px var(--gutter); }

  .hero {
    position: static;
    left: auto;
    /* Was transform:none, which fully disabled Motion.heroParallax here
       (the JS only ever writes the --parallax-y custom property, never
       touches transform directly, so a flat "none" silently ate the
       effect). translateY doesn't affect layout under position:static, so
       re-enabling it here is safe with the negative-margin full-bleed
       trick below. */
    transform: translateY(var(--parallax-y, 0px));
    width: auto;
    padding: 48px 0 40px;
    margin-left: calc(-1 * var(--gutter));
    margin-right: calc(-1 * var(--gutter));
  }
  .hero__art {
    position: static;
    /* width:auto + negative horizontal margins (2026-08-23, after
       .hero__art moved inside .hero__inner) instead of width:100% — since
       .hero__inner carries its own gutter padding here, width:100% alone
       would inset the image by that padding on both sides, losing the
       full-bleed-to-the-viewport-edge treatment. Same trick .hero itself
       already uses one level up, and the one Checkout's own mobile
       .hero__art rule uses for the same reason. */
    width: auto;
    min-width: 0;
    height: 416px; /* scaled with the 30% size-up above (2026-08-23) */
    overflow: hidden;
    margin: 0 calc(-1 * var(--gutter)) 32px;
  }
  .hero__shot {
    /* contain, not cover — this is a transparent two-phone composite, not a
       full-bleed screenshot, so cropping it would cut phones off oddly. */
    width: 100%;
    height: 100%;
    object-fit: contain;
  }
  .hero__inner { padding: 0 var(--gutter); }
  .hero__top { min-height: 0; max-width: none; padding-bottom: 0; }
  .hero__eyebrow { margin-bottom: 14px; }
  .hero__title { font-size: 40px; letter-spacing: -0.03em; margin-bottom: 24px; }
  .hero__meta { gap: 20px 32px; }
  .hero__description { font-size: 17px; line-height: 28px; margin: 0 0 32px; }

  .statement { font-size: 21px; line-height: 32px; margin-bottom: var(--gap-l); }
  .prose { font-size: 16px; line-height: 26px; }
  .prose--lead { font-size: 17px; line-height: 28px; }

  /* Solution-to-hero-bottom gap at this breakpoint: .block is static/
     auto-height here too (no dead space below the Solution text), so the
     gap is exactly .block's own margin-bottom (--gap-xl, 88px) plus .hero's
     own bottom padding (40px) = 128px — same 128px target as tablet above,
     for a different reason (two stacked values here vs. one there). Uses
     the same capped 128px parallax offset (maxOffsetCompact in index.html's
     Motion.heroParallax call, shared with the tablet breakpoint) so 0
     static padding-top here plus the capped offset lands on 128px once
     fully scrolled past, matching. */
  .metrics { padding-top: 0; }
  .metrics__row { flex-direction: column; gap: 28px; }
  .metric__value { font-size: 44px; }
  .metric__label { max-width: 100%; }

  .block { position: static; height: auto; }
  .entry { position: static; width: 100%; gap: 10px; margin-bottom: 32px; }
  .entry:last-child { margin-bottom: 0; }

  .chapter__head { gap: 4px; margin-bottom: 28px; }
  .chapter__num { font-size: 14px; line-height: 19px; }
  .chapter__title { font-size: 28px; }

  .finding { flex-direction: column; gap: 12px; padding: 24px 0; }
  .finding__icon { flex: none; }
  .finding__icon svg { margin-top: 0; }
  .finding__title { font-size: 19px; line-height: 26px; }
  .finding__points li { font-size: 15px; line-height: 24px; }

  .hypothesis__text { font-size: 18px; line-height: 29px; }

  /* .hero__art is a fixed 320px band on mobile, so the hero placeholder has
     to fill it rather than keep its own aspect ratio (which would overflow). */
  .ph--hero { aspect-ratio: auto; height: 100%; }
  .ph--tall { height: 280px; }
  .ph--grid { height: 220px; }
  .ph__label { font-size: 14px; line-height: 22px; }
  .figure--grid { grid-template-columns: 1fr; gap: 16px; }

  .measures { flex-direction: column; gap: 28px; }
  .measure__title { font-size: 17px; line-height: 25px; }

  .bullets li { font-size: 15px; line-height: 24px; }

  .figure--flow .figure__card,
  .figure--matrix .figure__card { padding: 20px 12px; }
  .figure--pair { flex-direction: column; gap: 16px; }
  .figure--pair .figure__card { height: 260px; padding: 20px; }
  .figure--devices .figure__card { height: 470px; }
  /* Phones wrap two-up rather than shrinking to unreadable widths. */
  .figure--phones .figure__card { flex-wrap: wrap; padding: 28px 16px; gap: 16px; }
  .figure--phones .figure__img,
  .figure--phones-4 .figure__img { width: calc(50% - 8px); }
  .figure--phones-1 .figure__img { width: 60%; }

  .compare__pair { flex-direction: column; gap: 32px; }
  .shot { width: 100%; }
  .shot:first-child { flex: 0 0 auto; }
  /* Fixed height + object-fit:cover, matching .next__shots img's mobile
     treatment — without this, .shot img's height:auto (desktop rule) lets
     each source image's own aspect ratio dictate the card height, so
     "before" and "after" end up wildly different heights instead of a
     matching pair. */
  .shot img { width: auto; height: 300px; max-width: 100%; object-fit: cover; object-position: top; }

  .verdict__words { font-size: 24px; }

  .result { padding: 56px var(--gutter); }
  .result__figure { font-size: 60px; }
  .result__unit { font-size: 16px; margin-bottom: 36px; }
  .result__copy p { font-size: 16px; line-height: 27px; }

  .next__statement { font-size: 17px; line-height: 28px; }
  .next__shots { max-width: 100%; }

  .foot { flex-direction: column; align-items: flex-start; gap: 20px; padding: 32px 0 48px; }
}
