/* site.css — the one stylesheet for displaybridge.app (merged 2026-10-11
   from style.css + story.css; user decision: one CSS file).

   Two page types share one file and the SAME class names (.nav, .foot,
   .btn, .film, the :root palette) with different values, so each half is
   scoped and cannot leak into the other:

     body:not(.story)  → every prose page (/faq, /setup, /vs, guides,
                         legal pages): light theme, sticky nav.
     body.story        → the four homepages (index.html carries
                         <body class="story">): dark theme, fixed nav,
                         the scroll-story scenes driven by story.js.

   A one-sided scope was tried first and leaked the prose footer
   background and button line-heights into the homepage — the computed
   style fingerprint caught it. Scoping both halves gives 0 differences
   on all page types. Global rules identical in both originals (the
   box-sizing reset, html) are emitted once below. @keyframes caret was
   identical and is shared; the story's hint differs → story-hint.

   ?v= on the <link> busts the 1 h edge cache — bump it after changes. */

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}

/* ═══ PROSE PAGES ═══ */
body:not(.story){
  --ink:#1d1d1f;
  --ink-2:#6e6e73;
  --line:#e3e3e6;
  --bg:#ffffff;
  --bg-soft:#f5f5f7;
  --dark:#000000;
  --dark-2:#161617;
  --dark-ink:#f5f5f7;
  --dark-ink-2:#a1a1a6;
  --dark-line:#2a2a2c;
  --blue:#0071e3;
  --blue-hi:#0077ed;
  --glass:#5ac8fa;
  --wrap:1000px;
  --pad:24px;
  --sans:-apple-system,BlinkMacSystemFont,"SF Pro Display","SF Pro Text",
         "Helvetica Neue",Helvetica,Arial,sans-serif;
}
@media (prefers-reduced-motion:reduce){}
body:not(.story){
  margin:0;
  font-family:var(--sans);
  font-size:17px;
  line-height:1.5;
  color:var(--ink);
  background:var(--bg);
  -webkit-font-smoothing:antialiased;
}
body:not(.story) img,body:not(.story) video{display:block;max-width:100%}
body:not(.story) a{color:var(--blue);text-decoration:none}
body:not(.story) a:hover{text-decoration:underline}

