/* IMPACT website v2 · site shell (DIRECTION 2.2, 5.3 to 5.5). Light, calm, one idea per screen.
   html.stage = scroll film (fixed stage, empty runway, copy windows); html.page = the same DOM as a stacked page. */

*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  /* iPhone round: no grey iOS tap box over links and buttons (each has its own pressed state), and no double-tap zoom of
     the fixed film (a quick second tap on WEAVER / HUNTER / SABLE zoomed the page); pinch zoom stays */
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
body { margin: 0; background: var(--paper); font-size: 17px; line-height: 1.5; font-weight: 450; }
img, canvas, picture, video { display: block; }
img { max-width: 100%; }
button { font: inherit; color: inherit; }
a { color: inherit; }
h1, h2, h3, p, figure, ul { margin: 0; }
[hidden] { display: none !important; }

.sr-only {
  position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 2px; }
.nav.on-dark :focus-visible { outline-color: var(--paper); }

/* ------------------------------------------------------------------ type (5.2) */
.display { font-size: var(--fs-display); font-weight: 600; letter-spacing: -0.035em; line-height: 0.96; }
.h2 { font-size: var(--fs-h2); font-weight: 600; letter-spacing: -0.03em; line-height: 1; }
.product { font-size: var(--fs-product); font-weight: 700; letter-spacing: 0.01em; line-height: 0.95; text-transform: uppercase; }
.lede { font-size: var(--fs-lede); font-weight: 560; letter-spacing: -0.008em; line-height: 1.32; max-width: 36ch; text-wrap: balance; }
/* integration r3 (design review r3-0): ledes 22.3 px at weight 560 and 1.32 leading (WP13: 20.9 px, 500, 1.4 read as a faint
   caption under 66 to 104 px semibold heads); the hero sub 25 px at 1440, 18 px on phones */
#hero-copy .lede { font-size: clamp(18px, 1.75vw, 28px); font-weight: 560; }
/* the four site headlines (design review r3-0): one series, 81 px at 1440, always "Protect the" over the place (span.nb
   phrases plus a 6.2em measure; the refinery rule of the v2 AD fix is now the rule for all four) */
.h2-site { font-size: var(--fs-h2-site); font-weight: 600; letter-spacing: -0.03em; line-height: 0.98; max-width: 6.2em; }
/* single-sentence ledes set on one line on wide screens (design review 2): no "drones hit / three AWS data centers." under
   a one-line H2, and the three product ledes read alike. The refinery and grid keep two lines (they clear the stacks). */
@media (min-width: 700px) { #dc-copy .lede, #airport-copy .lede, .copy-product .lede { max-width: none; } }
.copy .lede { margin-top: 20px; }
.copy .pill-lg { margin-top: var(--s-32); }
a.cite { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); transition: text-decoration-color 200ms linear; }
@media (hover: hover) { a.cite:hover { text-decoration-color: currentColor; } }
.pm-cite { display: none; }
html.page .pm-cite { display: block; margin-top: var(--s-12); max-width: 44ch; font-size: var(--fs-small); font-weight: 450; letter-spacing: 0.01em; line-height: 1.4; }
/* the 13 px source line lands on the desert of the dc poster (v1-dc f144) at 1280x800 and on phones held sideways: the
   same soft paper halo as the nav wordmark over busy photos keeps it legible (adversarial QA r3) */
html.page .pm-cite { text-shadow: 0 0 1px rgba(242, 239, 232, 0.95), 0 0 6px rgba(242, 239, 232, 0.9), 0 0 14px rgba(242, 239, 232, 0.75); }
/* phones held sideways, page mode: each frame shows the whole 16:9 poster, so the ledes of the site frames run down onto
   the horizon and the ground; same halo */
@media (orientation: landscape) and (max-height: 500px) {
  html.page .pm-frame > .copy .lede { text-shadow: 0 0 1px rgba(242, 239, 232, 0.95), 0 0 6px rgba(242, 239, 232, 0.9), 0 0 14px rgba(242, 239, 232, 0.75); }
}
.display, .h2, .product { text-wrap: balance; }

/* split lines: masks sized so descenders survive the tight leading */
.split .ln { display: block; overflow: hidden; padding: 0 0.08em 0.14em; margin: 0 -0.08em -0.14em; }
.split .ln-i { display: block; }
@media (min-width: 360px) { .lede .nb { white-space: nowrap; } }
@media (min-width: 700px) { .display .nb, .h2 .nb { white-space: nowrap; } }
.split .w.nb { white-space: nowrap; }
.split.phrases .nb { white-space: nowrap; }

/* ------------------------------------------------------------------ skip link */
.skip {
  position: fixed; left: var(--margin); top: 12px; z-index: 100;
  padding: 10px 16px; border-radius: 22px; background: var(--ink); color: var(--paper);
  font-size: var(--fs-label); font-weight: 500; text-decoration: none;
  transform: translateY(-160%); transition: transform 200ms var(--ease-out);
}
.skip:focus, .skip:focus-visible { transform: none; }

/* ------------------------------------------------------------------ wordmark (Exo 2 Black lockup as a mask, so it can flip colour) */
.wordmark {
  display: block; aspect-ratio: 834 / 191; background-color: currentColor;
  -webkit-mask: url('../assets/logos/impact-current.svg') center / contain no-repeat;
  mask: url('../assets/logos/impact-current.svg') center / contain no-repeat;
}

/* ------------------------------------------------------------------ veil (5.5 item 1) */
.veil {
  position: fixed; inset: 0; z-index: 90; background: var(--paper); color: var(--ink);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 28px;
  transition: opacity var(--veil-ms, 800ms) var(--ease-in-out), visibility 0s linear var(--veil-ms, 800ms);
}
.veil.is-out { opacity: 0; visibility: hidden; pointer-events: none; }
.veil-mark { width: 132px; transform-origin: 0 0; }
/* first-visit hand-off (integration r3): the paper clears instead of the whole veil fading, the track goes in 250 ms and
   the wordmark flies to the nav wordmark's box (main.js sets the transform), which shows only once it has landed */
.veil.is-morph { transition: background-color var(--veil-ms, 800ms) var(--ease-in-out), visibility 0s linear var(--veil-ms, 800ms); }
.veil.is-morph.is-out { opacity: 1; background-color: rgba(242, 239, 232, 0); }
.veil.is-morph .veil-mark { transition: transform var(--veil-ms, 800ms) var(--ease-out-expo); }
.veil.is-morph.is-out .veil-track { opacity: 0; transition: opacity 250ms linear; }
html.veil-morph .nav-mark .wordmark { opacity: 0; }
.veil-track { position: relative; width: 160px; height: 1px; background: var(--deep); overflow: hidden; }
.veil-fill { position: absolute; inset: 0; background: var(--ink); transform: scaleX(0); transform-origin: 0 50%; transition: transform 240ms linear; }
html:not(.stage) .veil { display: none; }

