:root {
  --paper: #f6f5f4;
  --surface: #ffffff;
  --ink: #191918;
  --ink-2: #31302e;
  --muted: #615d59;
  --hairline: #dedbd8;
  --accent: #2563eb;
  --accent-hover: #1d4ed8;
  --accent-soft: #eaf0fd;
  --accent-border: #bdd0f9;
  --positive: #18744f;
  --negative: #c73a2e;
  --warning: #9a4d08;
  /* Per-card canvas tints. The deck REQUIRES each card to be opaque and
     visually distinct from the one it slides over, so these are structural,
     not decoration. All sit in a narrow pale band (L ~0.96-0.98) so the stack
     reads as one paper family rather than a colour-coded rainbow; the cobalt
     accent stays the only saturated colour on the page. Documented here as
     tokens so they are a named part of the system, not literals scattered
     through the card rules. */
  --card-hero: #f8f6f1;
  --card-connect: #edf3ff;
  --card-ai: #ffffff;
  --card-investing: #eef7f1;
  --card-property: #fff3eb;
  --card-spending: #eaf7f6;
  --card-structures: #f4effa;
  --card-tax: #fff2ef;

  /* Inverted ramp for the one dark surface (the signup band). The light-theme
     ink/muted tokens are unusable on near-black, so these are the on-dark
     equivalents. Measured against --ink (#191918): body 12.15:1, border
     4.82:1, ok 11.8:1, err 9.65:1 — all clear of the 4.5:1 AA bar. */
  --on-dark-body: #d9d6d2;
  --on-dark-border: #8a8580;
  --on-dark-ok: #9ee2bf;
  --on-dark-err: #ffaaa2;

  --header-height: 70px;
  --content: 1180px;
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --shadow-raised: 0 2px 8px rgba(25, 25, 24, 0.08);
  --shadow-modal: 0 18px 52px rgba(25, 25, 24, 0.16);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  color: var(--ink);
  background: var(--paper);
  scroll-behavior: smooth;
}

* {
  box-sizing: border-box;
}

html {
  background: var(--paper);
  /* EXACTLY the sticky offset of `.card`, with no breathing room added. The
     anchor target IS a sticky card, so any extra padding lands the jump that
     many pixels SHORT of the card's own slot — the previous slot then still
     owns the scroll position, and the nav reports the previous card while the
     right one is on screen. Keep this in lock-step with `.card { top }`. */
  scroll-padding-top: var(--header-height);
}

body {
  margin: 0;
  min-width: 320px;
  background: var(--paper);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

body.is-modal-open {
  overflow: hidden;
}

img,
video,
svg {
  display: block;
}

button,
input {
  font: inherit;
}

a {
  color: inherit;
}

button,
a,
input,
summary,
[tabindex] {
  -webkit-tap-highlight-color: transparent;
}

button:focus,
a:focus,
input:focus,
summary:focus,
[tabindex]:focus {
  outline: 3px solid var(--accent-border);
  outline-offset: 3px;
}

.skip-link {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 500;
  transform: translateY(-180%);
  padding: 10px 14px;
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 600;
  box-shadow: var(--shadow-raised);
}

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

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  height: var(--header-height);
  background: rgba(246, 245, 244, 0.96);
  border-bottom: 1px solid rgba(25, 25, 24, 0.1);
}

.site-header__bar {
  width: min(100%, calc(var(--content) + 64px));
  height: 100%;
  margin: 0 auto;
  padding: 0 24px;
  display: flex;
  align-items: center;
  gap: 24px;
}

.wordmark {
  display: inline-flex;
  flex: none;
  text-decoration: none;
}

.story-nav {
  min-width: 0;
  flex: 1;
  display: flex;
  align-items: stretch;
  gap: 22px;
  height: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

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

.story-nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: none;
  color: var(--muted);
  text-decoration: none;
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
}

.story-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: transparent;
}

.story-nav a:hover,
.story-nav a[aria-current="location"] {
  color: var(--ink);
}

.story-nav a[aria-current="location"]::after {
  background: var(--accent);
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: none;
}

.text-link {
  color: var(--ink-2);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 600;
}

.text-link:hover {
  text-decoration: underline;
  text-underline-offset: 4px;
}

.button {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 999px;
  padding: 10px 20px;
  cursor: pointer;
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.35;
  /* justify-content centres the text as ONE flex item, but if a long label
     wraps, the second line is left-ragged inside a centred pill — this
     shipped visibly on the hero CTAs at 390px. text-align centres the lines
     within the item, so any future label that wraps stays centred. */
  text-align: center;
  transition: background 160ms ease, color 160ms ease, transform 160ms ease;
}

.button:hover {
  transform: translateY(-1px);
}

.button:active {
  transform: translateY(0);
}

.button--primary {
  background: var(--accent);
  color: #fff;
}

.button--primary:hover {
  background: var(--accent-hover);
}

.button--quiet {
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--hairline);
}

.button--quiet:hover {
  background: #f1f0ef;
}

.button--compact {
  min-height: 38px;
  padding: 8px 16px;
  font-size: 0.8125rem;
}

/* ── The deck ─────────────────────────────────────────────────────────
   Each `.deck__slot` is a tall scroll track whose `.card` sticks to the top
   of the viewport; the next slot's card then rises over it, so cards pin and
   stack. Pure CSS sticky, so native scrolling is never intercepted: the
   document really is ~10 screens tall and deep links, Cmd+F and scroll
   restoration all keep working.

   Every card must be in its OWN slot. Sticky siblings inside one container
   release together, which would break the stack. */

.deck {
  position: relative;
}

.deck__slot {
  /* Scroll distance per card. 200svh gives one screen of "pinned" reading
     plus one screen of travel for the next card to cover it. */
  height: 200vh;
}

@supports (height: 100svh) {
  .deck__slot {
    height: 200svh;
  }
}

/* The final slot needs no travel beyond its own screen: there is no next
   card to rise over it, and extra height would leave the finale pinned with
   dead space before the trust band. */
.deck__slot--last {
  height: auto;
}

.card {
  position: sticky;
  top: var(--header-height);
  /* svh, not vh: mobile browser chrome collapsing must not resize the card
     mid-scroll. The card fills exactly the space under the sticky header. */
  height: calc(100vh - var(--header-height));
  overflow: hidden; /* clip — a card never scrolls internally */
  background: var(--paper);
  border-top: 1px solid rgba(25, 25, 24, 0.08);
  /* `position: sticky` already makes this the containing block for the
     absolutely-positioned .pane-nav, and isolate keeps each card's stacking
     context self-contained as later cards slide over it. */
  isolation: isolate;
}

@supports (height: 100svh) {
  .card {
    height: calc(100svh - var(--header-height));
  }
}

/* Opaque per-card backgrounds are load-bearing: a transparent card would
   show the pinned card beneath it. Values live as `--card-*` tokens in :root. */
.card--hero {
  background: var(--card-hero);
}