body:not(.story) .skip{position:absolute;left:-9999px;top:0;background:#fff;padding:12px 18px;z-index:200}
body:not(.story) .skip:focus{left:8px;top:8px}

/* ── layout ─────────────────────────────────────────────────────── */
body:not(.story) .wrap{max-width:var(--wrap);margin-inline:auto;padding-inline:var(--pad)}
body:not(.story) .wrap--narrow{max-width:760px}
body:not(.story) .center{text-align:center}
body:not(.story) .band{padding-block:clamp(72px,11vw,140px)}
body:not(.story) .band--soft{background:var(--bg-soft)}
body:not(.story) .band--dark{background:var(--dark);color:var(--dark-ink)}
body:not(.story) .band--dark .lead{color:var(--dark-ink-2)}

/* ── type ───────────────────────────────────────────────────────── */
body:not(.story) h1,body:not(.story) h2,body:not(.story) h3{margin:0;font-weight:600;letter-spacing:-.022em;line-height:1.06;
  overflow-wrap:break-word}
body:not(.story) h1{font-size:clamp(2.4rem,8.2vw,4.6rem);letter-spacing:-.03em}
body:not(.story) h2{font-size:clamp(1.85rem,6.2vw,3.3rem)}
/* The hand-set line breaks are a wide-screen luxury; on a phone they turn
   into lines too long to fit and push the whole document sideways. */
@media (max-width:620px){body:not(.story) h1 br,body:not(.story) h2 br{display:none}}
body:not(.story) h3{font-size:1.32rem;letter-spacing:-.012em;line-height:1.2}
body:not(.story) p{margin:0}

body:not(.story) .eyebrow{
  margin:0 0 14px;
  font-size:.75rem;
  font-weight:600;
  letter-spacing:.13em;
  text-transform:uppercase;
  color:var(--ink-2);
}
body:not(.story) .eyebrow--glass{color:var(--glass)}

body:not(.story) .lead{
  margin:22px auto 0;
  max-width:46ch;
  font-size:clamp(1.05rem,1.35vw,1.26rem);
  line-height:1.5;
  color:var(--ink-2);
}
body:not(.story) .lead--center{text-align:center}
body:not(.story) .lead--hero{color:rgba(245,245,247,.86);text-shadow:0 1px 24px rgba(0,0,0,.5)}

body:not(.story) .fineprint{margin:22px 0 0;font-size:.82rem;color:rgba(245,245,247,.62)}
body:not(.story) .afternote{margin:56px auto 0;max-width:46ch;font-size:1.02rem;color:var(--ink-2)}
body:not(.story) .afternote b{color:var(--ink)}
body:not(.story) .afternote--dark{color:var(--dark-ink-2)}
body:not(.story) .afternote--dark b{color:var(--dark-ink)}

/* ── buttons ────────────────────────────────────────────────────── */
body:not(.story) .btn{
  display:inline-block;
  border-radius:980px;
  padding:13px 26px;
  font-size:1.02rem;
  line-height:1.2;
  font-weight:400;
  border:1px solid transparent;
  transition:background-color .18s ease,border-color .18s ease,transform .18s ease;
}
body:not(.story) .btn:hover{text-decoration:none}
body:not(.story) .btn--blue{background:var(--blue);color:#fff}
body:not(.story) .btn--blue:hover{background:var(--blue-hi)}
body:not(.story) .btn--ghost{
  background:rgba(255,255,255,.14);
  color:#fff;
  border-color:rgba(255,255,255,.32);
  backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);
}
body:not(.story) .btn--ghost:hover{background:rgba(255,255,255,.24)}
body:not(.story) .btn--plain{background:transparent;color:var(--blue);border-color:var(--line)}
body:not(.story) .btn--plain:hover{background:#fff}
body:not(.story) .btn--sm{padding:7px 16px;font-size:.86rem}
body:not(.story) .cta{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-top:34px}

/* ── sticky nav ─────────────────────────────────────────────────── */
body:not(.story) .nav{
  position:sticky;top:0;z-index:100;
  background:rgba(0,0,0,.72);
  backdrop-filter:saturate(180%) blur(20px);
  -webkit-backdrop-filter:saturate(180%) blur(20px);
  border-bottom:1px solid rgba(255,255,255,.08);
}
body:not(.story) .nav__in{
  max-width:1120px;margin-inline:auto;padding:0 var(--pad);
  height:52px;display:flex;align-items:center;gap:26px;
}
body:not(.story) .nav__mark{color:#f5f5f7;font-weight:600;letter-spacing:-.01em;font-size:1.02rem}
body:not(.story) .nav__mark:hover{text-decoration:none}
body:not(.story) .nav__links{display:flex;gap:26px;margin-left:auto;font-size:.83rem}
body:not(.story) .nav__links a{color:rgba(245,245,247,.82)}
body:not(.story) .nav__links a:hover{color:#fff;text-decoration:none}
body:not(.story) .nav .btn{margin-left:26px}
@media (max-width:860px){
  body:not(.story) .nav__links{display:none}
  body:not(.story) .nav .btn{margin-left:auto}
}

/* ── hero ───────────────────────────────────────────────────────── */
body:not(.story) .hero{
  position:relative;
  min-height:min(88vh,860px);
  display:flex;align-items:center;justify-content:center;
  overflow:hidden;
  background:#08080a;
  text-align:center;
}
body:not(.story) .hero__media{position:absolute;inset:0}
/* The still is a CSS background on the layer, NOT the video's poster
   attribute. A <video> whose sources fail — which is every visit until
   the films are shot — stops painting its poster once play() has been
   called, and browsers disagree about exactly when. The background is
   immune to all of that; the video simply paints over it once it has
   real frames to show. */
body:not(.story) .hero__layer{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;border:0;
  background-position:center;background-size:cover;background-repeat:no-repeat;
  opacity:0;transition:opacity .85s ease;
}
body:not(.story) .hero__layer--imac{background-image:url("assets/hero-poster-imac.svg")}
body:not(.story) .hero__layer--macbooks{background-image:url("assets/hero-poster-macbooks.svg")}
body:not(.story) .hero__layer.is-on{opacity:1}
@media (prefers-reduced-motion:reduce){body:not(.story) .hero__layer{transition:none}}
body:not(.story) .hero__scrim{
  position:absolute;inset:0;
  background:
    radial-gradient(120% 80% at 50% 42%,rgba(0,0,0,.12) 0%,rgba(0,0,0,.62) 70%,rgba(0,0,0,.86) 100%),
    linear-gradient(180deg,rgba(0,0,0,.55) 0%,rgba(0,0,0,0) 28%);
}
body:not(.story) .hero__body{position:relative;padding:120px var(--pad) 130px;max-width:900px;color:#fff}
body:not(.story) .hero h1{color:#fff;text-shadow:0 2px 40px rgba(0,0,0,.45)}
/* Rotating headline. All spans share one grid cell, so the box is always
   as tall as the LONGEST variant — the typing never shifts the page. */
body:not(.story) .rotator{display:grid}
body:not(.story) .rotator>span{grid-area:1/1}
body:not(.story) .rotator__ghost{visibility:hidden;pointer-events:none}
body:not(.story) .rotator__live::after{
  content:"";display:inline-block;width:3px;height:.78em;
  margin-left:.07em;vertical-align:-.04em;border-radius:1px;
  background:var(--glass);animation:caret 1.05s steps(1) infinite;
}
@keyframes caret{0%,50%{opacity:1}51%,100%{opacity:0}}
/* Not typing means no caret to blink. */
body:not(.story) .rotator.is-static .rotator__live::after{display:none}
@media (prefers-reduced-motion:reduce){
  body:not(.story) .rotator__live::after{display:none}
}

body:not(.story) .hero__hint{position:absolute;bottom:26px;left:50%;transform:translateX(-50%)}
body:not(.story) .hero__hint span{
  display:block;width:20px;height:32px;border:1.5px solid rgba(255,255,255,.4);
  border-radius:12px;position:relative;
}
body:not(.story) .hero__hint span::after{
  content:"";position:absolute;left:50%;top:7px;width:3px;height:6px;
  margin-left:-1.5px;border-radius:2px;background:rgba(255,255,255,.75);
  animation:hint 1.9s ease-in-out infinite;
}
@keyframes hint{0%,100%{transform:translateY(0);opacity:1}60%{transform:translateY(9px);opacity:.2}}
@media (prefers-reduced-motion:reduce){body:not(.story) .hero__hint span::after{animation:none}}

/* ── stats ──────────────────────────────────────────────────────── */
body:not(.story) .stats{
  list-style:none;margin:64px 0 0;padding:0;
  display:grid;gap:36px 24px;
  grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
}
body:not(.story) .stats li{display:flex;flex-direction:column;gap:10px}
body:not(.story) .stats b{
  font-size:clamp(2.1rem,3.6vw,2.9rem);font-weight:600;
  letter-spacing:-.03em;line-height:1;
  background:linear-gradient(180deg,#fff 30%,#9fd8f5 130%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
body:not(.story) .stats .u{font-size:.5em;letter-spacing:-.01em}
body:not(.story) .stats span{font-size:.86rem;line-height:1.45;color:var(--dark-ink-2)}
/* Sitting under the steps rather than under a headline, the row needs a
   rule to read as a separate statement instead of a fifth step. */
body:not(.story) .stats--tight{
  margin-top:64px;padding-top:52px;
  border-top:1px solid var(--dark-line);
}

/* ── film ───────────────────────────────────────────────────────── */
body:not(.story) .film{margin:52px 0 0}
body:not(.story) .film__btn{
  display:block;position:relative;width:100%;padding:0;border:0;cursor:pointer;
  background:#000;border-radius:22px;overflow:hidden;
  box-shadow:0 24px 60px -24px rgba(0,0,0,.45);
  transition:transform .25s ease;
}
body:not(.story) .film__btn:hover{transform:translateY(-3px)}
body:not(.story) .film__btn[disabled]{cursor:default}
body:not(.story) .film__btn[disabled]:hover{transform:none}
body:not(.story) .film__btn img{width:100%;height:auto;opacity:.92}
body:not(.story) .film__play{
  position:absolute;left:50%;top:50%;width:78px;height:78px;margin:-39px 0 0 -39px;
  border-radius:50%;background:rgba(255,255,255,.92);
  box-shadow:0 8px 30px rgba(0,0,0,.35);
}
body:not(.story) .film__play::after{
  content:"";position:absolute;left:31px;top:25px;
  border-left:24px solid #111;border-top:14px solid transparent;
  border-bottom:14px solid transparent;
}
body:not(.story) .film__label{
  position:absolute;left:50%;bottom:26px;transform:translateX(-50%);
  font-size:.82rem;color:rgba(255,255,255,.85);letter-spacing:.02em;
}
body:not(.story) .film iframe{display:block;width:100%;aspect-ratio:16/9;border:0;border-radius:22px}

/* ── the four setup steps ───────────────────────────────────────── */
body:not(.story) .flow{
  list-style:none;margin:66px 0 0;padding:0;text-align:left;
  display:grid;gap:38px 26px;
  grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
}
body:not(.story) .flow li{position:relative;display:flex;flex-direction:column;gap:13px}
body:not(.story) .flow__n{
  display:grid;place-items:center;width:36px;height:36px;border-radius:50%;
  background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.2);
  color:#fff;font-size:.92rem;font-weight:600;
}
body:not(.story) .flow h3{color:var(--dark-ink);font-size:1.12rem}
body:not(.story) .flow p{color:var(--dark-ink-2);font-size:.96rem;line-height:1.5}

/* Between one row and one column, auto-fit lands on 3 + 1, which reads as
   a mistake. Four items want 2x2. */
@media (min-width:600px) and (max-width:939px){
  body:not(.story) .flow,body:not(.story) .stats--tight{grid-template-columns:repeat(2,1fr)}
}

/* The connector only makes sense while all four sit on one row; once the
   grid wraps it would point at nothing, so it appears only above the width
   where four columns still fit. */
@media (min-width:940px){
  body:not(.story) .flow li:not(:last-child)::after{
    content:"";position:absolute;left:48px;right:-26px;top:18px;height:1px;
    background:linear-gradient(90deg,rgba(90,200,250,.5),rgba(255,255,255,.07));
  }
}

/* ── cards ──────────────────────────────────────────────────────── */
body:not(.story) .cards{display:grid;gap:20px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
body:not(.story) .card{
  background:#fff;border:1px solid var(--line);border-radius:20px;
  padding:30px 28px;
}
body:not(.story) .card p{margin-top:10px;color:var(--ink-2);font-size:.97rem}

/* ── requirements table ─────────────────────────────────────────── */
body:not(.story) .req{
  margin:60px auto 0;max-width:820px;text-align:left;
  border:1px solid var(--dark-line);border-radius:20px;overflow:hidden;
  background:var(--dark-2);
}
body:not(.story) .req__row{
  display:grid;grid-template-columns:1.1fr 1.3fr 1.3fr;gap:18px;
  padding:16px 24px;border-top:1px solid var(--dark-line);
  font-size:.95rem;
}
body:not(.story) .req__row:first-child{border-top:0}
body:not(.story) .req__row span:first-child{color:var(--dark-ink-2)}
body:not(.story) .req__row--head{
  font-size:.76rem;text-transform:uppercase;letter-spacing:.1em;
  font-weight:600;color:var(--dark-ink-2);background:rgba(255,255,255,.03);
}
body:not(.story) .req__row--head span{color:var(--dark-ink-2)!important}
body:not(.story) .req__span{grid-column:2 / -1}
@media (max-width:700px){
  body:not(.story) .req__row{grid-template-columns:1fr;gap:4px}
  body:not(.story) .req__row--head{display:none}
  body:not(.story) .req__row span:first-child{
    font-size:.74rem;text-transform:uppercase;letter-spacing:.1em;margin-bottom:4px;
  }
  body:not(.story) .req__span{grid-column:auto}
}

body:not(.story) .honest{margin:56px auto 0;max-width:640px;text-align:left}
body:not(.story) .honest h3{color:var(--dark-ink);margin-bottom:18px;text-align:center}
body:not(.story) .honest ul{list-style:none;margin:0;padding:0;display:grid;gap:14px}
body:not(.story) .honest li{
  padding-left:20px;position:relative;color:var(--dark-ink-2);font-size:.98rem;
}
body:not(.story) .honest li::before{
  content:"";position:absolute;left:0;top:.62em;width:7px;height:1.5px;background:var(--glass);
}
body:not(.story) .honest b{color:var(--dark-ink);font-weight:600}

/* ── license ────────────────────────────────────────────────────────
   Deliberately quiet: no card, no shadow, no oversized number. The page
   sells the picture; the price is a fact you find when you look for it. */
body:not(.story) .terms{
  list-style:none;margin:38px auto 0;padding:0;max-width:430px;
  display:grid;gap:12px;text-align:left;
}
body:not(.story) .terms li{padding-left:26px;position:relative;font-size:.97rem;color:var(--ink-2)}
body:not(.story) .terms b{color:var(--ink);font-weight:600}
body:not(.story) .terms li::before{
  content:"";position:absolute;left:2px;top:.34em;width:6px;height:11px;
  border:solid var(--ink-2);border-width:0 1.6px 1.6px 0;transform:rotate(43deg);
}
body:not(.story) .quietbuy{margin-top:36px;font-size:.97rem}

/* ── faq ────────────────────────────────────────────────────────── */
body:not(.story) .faq{margin-top:50px;border-top:1px solid var(--line)}
body:not(.story) .faq details{border-bottom:1px solid var(--line)}
body:not(.story) .faq summary{
  list-style:none;cursor:pointer;padding:20px 34px 20px 0;position:relative;
  font-size:1.05rem;font-weight:500;letter-spacing:-.01em;
}
body:not(.story) .faq summary::-webkit-details-marker{display:none}
body:not(.story) .faq summary::after{
  content:"";position:absolute;right:6px;top:26px;width:9px;height:9px;
  border:solid var(--ink-2);border-width:0 1.6px 1.6px 0;
  transform:rotate(45deg);transition:transform .2s ease;
}
body:not(.story) .faq details[open] summary::after{transform:rotate(-135deg);top:30px}
body:not(.story) .faq p{padding:0 40px 22px 0;color:var(--ink-2);font-size:.99rem}

/* ── app screenshot ─────────────────────────────────────────────── */
body:not(.story) .shot{margin:56px auto 0;max-width:900px}
body:not(.story) .shot img{
  width:100%;height:auto;border-radius:16px;
  border:1px solid var(--line);
  box-shadow:0 30px 70px -34px rgba(0,0,0,.55);
}
body:not(.story) .shot figcaption{
  margin:22px auto 0;max-width:52ch;
  font-size:.88rem;line-height:1.55;color:var(--ink-2);
}

/* ── privacy ────────────────────────────────────────────────────── */
/* Sits after the headline at cap height. Slightly lighter than the words
   so it reads as punctuation on the sentence, not as a badge. */
body:not(.story) .lock{
  width:.66em;height:.66em;display:inline-block;
  margin-left:.22em;vertical-align:.01em;opacity:.55;
}

/* ── prose pages (legal + the SEO subpages added 2026-09-06) ─────
   One class carries both. The legal pages are three short sections; the
   subpages are long articles, so this block also has to hold lists, a
   comparison table and keyboard keys without a second stylesheet. */
body:not(.story) .prose h1{font-size:clamp(2.2rem,4.6vw,3rem);margin-bottom:28px}
body:not(.story) .prose h2{font-size:1.42rem;letter-spacing:-.014em;margin:46px 0 10px}
body:not(.story) .prose h3{font-size:1.06rem;letter-spacing:-.008em;margin:30px 0 8px}
body:not(.story) .prose p{color:var(--ink-2);line-height:1.62;max-width:62ch}
body:not(.story) .prose p+p{margin-top:14px}
body:not(.story) .prose b{color:var(--ink);font-weight:600}
/* The one paragraph that sits directly under the h1 and sets up the page. */
body:not(.story) .prose .lede{font-size:1.1rem;max-width:56ch}
body:not(.story) .prose ul,body:not(.story) .prose ol{
  margin:14px 0 0;padding-left:22px;max-width:62ch;
  color:var(--ink-2);line-height:1.62;
}
body:not(.story) .prose li{margin:9px 0}
body:not(.story) .prose kbd{
  font:inherit;font-size:.86em;padding:1px 6px;color:var(--ink);
  background:var(--bg-soft);border:1px solid var(--line);
  border-bottom-width:2px;border-radius:5px;
}
body:not(.story) .prose .draft{
  margin:0 0 8px;padding:14px 18px;border-radius:12px;
  background:#fff6e0;border:1px solid #f0dfae;color:#7a5b10;font-size:.9rem;
}
body:not(.story) .prose .checked{margin-top:14px;font-size:.82rem}
body:not(.story) .prose .back{margin-top:48px}

/* Comparison table. Seven rows of four columns cannot shrink to a phone
   without becoming unreadable, so it scrolls inside its own box rather
   than pushing the whole document sideways. */
body:not(.story) .tablewrap{
  margin:26px 0 0;overflow-x:auto;-webkit-overflow-scrolling:touch;
  border:1px solid var(--line);border-radius:14px;
}
body:not(.story) .cmp{border-collapse:collapse;width:100%;min-width:660px;font-size:.93rem}
body:not(.story) .cmp th,body:not(.story) .cmp td{
  text-align:left;padding:13px 16px;vertical-align:top;
  border-top:1px solid var(--line);color:var(--ink-2);line-height:1.5;
}
body:not(.story) .cmp thead th{
  border-top:0;background:var(--bg-soft);color:var(--ink);
  font-size:.74rem;text-transform:uppercase;letter-spacing:.09em;font-weight:600;
}
body:not(.story) .cmp tbody th{color:var(--ink);font-weight:600}
body:not(.story) .cmp tbody tr:last-child td,body:not(.story) .cmp tbody tr:last-child th{background:#f4f9ff}

/* ── language switcher ──────────────────────────────────────────────
   A thin bar above the footer rather than a control in the nav: it is
   the least interesting thing on the page to someone already reading in
   their own language, and it must not compete with Download. Added
   2026-09-06 with the de/fr/es translations. */
body:not(.story) .langbar{
  display:flex;gap:18px;flex-wrap:wrap;justify-content:center;
  padding:20px var(--pad);border-top:1px solid var(--line);
  font-size:.82rem;
}
body:not(.story) .langbar a{color:var(--ink-2)}
body:not(.story) .langbar a[aria-current]{color:var(--ink);font-weight:600;text-decoration:none;cursor:default}

/* ── closer + footer ────────────────────────────────────────────── */
body:not(.story) .closer h2{margin-bottom:4px}
body:not(.story) .foot{background:var(--bg-soft);padding:44px 0 56px;font-size:.82rem;color:var(--ink-2)}
body:not(.story) .foot__mark{font-weight:600;color:var(--ink)}
body:not(.story) .foot__links{display:flex;gap:20px;flex-wrap:wrap;margin:14px 0 18px}
body:not(.story) .foot__links a{color:var(--ink-2)}
body:not(.story) .foot__note{max-width:60ch;line-height:1.6}


/* ── setup video on /setup: a local poster with a play button; film.js
   replaces the button with the youtube-nocookie player on click ── */
body:not(.story) .film{
  position:relative;width:100%;aspect-ratio:16/9;
  margin:34px 0 0;border-radius:14px;overflow:hidden;background:#000;
  box-shadow:0 14px 44px rgba(0,0,0,.22), 0 0 0 1px rgba(0,0,0,.06)}
body:not(.story) .film__btn{
  display:block;width:100%;height:100%;padding:0;border:0;margin:0;
  background:none;color:inherit;cursor:pointer;font:inherit}
body:not(.story) .film__btn img{display:block;width:100%;height:100%;object-fit:cover}
body:not(.story) .film__play{
  position:absolute;left:50%;top:50%;width:80px;height:80px;
  transform:translate(-50%,-50%);border-radius:50%;
  background:rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.35);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  transition:background .2s,transform .2s}
body:not(.story) .film__play::after{
  content:"";position:absolute;left:56%;top:50%;transform:translate(-50%,-50%);
  border-style:solid;border-width:14px 0 14px 24px;
  border-color:transparent transparent transparent #fff}
body:not(.story) .film__btn:hover .film__play{background:var(--blue);transform:translate(-50%,-50%) scale(1.06)}
body:not(.story) .film__btn:focus-visible{outline:3px solid var(--blue);outline-offset:-3px}
body:not(.story) .film__label{
  position:absolute;left:50%;bottom:16px;transform:translateX(-50%);
  padding:6px 14px;border-radius:99px;background:rgba(0,0,0,.6);
  color:#fff;font-size:.9rem;font-weight:600;white-space:nowrap}
body:not(.story) .film iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
body:not(.story) .prose .film__note{margin-top:12px;font-size:.9rem;color:var(--ink-2)}
body:not(.story) .prose .film__note a{color:var(--ink-2);text-decoration:underline}

/* ── guides (generated pages, Web/scripts/build-guides.py) ──────────── */
body:not(.story) .crumbs{margin:0 0 22px;font-size:.84rem;color:var(--ink-2)}
body:not(.story) .crumbs ol{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:6px}
body:not(.story) .crumbs li+li::before{content:"›";margin-right:6px;color:var(--line)}
body:not(.story) .crumbs a{color:var(--ink-2)}
body:not(.story) .guide-meta{margin:-14px 0 34px;font-size:.84rem;color:var(--ink-2)}
body:not(.story) .callout{margin:28px 0;padding:18px 22px;border-radius:14px;
  background:var(--bg-soft);border:1px solid var(--line);max-width:62ch}
body:not(.story) .callout p{margin:0}
body:not(.story) .callout p+p{margin-top:10px}
body:not(.story) .callout--honest{border-color:rgba(90,200,250,.5)}
body:not(.story) .market{margin:28px 0}
body:not(.story) .market table{width:100%;border-collapse:collapse;font-size:.95rem}
body:not(.story) .market th,body:not(.story) .market td{text-align:left;padding:10px 12px;border-top:1px solid var(--line);vertical-align:top}
body:not(.story) .market th{font-size:.76rem;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-2);border-top:0}
body:not(.story) .market__checked{margin:8px 0 0;font-size:.8rem;color:var(--ink-2)}
body:not(.story) .related{margin-top:56px;padding-top:30px;border-top:1px solid var(--line)}
body:not(.story) .related h2{margin-top:0}
body:not(.story) .related .cards{margin-top:18px}
body:not(.story) .related .card{display:block;padding:18px 20px;border:1px solid var(--line);border-radius:14px;color:inherit}
body:not(.story) .related .card:hover{text-decoration:none;border-color:var(--ink-2)}
body:not(.story) .related .card b{display:block;margin-bottom:6px}
body:not(.story) .related .card span{font-size:.92rem;color:var(--ink-2)}
body:not(.story) .guide-faq{margin-top:56px}
/* On-this-page outline (guides): a second grid column left of the
   article from 1180px up; hidden below that. Dots on a vertical line,
   current section bold + blue — guides.js toggles .is-current. */
body:not(.story) .guide-layout{display:grid;grid-template-columns:minmax(0,760px);justify-content:center;column-gap:48px}
body:not(.story) .guide-layout>.wrap{margin-inline:0;width:100%}
body:not(.story) .toc{display:none;position:sticky;top:84px;align-self:start;max-height:calc(100vh - 40px);overflow:auto;font-size:.92rem;padding-left:var(--pad)}  /* guides.js re-sets top + margin-top so the list floats at the vertical middle of the viewport */
@media (min-width:1240px){
  body:not(.story) .guide-layout{grid-template-columns:330px minmax(0,760px)}
  body:not(.story) .toc{display:block}
}
body:not(.story) .toc__title{margin:0 0 14px;font-size:.7rem;letter-spacing:.11em;text-transform:uppercase;color:var(--ink-2);font-weight:600}
body:not(.story) .toc ol{list-style:none;margin:0;padding:0;border-left:1px solid var(--line)}
body:not(.story) .toc li{position:relative;margin:0;padding:6px 0 6px 20px}
body:not(.story) .toc li::before{content:"";position:absolute;left:-5px;top:13px;width:9px;height:9px;border-radius:50%;background:#fff;border:2px solid var(--line);box-sizing:border-box;transition:background .15s,border-color .15s}
body:not(.story) .toc li.is-current::before{background:var(--blue);border-color:var(--blue)}
body:not(.story) .toc a{display:block;color:var(--ink-2);text-decoration:none;line-height:1.35;transition:color .15s}
body:not(.story) .toc a:hover{color:var(--ink);text-decoration:none}
body:not(.story) .toc a.is-current{color:var(--blue);font-weight:600}
body:not(.story) .toc li.toc__h3{padding-left:34px;font-size:.86rem}
body:not(.story) .toc li.toc__h3::before{width:7px;height:7px;left:-4px;top:14px}
body:not(.story) .ico{width:1.5em;height:1.5em;vertical-align:-.32em;margin-right:.45em;fill:currentColor;color:var(--blue)}
body:not(.story) .cmp--rows tbody tr:last-child td,body:not(.story) .cmp--rows tbody tr:last-child th{background:none}
body:not(.story) .cmp--rows tbody th{width:11em}
/* Comparison cells marked better/worse (guides, user request 2026-10-11):
   light green = the better option in that row, light red = the worse.
   Selector weight matches the last-row reset above, so it must stay
   below it. */
body:not(.story) .cmp tbody tr td.better{background:#e7f6ea}
body:not(.story) .cmp tbody tr td.worse{background:#fdeaea}
body:not(.story) .prose .cmp-key{margin:10px 0 0;font-size:.88rem;color:var(--ink-2)}
body:not(.story) .cmp-key .sw{display:inline-block;width:.9em;height:.9em;border-radius:3px;vertical-align:-.1em;margin-right:.35em}
body:not(.story) .cmp-key .sw--better{background:#cdeed4}
body:not(.story) .cmp-key .sw--worse{background:#f8d2d2}
body:not(.story) .prose .shot{margin:34px 0 30px;max-width:none}
body:not(.story) .prose .lead{margin:0 0 8px;max-width:62ch;text-align:left}
/* Smaller screenshots (a macOS dialog, one settings pane) shown near their
   native size instead of stretched to the column — guides, 2026-10-11. */
body:not(.story) .prose .shot--narrow{max-width:420px;margin-left:auto;margin-right:auto}
body:not(.story) .prose .shot--medium{max-width:560px;margin-left:auto;margin-right:auto}
body:not(.story) .prose [id]{scroll-margin-top:76px}  /* sticky nav is 53px; in-page links must not land under it */
body:not(.story) .prose figure.shot img{box-shadow:0 18px 40px -24px rgba(0,0,0,.45)}
body:not(.story) .prose figure.shot video{display:block;width:100%;height:auto;border-radius:16px;border:1px solid var(--line);box-shadow:0 18px 40px -24px rgba(0,0,0,.45);background:#000}
body:not(.story) .guide-index .card{display:block;padding:20px 22px;border:1px solid var(--line);border-radius:14px;color:inherit}
body:not(.story) .guide-index .card:hover{text-decoration:none;border-color:var(--ink-2)}
body:not(.story) .guide-index .card b{display:block;margin-bottom:6px;font-size:1.05rem}
body:not(.story) .guide-index .card span{font-size:.92rem;color:var(--ink-2)}

/* ═══ HOMEPAGE (scroll story) ═══ */
body.story{
  --ink:#f5f5f7;
  --ink-2:#a1a1a6;
  --line:#2a2a2c;
  --bg:#000000;
  --bg-2:#0b0b0d;
  --blue:#0071e3;
  --blue-hi:#0077ed;
  --glass:#5ac8fa;
  --green:#30d158;
  --pad:24px;
  --sans:-apple-system,BlinkMacSystemFont,"SF Pro Display","SF Pro Text",
         "Helvetica Neue",Helvetica,Arial,sans-serif;
}
body.story{
  margin:0;
  font-family:var(--sans);
  font-size:17px;
  line-height:1.5;
  color:var(--ink);
  background:var(--bg);
  -webkit-font-smoothing:antialiased;
}
body.story img,body.story svg,body.story canvas{display:block;max-width:100%}
body.story a{color:var(--glass);text-decoration:none}
body.story a:hover{text-decoration:underline}
body.story b{font-weight:600}

body.story .skip{position:absolute;left:-9999px;top:0;background:#fff;color:#000;
  padding:12px 18px;z-index:200}
body.story .skip:focus{left:8px;top:8px}

/* ── type ───────────────────────────────────────────────────────── */
body.story h1,body.story h2{margin:0;font-weight:600;letter-spacing:-.025em;line-height:1.05}
body.story h1{font-size:clamp(2.6rem,8.4vw,5rem);letter-spacing:-.032em}
body.story h2{font-size:clamp(2rem,6.4vw,3.6rem)}
@media (max-width:620px){body.story h1 br,body.story h2 br{display:none}}
body.story p{margin:0}

body.story .eyebrow{
  margin:0 0 16px;
  font-size:.75rem;
  font-weight:600;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--glass);
}
body.story .lead{
  margin:24px auto 0;
  max-width:44ch;
  font-size:clamp(1.05rem,1.4vw,1.28rem);
  color:var(--ink-2);
}
body.story .fineprint{margin-top:22px;font-size:.85rem;color:var(--ink-2)}

/* ── nav ────────────────────────────────────────────────────────── */
body.story .nav{position:fixed;inset:0 0 auto;z-index:100;
  background:rgba(0,0,0,.55);
  -webkit-backdrop-filter:saturate(180%) blur(18px);
  backdrop-filter:saturate(180%) blur(18px);
  border-bottom:1px solid rgba(255,255,255,.08)}
body.story .nav__in{max-width:1080px;margin-inline:auto;padding:10px var(--pad);
  display:flex;align-items:center;justify-content:space-between}
body.story .nav__mark{font-weight:600;color:var(--ink);letter-spacing:-.01em}
body.story .nav__mark:hover{text-decoration:none}
body.story .nav__tag{
  margin-left:8px;padding:2px 8px;border-radius:99px;
  font-size:.68rem;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;vertical-align:2px;
  color:var(--glass);border:1px solid rgba(90,200,250,.45)}
/* Page links, added 2026-09-06 with the SEO subpages. The nav markup is
   shared byte-for-byte with the prose pages (check-site.py enforces it),
   so the same classes have to exist in both stylesheets — this one renders
   it on black, style.css renders it on white. */
body.story .nav__links{display:flex;gap:24px;margin-left:auto;font-size:.83rem}
body.story .nav__links a{color:var(--ink-2)}
body.story .nav__links a:hover{color:var(--ink);text-decoration:none}
body.story .nav .btn{margin-left:24px}
@media (max-width:860px){
  body.story .nav__links{display:none}
  body.story .nav .btn{margin-left:auto}
}

/* ── buttons ────────────────────────────────────────────────────── */
body.story .btn{display:inline-block;padding:12px 26px;border-radius:99px;
  font-weight:500;font-size:1.02rem;border:1px solid transparent}
body.story .btn:hover{text-decoration:none}
body.story .btn--sm{padding:6px 16px;font-size:.9rem}
body.story .btn--blue{background:var(--blue);color:#fff}
body.story .btn--blue:hover{background:var(--blue-hi)}
body.story .btn--ghost{background:rgba(255,255,255,.1);color:var(--ink);
  border-color:rgba(255,255,255,.16)}
body.story .btn--ghost:hover{background:rgba(255,255,255,.2)}
body.story .cta{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-top:34px}

/* ── hero: live background video + overlaid text ────────────────────
   One 100svh section, no pin. The clip (a hand plugs the cable, the
   iMac comes alive) runs full-bleed behind the copy; the top of the
   frame is the iMac's bright alpine wallpaper, so a strong top scrim
   carries the white text, easing to nearly clear mid-frame where the
   action is, with a light foot for the scroll hint. The text block
   sits in the upper part of the viewport for the same reason. */
body.story .vhero{
  position:relative;height:84svh;overflow:hidden;  /* 80→84svh 2026-09-05, user: a hair taller */
  display:flex;flex-direction:column;align-items:center;
  justify-content:center;text-align:center;
  padding:52px var(--pad) 40px}
/* the brightness filter is not only styling: while a video plays,
   browsers may promote it to a hardware overlay that composites ABOVE
   sibling layers — the scrim then only kicks in when the window loses
   focus and the overlay is demoted (observed 2026-09-05). Any filter
   forces the video back into the normal compositing path, so the
   scrim reliably applies AND the clip itself is pre-darkened. */
body.story .vhero__video{
  position:absolute;inset:0;width:100%;height:100%;z-index:0;
  margin-inline:auto;
  /* bottom-anchored cover crop (user call 2026-09-05): when the
     squarish clip is taller than the hero, the excess is cut from
     the top — the cable being plugged in at the bottom edge must
     stay in frame on wide viewports. This is only the STARTING
     frame: story.js pans object-position up with playback (heroPan)
     so the clip ends framed on the iMac. */
  object-fit:cover;object-position:50% 100%;
  filter:brightness(.72);
  will-change:transform}
/* very wide screens (16:9 monitors and wider): cap the clip's width so
   the cover crop cannot over-zoom — uncapped, a wide screen showed only
   the iMac's screen and nothing of the MacBook (user report
   2026-09-05). 118svh (105 → 118 same evening, user: a bit wider)
   keeps ~3/4 of the machines band in frame at any moment; the sides
   letterbox into the page's black. */
@media (min-aspect-ratio:16/9){
  body.story .vhero__video{width:118svh}
}
/* deliberately heavy darkening (user call 2026-09-05: with a light
   scrim the text lost against the clip) — the video reads as a moody
   backdrop, the copy carries the section */
body.story .vhero__scrim{
  position:absolute;inset:0;z-index:1;transform:translateZ(0);
  background:linear-gradient(180deg,
    rgba(0,0,0,.72) 0%, rgba(0,0,0,.5) 22%,
    rgba(0,0,0,.36) 46%, rgba(0,0,0,.28) 72%, rgba(0,0,0,.45) 100%)}
body.story .vhero__body{
  position:relative;z-index:2;max-width:900px;
  text-shadow:0 1px 22px rgba(0,0,0,.85)}
body.story .vhero .hero__hint{z-index:2}

/* rotating headline — ported from the live site's style.css: all
   spans share one grid cell, so the box is always as tall as the
   LONGEST variant and the typing never shifts the layout below.
   The caret is the live span's ::after; no typing, no caret. */
body.story .rotator{display:grid}
body.story .rotator>span{grid-area:1/1}
body.story .rotator__ghost{visibility:hidden;pointer-events:none}
body.story .rotator__live::after{
  content:"";display:inline-block;width:3px;height:.78em;
  margin-left:.07em;vertical-align:-.04em;border-radius:1px;
  background:var(--glass);animation:caret 1.05s steps(1) infinite}

body.story .rotator.is-static .rotator__live::after{display:none}
@media (prefers-reduced-motion:reduce){body.story .rotator__live::after{display:none}}
/* soft local vignette so the copy stays readable where the clip is
   busy (the MacBook screen sits mid-frame on wide viewports) */
body.story .vhero__body::before{
  content:"";position:absolute;inset:-18% -22%;z-index:-1;
  background:radial-gradient(closest-side,
    rgba(0,0,0,.55), rgba(0,0,0,.28) 55%, transparent 100%)}
body.story .vhero__body .lead{color:rgba(245,245,247,.88)}
body.story .hero__hint{position:absolute;left:50%;bottom:26px;translate:-50% 0;
  width:26px;height:42px;border:1.5px solid rgba(255,255,255,.35);
  border-radius:14px}
body.story .hero__hint span{position:absolute;left:50%;top:8px;translate:-50% 0;
  width:3px;height:8px;border-radius:2px;background:rgba(255,255,255,.65);
  animation:story-hint 1.8s ease-in-out infinite}
@keyframes story-hint{
  0%,20%{transform:translateY(0);opacity:1}
  70%{transform:translateY(12px);opacity:0}
  100%{transform:translateY(0);opacity:0}}

/* ── scenes: pin + progress plumbing ────────────────────────────── */
body.story .scene{--p:1; position:relative}
/* pixels shortened 360→240svh (user call 2026-09-05: reaching the
   ×60 punch took too much scrolling) */
body.story .scene--pixels{height:240svh}
/* latency 300→235svh (user call 2026-09-05: scrubbing through took
   too much scroll — the runway is height minus one viewport, so
   200→135svh of scrub ≈ 50% faster) */
body.story .scene--latency{height:235svh}
/* (scene--size is gone 2026-09-05: the footprint became a static band
   right under Setup — see index.html) */

/* pinned frame is 80svh, matching the hero (user call 2026-09-05) —
   progress still hits 1 while pinned, since the pin only releases
   once the wrapper's remaining height drops below the pin's own.
   top:10svh (user call, same day): with top:0 the frame hugged the
   viewport top and the spare 20svh sat as a black band at the
   bottom — sticking 10svh down splits the spare evenly, so the
   content rides the vertical center while you scrub the scene */
body.story .scene__pin{
  position:sticky;top:10svh;height:80svh;overflow:hidden;
  display:flex;flex-direction:column;align-items:center;
  justify-content:center;text-align:center;
  padding:84px var(--pad) 40px}
body.story .scene__title{max-width:16ch}
body.story .scene__title--tight{margin-bottom:8px}
body.story .eyebrow--fixed{margin-bottom:6px}

body.story .caption{
  max-width:46ch;margin:18px auto 0;
  font-size:clamp(1.02rem,1.35vw,1.22rem);
  color:var(--ink-2)}
body.story .caption b{color:var(--ink)}
body.story .caption--wide{max-width:52ch}

/* punchline paragraphs fade in near the end of each scene */
body.story [data-punch]{
  opacity:clamp(0, calc((var(--p) - .72) * 6), 1);
  transform:translateY(calc((1 - clamp(0, calc((var(--p) - .72) * 6), 1)) * 18px))}

body.story .caption .green{color:var(--green)}

/* ── setup: the four steps ──────────────────────────────────────────
   Ported from the live site's #how band (.flow), restyled for this
   page's black. A plain static band between the pinned scenes. */
/* section spacing tightened 2026-09-05 (user call: too much blank
   between sections — was clamp(88px,12vw,150px), doubled where two
   bands meet) */
body.story .band{padding-block:clamp(56px,8vw,96px);text-align:center}
body.story .band + .band{padding-top:clamp(20px,3vw,36px)}
body.story .band__wrap{max-width:1000px;margin-inline:auto;padding-inline:var(--pad)}
body.story .lead--band{max-width:52ch}

/* the real app window between the lead and the steps — widened
   640→840px 2026-09-05 (user call: the shot deserved more presence) */
body.story .flow__shot{
  display:block;width:min(840px,96%);height:auto;
  margin:56px auto 0;border-radius:12px;
  box-shadow:0 18px 60px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.08)}

body.story .flow{
  list-style:none;margin:66px 0 0;padding:0;text-align:left;
  display:grid;gap:38px 26px;
  grid-template-columns:repeat(auto-fit,minmax(210px,1fr))}
body.story .flow li{position:relative;display:flex;flex-direction:column;gap:13px}
body.story .flow__n{
  display:grid;place-items:center;width:36px;height:36px;border-radius:50%;
  background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.2);
  color:#fff;font-size:.92rem;font-weight:600}
body.story .flow h3{
  margin:0;color:var(--ink);
  font-size:1.12rem;font-weight:600;letter-spacing:-.012em;line-height:1.2}
body.story .flow p{color:var(--ink-2);font-size:.96rem;line-height:1.5}

/* between one row and one column, auto-fit lands on 3 + 1, which reads
   as a mistake — four items want 2x2 */
@media (min-width:600px) and (max-width:939px){
  body.story .flow{grid-template-columns:repeat(2,1fr)}}

/* the connector only makes sense while all four sit on one row */
@media (min-width:940px){
  body.story .flow li:not(:last-child)::after{
    content:"";position:absolute;left:48px;right:-26px;top:18px;height:1px;
    background:linear-gradient(90deg,rgba(90,200,250,.5),rgba(255,255,255,.07))}}

/* ── scene 2: pixels ────────────────────────────────────────────── */
body.story .pixels__grid{position:absolute;inset:0;width:100%;height:100%;opacity:.6}
body.story .pixels__body{position:relative}
body.story .pixels__count{margin-top:26px}
body.story .pixels__count b{
  display:block;
  font-size:clamp(2.6rem,9vw,6rem);
  font-weight:700;letter-spacing:-.03em;line-height:1;
  font-variant-numeric:tabular-nums;
  color:var(--ink);
  text-shadow:0 0 44px rgba(0,113,227,.55)}
body.story .pixels__count span{
  display:block;margin-top:10px;
  font-size:1rem;color:var(--ink-2);letter-spacing:.02em}
body.story .pixels__times{
  margin-top:18px;font-size:clamp(1.2rem,2.4vw,1.7rem);font-weight:600;
  color:var(--glass);
  opacity:clamp(0, calc((var(--p) - .48) * 7), 1);
  transform:translateY(calc((1 - clamp(0, calc((var(--p) - .48) * 7), 1)) * 14px))}

/* ── scene 3: latency ───────────────────────────────────────────── */
/* the wingbeat artwork: full-bleed behind the ruler, right-anchored.
   The canvas is drawn by story.js; the CSS background is the no-JS /
   reduced-motion fallback (one static frame, art is mostly black so
   it blends into the page). Fades in as the scene pins, and stays
   dimmed (backdrop, not subject — the text must win). */
body.story .wing{
  position:absolute;inset:0;pointer-events:none;
  background:#000 url(assets/wing/w05.jpg) 63% center / cover no-repeat;
  opacity:calc(clamp(0, calc(var(--p) * 5), 1) * .55)}
body.story .wing__canvas{position:absolute;inset:0;width:100%;height:100%}
body.story .wing::after{ /* keep the left text column readable on top of the art */
  content:"";position:absolute;inset:0;
  background:linear-gradient(90deg, rgba(0,0,0,.72) 0 30%, transparent 62%)}
@media (max-width:900px){body.story .wing{opacity:calc(clamp(0, calc(var(--p) * 5), 1) * .4)}}

body.story .latency__body{position:relative;width:min(720px,100%)}
body.story .latency__big{margin:18px 0 30px;line-height:1}
body.story .latency__big b{
  font-size:clamp(4rem,13vw,8.5rem);font-weight:700;letter-spacing:-.04em;
  font-variant-numeric:tabular-nums;
  text-shadow:0 0 44px rgba(0,113,227,.5)}
body.story .latency__big .u{
  margin-left:6px;font-size:clamp(1.4rem,3.4vw,2.2rem);
  font-weight:600;color:var(--ink-2)}

body.story .ruler{margin-top:8px;text-align:left}
body.story .ruler__row{margin-top:20px}
/* bar lengths + reveal indices live HERE, not in style="" attributes:
   the deployed CSP (style-src 'self', no unsafe-inline) nulls inline
   style attributes — exactly this broke the bars on the live site on
   2026-09-05. Keep this page free of style="" attributes. */
body.story .ruler__row:nth-child(1){--w:100%}
body.story .ruler__row:nth-child(2){--w:24.66%}
body.story .ruler__row:nth-child(3){--w:13.33%}
body.story .ruler__label{display:block;font-size:.92rem;color:var(--ink-2);margin-bottom:7px}
body.story .ruler__bar{position:relative;height:30px;display:flex;align-items:center}
body.story .ruler__bar i{
  display:block;height:100%;border-radius:7px;
  width:calc(var(--w) * clamp(0, calc((var(--p) - .3) * 3), 1));
  background:rgba(255,255,255,.16);
  transition:none}
body.story .ruler__bar b{margin-left:12px;font-size:.95rem;color:var(--ink-2);white-space:nowrap}
body.story .ruler__row--us .ruler__bar i{
  background:linear-gradient(90deg,var(--blue),var(--glass));
  box-shadow:0 0 22px rgba(0,113,227,.5)}
body.story .ruler__row--us .ruler__label,body.story .ruler__row--us .ruler__bar b{color:var(--ink);font-weight:600}
body.story .ruler__ticks{
  display:flex;justify-content:space-between;margin-top:16px;
  padding-top:10px;border-top:1px solid var(--line);
  font-size:.8rem;color:var(--ink-2);font-variant-numeric:tabular-nums}

/* ── requirements table + honest list ─────────────────────────────
   Ported verbatim from the live site's style.css (2026-09-05, user
   request) — only the tokens are mapped: dark-2→#161617 literal,
   dark-ink→--ink, dark-ink-2→--ink-2, dark-line→--line (identical
   values on both sheets). Keep in sync with public/style.css. */
body.story .req{
  margin:60px auto 0;max-width:820px;text-align:left;
  border:1px solid var(--line);border-radius:20px;overflow:hidden;
  background:#161617}
body.story .req__row{
  display:grid;grid-template-columns:1.1fr 1.3fr 1.3fr;gap:18px;
  padding:16px 24px;border-top:1px solid var(--line);
  font-size:.95rem}
body.story .req__row:first-child{border-top:0}
body.story .req__row span:first-child{color:var(--ink-2)}
body.story .req__row--head{
  font-size:.76rem;text-transform:uppercase;letter-spacing:.1em;
  font-weight:600;color:var(--ink-2);background:rgba(255,255,255,.03)}
body.story .req__row--head span{color:var(--ink-2)!important}
body.story .req__span{grid-column:2 / -1}
@media (max-width:700px){
  body.story .req__row{grid-template-columns:1fr;gap:4px}
  body.story .req__row--head{display:none}
  body.story .req__row span:first-child{
    font-size:.74rem;text-transform:uppercase;letter-spacing:.1em;
    margin-bottom:4px}
  body.story .req__span{grid-column:auto}}

body.story .honest{margin:56px auto 0;max-width:640px;text-align:left}
body.story .honest h3{color:var(--ink);margin-bottom:18px;text-align:center}
body.story .honest ul{list-style:none;margin:0;padding:0;display:grid;gap:14px}
body.story .honest li{
  padding-left:20px;position:relative;color:var(--ink-2);font-size:.98rem}
body.story .honest li::before{
  content:"";position:absolute;left:0;top:.62em;width:7px;height:1.5px;
  background:var(--glass)}
body.story .honest b{color:var(--ink);font-weight:600}

/* ── faq ─────────────────────────────────────────────────────────────
   Ported verbatim from the live site's style.css (2026-09-05, user
   request) — token names match this sheet, so the rules carry over
   unchanged; only band__wrap--narrow is new (the live site uses its
   wrap--narrow, 760px). Keep in sync with public/style.css. */
body.story .band__wrap--narrow{max-width:760px}
body.story .faq{margin-top:50px;border-top:1px solid var(--line);text-align:left}
body.story .faq details{border-bottom:1px solid var(--line)}
body.story .faq summary{
  list-style:none;cursor:pointer;padding:20px 34px 20px 0;position:relative;
  font-size:1.05rem;font-weight:500;letter-spacing:-.01em}
body.story .faq summary::-webkit-details-marker{display:none}
body.story .faq summary::after{
  content:"";position:absolute;right:6px;top:26px;width:9px;height:9px;
  border:solid var(--ink-2);border-width:0 1.6px 1.6px 0;
  transform:rotate(45deg);transition:transform .2s ease}
body.story .faq details[open] summary::after{transform:rotate(-135deg);top:30px}
body.story .faq p{padding:0 40px 22px 0;color:var(--ink-2);font-size:.99rem}

/* ── the footprint (static band since 2026-09-05, was scene 4) ──── */
body.story .size__body{width:min(680px,100%)}
/* inside the band there is no centering flex parent (the pinned
   scenes centered these blocks via .scene__pin) — center them here */
body.story .band .size__body{margin-inline:auto}
body.story .band .size__body .scene__title{margin-inline:auto}
body.story .size__big{
  color:var(--glass);
  text-shadow:0 0 44px rgba(90,200,250,.4)}
/* the download facts, revealing one after another as the scene
   scrolls: each line gets a stagger index --i and derives its own
   0→1 reveal --t from the scene's --p. With --p at its no-JS /
   reduced-motion default of 1, every --t is 1 — the list is just
   there. */
body.story .size__facts{
  list-style:none;margin:36px auto 0;padding:0;
  width:min(46ch,100%);text-align:left}
body.story .size__facts li:nth-child(1){--i:0}
body.story .size__facts li:nth-child(2){--i:1}
body.story .size__facts li:nth-child(3){--i:2}
body.story .size__facts li{
  --t:clamp(0, calc((var(--p) - .28 - var(--i) * .1) * 5), 1);
  position:relative;padding-left:30px;margin-top:16px;
  color:var(--ink-2);font-size:1rem;line-height:1.5;
  opacity:var(--t);
  transform:translateY(calc((1 - var(--t)) * 14px))}
body.story .size__facts li::before{
  content:"";position:absolute;left:2px;top:.4em;
  width:13px;height:7px;
  border-left:2.2px solid var(--glass);
  border-bottom:2.2px solid var(--glass);
  transform:rotate(-45deg)}

/* first CTA: button + the 6 MB note side by side (stacks on narrow
   viewports); rides the shared [data-punch] end-of-scene reveal */
body.story .size__cta{
  margin-top:44px;
  display:flex;align-items:center;justify-content:center;
  gap:12px 26px;flex-wrap:wrap}
body.story .size__ctanote{
  font-size:.92rem;line-height:1.45;color:var(--ink-2);text-align:left}
@media (max-width:520px){body.story .size__ctanote{text-align:center}}

/* ── impressions: card gallery slider ───────────────────────────────
   Apple-highlights-style carousel. The track is a plain scroll-snap
   scroller (works with JS off; scrollbar hidden), each card a big
   rounded image with the caption overlaid top-left on a soft scrim.
   Side padding of the track = the peek: the active card centers and
   the neighbours show at the edges. Controls (dot-pill + play/pause)
   are revealed by story.js. */
body.story .gallery{padding:8svh 0 6svh} /* was 14/10svh — tightened 2026-09-05 */
body.story .gallery__head{text-align:center;padding:0 var(--pad);margin-bottom:44px}
body.story .gallery__track{
  display:flex;gap:22px;
  overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;
  /* center the snapped card: side padding = half the leftover space;
     the svh term caps a card (3:2) at ~78svh tall, so the gallery
     stays inside ~80% of the viewport like the scenes */
  --card-w:min(1060px, 88vw, 117svh);
  padding:0 calc((100vw - var(--card-w)) / 2) 8px;
  scrollbar-width:none}
body.story .gallery__track::-webkit-scrollbar{display:none}
body.story .gallery__track:focus-visible{outline:2px solid var(--glass);outline-offset:4px}
body.story .gallery__card{
  position:relative;flex:0 0 var(--card-w);margin:0;
  scroll-snap-align:center;
  border-radius:28px;overflow:hidden;
  background:var(--bg-2);
  box-shadow:0 24px 70px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.06)}
body.story .gallery__card img{width:100%;aspect-ratio:3/2;height:auto;object-fit:cover}
body.story .gallery__cap{
  position:absolute;inset:0;padding:clamp(20px,3.4vw,44px);
  display:flex;flex-direction:column;gap:6px;align-items:flex-start;
  text-align:left;pointer-events:none;
  background:linear-gradient(160deg, rgba(0,0,0,.55) 0%, rgba(0,0,0,.18) 34%, transparent 55%)}
body.story .gallery__cap b{
  font-size:clamp(1.25rem,2.6vw,1.9rem);font-weight:600;
  letter-spacing:-.02em;line-height:1.15;max-width:18ch;
  text-shadow:0 1px 14px rgba(0,0,0,.55)}
body.story .gallery__cap span{
  font-size:clamp(.9rem,1.3vw,1.05rem);color:rgba(245,245,247,.85);
  max-width:34ch;text-shadow:0 1px 10px rgba(0,0,0,.6)}

/* caption choreography (JS-driven, so the no-JS page keeps every
   caption visible): --cap-o/--cap-x come from story.js and follow the
   card's real scroll offset — opacity (1-|off|)³ + a -0.45× horizontal
   lag, the same formula Apple's media-card gallery uses. The fade sits
   on the whole cap (scrim included: peeking cards show a clean image),
   the lag only on the text so the scrim never slides off the edge. */
body.story .gallery.has-js .gallery__card{cursor:pointer}
body.story .gallery.has-js .gallery__cap{opacity:var(--cap-o,1)}
body.story .gallery.has-js .gallery__cap>*{
  transform:translateX(var(--cap-x,0px));
  will-change:transform}

/* controls: dot-pill + round play/pause, centered under the track */
body.story .gallery__controls{
  display:flex;justify-content:center;align-items:center;gap:16px;
  margin-top:34px}
body.story .gallery__dots{
  display:flex;align-items:center;gap:8px;
  padding:0 14px;height:44px;border-radius:99px;
  background:rgba(255,255,255,.1)}
body.story .gallery__dot{
  width:8px;height:8px;border-radius:99px;border:0;padding:0;
  background:rgba(255,255,255,.4);cursor:pointer;
  transition:width .4s cubic-bezier(.4,0,.2,1), background .3s}
body.story .gallery__dot:hover{background:rgba(255,255,255,.65)}
body.story .gallery__dot.is-on{width:34px;background:var(--ink)}
body.story .gallery__play{
  display:grid;place-items:center;
  width:44px;height:44px;border-radius:50%;border:0;cursor:pointer;
  background:rgba(255,255,255,.1);color:var(--ink)}
body.story .gallery__play:hover{background:rgba(255,255,255,.2)}
body.story .gallery__play svg{width:20px;height:20px}
body.story .gallery__play .i-play{display:none}
body.story .gallery__play.is-paused .i-play{display:block}
body.story .gallery__play.is-paused .i-pause{display:none}

@media (prefers-reduced-motion:reduce){
  body.story .gallery__dot{transition:none}}

/* ── why: advantage card grid ───────────────────────────────────────
   Apple-shop-strip-style recap (our own implementation): left-aligned
   headline, then one rounded dark card per advantage. Pure CSS grid —
   4 / 2 / 1 columns. story.js flags .why--reveal and .is-in for the
   staggered fade-up; without that class the cards are simply visible
   (no-JS and reduced-motion path). */
body.story .why{padding:8svh var(--pad) 5svh} /* was 12/8svh — tightened 2026-09-05 */
body.story .why__in{max-width:1180px;margin-inline:auto}
body.story .why__head{text-align:left}
body.story .why__head h2{max-width:20ch}
body.story .why__grid{
  list-style:none;margin:44px 0 0;padding:0;
  display:grid;gap:18px;
  grid-template-columns:repeat(4,1fr)}
@media (max-width:1080px){body.story .why__grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){body.story .why__grid{grid-template-columns:1fr}}
body.story .why__card{
  display:flex;flex-direction:column;gap:10px;
  padding:26px 24px 30px;border-radius:24px;text-align:left;
  background:var(--bg-2);
  box-shadow:0 0 0 1px rgba(255,255,255,.07), 0 18px 44px rgba(0,0,0,.45)}
body.story .why__kicker{
  font-size:.75rem;font-weight:600;letter-spacing:.12em;
  text-transform:uppercase;color:var(--glass)}
body.story .why__card h3{
  margin:0;font-size:clamp(1.25rem,1.9vw,1.5rem);font-weight:600;
  letter-spacing:-.02em;line-height:1.16}
body.story .why__card p:last-child{
  font-size:.95rem;line-height:1.5;color:var(--ink-2)}
/* the staggered fade-up (delay per card set inline by story.js) */
body.story .why--reveal .why__card{
  opacity:0;transform:translateY(22px);
  transition:opacity .6s ease var(--d,0s),
             transform .7s cubic-bezier(.2,.6,.2,1) var(--d,0s)}
body.story .why--reveal .why__card.is-in{opacity:1;transform:none}

/* ── closer + foot ──────────────────────────────────────────────── */
body.story .closer{min-height:80svh;display:grid;place-items:center;
  text-align:center;padding:80px var(--pad)}
body.story .closer__lead{margin:18px auto 0;max-width:44ch}
body.story .closer__lead b{color:var(--ink)}
/* requirements/license strip (2026-09-05, replaces the one-line
   fineprint): three quiet left-aligned columns under the CTA */
body.story .closer__facts{
  margin:64px auto 0;padding-top:30px;border-top:1px solid var(--line);
  width:min(880px,100%);display:grid;gap:28px;
  grid-template-columns:repeat(3,1fr);text-align:left}
body.story .closer__facts dt{
  font-size:.78rem;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;color:var(--glass)}
body.story .closer__facts dd{
  margin:8px 0 0;font-size:.92rem;line-height:1.55;color:var(--ink-2)}
@media (max-width:720px){body.story .closer__facts{grid-template-columns:1fr;gap:22px}}
body.story .foot{padding:28px var(--pad) 44px;text-align:center;
  font-size:.85rem;color:var(--ink-2);border-top:1px solid var(--line)}

/* ── reduced motion: everything just sits in its finished state ── */
@media (prefers-reduced-motion:reduce){
  body.story .scene{height:auto !important}
  body.story .scene__pin{position:static;height:auto;min-height:0;padding-block:96px}
  body.story .hero__hint span{animation:none}
}

/* Link out of the homepage's three-question teaser to the full /faq. */
body.story .faq__more{margin-top:28px;font-size:.98rem}

/* Language switcher — same markup as on the prose pages, on black. */
body.story .langbar{
  display:flex;gap:18px;flex-wrap:wrap;justify-content:center;
  padding:20px var(--pad);border-top:1px solid var(--line);
  font-size:.82rem;
}
body.story .langbar a{color:var(--ink-2);text-decoration:none}
body.story .langbar a:hover{color:var(--ink);text-decoration:underline}
body.story .langbar a[aria-current]{color:var(--ink);font-weight:600;text-decoration:none;cursor:default}

/* ── production legal footer (added at promotion, 2026-09-05) ───── */
/* .wrap only exists so the shared footer markup is identical here and on
   the prose pages; this page's .foot already has its own side padding. */
body.story .foot .wrap{max-width:1000px;margin-inline:auto}
body.story .foot__note{max-width:60ch;margin-inline:auto;line-height:1.6}
body.story .foot__mark{font-weight:600;color:var(--ink)}
body.story .foot__links{display:flex;gap:20px;justify-content:center;flex-wrap:wrap;margin:10px 0 12px}
body.story .foot__links a{color:var(--ink-2);text-decoration:none}
body.story .foot__links a:hover{color:var(--ink);text-decoration:underline}


/* ── setup video (#video): a local poster with a play button; story.js
   replaces the button with the youtube-nocookie player on click ── */
body.story .film{
  position:relative;width:min(960px,100%);aspect-ratio:16/9;
  margin:48px auto 0;border-radius:14px;overflow:hidden;background:#000;
  box-shadow:0 18px 60px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.08)}
body.story .film__btn{
  display:block;width:100%;height:100%;padding:0;border:0;margin:0;
  background:none;color:inherit;cursor:pointer;font:inherit}
body.story .film__btn img{display:block;width:100%;height:100%;object-fit:cover}
body.story .film__play{
  position:absolute;left:50%;top:50%;width:88px;height:88px;
  transform:translate(-50%,-50%);border-radius:50%;
  background:rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.35);
  backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
  transition:background .2s,transform .2s}
body.story .film__play::after{
  content:"";position:absolute;left:56%;top:50%;transform:translate(-50%,-50%);
  border-style:solid;border-width:15px 0 15px 26px;
  border-color:transparent transparent transparent #fff}
body.story .film__btn:hover .film__play{background:var(--blue);transform:translate(-50%,-50%) scale(1.06)}
body.story .film__btn:focus-visible{outline:3px solid var(--blue);outline-offset:-3px}
body.story .film__label{
  position:absolute;left:50%;bottom:18px;transform:translateX(-50%);
  padding:6px 14px;border-radius:99px;background:rgba(0,0,0,.6);
  color:#fff;font-size:.9rem;font-weight:600;white-space:nowrap}
body.story .film iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
body.story .film__note{margin-top:16px}
body.story .film__note a{color:var(--ink-2);text-decoration:underline}
body.story .film__note a:hover{color:var(--ink)}

/* CJK pages (ja, ko, zh-hant; added 2026-10-12). The platform's CJK face
   comes right after -apple-system, so Latin words keep SF and CJK glyphs get
   Hiragino / Apple SD Gothic Neo / PingFang (Yu Gothic, Malgun Gothic,
   Microsoft JhengHei on Windows). The negative letter-spacing tuned for SF
   crushes CJK glyphs together, so it is neutralised; Korean keeps words
   whole (it uses spaces), Japanese and Chinese break strictly (no line may
   start with a closing mark). */
html:lang(ja) body{--sans:-apple-system,BlinkMacSystemFont,"Hiragino Sans","Hiragino Kaku Gothic ProN","Yu Gothic UI","Yu Gothic",Meiryo,sans-serif;line-break:strict}
html:lang(ko) body{--sans:-apple-system,BlinkMacSystemFont,"Apple SD Gothic Neo","Malgun Gothic","Noto Sans KR",sans-serif;word-break:keep-all;overflow-wrap:anywhere}
html:lang(zh-hant) body{--sans:-apple-system,BlinkMacSystemFont,"PingFang TC","Microsoft JhengHei","Noto Sans TC",sans-serif;line-break:strict}
html:is(:lang(ja),:lang(ko),:lang(zh-hant)) body *{letter-spacing:0!important}
/* Japanese headings: break between phrases, not inside a word (Chrome/Edge; Safari ignores it) */
html:lang(ja) :is(h1,h2,h3){word-break:auto-phrase}