/* ------------------------------------------------------------------ nav (5.5 item 2) */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40; height: var(--nav-h);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 max(var(--margin), calc((100vw - var(--content-max)) / 2));
  color: var(--ink); pointer-events: none; transition: color 250ms linear, background-color 200ms linear;
}
/* the film runs full bleed, so on the stage the nav, copy and status hug the screen margins on wide screens instead of
   the 1600 px content box (design review r2-0: 480 px of dead margin left of the H1 at 2560); proof keeps its grid */
html.stage .nav { padding: 0 var(--margin); }
/* over the proof curtain: a paper backing that fades out 28 px below the nav, so the globe and founder photos slide under
   a haze edge instead of being sliced at y 72 (WP13, both design reviews) */
html.nav-over-flow .nav { background: none; }
.nav::before {
  content: ''; position: absolute; inset: 0 0 -28px; z-index: -1; pointer-events: none; opacity: 0;
  background: linear-gradient(to bottom, var(--paper) 62%, rgba(242, 239, 232, 0)); transition: opacity 200ms linear;
}
html.nav-over-flow .nav::before { opacity: 1; }
.nav > * { pointer-events: auto; }
.nav-mark { display: block; padding: 8px 0; }
.nav-mark .wordmark { width: 96px; } /* 22 px tall */
.nav-right { display: flex; align-items: center; gap: 28px; }
.nav-specs {
  border: 0; background: none; padding: 10px 0; cursor: pointer;
  font-size: var(--fs-label); font-weight: 500; line-height: 1.3; color: inherit;
  opacity: 0; visibility: hidden; transition: opacity 300ms var(--ease-out), visibility 0s 300ms;
}
.nav.has-specs .nav-specs, html.page .nav-specs { opacity: 1; visibility: visible; transition: opacity 300ms var(--ease-out); }
/* finisher (2 Oct): the short label shows on phones only (below), so the button's name is always the visible word */
.nav-specs-s { display: none; }
.pill {
  display: inline-flex; align-items: center; justify-content: center; height: 44px; padding: 0 20px; border-radius: 22px;
  background: var(--ink); color: var(--paper); text-decoration: none; white-space: nowrap;
  font-size: var(--fs-button); font-weight: 600; letter-spacing: 0.01em;
  transition: background-color 200ms linear, color 250ms linear;
}
.pill:active { background: var(--graphite); }
@media (hover: hover) { .pill:hover { background: var(--graphite); } }
.nav .pill { transition: background-color 200ms linear, color 250ms linear, opacity 300ms var(--ease-out), visibility 0s; }
html.stage:has(#close-copy.is-in) .nav .pill { opacity: 0; visibility: hidden; transition: opacity 300ms var(--ease-out), visibility 0s 300ms; }
/* nav_backing frames (r3 swaps: product partly under the nav): ink stays, the items get paper chips */
.nav-mark { border-radius: 12px; transition: background-color 200ms linear, filter 250ms linear; }
.nav.has-specs .nav-specs { transition: opacity 300ms var(--ease-out), background-color 200ms linear; }
.nav.on-backing .nav-specs { background: var(--paper); border-radius: 22px; padding: 10px 14px; margin: 0 -14px; }
.nav.on-backing .nav-mark { background: var(--paper); padding: 8px 10px; margin: 0 -10px; }
/* r3 swap frames (integration r3, both design reviews r3): Specifications fades out instead of a chip on the magazines */
.nav.has-specs.specs-off .nav-specs { opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 300ms var(--ease-out), visibility 0s 300ms; }
/* outdoor reels, dark or busy nav box (design review r2-1): ink keeps, the wordmark gets a soft paper halo; the ink pill
   reads on any photo (no flip, no chips) */
.nav.on-photo .nav-mark { filter: drop-shadow(0 0 1px rgba(242, 239, 232, 0.9)) drop-shadow(0 0 12px rgba(242, 239, 232, 0.7)); }
.nav.on-dark { color: var(--paper); }
.nav.on-dark .pill { background: var(--paper); color: var(--ink); }
@media (hover: hover) { .nav.on-dark .pill:hover { background: var(--raised); } }
.pill-lg { height: 56px; padding: 0 32px; border-radius: 28px; }

/* ------------------------------------------------------------------ stage (5.5 item 3) */
.film { position: fixed; left: 0; top: 0; z-index: 0; width: 100%; height: 100lvh; background: var(--paper); pointer-events: none; overflow: hidden; container-type: size; --fa: 1.77778; }
.layer { position: absolute; inset: 0; visibility: hidden; }
/* integration r5: poster and canvas hold the whole frame at cover size (the frame aspect --fa, in container units, so a
   window drag never squeezes the picture: QA r4 Q1) and are placed by the layer's --k (scale, 1 = cover) and --ox / --oy
   (px from centred), written by stage.js: cover with focus anchors for outdoor reels, the studio fit for studio reels */
.layer img, .layer canvas {
  position: absolute; left: 0; top: 0; max-width: none;
  width: max(100cqw, 100cqh * var(--fa)); height: max(100cqh, 100cqw / var(--fa));
  transform-origin: 0 0;
  transform: translate(calc(50cqw - 50% * var(--k, 1) + var(--ox, 0px)), calc(50cqh - 50% * var(--k, 1) + var(--oy, 0px))) scale(var(--k, 1));
}
.runway { position: relative; width: 1px; height: 0; pointer-events: none; }
#lvh-probe, #svh-probe { position: fixed; top: 0; left: 0; width: 0; height: 100vh; height: 100lvh; visibility: hidden; pointer-events: none; contain: strict; }
#svh-probe { height: 100svh; }
.video-host { position: fixed; left: 0; top: 0; width: 2px; height: 2px; overflow: hidden; opacity: 0; pointer-events: none; }
html:not(.stage) .film, html:not(.stage) .runway, html:not(.stage) .status, html:not(.stage) .video-host { display: none; }

/* ------------------------------------------------------------------ stage mode: fixed screens, copy windows */
html.stage .sec { position: fixed; inset: 0; z-index: 10; pointer-events: none; }
html.stage .pm-frame, html.stage .pm-block, html.stage .beats { display: contents; }
html.stage .pm-poster, html.stage .pm-states { display: none; }

html.stage .copy {
  /* type zone from 12vh, but never under the nav (short windows, phones in landscape), as page mode */
  position: absolute; top: max(12vh, calc(var(--nav-h) + 24px)); left: var(--margin);
  width: calc(var(--col) * 6 + var(--gutter) * 5);
}
html.stage .copy-hero .display { width: calc(var(--col) * 7 + var(--gutter) * 6); }
html.stage .copy-product .product { white-space: nowrap; }

/* reveal (5.4): lines rise out of masks, the line fades up after 120 ms, exit is a 200 ms fade */
/* hidden lines sit fully below their mask: .ln is the line box plus 0.14em padding, and tight leading lets ascenders
   ride above the line box, so 104% of the line left a 3 px glyph sliver on phones (v3-airport AD, 30 Sep) */
html.stage .copy .ln-i { transform: translateY(calc(100% + 0.3em)); }
html.stage .copy .lede, html.stage .copy .pill, html.stage .beat, html.stage .cue { opacity: 0; }
html.stage .copy .lede, html.stage .copy .pill { transform: translateY(var(--rise-line)); }
html.stage .copy.is-in .ln-i { transform: none; transition: transform var(--t-reveal) var(--ease-out-expo) calc(var(--i, 0) * var(--stagger)); }
html.stage .copy.is-in .lede, html.stage .copy.is-in .pill {
  opacity: 1; transform: none;
  transition: opacity var(--t-line) var(--ease-out) var(--d-line), transform var(--t-line) var(--ease-out) var(--d-line), background-color 200ms linear;
}
html.stage .copy.is-in .pill { transition-delay: var(--d-pill), var(--d-pill), 0s; }
/* outdoor ledes on busy frames (integration r4): the nav's soft paper halo, only on the frames timeline lede_busy lists
   (work/int-r4/ledebusy.py: gradient energy and luminance behind the lede box, per reel and orientation; main.js) */
html.stage .copy.is-in .lede { transition-property: opacity, transform, background-color, text-shadow; }
html.lede-halo .copy .lede { text-shadow: 0 0 1px rgba(242, 239, 232, 0.95), 0 0 6px rgba(242, 239, 232, 0.9), 0 0 14px rgba(242, 239, 232, 0.75); }
html.stage .beat.is-in, html.stage .cue.is-in { opacity: 1; transition: opacity var(--t-line) var(--ease-out); }
/* the hero, the page's signature motion (design review r2-0): 1200 ms expo-out, 140 ms line stagger, the sub at 420 ms,
   the cue 300 ms after the sub */
#hero-copy { --t-reveal: 1200ms; --stagger: 140ms; --d-line: 420ms; }
html.stage .cue.is-in { transition: opacity var(--t-line) var(--ease-out) 720ms; }
html.stage .copy.is-out, html.stage .cue.is-out { opacity: 0; transition: opacity var(--t-exit) linear; }
/* exits drift 6 px up, mirroring the rise of the entry (integration r3); a block cut by the next entry drops at once */
html.stage .copy.is-out:not(.copy-beat):not(.beat) { transform: translateY(-6px); transition: opacity var(--t-exit) linear, transform var(--t-exit) var(--ease-out); }
html.stage .copy.is-cut, html.stage .copy.is-cut * { opacity: 0 !important; transition: none !important; }
html.stage .copy.is-out .ln-i, html.stage .copy.is-out .lede, html.stage .copy.is-out .pill { transform: none; opacity: 1; transition: none; }
html.stage .copy.is-out.beat { opacity: 0; }
html.stage .copy.is-in a, html.stage .copy.is-in button { pointer-events: auto; }
/* data-reveal-once (the configure H2, three windows): after its first exit the lines stay set and the block only fades */
html.stage .copy.was-in { opacity: 0; transition: opacity var(--t-exit) linear; }
html.stage .copy.was-in .ln-i { transform: none !important; transition: none !important; }
html.stage .copy.was-in.is-in { opacity: 1; transition: opacity 300ms var(--ease-out); }

/* silent beat: CUT in integration round 2 (both design reviews r2: two text cards back to back on blank sky, and
   "Protection" right before four "Protect the ..." headlines). Markup kept; delete this rule and restore the dc window
   (work/int-r2/apply_timeline_r2.py --revert) to bring it back. */
#dc-beat { display: none !important; }
/* silent beat: centred display line, nothing else on screen */
html.stage .copy-beat { inset: 0; top: 0; left: 0; width: auto; display: flex; align-items: center; justify-content: center; padding: 0 var(--margin); }
.copy-beat .display { text-align: center; max-width: 14ch; }
.copy-beat .ln-i { text-align: center; }

/* scroll cue */
.cue {
  position: absolute; left: 50%; bottom: calc(28px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  font-size: var(--fs-label); font-weight: 500; line-height: 1.3; white-space: nowrap;
}
.cue-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ink); animation: cue 1.6s var(--ease-sine) infinite; }
.cue.is-still .cue-dot { animation: none; }
.cue { transition: color 250ms linear; }
html.cue-dark .cue { color: var(--paper); }
html.cue-dark .cue-dot { background: var(--paper); }
@keyframes cue {
  0% { transform: translateY(0); opacity: 1; }
  65% { transform: translateY(8px); opacity: 0; }
  66% { transform: translateY(0); opacity: 0; }
  100% { transform: translateY(0); opacity: 1; }
}
html.page .cue { display: none; }
/* desktop: on the dock, just above the container roof (main.js placeCue) */
.cue.on-dock { bottom: auto; }