.card--connect {
  background: var(--card-connect);
}

.card--ai {
  background: var(--card-ai);
}

.card--investing {
  background: var(--card-investing);
}

.card--property {
  background: var(--card-property);
}

.card--spending {
  background: var(--card-spending);
}

.card--structures {
  background: var(--card-structures);
}

.card--tax {
  background: var(--card-tax);
}

.card--finale {
  background: var(--card-hero);
}

/* ── Panes: the card's horizontal axis ───────────────────────────────── */

.card__panes {
  display: flex;
  height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  /* contain: a sideways swipe must not chain to browser back-navigation
     (the iOS edge-swipe conflict). */
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

/* Drag affordance. `grab` is the standing invitation; `grabbing` confirms the
   gesture took. Applied to the track, not the panes, so a nested control keeps
   its own cursor. Touch never sees either.

   Scoped to [data-pane-draggable], which wrap.js sets ONLY on tracks it wired
   for dragging. `.card__panes` alone would also match the single-pane hero,
   entry and finale cards, showing a grab cursor that promises an interaction
   those cards do not have. A cursor is a claim about what will happen; it must
   never appear on a surface that cannot honour it. */
@media (hover: hover) and (pointer: fine) {
  .card__panes[data-pane-draggable] {
    cursor: grab;
  }

  .card__panes[data-pane-draggable].is-dragging {
    cursor: grabbing;
  }
}

/* While dragging, kill selection and image-drag: without this the browser
   highlights the body copy under the cursor and tries to drag the screenshot
   out as a file, both of which fight the gesture. */
.card__panes.is-dragging,
.card__panes.is-dragging * {
  user-select: none;
  -webkit-user-select: none;
}

.card__panes.is-dragging img {
  -webkit-user-drag: none;
  pointer-events: none;
}

/* The one-shot nudge. Panes shift a few px toward the next one and ease back,
   so the horizontal axis announces itself without the reader waiting on an
   animation. Transform, not scroll position: nudging scrollLeft would fight
   scroll-snap and leave the track fractionally off its boundary. */
@keyframes pane-nudge {
  0% {
    transform: translateX(0);
  }
  38% {
    transform: translateX(-14px);
  }
  100% {
    transform: translateX(0);
  }
}

.card__panes.is-hinting .pane {
  animation: pane-nudge 1s cubic-bezier(0.22, 1, 0.36, 1) 1;
}

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

.pane {
  /* The horizontal gutter as a token: the keyed lead art bleeds outward by
     exactly this much (negative margin below), so bleed and padding can
     never drift apart. */
  --pane-pad-x: max(24px, calc((100vw - var(--content)) / 2));
  flex: 0 0 100%;
  min-width: 0;
  height: 100%;
  scroll-snap-align: start;
  overflow: hidden;
  padding: clamp(20px, 3.4vh, 48px) var(--pane-pad-x);
}

/* No `:has()` guard for single-pane cards: the project's browser floor
   excludes it (Safari 14), and a lone `flex: 0 0 100%` pane cannot overflow,
   so there is nothing to scroll. */

/* ── Hero pane ────────────────────────────────────────────────────────── */

.pane--hero {
  position: relative;
  display: flex;
  align-items: center;
  padding: 0;
}

.hero__art {
  position: absolute;
  inset: 0;
  width: 100%;
  margin: 0;
  /* NO translateX here. A `translateX(13%)` used to live on this rule to push a
     CENTRED ring composition out from under the mask below. The illustration is
     now composed right-of-centre with its own empty left margin, so that shift
     became pure overflow: being a percentage of the box, it threw a constant
     12% of the artwork off the right edge at EVERY desktop width (149px at
     1280, 170px at 1440, 233px at 1920), clipping the shares sheet, the keys
     and the receipt. Do not reintroduce it. */
  /* Matches the illustration's own paper tone (#F5F4ED, sampled from the file)
     rather than the card's #F8F6F1, so the bands `contain` leaves above and
     below the art are seamless. The two differ by 1.021:1 — imperceptible
     either way — but matching the image removes the question. */
  background: #f5f4ed;
  /* The fade must track the COPY COLUMN's pixel edge, not a viewport %.
     The copy's right edge is fixed at 614px (24px content padding + 590px
     measure) until the 1244px content cap, then grows with the centred
     content box: (100vw - 1244px) / 2 + 614px = 50vw - 8px. Percent-only
     stops (37/44/55%) crossed under that edge on every viewport narrower
     than ~1116px and grazed it up to ~1400px on tall windows (where
     `contain` lets the image span the full width) — opaque ink rendered
     beneath the paragraph. So each stop carries the copy edge as a floor:
     art stays invisible until the copy column ends, reaches 10% alpha 34px
     later, and is fully opaque only 88px past it. If `.hero__content`
     padding or the 590px measure changes, these constants move with it
     (and so does the mirrored bound in README.md's fit check). */
  -webkit-mask-image: linear-gradient(to right, transparent 0, transparent max(614px, 50vw - 8px), rgba(0, 0, 0, 0.1) max(648px, 50vw + 26px), #000 max(702px, 50vw + 80px));
  mask-image: linear-gradient(to right, transparent 0, transparent max(614px, 50vw - 8px), rgba(0, 0, 0, 0.1) max(648px, 50vw + 26px), #000 max(702px, 50vw + 80px));
}

.hero__art img {
  width: 100%;
  height: 100%;
  /* `contain`, not `cover`: the art is 3:2 but this box runs from 1.7 to 2.1
     wide, so `cover` cropped 120-330px off the top and bottom and cut the
     crypto coins (-52px at 1280x680) plus her feet. `contain` guarantees the
     whole frame is visible at every width. Anchored right so the subject stays
     clear of the copy column and under the opaque part of the mask. */
  object-fit: contain;
  object-position: right center;
}

.hero__content {
  position: relative;
  z-index: 1;
  width: min(100%, calc(var(--content) + 64px));
  margin: 0 auto;
  padding: clamp(48px, 8vh, 96px) 24px;
}

.hero__content > * {
  max-width: 590px;
}

/* Display type inside a fixed-height card must scale with the SHORTER axis
   too, not width alone: a 78px headline that fits 1440x900 overflows a
   1280x680 laptop where only ~610px is usable. `vmin` couples the two. */
.pane--hero h1 {
  max-width: 600px;
  margin: 0;
  font-size: clamp(2.1rem, 4.6vmin + 1rem, 4.4rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
  text-wrap: balance;
}

.hero__lede {
  margin: clamp(12px, 2.2vh, 26px) 0 0;
  color: var(--ink-2);
  font-size: clamp(1rem, 1.1vmin + 0.8rem, 1.25rem);
  line-height: 1.5;
  text-wrap: pretty;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: clamp(16px, 2.6vh, 28px);
}

.hero__eyebrow {
  margin: 0 0 clamp(8px, 1.6vh, 18px);
  color: var(--muted);
  font-size: clamp(0.9rem, 0.9vmin + 0.7rem, 1.1rem);
  font-weight: 600;
}

/* The narrow-viewport header CTA. Hidden by default; the =820px block below
   reveals it when site-header__actions goes away, so the ask survives on
   the viewport where most visitors arrive. */
.header-cta-compact {
  display: none;
  flex: none;
  margin-left: auto;
}

/* ── Lead pane: the card's headline, lede and illustration ───────────── */

.pane--lead {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(300px, 0.82fr) minmax(380px, 1.18fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: center;
}

.pane--lead-reverse .chapter-copy {
  order: 2;
}

.pane--lead-reverse .scene-media {
  order: 1;
}

.chapter-copy {
  max-width: 560px;
  min-width: 0;
}

/* Display type must scale with the SHORTER axis too. The old
   `clamp(2.2rem, 4.2vw, 4.35rem)` reached 70px, which alone consumed a
   quarter of a 610px card before the lede and list. */
.chapter-copy h2 {
  margin: 0;
  font-size: clamp(1.65rem, 3.6vmin + 0.65rem, 3.4rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.04;
  text-wrap: balance;
}

/* Outside the deck, headings keep the original generous scale — those
   sections scroll normally and have no height ceiling. */
.trust-band h2,
.accountant-band h2,
.faq h2,
.signup h2 {
  margin: 0;
  font-size: clamp(2.2rem, 4.2vw, 4.35rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1.02;
  text-wrap: balance;
}

/* The finale IS a card, so it takes the height-aware scale. */
.card--finale h2 {
  margin: 0;
  font-size: clamp(1.8rem, 3.8vmin + 0.7rem, 3.6rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1.04;
  text-wrap: balance;
}

/* Vertical rhythm inside a card is height-relative: fixed margins accumulate
   and a stack that fits 900px overflows 680px. `vh` in the middle of each
   clamp lets the whole copy block breathe or compress with the viewport. */
.chapter-lede {
  margin: clamp(10px, 1.8vh, 24px) 0 0;
  color: var(--ink-2);
  font-size: clamp(0.95rem, 1vmin + 0.75rem, 1.2rem);
  line-height: 1.55;
  max-width: 54ch;
  text-wrap: pretty;
}

.plain-checks {
  margin: clamp(12px, 2.2vh, 28px) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: clamp(6px, 1.1vh, 12px);
}

.plain-checks li {
  position: relative;
  padding-left: 28px;
  color: var(--ink-2);
  font-size: 0.9375rem;
  line-height: 1.5;
}

.plain-checks li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.44em;
  width: 11px;
  height: 6px;
  border-left: 2px solid var(--positive);
  border-bottom: 2px solid var(--positive);
  transform: rotate(-45deg);
}

.general-info {
  margin: clamp(10px, 1.8vh, 24px) 0 0;
  color: var(--muted);
  font-size: 0.8125rem;
  line-height: 1.5;
}

.workflow {
  margin: clamp(14px, 2.4vh, 32px) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(10px, 1.6vh, 18px) 24px;
}

.workflow li {
  display: grid;
  grid-template-columns: 28px 1fr;
  column-gap: 10px;
}

.workflow li > span {
  grid-row: 1 / 3;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
}

.workflow strong {
  font-size: 0.9rem;
}

.workflow small {
  margin-top: 3px;
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.4;
}

.scene-media {
  position: relative;
  width: 100%;
  min-width: 0;
  margin: 0;
}

.scene-media--art {
  /* The lead illustrations are keyed RGBA (-cut.webp): their paper ground is
     transparent, so the ink sits directly on the card tint. There is no
     rectangle to round or clip, and the figure must NOT clip - the art
     column bleeds into the pane's outer gutter (see the stretch rules after
     the size caps). The pane's own overflow:hidden still bounds everything
     at the card edge. */
  border-radius: 0;
  overflow: visible;
}

.scene-media--art picture,
.scene-media--art picture img,
.scene-media--art video {
  width: 100%;
  height: auto;
}

.scene-media--art picture img {
  object-fit: cover;
}

/* Cap the illustration to the card so a 3:2 image cannot push the pane past
   the viewport. `contain` keeps the whole scene visible rather than cropping
   the protagonist. Declared AFTER the `cover` rule above and with a
   higher-specificity selector, so it wins regardless of source order. */
.pane--lead .scene-media {
  max-height: 100%;
}

.pane--lead .scene-media--art picture img,
.pane--lead .scene-media--art video {
  max-height: calc(100vh - var(--header-height) - 90px);
  object-fit: contain;
}

@supports (height: 100svh) {
  .pane--lead .scene-media--art picture img,
  .pane--lead .scene-media--art video {
    max-height: calc(100svh - var(--header-height) - 90px);
  }
}

/* Keyed art fills the pane's height and escapes the content column toward
   the card's outer edge - the hero's "one scene" feel without layering.
   `contain` + transparency make the letterbox and the bleed invisible; only
   ink shows. FILM figures are EXCLUDED: their 16:9 video registers to the
   3:2 still via `cover` in a box whose size the still's intrinsic ratio
   dictates - stretching that box breaks the fade-in registration. They get
   keyed stills + the multiply treatment below until the films are
   re-rendered on tint grounds (ILLUSTRATION-PROMPTS.md ground-tone table).
   Fit-check stays green by construction: the stretched figure's bottom is
   the grid row's bottom (CLIP over = 0) and the bleed is strictly outward,
   away from .chapter-copy (no OVERLAP). */
.pane--lead .scene-media--art:not(.scene-media--film) {
  align-self: stretch;
  /* width:auto, NOT the base .scene-media's 100%: a grid item at width:100%
     is pinned to its track, so the negative bleed margin below would only
     leave phantom space beyond it (measured at 1920: the figure stopped at
     the content edge). With auto, grid stretch resolves the width to
     track + bleed and the art genuinely reaches the card edge. */
  width: auto;
}

.pane--lead .scene-media--art:not(.scene-media--film) picture,
.pane--lead .scene-media--art:not(.scene-media--film) picture img {
  height: 100%;
}

.pane--lead:not(.pane--lead-reverse) .scene-media--art:not(.scene-media--film) {
  margin-right: calc(-1 * var(--pane-pad-x));
}

.pane--lead:not(.pane--lead-reverse) .scene-media--art:not(.scene-media--film) picture img {
  object-position: right center;
}

.pane--lead-reverse .scene-media--art:not(.scene-media--film) {
  margin-left: calc(-1 * var(--pane-pad-x));
}

.pane--lead-reverse .scene-media--art:not(.scene-media--film) picture img {
  object-position: left center;
}

.scene-media--art video {
  position: absolute;
  inset: 0;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 180ms ease;
}

.scene-media--art video.is-ready {
  opacity: 1;
}

/* Film stopgap until the films are re-rendered on tint grounds: the card
   tint is BAKED INTO the film files (assets/vid/*-w.mp4 - one ffmpeg
   colorlevels pass: per-channel white-point lift measured from each film's
   decoded paper, then output max scaled to the card tint; the ai film only
   lifts to white because its card IS white). This is the multiply-blend
   math done offline, which is deliberate: measured 2026-08-19, CSS
   `mix-blend-mode: multiply` on the <video> rendered EXACTLY on-tint while
   paused but Chromium's playback compositing path silently drops the blend
   (and a CSS `filter` on the same element kills it even when paused), and
   Safari never blends <video> at all. Baked files behave identically in
   every browser. Accepted until re-render: interior whites in the film
   read as tint while it plays. When a card's film is re-rendered on its
   tint ground, delete its -w files and point index.html back at the base
   names. */

/* The film's 16:9 frame letterboxes inside the 3:2 figure (the size-cap
   rule above sets contain), so the keyed still behind it must leave as the
   film arrives or its ink peeks above and below the film's band. wrap.js
   adds .is-motion alongside the video's .is-ready. */
.scene-media--film.is-motion picture img {
  opacity: 0;
  transition: opacity 180ms ease;
}

.scene-media--product {
  padding: 10px;
  border: 2px solid var(--ink);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: 4px 5px 0 rgba(25, 25, 24, 0.84);
}

.motion-control {
  position: absolute;
  right: 12px;
  bottom: 12px;
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(25, 25, 24, 0.2);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.94);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--shadow-raised);
}

.motion-control[hidden] {
  display: none;
}

.motion-control svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.motion-control__pause {
  display: none;
}

.motion-control.is-playing .motion-control__play {
  display: none;
}

.motion-control.is-playing .motion-control__pause {
  display: block;
}

/* ── Pane navigation (the horizontal axis controls) ──────────────────── */

/* A quiet forward cue on the lead pane, so the sideways axis is discoverable
   without a permanent instruction banner. */
.pane-hint {
  margin: clamp(12px, 2vh, 26px) 0 0;
  color: var(--accent);
  font-size: 0.875rem;
  font-weight: 600;
}

.pane-nav {
  position: absolute;
  right: max(24px, calc((100vw - var(--content)) / 2));
  bottom: clamp(14px, 2.4vh, 26px);
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px;
  border: 1px solid rgba(25, 25, 24, 0.12);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  box-shadow: var(--shadow-raised);
}

/* 44px, not 38: these are the primary repeated interaction on touch (the whole
   horizontal axis runs through them), so they take the WCAG 2.5.5 AAA target
   rather than the 24px AA floor. */
.pane-nav button {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(25, 25, 24, 0.18);
  border-radius: 50%;
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}

.pane-nav button:hover:not(:disabled) {
  background: var(--accent-soft);
  border-color: var(--accent-border);
}

.pane-nav button:disabled {
  opacity: 0.38;
  cursor: default;
}

.pane-nav__status {
  margin: 0;
  min-width: 4.5ch;
  color: var(--muted);
  text-align: center;
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

.pane-nav svg,
.zoom-icon svg,
.lightbox__close svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── Detail pane: one screenshot + its copy, full-screen ─────────────── */

.pane--detail {
  display: grid;
  grid-template-columns: minmax(260px, 0.72fr) minmax(380px, 1.28fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: center;
}

.pane-detail__copy {
  max-width: 46ch;
  min-width: 0;
}

.pane-detail__eyebrow {
  margin: 0 0 clamp(6px, 1.2vh, 14px);
  color: var(--accent);
  font-size: 0.78rem;
  font-weight: 600;
}

.pane--detail h3,
.pane--detail-text h3 {
  margin: 0;
  font-size: clamp(1.3rem, 2.2vmin + 0.6rem, 2.2rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
  text-wrap: balance;
}

.pane--detail p {
  margin: clamp(8px, 1.4vh, 16px) 0 0;
  color: var(--ink-2);
  font-size: clamp(0.9375rem, 0.9vmin + 0.75rem, 1.1rem);
  line-height: 1.55;
}

/* The screenshot is the pane's hero. Cap it to the pane and letterbox rather
   than crop: these are real product screens and a cropped chart is a lie. */
.pane--detail .screenshot-button {
  max-height: 100%;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 0 0 1px rgba(25, 25, 24, 0.12);
}

.pane--detail .screenshot-button img {
  width: 100%;
  height: auto;
  max-height: calc(100vh - var(--header-height) - 96px);
  object-fit: contain;
}

@supports (height: 100svh) {
  .pane--detail .screenshot-button img {
    max-height: calc(100svh - var(--header-height) - 96px);
  }
}

/* A text-only detail pane has no screenshot column. */
.pane--detail-text {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
}

.pane--detail-text .provider-list {
  max-width: 62ch;
}

.provider-list p {
  margin: clamp(8px, 1.4vh, 16px) 0 0;
  color: var(--ink-2);
  font-size: clamp(0.9375rem, 0.9vmin + 0.75rem, 1.05rem);
  line-height: 1.55;
}

.provider-list p + p {
  margin-top: clamp(10px, 1.6vh, 16px);
}

/* ── Connect card: intake chips, lead logo strip, logo wall ──────────────
   The card is a FIXED-HEIGHT box, so every rule here keeps `vh`/`vmin` in the
   middle of its clamp: fixed margins accumulate and a stack that fits 900px
   overflows 680px. */

/* Three short guarantees, replacing the old three-item <dl>. A chip row costs
   roughly a third of the vertical space of stacked dt/dd pairs, and that
   reclaimed height is what stops the artwork collapsing on a phone. */
.connect-chips {
  margin: clamp(12px, 2.2vh, 26px) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(6px, 1vh, 10px);
}

.connect-chips li {
  padding: 6px 12px;
  border: 1px solid var(--accent-border);
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-hover);
  font-size: clamp(0.8125rem, 0.5vmin + 0.7rem, 0.9375rem);
  font-weight: 600;
  white-space: nowrap;
}

/* Recognition hit on the lead pane: six marks, desktop only. Below 821px the
   pane stacks and every spare pixel belongs to the artwork, so this hides and
   the full wall on pane 2 does the work instead. */
.connect-strip {
  margin: clamp(14px, 2.4vh, 28px) 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}

.connect-strip__note {
  margin: clamp(8px, 1.2vh, 12px) 0 0;
  color: var(--muted);
  font-size: clamp(0.8125rem, 0.5vmin + 0.7rem, 0.875rem);
  line-height: 1.5;
}

/* Wider than the 62ch prose measure of a text pane: this pane's content is a
   grid of marks, not a paragraph, and a wider bound fits each row in one line
   at desktop so the three ingestion methods read as three bands. */
.logo-wall {
  width: 100%;
  max-width: 860px;
}

.logo-wall__label {
  margin: clamp(12px, 2vh, 24px) 0 clamp(7px, 1.1vh, 11px);
  color: var(--muted);
  font-size: clamp(0.8125rem, 0.5vmin + 0.7rem, 0.9375rem);
  line-height: 1.45;
}

.logo-wall__label strong {
  color: var(--ink);
  font-weight: 700;
}

/* Wraps, never scrolls. A nested horizontal scroller would compete with the
   pane track's own `scroll-snap-type: x mandatory` and its drag handlers for
   the same gesture, so swipes would land ambiguously. Wrapping also needs no
   reduced-motion fallback. */
.logo-wall__row {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(6px, 1vh, 10px);
}

/* Uniform white chip. Full-colour brand marks clash with each other by
   definition (CommBank yellow beside Westpac red beside ING orange); giving
   every mark the same white card, the same box and the same hairline is what
   lets 24 of them read as one system inside the ink aesthetic. */
.logo-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(46px, 5.4vmin + 22px, 62px);
  height: clamp(46px, 5.4vmin + 22px, 62px);
  padding: 7px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-md);
  background: var(--surface);
}

.logo-chip img {
  width: 100%;
  height: 100%;
  /* `contain`, so a wordmark and a roundel both sit inside the same box
     without cropping either. */
  object-fit: contain;
}

/* Optical size correction. A few fetched marks are drawn with heavy internal
   whitespace (Macquarie, ING), so `contain` fits their PADDING to the box and
   the visible mark lands far smaller than its neighbours. Trimming the chip's
   own padding for those keys equalises what the eye actually measures, which is
   the mark, not the file. */
.logo-chip--tight {
  padding: 2px;
}

/* Text fallback for an institution with no committed mark (Pearler), and for
   the two watch-only chains and the "any other bank" catch-all, which are
   capabilities rather than brands. Auto width: the label IS the content. */
.logo-chip--text {
  width: auto;
  min-width: 0;
  padding: 0 12px;
  color: var(--ink-2);
  font-size: clamp(0.75rem, 0.45vmin + 0.65rem, 0.8125rem);
  font-weight: 600;
  text-align: center;
  white-space: nowrap;
}

.screenshot-button {
  position: relative;
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: #f7f7f7;
  color: var(--ink);
  cursor: zoom-in;
}

.screenshot-button img {
  width: 100%;
  height: auto;
}

.zoom-icon {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border: 1px solid rgba(25, 25, 24, 0.18);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.94);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 140ms ease, transform 140ms ease;
}

.screenshot-button:hover .zoom-icon,
.screenshot-button:focus .zoom-icon {
  opacity: 1;
  transform: translateY(0);
}

.pane--finale {
  position: relative;
  display: flex;
  align-items: center;
  padding: 0;
}

.finale__art {
  position: absolute;
  inset: 0;
  margin: 0;
}

.finale__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.finale__content {
  position: relative;
  z-index: 1;
  width: min(100%, calc(var(--content) + 64px));
  margin: 0 auto;
  padding: clamp(28px, 6vh, 80px) 24px;
}

.finale__content > * {
  max-width: 560px;
}

.finale__content p {
  margin: clamp(12px, 2.2vh, 22px) 0 clamp(16px, 2.8vh, 28px);
  color: var(--ink-2);
  font-size: clamp(1rem, 1vmin + 0.8rem, 1.1rem);
  line-height: 1.55;
  text-wrap: pretty;
}

.trust-band,
.faq,
.signup {
  padding: clamp(72px, 10vw, 128px) max(24px, calc((100vw - var(--content)) / 2));
}

.trust-band {
  background: var(--surface);
}

.trust-band__intro {
  display: grid;
  grid-template-columns: 1fr 0.8fr;
  gap: 50px;
  align-items: end;
}

.trust-band__intro h2 {
  font-size: clamp(2.1rem, 4vw, 3.8rem);
}

.trust-band__intro p {
  margin: 0;
  color: var(--muted);
  /* Body step, 15px (DESIGN.md §3). */
  font-size: 0.9375rem;
  line-height: 1.6;
}

.trust-points {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  margin-top: 58px;
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}

.trust-points article {
  padding: 26px 24px 28px;
}

.trust-points article + article {
  border-left: 1px solid var(--hairline);
}

.trust-points h3 {
  margin: 0;
  font-size: 0.95rem;
}

.trust-points p {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.55;
}

.accountant-band {
  width: min(calc(100% - 48px), var(--content));
  margin: clamp(56px, 8vw, 96px) auto;
  padding: clamp(36px, 5vw, 58px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 40px;
  background: #edf3ff;
  border-radius: var(--radius-lg);
}

.accountant-band > div {
  max-width: 720px;
}

.accountant-band h2 {
  font-size: clamp(1.85rem, 3vw, 3rem);
}

.accountant-band p {
  margin: 14px 0 0;
  color: var(--ink-2);
  line-height: 1.6;
}

.faq {
  display: grid;
  grid-template-columns: 0.7fr 1.3fr;
  gap: clamp(44px, 8vw, 100px);
  background: #f2f1f0;
}

.faq__intro h2 {
  font-size: clamp(2.2rem, 4vw, 3.8rem);
}

.faq__intro p {
  margin: 14px 0 0;
  color: var(--muted);
  line-height: 1.6;
}

.faq__items {
  border-top: 1px solid var(--hairline);
}

.faq details {
  border-bottom: 1px solid var(--hairline);
}

.faq summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 19px 2px;
  /* Title-ish weight at the body step; summaries are interactive labels. */
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.4;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: "+";
  flex: none;
  color: var(--accent);
  font-size: 1.5rem;
  font-weight: 400;
  line-height: 1;
  transition: transform 160ms ease;
}

.faq details[open] summary::after {
  transform: rotate(45deg);
}

.faq details p {
  max-width: 68ch;
  margin: 0 0 22px;
  color: var(--muted);
  font-size: 0.95rem;
  line-height: 1.65;
}

.signup {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: clamp(48px, 8vw, 110px);
  align-items: center;
  background: var(--ink);
  color: #fff;
}

.signup h2 {
  font-size: clamp(2.3rem, 4.4vw, 4.1rem);
}

.signup__copy p {
  max-width: 46ch;
  margin: 16px 0 0;
  color: var(--on-dark-body);
  line-height: 1.6;
}

.signup__form label {
  display: block;
  margin-bottom: 9px;
  font-size: 0.82rem;
  font-weight: 600;
}

.signup__fields {
  display: flex;
  gap: 10px;
}

.signup__fields input {
  min-width: 0;
  flex: 1;
  min-height: 48px;
  padding: 11px 14px;
  border: 1px solid var(--on-dark-border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  /* Deliberately 16px, off the 15px body step: iOS Safari zooms the whole page
     when a focused input's text is under 16px. The zoom is worse than the
     one-step deviation, and this is the page's only conversion field. */
  font-size: 1rem;
}

.signup__fields input::placeholder {
  color: var(--muted);
}

.signup__privacy {
  margin: 12px 0 0;
  color: var(--on-dark-body);
  font-size: 0.85rem;
}

.signup__privacy a {
  color: inherit;
  text-decoration: underline;
}

.signup__note {
  min-height: 1.5em;
  margin: 12px 0 0;
  color: var(--on-dark-body);
  font-size: 0.85rem;
  line-height: 1.5;
}

.signup__note.is-ok {
  color: var(--on-dark-ok);
}

.signup__note.is-err {
  color: var(--on-dark-err);
}

.signup__note small {
  display: block;
  margin-top: 3px;
  color: var(--on-dark-body);
}

.site-footer {
  max-width: var(--content);
  margin: 0 auto;
  padding: 34px 24px 42px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 18px 30px;
  background: var(--paper);
}

.site-footer nav {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 24px;
}

.site-footer nav a {
  color: var(--ink-2);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 600;
}

.site-footer nav a:hover {
  text-decoration: underline;
  text-underline-offset: 4px;
}

.site-footer p {
  width: 100%;
  margin: 0;
  color: var(--muted);
  font-size: 0.78rem;
}

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: 32px;
}

.lightbox[hidden] {
  display: none;
}

.lightbox__backdrop {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: rgba(25, 25, 24, 0.72);
  cursor: zoom-out;
}

.lightbox__panel {
  position: relative;
  z-index: 1;
  width: min(94vw, 1480px);
  max-height: 90vh;
  padding: 10px;
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-modal);
}

.lightbox__panel img {
  width: 100%;
  max-height: calc(90vh - 20px);
  object-fit: contain;
  border-radius: var(--radius-sm);
}

.lightbox__close {
  position: absolute;
  top: -18px;
  right: -18px;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 50%;
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--shadow-raised);
}

/* The old `scroll-snap-type: y proximity` block is gone: the deck supersedes
   it. Snapping the document while cards pin fights the sticky release and
   made the browser fidget between a slot's start and its card's rest
   position. Each card holds the viewport for a full screen of scrolling on
   its own, which is the effect that snap was approximating. */

@media (max-width: 1080px) {
  .site-header__bar {
    gap: 16px;
  }

  .site-header__actions .text-link {
    display: none;
  }

  .pane--lead,
  .pane--detail {
    grid-template-columns: minmax(260px, 0.9fr) minmax(320px, 1.1fr);
    gap: clamp(20px, 3vw, 36px);
  }

  .trust-points {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .trust-points article:nth-child(3) {
    border-left: 0;
    border-top: 1px solid var(--hairline);
  }

  .trust-points article:nth-child(4) {
    border-top: 1px solid var(--hairline);
  }

}

/* ── Hero stack: every viewport 1140px and narrower ──────────────────────
   Art and copy are SEPARATE rows here, not layered. The side-by-side hero
   cannot be made safe below ~1133px: the copy column's right edge is a
   fixed 614px, the artwork's blank reserve is a fraction of the image
   (ink starts 45.4% in), and the drawn figure (~62% of the image in)
   would sit inside the mask's 702px text guard — protecting the paragraph
   would mean erasing her. At 375px the failure was literal (the eyebrow
   landed on the protagonist); at 821-1140px she stood under the lede.
   Flow the art as its own row instead, so the two can never intersect.
   The <source media> in index.html swaps to the tight-cropped mobile file
   at exactly this width — move both together, always. */
@media (max-width: 1140px) {
  .pane--hero {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(8px, 1.6vh, 18px);
    padding: clamp(10px, 2vh, 22px) 0 clamp(14px, 3vh, 34px);
  }

  .pane--hero .hero__art {
    position: relative;
    inset: auto;
    flex: 1 1 auto;
    min-height: 20vh;
    width: 100%;
    transform: none;
    -webkit-mask-image: none;
    mask-image: none;
  }

  /* `contain`, not `cover`. The mobile asset is a 729x786 near-square crop
     of the scene; `cover` in a wide, shallow art row would crop the figure
     top and bottom (an earlier `center 22%` anchor lost her feet on a short
     phone — measured: 82 ink pixels off the bottom at 360x740). `contain`
     fits the whole scene into whatever height the copy leaves, and the
     figure background matches the art, so the letterbox bands are
     invisible. */
  .pane--hero .hero__art img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: center;
  }

  .hero__content {
    flex: 0 0 auto;
    padding: 0 24px;
    /* Centre the copy as a unit under the centred art (648 = the 600px h1
       measure + the 24px gutters; the base rule's `margin: 0 auto` does the
       centring). Phones are unaffected: under 648px wide, `width: 100%`
       already wins. */
    max-width: 648px;
  }
}

@media (max-width: 820px) {
  :root {
    --header-height: 64px;
  }

  html {
    /* Header bar + the nav row beneath it = the card's sticky offset here.
       Must match `.card { top }` in this block exactly (see the note on the
       base rule: extra padding lands an anchor jump short of its own slot). */
    scroll-padding-top: calc(var(--header-height) + 46px);
  }

  .site-header {
    height: auto;
  }

  .site-header__bar {
    height: var(--header-height);
    padding-inline: 18px;
    gap: 14px;
  }

  .site-header__actions {
    display: none;
  }

  /* Sign-in moves to the footer at this width; the ASK stays in the header.
     Losing the CTA on mobile loses it for most of the audience. */
  .header-cta-compact {
    display: inline-flex;
  }

  .story-nav {
    order: 3;
    position: absolute;
    left: 0;
    right: 0;
    top: var(--header-height);
    height: 46px;
    gap: 20px;
    /* Trailing padding + a fade (below) tell the reader there is more nav to
       the right. The nav is down to four grouped items (~437px of row at
       0.78rem), so a 390px phone still scrolls by one label ("Tax time"
       sits in the fade at rest) and the fade is load-bearing there; from
       ~465px wide the row fits outright. A hard clip at the edge reads as
       "that is all of them". */
    padding: 0 18px;
    background: rgba(255, 255, 255, 0.97);
    border-bottom: 1px solid var(--hairline);
    -webkit-mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 14px,
      #000 calc(100% - 26px),
      transparent 100%
    );
    mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 14px,
      #000 calc(100% - 26px),
      transparent 100%
    );
  }

  .story-nav a {
    font-size: 0.78rem;
  }

  /* A scroll-clipped focus ring is invisible; keep it inside the mask. */
  .story-nav a:focus {
    outline-offset: -2px;
  }

  .story-nav a:last-child {
    padding-right: 12px;
  }

  /* The mobile nav row sits BELOW the header bar, so the card must start
     under both or its first line hides behind the nav. */
  .card {
    top: calc(var(--header-height) + 46px);
    height: calc(100vh - var(--header-height) - 46px);
  }

  @supports (height: 100svh) {
    .card {
      height: calc(100svh - var(--header-height) - 46px);
    }
  }

  /* The hero already stacks in the 1140px block above; only the phone-tight
     gutter differs from that band's 24px. */
  .hero__content {
    padding: 0 20px;
  }

  .pane--hero h1 {
    font-size: clamp(1.9rem, 7.4vw, 3rem);
    line-height: 1.02;
  }

  .hero__lede {
    font-size: clamp(0.9375rem, 3.4vw, 1.05rem);
    line-height: 1.45;
  }

  /* One column: a two-up grid at 375px leaves neither side readable. Copy
     takes the space it needs; the artwork gets the rest, with a floor so it
     never collapses to an unreadable strip (a 115px row showed a sliver of
     the protagonist and read as a broken image). */
  /* FLEX, not grid, for the single-column stack. A grid whose rows can exceed
     the container overlaps them instead of stacking (a `minmax(22vh, auto)`
     art row drew straight over the copy). Flex with `min-height: 0` on both
     children divides the available space and can never overlap. */
  .pane--lead,
  .pane--detail {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(8px, 1.6vh, 20px);
  }

  /* Copy takes what it needs; the artwork takes the remainder, down to a floor
     where it still reads rather than becoming a sliver. */
  .pane--lead .chapter-copy,
  .pane--detail .pane-detail__copy {
    flex: 0 0 auto;
    min-height: 0;
  }

  /* THE ONLY `.pane--lead .scene-media` RULE IN THIS BREAKPOINT. There were
     two: this one declaring a `min-height: 22vh` floor, and a second one ~130
     lines below re-declaring the SAME selector with `min-height: 0`. Equal
     specificity, so later source order won, the floor was dead, and the
     artwork got only whatever the copy left over — collapsing to the sliver at
     the bottom of the card on a phone. Merged here; keep it merged. Any
     further mobile art tuning edits THIS block.

     `min-height` is the floor and `max-height` the ceiling, so the art can
     neither collapse nor crowd out the copy. `height: auto` (not `100%`) lets
     flex resolve the box between them. */
  .pane--lead .scene-media {
    flex: 1 1 auto;
    height: auto;
    min-height: 34svh;
    max-height: 46svh;
  }

  /* The screenshot shrinks to the image, it does NOT stretch to fill: a 16:9
     shot in a tall flex box letterboxes with dead margins and reads as a
     broken frame. `align-self: stretch` keeps it full-width while `flex: 0 1`
     stops it claiming spare vertical space. */
  .pane--detail .screenshot-button {
    flex: 0 1 auto;
    align-self: stretch;
    min-height: 0;
  }

  .pane--detail .screenshot-button img {
    max-height: 40vh;
  }

  /* Anchor a detail pane's stack to the TOP. Centering a short column leaves a
     tall gap above the eyebrow, which reads as a mis-rendered pane; the copy
     should start where the eye lands. */
  .pane--detail {
    justify-content: flex-start;
  }

  .pane--detail-text {
    justify-content: center;
  }

  /* The logo wall is 26 chips in three labelled rows, so it is the one pane
     that can genuinely exceed a phone's card height. Anchor it to the top and
     shrink the chips rather than centring an overflowing column.
     A modifier class, not `:has(.logo-wall)`: `:has()` is below this project's
     browser floor (Safari 14). */
  .pane--detail-wall {
    justify-content: flex-start;
  }

  .logo-wall__label {
    margin: 12px 0 6px;
    font-size: 0.8125rem;
  }

  .logo-wall__row {
    gap: 6px;
  }

  .logo-chip {
    width: 42px;
    height: 42px;
    padding: 5px;
  }

  .logo-chip--text {
    width: auto;
    padding: 0 9px;
    font-size: 0.6875rem;
  }

  /* Copy first on every card at this width: reading order beats alternation,
     and the reverse zig-zag has no meaning in a single column. */
  .pane--lead .chapter-copy,
  .pane--lead-reverse .chapter-copy {
    order: 1;
  }

  .pane--lead .scene-media,
  .pane--lead-reverse .scene-media {
    order: 2;
  }

  .pane--detail .pane-detail__copy {
    order: 1;
  }

  .pane--detail .screenshot-button {
    order: 2;
  }

  .chapter-copy {
    max-width: none;
    min-height: 0;
  }

  .chapter-copy h2 {
    font-size: clamp(1.4rem, 5.6vw, 2.1rem);
    line-height: 1.08;
  }

  /* Tighten the copy block itself rather than shrinking the artwork further:
     with a 28vh art floor the tall cards (connect 475px, tax 474px) pushed
     past the pane by ~110px. These are the levers that recover it without
     dropping any content. */
  .pane--lead .chapter-lede {
    font-size: 0.9375rem;
    line-height: 1.45;
    margin-top: 8px;
  }

  /* The lead-pane logo strip and its note are DESKTOP ONLY. Below this
     breakpoint the pane stacks and every spare pixel belongs to the artwork;
     the full logo wall on pane 2 carries the recognition instead. Removing
     them from flow (not just visually) is what funds the art's 34svh floor. */
  .pane--lead .connect-strip,
  .pane--lead .connect-strip__note {
    display: none;
  }

  .pane--lead .connect-chips {
    margin-top: 10px;
    gap: 6px;
  }

  .pane--lead .connect-chips li {
    padding: 5px 10px;
    font-size: 0.8125rem;
  }


  .pane--lead .plain-checks {
    margin-top: 10px;
    gap: 5px;
  }

  .pane--lead .plain-checks li {
    font-size: 0.875rem;
    line-height: 1.4;
  }

  .pane--lead .general-info,
  .pane--lead .pane-hint {
    margin-top: 8px;
    font-size: 0.78rem;
  }

  .pane--lead .workflow {
    margin-top: 10px;
    gap: 8px 14px;
  }

  .pane--lead .workflow strong {
    font-size: 0.8125rem;
  }

  .pane--lead .workflow small {
    font-size: 0.72rem;
  }

  .card--finale h2 {
    font-size: clamp(1.6rem, 6.4vw, 2.5rem);
  }

  .scene-media {
    max-width: 560px;
    margin-inline: auto;
  }

  /* NB: the `.pane--lead .scene-media` sizing rule that used to live here has
     been merged into the single block earlier in this breakpoint (search
     "THE ONLY"). It re-declared the same selector and silently killed that
     rule's `min-height` floor. Do not re-add a sizing rule for this selector
     here.

     The image must still fit the box flex resolved for it: without an explicit
     height the 3:2 asset renders at its own aspect ratio (596px tall in a
     114px box) and `overflow: hidden` crops it to a useless strip — the
     artwork looked "present" while showing a sliver of the protagonist. */
  .pane--lead .scene-media--art {
    display: flex;
  }

  .pane--lead .scene-media--art picture {
    display: flex;
    width: 100%;
    min-height: 0;
  }

  /* `cover`, not `contain`. The mobile asset is a purpose-composed 9:16 crop,
     so filling the box is correct and `contain` would letterbox it inside an
     already-short row — dead margins on both sides of a small picture, which
     is what made the art read as an afterthought. `object-position` biases to
     the upper half where the protagonist is composed. */
  .pane--lead .scene-media--art picture img {
    width: 100%;
    height: 100%;
    max-height: none;
    object-fit: cover;
    object-position: center 28%;
  }

  /* Neutralise the desktop stretch + outward bleed here: the stacked art row
     is already full-width, and widening the box would raise the cover scale
     and re-crop the purpose-composed 9:16 subjects. Selectors mirror BOTH
     desktop bleed variants (4 class-level each) so these later declarations
     win the tie - the plain 3-class form loses to the :not(reverse) rule. */
  .pane--lead:not(.pane--lead-reverse) .scene-media--art:not(.scene-media--film),
  .pane--lead-reverse .scene-media--art:not(.scene-media--film) {
    align-self: auto;
    width: 100%;
    margin-inline: 0;
  }

  .pane--lead .scene-media--product {
    display: flex;
    align-items: center;
  }

  .pane--lead .scene-media--product .screenshot-button img {
    max-height: 100%;
    object-fit: contain;
  }

  /* Leave room for the pane-nav pill above the pane's bottom edge. */
  .pane {
    padding: clamp(14px, 2.4vh, 26px) 20px clamp(66px, 9vh, 84px);
  }

  .pane-nav {
    right: 50%;
    transform: translateX(50%);
    bottom: clamp(12px, 2vh, 20px);
  }

  .trust-band__intro,
  .faq,
  .signup {
    grid-template-columns: 1fr;
  }

  .trust-band__intro {
    gap: 20px;
  }

  .accountant-band {
    align-items: flex-start;
    flex-direction: column;
  }

  /* Art on top, copy below — never overlapping. The desktop finale layers copy
     over a full-bleed image, which works because the art's subject sits right
     of the text column; at 375px there is no such room and the headline landed
     on top of the figure. Constrain the art to the upper half and mask its
     lower edge so the copy sits on clean paper. */
  .pane--finale {
    align-items: flex-end;
  }

  .pane--finale .finale__art {
    inset: 0 0 auto;
    height: 44%;
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 74%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 0%, #000 74%, transparent 100%);
  }

  .finale__art img {
    object-position: center top;
  }

  .finale__content {
    padding: 0 20px clamp(20px, 5vh, 60px);
  }

  .faq {
    gap: 34px;
  }

  .signup {
    gap: 34px;
  }
}