/* inside beats: CUT by WP13 (30 Sep, DIRECTION 2.2 section 7 cut rule after design review 2 "too complex"; review 1 found
   the Decide and Host dots off their parts). The markup stays for a one-line revert (delete the next rule and restore the
   timeline beats with work/wp13/apply_timeline_wp13.py --revert). */
.beats { display: none !important; }
/* inside beats: dot on the part, label 16 px beside it, flips left in the right 30% of the frame */
html.stage .beat { position: absolute; top: 0; left: 0; width: 0; height: 0; translate: var(--bx, -999px) var(--by, -999px); }
.beat-dot { position: absolute; left: -4px; top: -4px; width: 8px; height: 8px; border-radius: 50%; background: var(--ink); box-shadow: 0 0 0 2px var(--paper); }
.beat-label { font-size: var(--fs-label); font-weight: 500; line-height: 1.3; }
.beat-name, .beat-text, .beat-sep { display: inline; font-size: inherit; line-height: inherit; }
.beat-name { font-weight: 600; letter-spacing: 0; }
html.stage .beat-label { position: absolute; left: 20px; top: 0; transform: translateY(-50%); white-space: nowrap; }
html.stage .beat.is-flip .beat-label { left: auto; right: 20px; }

/* status line (5.5 item 7): HIDDEN everywhere since integration round 2 (design review r2-1 must: micro-label HUD
   furniture, often the wrong word; r2-0 should: keep it only as hero and close bookends). The timeline keeps status data
   only for hero and close, so deleting the next rule gives the bookend version. */
.status { display: none !important; }
.status {
  position: fixed; z-index: 30; bottom: 28px; left: var(--margin);
  display: flex; align-items: center; gap: 10px; pointer-events: none;
  font-size: var(--fs-small); font-weight: 450; letter-spacing: 0.01em; line-height: 1.4; color: var(--ink-80);
  opacity: 0; transition: opacity var(--t-status) linear, color var(--t-status) linear;
}
.status.is-on { opacity: 1; }
.status.on-paper { color: var(--steel); }
.status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ink); flex: none; }
.status-text { transition: opacity 125ms linear; }
.status.is-swap .status-text { opacity: 0; }

/* segmented control (5.5 item 9) */
.control {
  position: relative; display: inline-flex; align-items: center; height: 52px; padding: 4px; border-radius: 26px;
  background: var(--raised); color: var(--ink);
}
html.stage .control {
  position: absolute; left: 50%; bottom: 28px; transform: translateX(-50%); pointer-events: auto;
  opacity: 0; visibility: hidden; transition: opacity 400ms var(--ease-out), visibility 0s linear 400ms;
}
html.stage .control.is-on { opacity: 1; visibility: visible; transition: opacity 400ms var(--ease-out); }
.control button {
  position: relative; z-index: 1; height: 44px; min-width: 44px; padding: 0 22px; border: 0; border-radius: 22px;
  background: transparent; cursor: pointer; white-space: nowrap; -webkit-tap-highlight-color: transparent;
  font-size: var(--fs-label); font-weight: 500; line-height: 1.3; color: var(--ink);
  transition: color 240ms var(--ease-ind), background-color 200ms linear;
}
/* iPhone round: hover only where a pointer hovers; on touch :hover stuck to the last tapped button (WEAVER kept a grey pill
   after the scroll had moved the state to HUNTER) */
@media (hover: hover) { .control button:hover:not([aria-pressed='true']) { background: var(--deep); } }
.control button[aria-pressed='true'] { color: var(--paper); }
/* control transitions r2: the indicator settles in 240 ms, before the picture changes (was 400 ms expo) */
.control { --ease-ind: cubic-bezier(.3, .7, .4, 1); }
.control-ind {
  position: absolute; left: 0; top: 4px; height: 44px; width: 0; border-radius: 22px; background: var(--ink);
  transition: transform 240ms var(--ease-ind), width 240ms var(--ease-ind);
}
.control-ind.no-anim { transition: none; }
/* after a click (jump.js pin, .is-cap) the picked interceptor's lede line sits above the control: 150 ms out, 250 ms in */
.control-cap { display: none; }
html.stage .control-cap {
  display: block; position: absolute; left: 0; right: 0; bottom: calc(100% + 12px); height: 1.3em; margin: 0;
  font-size: var(--fs-label); font-weight: 500; line-height: 1.3; color: var(--steel); text-align: center; pointer-events: none;
}
.control-cap span { position: absolute; left: 0; right: 0; white-space: nowrap; opacity: 0; transition: opacity 150ms linear; }
.control.is-cap[data-k='0'] .control-cap span:nth-child(1),
.control.is-cap[data-k='1'] .control-cap span:nth-child(2),
.control.is-cap[data-k='2'] .control-cap span:nth-child(3) { opacity: 1; transition: opacity 250ms var(--ease-out) 150ms; }
/* the headline stays and dims while the products cross it: portrait click swap (jump.js), near-square swaps (main.js crossH) */
html.stage #configure-copy > * { transition: opacity 200ms cubic-bezier(.4, 0, .2, 1); }
html.stage .copy.is-dim > * { opacity: 0.3; }
/* integration r3 (design review r3-1 could): on wide screens the configurator's only UI reads as UI, not a footnote */
@media (min-width: 1280px) and (min-height: 501px) {
  .control { height: 56px; border-radius: 28px; }
  .control button { height: 48px; border-radius: 24px; padding: 0 26px; font-size: 16px; font-weight: 600; }
  .control-ind { height: 48px; border-radius: 24px; }
}

/* proof: a paper curtain in the runway (flow segment, native scroll) */
html.stage #proof { position: absolute; left: 0; right: 0; top: 0; z-index: 20; background: var(--paper); }
/* Until layout() places the curtain (inline top), park it fixed far below the viewport: its lazy founder photos and press
   logos stay unloaded at parse time (they loaded at boot, over the 400 KB first paint budget) and, being fixed, it adds
   nothing to the document height. */
html.stage #proof:not([style]) { position: fixed; top: 1000lvh; }
/* soft paper edges (24 lvh) above and below the curtain: the paper fade into the proof and the fade back into the close
   read as haze instead of a hard wipe while the curtain scrolls over the film (DIRECTION 2.1 rows 8 to 10) */
html.stage #proof::before, html.stage #proof::after { content: ''; position: absolute; left: 0; right: 0; height: 24lvh; pointer-events: none; }
html.stage #proof::before {
  bottom: 100%;
  background: linear-gradient(to bottom, rgba(242, 239, 232, 0) 0%, rgba(242, 239, 232, 0.1) 22%, rgba(242, 239, 232, 0.34) 46%,
    rgba(242, 239, 232, 0.68) 70%, rgba(242, 239, 232, 0.92) 88%, var(--paper) 100%);
}
html.stage #proof::after {
  top: 100%;
  background: linear-gradient(to top, rgba(242, 239, 232, 0) 0%, rgba(242, 239, 232, 0.1) 22%, rgba(242, 239, 232, 0.34) 46%,
    rgba(242, 239, 232, 0.68) 70%, rgba(242, 239, 232, 0.92) 88%, var(--paper) 100%);
}

/* footer strip (5.5 item 14) */
.foot {
  /* z 35: above the fixed status line (30), under the nav (40); the opaque strip covers the status at the page end */
  position: relative; z-index: 35; background: var(--paper); color: var(--charcoal);
  display: flex; flex-wrap: wrap; align-items: center; row-gap: 6px;
  padding: 22px max(var(--margin), calc((100vw - var(--content-max)) / 2)) calc(26px + env(safe-area-inset-bottom, 0px));
  font-size: var(--fs-small); font-weight: 450; letter-spacing: 0.01em; line-height: 1.4;
}
.foot > * + *::before { content: '·'; content: '·' / ''; display: inline-block; margin: 0 10px; color: var(--charcoal); text-decoration: none; }
.foot a, .foot button { color: inherit; text-decoration: none; background: none; border: 0; padding: 0; font: inherit; cursor: pointer; }
@media (hover: hover) { .foot a:hover, .foot button:hover { text-decoration: underline; text-underline-offset: 3px; } }
.foot a:active, .foot button:active, .nav-specs:active, .nav-mark:active { opacity: 0.6; }
.foot-mark .wordmark { width: 64px; display: inline-block; vertical-align: -2px; }
.foot > a::before, .foot > button::before { text-decoration: none; }

/* specs sheet (5.5 item 10; specs redesign 1 Oct, INTEGRATION_NOTES "Specs redesign"): a full-bleed paper sheet, no card.
   Four products side by side. The v2 stills share one camera, one scale and a baked floor shadow at 0.88 of the frame, so
   every still is the same 4:3 box at one width (--iw), bottom aligned: one scale, one floor, nothing sized per image.
   The stage row grows with the viewport height up to that size. 1100 px and wider the four products spread across the
   text measure (see the row block below). Under 1100 px: one product at a time (switcher, swipe, arrow keys;
   specs-sheet.js). */