@media (max-width: 560px) {
  .wordmark svg {
    width: 86px;
  }

  /* Tightened rhythm: on a phone every copy margin is height taken from the
     artwork row (the art is `flex: 1 1 auto` and absorbs the remainder). The
     copy block measured 51% of the pane vs 33% for the art; these trims hand
     ~25px back without dropping any content. */
  .hero__eyebrow {
    margin-bottom: 6px;
  }

  .hero__lede {
    margin-top: 10px;
  }

  .hero__actions {
    align-items: stretch;
    flex-direction: row;
    margin-top: 14px;
    gap: 8px;
  }

  /* `1 1 auto`, not `1 1 0`: a zero basis forces two EQUAL pills regardless
     of label length, and the longer label wrapped to two ragged lines at
     360-390px. With an auto basis each pill takes its label's width; when the
     pair genuinely cannot share a row, flex-wrap stacks them full-width — one
     line and a 44px pill either way.
     The size trims matter: the two labels' natural widths sit within a few px
     of the available row at 360-375px (pane padding + content padding eat
     80px), so without them the pair tips into stacking and the extra 54px
     comes straight out of the artwork row. */
  .hero__actions .button {
    min-width: 0;
    flex: 1 1 auto;
    padding-inline: 10px;
    font-size: 0.84rem;
  }

  .workflow {
    grid-template-columns: 1fr;
  }

  .trust-points {
    grid-template-columns: 1fr;
  }

  .trust-points article + article,
  .trust-points article:nth-child(3),
  .trust-points article:nth-child(4) {
    border-left: 0;
    border-top: 1px solid var(--hairline);
  }

  .accountant-band {
    width: calc(100% - 32px);
    padding: 30px 24px;
  }

  .signup__fields {
    flex-direction: column;
  }

  .site-footer {
    align-items: flex-start;
    flex-direction: column;
  }

  .site-footer nav {
    flex: none;
  }

  .lightbox {
    padding: 12px;
  }

  .lightbox__panel {
    width: 100%;
    padding: 6px;
  }

  .lightbox__close {
    top: 8px;
    right: 8px;
  }
}

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

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  .motion-control,
  [data-motion-video] {
    display: none !important;
  }

  .button:hover {
    transform: none;
  }

  /* ── The deck unwinds into an ordinary document ──────────────────────
     No pinning and no horizontal axis: every pane becomes a normal block in
     reading order, so the SAME information is present without stacking or
     sideways travel. Panes are the only content, so nothing is lost. */

  .deck__slot,
  .deck__slot--last {
    height: auto;
  }

  .card {
    position: static;
    height: auto;
    overflow: visible;
    isolation: auto;
  }

  .card__panes {
    display: block;
    height: auto;
    overflow: visible;
    scroll-snap-type: none;
  }

  /* Panes are stacked vertically here with no horizontal overflow, so nothing
     is draggable on any track — the grab cursor would be a false promise even
     on the multi-pane cards. */
  .card__panes[data-pane-draggable] {
    cursor: auto;
  }

  .pane {
    height: auto;
    min-height: 0;
    padding-block: clamp(36px, 6vw, 72px);
  }

  .pane--hero,
  .pane--finale {
    min-height: 70vh;
  }

  .pane--lead,
  .pane--detail {
    display: grid;
  }

  /* The illustration and screenshot caps existed to fit a fixed-height card;
     with normal flow they only shrink the artwork for no reason. */
  .pane--lead .scene-media--art picture img,
  .pane--lead .scene-media--art video,
  .pane--detail .screenshot-button img {
    max-height: none;
    object-fit: cover;
  }

  /* The desktop stretch + bleed serve a fixed-height card; in unwound flow a
     stretched figure would take the copy's row height and the cover override
     above would then crop ink. Selectors mirror BOTH desktop bleed variants
     (4 class-level each) so these later declarations win the tie. */
  .pane--lead:not(.pane--lead-reverse) .scene-media--art:not(.scene-media--film),
  .pane--lead-reverse .scene-media--art:not(.scene-media--film) {
    align-self: center;
    width: 100%;
    margin-inline: 0;
  }

  .pane--lead .scene-media--art:not(.scene-media--film) picture,
  .pane--lead .scene-media--art:not(.scene-media--film) picture img {
    height: auto;
  }

  /* The pane arrows and the forward cue steer an axis that no longer
     exists here. */
  .pane-nav,
  .pane-hint {
    display: none;
  }

  /* Without the deck, each card needs its own separation. */
  .card + .card,
  .deck__slot + .deck__slot .card {
    border-top: 1px solid rgba(25, 25, 24, 0.12);
  }
}