.specs {
  --sp-gap: clamp(24px, 3.4vw, 72px);
  --sp-stage: clamp(220px, 36vh, 440px);
  --sp-k: 1.4;
  --sp-hh: var(--nav-h);
  --sp-name: 38px;
  --sp-role: clamp(16px, 1.3vw, 20px);
  --sp-fact: clamp(15px, 1.15vw, 18px);
  --sp-fig: 66px;
  --sp-cap: clamp(13px, 1.04vw, 16px);
  --sp-tier: clamp(24px, 4.4vh, 44px);
  --sp-row: clamp(18px, 3.4vh, 34px);
  position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0;
  background: var(--paper); color: var(--ink); overflow: hidden auto; overscroll-behavior: contain;
}
.specs::backdrop { background: transparent; }
.specs[open] { animation: specs-in var(--t-dialog) var(--ease-out); }
.specs.is-closing { animation: specs-out var(--t-dialog) var(--ease-out) forwards; }
@keyframes specs-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes specs-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes sp-rise { from { opacity: 0; transform: translateY(14px); } }
.sp-in {
  display: flex; flex-direction: column; min-height: 100%;
  max-width: calc(var(--content-max) + 2 * var(--margin)); margin: 0 auto; padding: 0 var(--margin); overflow-x: clip;
}
/* head and switcher stay put over a scrolled sheet; the paper fill runs to both screen edges (border-image outset) */
.sp-head, .sp-switch { position: sticky; z-index: 2; border-image: linear-gradient(var(--paper), var(--paper)) fill 0 / 0 / 0 100vw; }
.sp-head { top: 0; flex: none; display: flex; align-items: center; justify-content: space-between; gap: 24px; height: var(--sp-hh); }
.sp-title { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; line-height: 1; }
.sp-title:focus { outline: none; }
.sp-close {
  position: relative; flex: none; width: 44px; height: 44px; border: 0; border-radius: 50%; background: var(--raised); cursor: pointer;
  transition: background-color 200ms linear;
}
.sp-close:active { background: var(--deep); }
@media (hover: hover) { .sp-close:hover { background: var(--deep); } }
.sp-close::before, .sp-close::after {
  content: ''; position: absolute; left: 14px; right: 14px; top: 21px; height: 2px; border-radius: 1px; background: var(--ink);
}
.sp-close::before { transform: rotate(45deg); }
.sp-close::after { transform: rotate(-45deg); }
.sp-switch { display: none; }
.sp-grid {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-rows: repeat(4, auto); column-gap: var(--sp-gap);
  margin: auto 0;
}
.sp-col {
  --iw: min(calc(var(--sp-k) * 100cqi), calc(var(--sp-stage) * 1.6878));
  display: grid; grid-row: 1 / -1; grid-template-rows: subgrid; align-content: start; min-width: 0; container-type: inline-size;
}
/* the stage shows the still from 0.16 to 0.95 of its height: mast tip to just under the floor shadow */
.sp-stage { position: relative; height: calc(var(--iw) * 0.5925); }
.sp-stage img {
  position: absolute; left: 50%; bottom: calc(var(--iw) * -0.0375); width: var(--iw); max-width: none; height: auto; translate: -50% 0;
  opacity: 0; transition: opacity 500ms var(--ease-out); pointer-events: none; user-select: none; -webkit-user-select: none;
}
.sp-stage img.is-ld { opacity: 1; }
.sp-name {
  margin-top: clamp(16px, 3vh, 32px); white-space: nowrap;
  font-size: min(var(--sp-name), 12cqi); font-weight: 700; letter-spacing: 0.005em; line-height: 1.05;
}
.sp-role { margin-top: 8px; font-size: var(--sp-role); font-weight: 450; line-height: 1.35; color: var(--steel); }
.sp-tier { margin-top: var(--sp-tier); }
.sp-facts { list-style: none; padding: 0; font-size: var(--sp-fact); font-weight: 450; line-height: 1.4; text-wrap: pretty; }
.sp-facts li + li { margin-top: 0.5em; }
/* figures: the number leads, the unit sits tight on its baseline, the caption under it, two per row */
.sp-figs { display: grid; grid-template-columns: 1fr 1fr; column-gap: 12px; row-gap: var(--sp-row); }
.sp-figs + .sp-facts { margin-top: var(--sp-row); }
.sp-fig { min-width: 0; }
.sp-v {
  display: block; white-space: nowrap; font-variant-numeric: lining-nums;
  font-size: min(var(--sp-fig), 16.5cqi); font-weight: 600; letter-spacing: -0.035em; line-height: 1;
}
.sp-u { font-size: 0.36em; font-weight: 600; letter-spacing: 0; margin-left: 0.06em; }
.sp-c { display: block; margin-top: 8px; white-space: nowrap; font-size: var(--sp-cap); font-weight: 450; line-height: 1.3; color: var(--steel); }
.sp-foot {
  /* iPhone round: the footnote clears the home indicator (env() is 0 elsewhere) */
  flex: none; padding: clamp(20px, 4vh, 40px) 0 max(clamp(18px, 3.4vh, 32px), env(safe-area-inset-bottom, 0px));
  font-size: var(--fs-small); font-weight: 450; letter-spacing: 0.01em; line-height: 1.4; color: var(--steel);
}
/* entrance: the renders rise a little, the words follow (5.4); reduced motion: fades only */
.specs[open] .sp-stage { animation: sp-rise 900ms var(--ease-out) both; animation-delay: calc(40ms + var(--i) * 60ms); }
.specs[open] .sp-col > :not(.sp-stage) { animation: specs-in 600ms var(--ease-out) both; animation-delay: calc(200ms + var(--i) * 60ms); }
/* the row, 1100 px and wider: the four products sit edge to edge on the text measure (fix round 1, art director). Each
   column carries its product's measured extent in its still (--bl left edge, --bw width, crop fractions at alpha 0.2,
   shadow excluded) and --cum, the widths of the products before it; the grid carries --S, the four widths summed. One
   width for all four stills: the smallest of k x column, the stage height, and the width at which four products and
   three gaps of --sp-g0 fill the measure. The three gaps then share what is left equally, so the container starts on the
   left text margin and the SABLE wingtip ends on the right one, under the close button. */
@media (min-width: 1100px) {
  .specs { --sp-g0: calc(var(--sp-gap) * 1.5); }
  .sp-col {
    --sp-w4: calc(400cqi + 3 * var(--sp-gap));
    --iw: min(calc(var(--sp-k) * 100cqi), calc(var(--sp-stage) * 1.6878), calc((var(--sp-w4) - 3 * var(--sp-g0)) / var(--S)));
    --sp-g: calc((var(--sp-w4) - var(--iw) * var(--S)) / 3);
  }
  .sp-stage img { left: calc(var(--iw) * (var(--cum) - var(--bl)) + var(--i) * (var(--sp-g) - 100cqi - var(--sp-gap))); translate: none; }
}
@media (min-width: 1800px) { .specs { --sp-k: 1.5; } }
@media (min-width: 2200px) { .specs { --sp-k: 1.66; } }
/* laptops with short screens: tighter type and air, so the footnote stays on screen */
@media (min-width: 1100px) and (max-height: 780px) {
  .specs { --sp-hh: 60px; --sp-name: 32px; --sp-fact: 15px; --sp-fig: 54px; --sp-cap: 13px; --sp-tier: 20px; --sp-row: 16px; }
  .sp-name { margin-top: 14px; }
  .sp-role { margin-top: 4px; }
  .sp-facts li + li { margin-top: 0.4em; }
  .sp-c { margin-top: 6px; }
}
@media (min-width: 1100px) and (max-height: 660px) {
  .specs { --sp-hh: 52px; --sp-stage: 31vh; --sp-name: 28px; --sp-role: 15px; --sp-fact: 14px; --sp-fig: 46px; --sp-tier: 16px; --sp-row: 12px; }
  .sp-name { margin-top: 10px; }
  .sp-foot { padding: 14px 0 max(14px, env(safe-area-inset-bottom, 0px)); }
}

/* one product at a time: phones, tablets, small laptop windows */
@media (max-width: 1099px) {
  .specs { --sp-k: 1.16; --sp-stage: min(44vh, 460px); --sp-w: 560px; --sp-name: 40px; --sp-role: 17px; --sp-fact: 17px; --sp-fig: 60px; --sp-cap: 15px; --sp-tier: 28px; --sp-row: 24px; }
  .sp-head, .sp-switch, .sp-foot { width: 100%; max-width: var(--sp-w); margin-left: auto; margin-right: auto; }
  .sp-switch { display: block; top: var(--sp-hh); padding: 4px 0 12px; }
  .sp-track {
    position: relative; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    height: 48px; padding: 4px; border-radius: 24px; background: var(--raised);
  }
  .sp-ind {
    position: absolute; top: 4px; bottom: 4px; left: 4px; width: calc((100% - 8px) / 4); border-radius: 20px;
    background: var(--ink); transform: translateX(calc(var(--p, 0) * 100%));
  }
  .sp-track button {
    position: relative; min-width: 0; padding: 0; border: 0; border-radius: 20px; background: none; cursor: pointer; white-space: nowrap;
    font-size: 15px; font-weight: 500; line-height: 1; transition: color 200ms linear, background-color 200ms linear;
    -webkit-tap-highlight-color: transparent;
  }
  .sp-track button[aria-pressed='true'] { color: var(--paper); }
  .sp-track button:focus-visible { outline-offset: -4px; border-radius: 20px; }
  .sp-track button[aria-pressed='true']:focus-visible { outline-color: var(--paper); }
  .sp-grid {
    display: flex; align-items: flex-start; column-gap: 0; margin: 0 calc(-1 * var(--margin));
    overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; overscroll-behavior-x: contain; scrollbar-width: none;
  }
  .sp-grid::-webkit-scrollbar { display: none; }
  .sp-col {
    display: block; flex: 0 0 100%; overflow-x: clip; scroll-snap-align: start; scroll-snap-stop: always;
    padding: 0 max(var(--margin), calc((100% - var(--sp-w)) / 2));
  }
  .sp-stage { margin-top: 8px; }
  .sp-name { margin-top: 20px; }
  .sp-role { margin-top: 6px; }
  .sp-foot { margin-top: auto; padding: 32px 0 max(24px, env(safe-area-inset-bottom, 0px)); }
  .specs[open] .sp-stage, .specs[open] .sp-col > :not(.sp-stage) { animation-delay: 40ms; }
}
@media (max-width: 1099px) and (hover: hover) {
  .sp-track button[aria-pressed='false']:hover { background: var(--deep); }
}
/* landscape tablets and small laptop windows: the render left, the words right */
@media (max-width: 1099px) and (min-width: 700px) and (min-aspect-ratio: 1/1) {
  .specs { --sp-w: 100%; --sp-k: 0.64; --sp-stage: min(58vh, 520px); --sp-fig: 56px; }
  .sp-switch { max-width: 560px; }
  .sp-grid { margin-block: auto; }
  .sp-foot { margin-top: 0; }
  .sp-col { display: grid; grid-template-columns: 55fr 45fr; grid-template-rows: 1fr auto auto auto 1fr; column-gap: var(--sp-gap); padding: 0 var(--margin); }
  .sp-stage { grid-column: 1; grid-row: 1 / -1; align-self: center; margin: 0; }
  .sp-name, .sp-role, .sp-tier { grid-column: 2; }
  .sp-name { grid-row: 2; margin-top: 0; }
  .sp-role { grid-row: 3; }
  .sp-tier { grid-row: 4; }
}
@media (max-width: 699px) {
  .specs { --sp-hh: 56px; --sp-name: clamp(32px, 9.4vw, 40px); --sp-fig: 58px; }
  .sp-close { width: 40px; height: 40px; }
  .sp-close::before, .sp-close::after { left: 13px; right: 13px; top: 19px; }
  .sp-track button { font-size: clamp(13px, 3.9vw, 15px); letter-spacing: -0.005em; }
}
@media (max-width: 699px) and (max-height: 760px) {
  .specs { --sp-stage: 27vh; --sp-name: clamp(30px, 9vw, 36px); --sp-fact: 16px; --sp-fig: 52px; --sp-tier: 20px; --sp-row: 18px; }
  .sp-switch { padding-bottom: 8px; }
  .sp-name { margin-top: 14px; }
  .sp-facts li + li { margin-top: 0.4em; }
  .sp-foot { padding: 20px 0 max(16px, env(safe-area-inset-bottom, 0px)); }
}

/* debug overlay (?debug=1) */
#debug {
  position: fixed; right: 12px; bottom: 12px; z-index: 99; max-width: min(560px, calc(100vw - 24px));
  padding: 10px 12px; border-radius: 10px; background: rgba(20, 20, 20, 0.82); color: #f2efe8;
  font: 500 11px/1.45 var(--font); font-variant-numeric: tabular-nums; pointer-events: none;
}

/* ------------------------------------------------------------------ page mode (5.5 item 15, fallback 2) */
html.page .sec { position: relative; }
html.page .pm-frame { position: relative; min-height: 100vh; min-height: 100dvh; overflow: hidden; }
html.page .pm-poster { position: absolute; inset: 0; }
html.page .pm-poster img { width: 100%; height: 100%; object-fit: cover; }
html.page .pm-frame > .copy { position: absolute; z-index: 1; top: max(calc(var(--nav-h) + 24px), 12vh); left: max(var(--margin), calc((100vw - var(--content-max)) / 2)); width: calc(var(--col) * 6 + var(--gutter) * 5); }
html.page .copy-hero .display { width: calc(var(--col) * 7 + var(--gutter) * 6); }
html.page .copy-beat { padding: 22vh var(--margin); display: flex; justify-content: center; }
html.page .beats { position: absolute; z-index: 1; left: max(var(--margin), calc((100vw - var(--content-max)) / 2)); top: calc(max(calc(var(--nav-h) + 24px), 12vh) + 2 * var(--fs-h2) + 48px); display: grid; gap: 12px; max-width: 34ch; }
html.page .beat-dot { display: none; }
html.page #configure { padding-top: calc(var(--nav-h) + 12vh); }
html.page #configure-copy { padding: 0 max(var(--margin), calc((100vw - var(--content-max)) / 2)); }
html.page #configure .control { display: flex; width: max-content; margin: var(--s-48) max(var(--margin), calc((100vw - var(--content-max)) / 2)) var(--s-32); }
html.page .pm-states {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gutter);
  padding: 0 max(var(--margin), calc((100vw - var(--content-max)) / 2)) var(--s-112);
}
html.page .pm-states figure { transition: opacity 400ms var(--ease-out); }
html.page .pm-states[data-active='0'] figure:not(:nth-child(1)),
html.page .pm-states[data-active='1'] figure:not(:nth-child(2)),
html.page .pm-states[data-active='2'] figure:not(:nth-child(3)) { opacity: 0.4; }
html.page .pm-still img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 20px; }
html.page .pm-states figcaption { margin-top: var(--s-12); font-size: var(--fs-label); font-weight: 500; }
html.page #proof { position: relative; }
html.page .pm-block .copy-product { top: 12vh; }
/* page mode is a document: the nav sits at the top of the page and scrolls away with it. Fixed and without a backing it
   ran over every heading, the globe and the press row below it (adversarial QA r2) */
html.page .nav { position: absolute; }
/* stages much wider than the 16:9 posters (ultrawide monitors, phones held sideways): each frame shows the whole picture,
   so the site copy keeps its sky and the SABLE keeps its nose, instead of a centre crop that set the refinery, airport and
   grid lines over towers and stacks (adversarial QA r2) */
@media (min-aspect-ratio: 185/100) { html.page .pm-frame { min-height: 56.25vw; } }

/* ------------------------------------------------------------------ phones */
@media (max-width: 699px) {
  html.stage .copy, html.page .pm-frame > .copy { left: 20px; right: 20px; width: auto; top: 96px; }
  html.stage .copy-hero .display, html.page .copy-hero .display { width: auto; }
  html.stage .copy-beat { left: 0; right: 0; top: 0; padding: 0 20px; }
  .nav { padding: 0 20px; }
  .nav-mark .wordmark { width: 84px; }
  /* finisher (2 Oct): phones get a short "Specs" button under the desktop rule (inside and configure, page mode), so the
     redesigned sheet is reachable without the footer; a 44 px target between the wordmark and the pill at 360 px */
  .nav-right { gap: 10px; }
  .nav-specs { min-width: 44px; min-height: 44px; padding: 0 8px; }
  .nav.on-backing .nav-specs { padding: 0 8px; margin: 0; }
  .nav-specs-l { display: none; }
  .nav-specs-s { display: inline; }
  .pill { height: 44px; padding: 0 18px; }
  .pill-lg { height: 56px; padding: 0 28px; }
  .status { left: 20px; bottom: 24px; }
  /* WP13 (design reviews): the control is the configurator's only UI on phones, and the first screen ends on the cue
     alone ("Scroll to open" and "Standing by" shared the bottom strip) */
  html.has-control .status, html.stage:has(#cue.is-in) .status { opacity: 0; }
  /* iPhone round: the control sits 20 px above the small viewport (Safari toolbar shown) instead of following the toolbar,
     which moved it 80 px on every show / hide while the product stayed (the fit keeps the product above it) and put it
     on the home indicator with the toolbar hidden */
  html.stage .control { left: 20px; right: 20px; bottom: auto; top: calc(100svh - 72px); transform: none; display: flex; }
  .control button { flex: 1 1 auto; padding: 0 10px; }
  html.stage .beat { translate: none; left: 20px; right: 20px; top: auto; bottom: 64px; width: auto; height: auto; }
  html.stage .beat-dot { display: none; }
  html.stage .beat-label { position: static; transform: none; white-space: normal; }
  html.page .pm-states { grid-template-columns: 1fr; }
  html.page #configure .control { margin-left: 20px; margin-right: 20px; width: auto; }
  html.page .beats { left: 20px; right: 20px; }
  .foot { padding: 18px 20px calc(22px + env(safe-area-inset-bottom, 0px)); column-gap: 16px; }
  .foot > * + *::before { content: none; }
}
/* integration r3: on landscape desktops the airport and grid frames keep their horizon at 27 to 30 % of the height in every
   frame of the copy window, so under a two-line stack their ledes sat on the grass, the tower and the pylons (lede
   luminance 0.47 to 0.55, gradient 0.13 to 0.24 at 1440x900; at 2560x1080 the airport line still crossed the tower;
   work/int-r3/probe). There the two shortest titles set on one line (seven columns) with a one-line lede, which keeps
   both on sky; the cloud and the refinery keep the two-line stack (the refinery needs it to clear the flare stack). */
@media (min-width: 700px) and (orientation: landscape) and (min-height: 501px) {
  html:is(.stage, .page) :is(#airport-h2, #grid-h2) { max-width: none; }
  html.stage :is(#airport-copy, #grid-copy), html.page .pm-frame > :is(#airport-copy, #grid-copy) { width: calc(var(--col) * 7 + var(--gutter) * 6); }
  :is(#airport-copy, #grid-copy) .lede { max-width: none; }
}
/* phones held sideways and other short landscape stages (integration r2): type sized by height, so the copy block keeps
   to the top third of the frame (at 844x390 the vw sizes ran it from 25 % to 63 % of the height, over every horizon),
   the block runs 8 columns so most headlines set on one line, and the control sits lower */
@media (orientation: landscape) and (max-height: 500px) {
  :root {
    --nav-h: 56px;
    --fs-display: clamp(30px, 11vh, 60px);
    --fs-h2: clamp(24px, 8vh, 44px);
    --fs-product: clamp(28px, 9.5vh, 52px);
    --fs-lede: clamp(14px, 3.9vh, 18px);
  }
  html.stage .copy { top: calc(var(--nav-h) + 12px); }
  /* the outdoor blocks run 8 columns (sky above the horizon is wide), the launch and site blocks 9 (integration r4: at 8
     the runway and grid ledes set on two lines at 720x450, 200 % zoom, the second over the horizon and the pylons; the
     hero and close keep 8, so their display lines keep the two-line rhyme); studio blocks keep 6 so the configure H2
     never runs into the container */
  html.stage :is(#hero-copy, #close-copy) { width: min(calc(var(--col) * 8 + var(--gutter) * 7), calc(100vw - 2 * var(--margin))); }
  html.stage :is(#launch-copy, #dc-copy, #refinery-copy, #airport-copy, #grid-copy) { width: min(calc(var(--col) * 9 + var(--gutter) * 8), calc(100vw - 2 * var(--margin))); }
  html.stage .copy-hero .display, html.stage #close-copy .display { width: auto; }
  .copy .lede { margin-top: var(--s-12); }
  /* one line each where the 8 columns allow: every site block ends by a third of the height */
  html.stage .copy .lede { max-width: none; }
  html.stage .h2-site { font-size: var(--fs-h2); max-width: none; }
  .copy .pill-lg { margin-top: var(--s-16); }
  #close-copy .pill-lg { margin-top: var(--s-12); }
  #hero-copy .lede { font-size: var(--fs-lede); }
  .nav-mark .wordmark { width: 84px; }
  /* integration r4: compact and at the bottom left (centred, it sat on the container base at every configure hold; the
     product stands right of centre in every configurator frame); 44 px targets kept */
  html.stage .control { left: var(--margin); right: auto; top: auto; bottom: calc(10px + env(safe-area-inset-bottom, 0px)); transform: none; }
  html.stage .control button { padding: 0 13px; }
  /* adversarial QA r5: the cue too (centred, its label sat on the container base at 667x375, 740x360, 844x390, 932x430) */
  .cue { bottom: calc(14px + env(safe-area-inset-bottom, 0px)); left: var(--margin); transform: none; }
}
/* iPads held upright and desktop windows in portrait (integration r3, r4): the control sits 16 px from the bottom so the
   configurator holds fit between the H2 and the control (work/int-r2/focus_keys.py focus_y_port) */
@media (min-width: 700px) and (orientation: portrait) { html.stage .control { bottom: 16px; } }
/* integration r4: every portrait stage gets the portrait frames (boot.js rung rule), page mode too (the <source> media) */
@media (orientation: portrait) {
  /* page mode on phones: the three state stills are the portrait posters, container in the lower half of the 9:16 frame
     (y 0.47 to 0.86); the 16:9 centre crop cut every container through its bays (final QA). Square, low in the frame.
     Same media as the portrait <source>, so iPads held upright (which get the portrait posters too) crop them the same
     way; with the old max-width 699px they showed only the roof leaves (adversarial QA r3). */
  html.page .pm-still img { aspect-ratio: 1 / 1; object-position: 50% 86%; }
}

/* ------------------------------------------------------------------ preferences */
@media (prefers-contrast: more) {
  /* only on copy that is showing (or fading out): hidden blocks of later sections would stack empty panels over it.
     The panel hugs its text (fit-content, capped at the type zone plus the padding, so the lines split for the type zone
     never re-wrap); the hero and close display lines run 7 columns wide, so their headings hug too. */
  html.stage .copy:is(.is-in, .is-out):not(.beat):not(.copy-beat) {
    background: var(--paper); padding: 16px 20px; margin: -16px -20px; border-radius: 16px;
    width: fit-content; max-width: calc(var(--col) * 6 + var(--gutter) * 5 + 40px);
  }
  /* same specificity as the rule above plus one class, or its 6-column cap wins and re-wraps "Start with yours." */
  html.stage .copy.copy-hero:is(.is-in, .is-out):not(.beat):not(.copy-beat) { max-width: calc(var(--col) * 7 + var(--gutter) * 6 + 40px); }
  html.stage .copy-hero:is(.is-in, .is-out) .display { width: fit-content; max-width: calc(var(--col) * 7 + var(--gutter) * 6); }
  /* the close display runs 8 columns since integration round 2 */
  html.stage .copy.copy-close:is(.is-in, .is-out):not(.beat):not(.copy-beat) { max-width: calc(var(--col) * 8 + var(--gutter) * 7 + 40px); }
  html.stage .copy-close:is(.is-in, .is-out) .display { max-width: calc(var(--col) * 8 + var(--gutter) * 7); }
  .status { color: var(--ink); }
}
@media (prefers-contrast: more) and (max-width: 699px) {
  /* phones: the text keeps its 20 px rails, the panel sits 8 px inside the screen edge instead of running into it */
  html.stage .copy:is(.is-in, .is-out):not(.beat):not(.copy-beat),
  html.stage .copy.copy-hero:is(.is-in, .is-out):not(.beat):not(.copy-beat) { padding: 12px; margin: -12px; border-radius: 14px; max-width: calc(100vw - 16px); }
  html.stage .copy-hero:is(.is-in, .is-out) .display { max-width: calc(100vw - 40px); }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
  html.stage .copy .ln-i, html.stage .copy .lede, html.stage .copy .pill { transform: none !important; }
  html.stage .copy.is-in .ln-i, html.stage .copy.is-in .lede, html.stage .copy.is-in .pill { transition: opacity 200ms linear !important; }
  .veil { transition-duration: 200ms; }
  .specs[open], .specs.is-closing { animation-duration: 200ms !important; }
  .specs[open] .sp-stage { animation-name: specs-in; }
}

/* ------------------------------------------------------------------ close copy in the type zone (r1 fix round 2) */
/* DIRECTION 2.2: the close block sits in the hero H1's type zone, 12vh to 48vh. The display drops to 0.72x the hero size
   (capped by the height left for the block on short screens) so it sets as two lines, "125,000 sites to protect." over
   "Start with yours.", rhyming with the two-line hero H1; the sub runs on one line and the button follows 24 px below. */
/* integration r2 (design review r2-0 could): 0.82x (85 px at 1440x900, was 74.6) for a closer first and last frame rhyme,
   8 columns so "125,000 sites to protect." keeps one line */
#close-copy .display { font-size: max(34px, min(calc(var(--fs-display) * 0.82), calc((36vh - 150px) / 1.95))); }
html.stage #close-copy .display, html.page #close-copy .display { width: calc(var(--col) * 8 + var(--gutter) * 7); max-width: calc(100vw - 2 * var(--margin)); }
#close-copy .lede { max-width: none; }
#close-copy .pill-lg { margin-top: var(--s-24); }
@media (max-width: 699px) {
  /* phones: the block keeps its 96 px top and full width; a slightly smaller display keeps it in the top third */
  #close-copy .display { font-size: calc(var(--fs-display) * 0.86); }
  html.stage #close-copy .display, html.page #close-copy .display { width: auto; max-width: none; }
  #close-copy .lede { max-width: 34ch; }
}
/* short phones (iPhone SE: 626 px large viewport; in-app browsers): the close CTA sat on the container roof. Below about
   720 px of large viewport the display, the sub and the button tighten; taller phones keep 39.6 / 20 / 24 px exactly */
@media (max-width: 699px) and (orientation: portrait) {
  #close-copy .display { font-size: max(34px, min(calc(var(--fs-display) * 0.86), 5.5vh)); }
  #close-copy .lede { margin-top: clamp(12px, calc(9.6vh - 46px), 20px); }
  #close-copy .pill-lg { margin-top: clamp(12px, calc(12vh - 61px), 24px); }
}
