/* ═══════════════════════════════════════════════════════════════════════
   Nibomont · landing.css — scroll-driven marketing page (prototype)

   Self-contained on purpose: no Tailwind, no CDN, no build step. The app's
   `static/tw.css` is a pregenerated utility SUBSET and `nibomont.css` loads
   after it, so a new page built on those would fail silently. Everything
   here is hand-written and local, which also keeps it CSP-clean.

   Structure: light hero -> light proof -> DARK product middle -> light FAQ
   -> navy close. The dark middle is the product story; the light bookends
   are the pitch.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Fonts ─────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Montserrat'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('/static/fonts/montserrat-400.woff2') format('woff2');
}
@font-face {
  font-family: 'Montserrat'; font-style: normal; font-weight: 500;
  font-display: swap; src: url('/static/fonts/montserrat-500.woff2') format('woff2');
}
@font-face {
  font-family: 'Montserrat'; font-style: normal; font-weight: 600;
  font-display: swap; src: url('/static/fonts/montserrat-600.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 100 700;
  font-display: swap; src: url('/static/fonts/ibm-plex-sans-latin.woff2') format('woff2');
}

/* ── Tokens ────────────────────────────────────────────────────────── */
:root {
  /* light ground */
  --paper:      #FBFAF8;
  --paper-2:    #F3F1ED;
  --paper-3:    #EAE7E1;
  --ink:        #111A24;
  --ink-mu:     #57626F;
  --ink-fa:     #AFB6BE;   /* the "unread" grey the scrub reveal starts from */
  --rule:       rgba(17, 26, 36, .11);
  --rule-soft:  rgba(17, 26, 36, .06);

  /* brand */
  --navy:       #1A2F50;
  --navy-mid:   #24406B;
  --navy-soft:  #34558A;
  --sky:        #6E97D6;

  /* dark ground */
  --floor:      #0A1220;
  --floor-2:    #0E1828;
  --card:       #111D30;
  --card-2:     #16243A;
  --on-dark:    #E9EEF5;
  --on-dark-mu: #94A3B8;
  --on-dark-fa: #46566E;   /* scrub start on dark */
  --rule-dark:  rgba(255, 255, 255, .10);
  --screen-bed: #12161d;   /* bezel-dark; the thin contain bands read as bezel */

  --shell:      min(1180px, 92vw);
  --gutter:     clamp(18px, 4vw, 40px);

  --ease:       cubic-bezier(.22, .61, .36, 1);
  --ease-out:   cubic-bezier(.16, 1, .3, 1);

  color-scheme: light;
}

/* ── Reset ─────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
}
img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
h1, h2, h3, h4 {
  font-family: 'Montserrat', system-ui, sans-serif;
  font-weight: 500;
  letter-spacing: -.022em;
  line-height: 1.07;
  margin: 0;
  text-wrap: balance;
}
p { margin: 0; }
:focus-visible { outline: 2px solid var(--navy-soft); outline-offset: 3px; border-radius: 3px; }

/* ── Type scale ────────────────────────────────────────────────────── */
.h-display { font-size: clamp(2.3rem, 5.6vw, 4.4rem); }
.h-section { font-size: clamp(1.85rem, 3.9vw, 3.05rem); }
.h-card    { font-size: clamp(1.2rem, 1.7vw, 1.55rem); }
.lede {
  font-size: clamp(1rem, 1.15vw, 1.12rem);
  color: var(--ink-mu);
  max-width: 54ch;
}
.dark .lede { color: var(--on-dark-mu); }

/* ── Layout ────────────────────────────────────────────────────────── */
.shell { width: var(--shell); margin-inline: auto; }
section { position: relative; }
.band { padding-block: clamp(80px, 11vh, 150px); }
.band-tight { padding-block: clamp(56px, 7vh, 96px); }

/* the dark product middle, closed with a big radius over the light below */
.dark {
  background: var(--floor);
  color: var(--on-dark);
}
.dark-open  { border-radius: clamp(20px, 3vw, 44px) clamp(20px, 3vw, 44px) 0 0; }
.dark-close { border-radius: 0 0 clamp(20px, 3vw, 44px) clamp(20px, 3vw, 44px); }

/* ── Section rule + eyebrow ────────────────────────────────────────── */
.rule-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  border-top: 1px solid var(--rule);
  padding-top: 14px;
  margin-bottom: clamp(40px, 7vh, 90px);
}
.dark .rule-head { border-color: var(--rule-dark); }
.eyebrow {
  font-family: 'Montserrat', sans-serif;
  font-size: .68rem; font-weight: 600;
  letter-spacing: .17em; text-transform: uppercase;
  color: var(--ink-fa);
}
.dark .eyebrow { color: var(--on-dark-fa); }
.rule-head .mark { width: 17px; opacity: .5; }

/* ── Buttons ───────────────────────────────────────────────────────── */
.pill {
  display: inline-flex; align-items: center; gap: 9px;
  white-space: nowrap;          /* a wrapped pill reads as a broken button */
  padding: 13px 26px; border-radius: 999px;
  font-family: 'Montserrat', sans-serif; font-size: .9rem; font-weight: 500;
  border: 1px solid transparent; cursor: pointer;
  transition: transform .35s var(--ease), background-color .3s, color .3s, border-color .3s;
  will-change: transform;
}
.pill:hover { transform: translateY(-2px); }
.pill-solid  { background: var(--ink); color: var(--paper); }
.pill-solid:hover { background: var(--navy); }
.pill-ghost  { border-color: var(--rule); color: var(--ink); background: transparent; }
.pill-ghost:hover { border-color: var(--ink); }
.dark .pill-solid { background: var(--paper); color: var(--floor); }
.dark .pill-ghost { border-color: var(--rule-dark); color: var(--on-dark); }
.dark .pill-ghost:hover { border-color: var(--on-dark); }
.pill .arr { transition: transform .35s var(--ease); }
.pill:hover .arr { transform: translateX(3px); }

/* ═══ Preloader ════════════════════════════════════════════════════ */
#preload {
  position: fixed; inset: 0; z-index: 200;
  background: var(--navy);
  display: grid; place-items: center;
  transition: opacity .7s var(--ease), visibility .7s;
}
#preload .mark { width: 74px; color: #fff; animation: pl-in .9s var(--ease-out) both; }
#preload .pl-word {
  margin-top: 18px; text-align: center;
  font-family: 'Montserrat', sans-serif; font-size: .72rem; font-weight: 500;
  letter-spacing: .34em; text-transform: uppercase; color: rgba(255,255,255,.62);
  animation: pl-in .9s .18s var(--ease-out) both;
}
@keyframes pl-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
body.ready #preload { opacity: 0; visibility: hidden; }

/* ═══ Fixed furniture ══════════════════════════════════════════════ */
#progress {
  position: fixed; top: 0; left: 0; height: 2px; width: 100%;
  z-index: 90; transform-origin: 0 50%; transform: scaleX(0);
  background: linear-gradient(90deg, var(--navy), var(--sky));
  pointer-events: none;
}
.nav {
  position: fixed; top: 0; inset-inline: 0; z-index: 80;
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px;
  padding: 14px var(--gutter);
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid transparent;
  transition: background-color .4s, border-color .4s, color .4s, padding .4s;
}
.nav.on-dark {
  background: color-mix(in srgb, var(--floor) 78%, transparent);
  color: var(--on-dark);
  border-bottom-color: var(--rule-dark);
}
.nav.scrolled { padding-block: 9px; border-bottom-color: var(--rule); }
.nav.on-dark.scrolled { border-bottom-color: var(--rule-dark); }
.brand { display: flex; align-items: center; gap: 10px; }
.brand .mark { width: 25px; }
.brand .wordmark {
  font-family: 'Montserrat', sans-serif; font-weight: 600;
  font-size: 1.02rem; letter-spacing: .015em;
  white-space: nowrap;
}
.brand .wordmark small {
  font-weight: 500; opacity: .55; letter-spacing: .04em; font-size: .8em;
}
.nav-links { display: flex; gap: 26px; align-items: center; }
.nav-links a {
  font-size: .87rem; color: inherit; opacity: .72;
  transition: opacity .25s;
}
.nav-links a:hover { opacity: 1; }
.nav .pill { padding: 10px 20px; font-size: .84rem; }
.nav-burger { display: none; }

/* The rail needs real gutter to live in. Below that there is none, so hide
   it rather than cram an aircraft into a 20px margin. */
#flight.too-narrow { display: none; }
@media (max-width: 820px) { #flight { display: none; } }

/* The corner mark shared the left gutter with the flight rail and the nav
   already carries the wordmark, so it is retired. */
.corner-mark { display: none; }
.to-top {
  position: fixed; bottom: 20px; right: var(--gutter); z-index: 75;
  width: 42px; height: 42px; border-radius: 999px;
  border: 1px solid var(--rule); background: var(--paper);
  display: grid; place-items: center; cursor: pointer;
  opacity: 0; transform: translateY(10px); pointer-events: none;
  transition: opacity .4s, transform .4s var(--ease), background-color .4s, border-color .4s;
}
.to-top.show { opacity: 1; transform: none; pointer-events: auto; }
.to-top:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

/* ═══ The plane ════════════════════════════════════════════════════
   A fixed overlay whose aircraft rides a bezier as total scroll
   progress goes 0 -> 1, banking with the path tangent. The trail is the
   same path revealed by dashoffset, so the plane always sits at the
   leading edge of its own contrail.                                   */
/* The flight rail lives in the LEFT GUTTER — the margin between the viewport
   edge and the centred content column — so the aircraft can never overlap a
   headline or a card. Nothing on the page is wider than `--shell`, which is
   what makes the gutter safe to draw in. */
#flight {
  position: fixed; top: 0; bottom: 0; left: 0; z-index: 60;
  width: max(0px, calc((100vw - var(--shell)) / 2));
  pointer-events: none; overflow: visible;
}
/* only the ROUTE svg stretches to the rail — the aircraft must keep its own
   box, or it inherits the rail's size and flies off-screen. */
#flight > #flightSvg { position: absolute; inset: 0; width: 100%; height: 100%; }
#flight > #planeSvg {
  position: absolute; left: 0; top: 0;
  width: var(--plane); height: var(--plane);
}
:root { --plane: clamp(20px, 2.3vw, 34px); }
#flight .trail {
  fill: none; stroke: var(--navy); stroke-width: 1.4;
  stroke-dasharray: 5 7; stroke-linecap: round;
  opacity: .2;
  transition: stroke .5s, opacity .5s;
}
#flight.over-dark .trail { stroke: var(--sky); opacity: .34; }
#flight .flown {
  fill: none; stroke: var(--navy); stroke-width: 1.6;
  stroke-linecap: round; opacity: .5;
  transition: stroke .5s, opacity .5s;
}
#flight.over-dark .flown { stroke: var(--sky); opacity: .8; }
#flight .plane {
  color: var(--navy);
  transition: color .5s;
  filter: drop-shadow(0 4px 10px rgba(17, 26, 36, .18));
}
#flight.over-dark .plane {
  color: #fff;
  filter: drop-shadow(0 0 14px rgba(110, 151, 214, .5));
}
/* The world map the flight arc is drawn over, echoed faintly behind the dark
   middle. STICKY, not absolute: #darkTop is not a positioning context, so
   inset:0 resolved against the viewport and the map covered only the first
   screen of a ~5,000px section. The negative margin keeps it out of flow. */
.world-bed {
  position: sticky; top: 0; z-index: 0;
  height: 100vh; margin-bottom: -100vh;
  background: url('/static/world-dots-navy.svg') center / 106% auto no-repeat;
  opacity: .085;
  pointer-events: none;
}
/* content rides above the backdrop */
.dark > section { position: relative; z-index: 1; }

/* ═══ Scrub reveal ═════════════════════════════════════════════════ */
.scrub > span.w {
  color: var(--ink-fa);
  transition: color .18s linear;
}
.dark .scrub > span.w { color: var(--on-dark-fa); }
.scrub > span.w.lit { color: var(--ink); }
.dark .scrub > span.w.lit { color: var(--on-dark); }
/* a half-greyed heading when JS is off: still legible, just flat */
.no-js .scrub > span.w { color: var(--ink); }

/* generic reveal */
.rise {
  opacity: 0; transform: translateY(22px);
  transition: opacity .8s var(--ease-out), transform .8s var(--ease-out);
}
.rise.in { opacity: 1; transform: none; }
.rise-2 { transition-delay: .09s; }
.rise-3 { transition-delay: .18s; }
.rise-4 { transition-delay: .27s; }

/* ═══ HERO ═════════════════════════════════════════════════════════ */
.hero {
  padding-top: clamp(120px, 17vh, 190px);
  text-align: center;
  background:
    radial-gradient(120% 80% at 50% -10%, #fff 0%, transparent 62%),
    var(--paper);
}
.hero h1 { margin-bottom: 22px; }
.hero .lede { margin-inline: auto; text-align: center; }
.hero-cta {
  display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;
  margin-top: 32px;
}
.hero-note {
  margin-top: 18px; font-size: .8rem; color: var(--ink-fa);
}

/* the laptop; rotateX flattens toward 0 as it is scrolled through */
.stage { perspective: 1700px; margin-top: clamp(48px, 8vh, 92px); }
.laptop {
  width: min(800px, 92vw); margin-inline: auto;
  transform-style: preserve-3d;
  /* NO will-change: it pins the composited raster and the screenshot inside
     is then sampled from a cached texture instead of painted at device
     resolution. See the sharpness note in landing.js. */
}
.laptop-lid {
  position: relative;
  border-radius: 14px 14px 4px 4px;
  padding: 9px 9px 0;
  background: linear-gradient(168deg, #2A3A4E 0%, #16202E 46%, #0D141E 100%);
  box-shadow:
    0 1px 0 rgba(255,255,255,.13) inset,
    0 36px 70px -26px rgba(12, 20, 32, .5),
    0 90px 120px -60px rgba(12, 20, 32, .4);
}
.laptop-cam {
  position: absolute; top: 4px; left: 50%; translate: -50% 0;
  width: 5px; height: 5px; border-radius: 999px;
  background: #39475A;
}
.laptop-screen {
  /* 16/9, not 16/10: the capture frame is fixed at ~1.99:1, so a 16/10 screen
     needed either a `cover` crop (which cut the right-hand columns AND forced
     a 1.6x upscale) or a 10% letterbox. At 16/9 `contain` keeps every column,
     upscales only 1.29x, and leaves ~5% bands that read as screen bezel. */
  aspect-ratio: 16 / 9;
  border-radius: 8px 8px 2px 2px;
  overflow: hidden;
  /* the console's own edge colour, so `contain` letterboxes invisibly rather
     than `cover` cropping columns off the right of the UI */
  background: var(--screen-bed);
  position: relative;
}
.laptop-screen img {
  width: 100%; height: 100%;
  object-fit: contain; object-position: center;
}
.laptop-glare {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(104deg, rgba(255,255,255,.11) 0%, transparent 34%, transparent 66%, rgba(255,255,255,.05) 100%);
}
.laptop-base {
  height: 13px; margin-inline: -3.4%;
  border-radius: 0 0 13px 13px;
  background: linear-gradient(180deg, #303F53 0%, #1B2430 62%, #121A24 100%);
  box-shadow: 0 22px 34px -18px rgba(12, 20, 32, .55);
  position: relative;
}
.laptop-base::after {
  content: ''; position: absolute; top: 0; left: 50%; translate: -50% 0;
  width: 15%; height: 4px; border-radius: 0 0 5px 5px;
  background: rgba(0,0,0,.3);
}
.shot-label {
  margin-top: 20px; text-align: center;
  font-size: .76rem; color: var(--ink-fa);
  font-family: 'Montserrat', sans-serif; letter-spacing: .04em;
}

/* the hand-off: the flat full-bleed card the laptop gives way to */
.handoff { margin-top: clamp(40px, 7vh, 84px); }
.handoff-frame {
  width: min(880px, 92vw); margin-inline: auto;
  border-radius: clamp(12px, 1.5vw, 20px);
  overflow: hidden;
  background: var(--screen-bed);
  border: 1px solid var(--rule);
  box-shadow: 0 40px 90px -40px rgba(12, 20, 32, .42);
}
.handoff-frame img { width: 100%; height: auto; }
.handoff-cap {
  width: var(--shell); margin: 18px auto 0;
  display: flex; gap: 10px 26px; flex-wrap: wrap; justify-content: center;
  font-size: .78rem; color: var(--ink-fa);
}
.handoff-cap b { color: var(--ink-mu); font-weight: 500; }

/* The stats row (.stats/.stat/.stats-foot) was deleted with the four
   carrier-fine figures it styled — see the comment in the #proof section of
   templates/marketing.html. */

/* ═══ Pinned horizontal carousel ══════════════════════════════════ */
.pin-wrap { position: relative; }
.pin-stick {
  position: sticky; top: 0;
  height: 100vh;
  display: flex; flex-direction: column;
  /* Was `justify-content: center`, which sliced BOTH ends whenever heading +
     card exceeded 100vh — measured 863px of content in an 833px stick, so the
     section title lost the tops of its glyphs and the cards ran off the
     bottom. Top-aligned, with the track taking whatever height is left, the
     text can never be clipped; only the screenshot bleeds, which it is drawn
     to do anyway. */
  justify-content: flex-start;
  padding-block: clamp(14px, 3.5vh, 44px) 0;
  overflow: hidden;
}
.pin-head { flex: 0 0 auto; width: var(--shell); margin: 0 auto 26px; }
.pin-head .h-section { max-width: 22ch; }
.pin-hint {
  display: flex; align-items: center; gap: 9px;
  margin-top: 14px; font-size: .72rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--on-dark-fa);
}
.pin-hint .bar {
  width: 74px; height: 2px; background: var(--rule-dark);
  position: relative; border-radius: 2px; overflow: hidden;
}
.pin-hint .bar i {
  position: absolute; inset: 0; background: var(--sky);
  transform-origin: 0 50%; transform: scaleX(0);
}
.track {
  display: flex; gap: clamp(14px, 1.6vw, 24px);
  padding-inline: max(calc((100vw - var(--shell)) / 2), var(--gutter));
  /* fill the height the heading left, and let the cards stretch into it:
     `min-height: 0` is load-bearing, since a flex item's default `auto`
     minimum would let the row grow past the stick and re-create the clip */
  flex: 1 1 auto; min-height: 0;
  /* flex-start, NOT stretch: a stretched card is as tall as the tallest one in
     the row, and the extra height lands under a screenshot that cannot fill it
     — which is exactly the dark band this replaced. Each card is its own
     height, capped by the row. */
  align-items: flex-start;
  /* no will-change: this row carries four screenshots (see landing.js) */
}
.fcard {
  /* viewport-relative so three cards always overrun the screen — otherwise
     the track has nothing to travel and the "Scroll" hint lies */
  flex: 0 0 clamp(300px, 42vw, 720px);
  background: var(--card);
  border: 1px solid var(--rule-dark);
  border-radius: 18px;
  padding: 28px 28px 0;
  display: flex; flex-direction: column;
  /* natural height, but never taller than the pinned row it sits in */
  min-height: 0; max-height: 100%;
  overflow: hidden;
  position: relative;
}
.fcard-ico {
  width: 30px; height: 30px; color: var(--sky); margin-bottom: 20px;
}
.fcard h3 { margin-bottom: 10px; }
.fcard p { color: var(--on-dark-mu); font-size: .92rem; max-width: 44ch; }
.fcard .shot {
  /* The shock absorber: the text above is fixed-size, so on a short viewport
     the screenshot gives up height and crops itself — you see less of the
     picture, never less of the sentence.
     `flex-grow` is 0 on purpose. With `1 1 auto` the box grew PAST its own
     image whenever the card had height to spare, and the bed colour showed
     through as a black band under every screenshot. It may shrink; it may
     never outgrow what it contains. */
  flex: 0 1 auto; min-height: 0;
  margin-top: 24px; margin-inline: -1px; margin-bottom: -1px;
  border-radius: 12px 12px 0 0;
  overflow: hidden; background: var(--screen-bed);
  /* a brighter hairline than the card's own, so the dark screenshot reads as
     a separate object instead of dissolving into the dark card */
  border: 1px solid rgba(255,255,255,.16); border-bottom: 0;
  box-shadow: 0 -10px 34px -14px rgba(0,0,0,.7),
              0 1px 0 rgba(255,255,255,.07) inset;
}
.fcard .shot img { width: 100%; height: auto; }
/* A portrait device cannot use the wide variant above, which is built to
   bleed a landscape screenshot off the card's bottom edge — dropped in
   unchanged it reads as a broken crop. This one is centred, framed, and
   bleeds only its own bottom. */
/* A WHOLE handset, bottom bezel included — not one bleeding off the card edge
   like the wide screenshots do. It therefore has to FIT the height it is given.

   Sized by an explicit height with the ratio deriving the width, and laid out
   as an inline-block rather than a flex item. Three cycles were measured on
   the way here, all of them the same shape — a width that wants to come from a
   height that is not yet known: `height: 100%` against a content-sized parent
   (a 647x1231 phone), `aspect-ratio` on a flex item whose main size flex has
   already decided (647x404), and shrink-wrapping the frame around a
   percentage-height image (647x403). Outside flex, with a definite height, the
   ratio simply works. */
.fcard .shot-phone {
  background: none; border: 0; box-shadow: none;
  border-radius: 0; margin-inline: 0;
  display: block; text-align: center;
  /* the device ends ABOVE the card edge, with room to read as finished rather
     than as something the card happened to stop at */
  padding: 2px 0 clamp(14px, 3.5%, 22px);
  flex: 0 1 auto; min-height: 0;
  overflow: hidden;
}
.shot-phone-frame {
  position: relative;
  display: inline-block; vertical-align: top;
  /* tracks the pinned viewport, which is exactly 100vh — the subtrahend is the
     fixed furniture above it (section heading, card text, paddings) */
  height: clamp(150px, calc(100vh - 442px), 460px);
  aspect-ratio: 1 / 1.86;
  width: auto; max-width: 100%;
  padding: 8px 8px 11px;          /* a slightly deeper chin, as a phone has */
  border-radius: 26px;
  background: linear-gradient(168deg, #2b3648 0%, #151d2a 52%, #0d141e 100%);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.1) inset,
    0 -14px 40px -18px rgba(0,0,0,.75),
    0 0 60px -22px rgba(110,151,214,.22);
}
.shot-phone-notch {
  position: absolute; top: 14px; left: 50%; translate: -50% 0;
  width: 34%; height: 5px; border-radius: 99px;
  background: #0a0f17; z-index: 2;
}
.shot-phone-frame img {
  /* fills the screen and crops, so the DEVICE decides the height. Anchored to
     the top: the useful part of the capture (the portal header and the first
     fields) is up there, and a centred crop would show neither end. */
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
  border-radius: 19px;
}

/* ═══ Flight path / annotated pipeline ════════════════════════════ */
.pipe { position: relative; }
.pipe-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 5vw, 70px); align-items: center;
}
.pipe-art {
  position: relative; aspect-ratio: 1 / 1;
  display: grid; place-items: center;
}
.pipe-art .ring {
  position: absolute; border: 1px solid var(--rule-dark); border-radius: 999px;
}
.pipe-art .ring:nth-child(1) { inset: 4%; }
.pipe-art .ring:nth-child(2) { inset: 19%; }
.pipe-art .ring:nth-child(3) { inset: 34%; }
.pipe-art .core {
  width: 40%; color: var(--sky);
  filter: drop-shadow(0 0 30px rgba(110,151,214,.35));
  will-change: transform;
}
.tag {
  position: absolute;
  background: color-mix(in srgb, var(--card-2) 88%, transparent);
  backdrop-filter: blur(8px);
  border: 1px solid var(--rule-dark);
  border-radius: 11px;
  padding: 9px 13px;
  min-width: 148px;
  opacity: 0; transform: translateY(9px) scale(.97);
  transition: opacity .6s var(--ease-out), transform .6s var(--ease-out);
}
.tag.in { opacity: 1; transform: none; }
.tag b {
  display: block; font-family: 'Montserrat', sans-serif;
  font-size: .82rem; font-weight: 500;
}
.tag span { font-size: .72rem; color: var(--on-dark-mu); }
.tag:nth-of-type(1) { top: 4%;  left: -6%; }
.tag:nth-of-type(2) { top: 25%; right: -8%; }
.tag:nth-of-type(3) { bottom: 29%; left: -9%; }
.tag:nth-of-type(4) { bottom: 6%; right: -4%; }
/* Between the two-column breakpoint and ~1150px the art panel is narrow while
   the pills stay content-sized, so the outward offsets push them to the very
   edge of the viewport. Tuck them inside the panel across that band. */
@media (min-width: 901px) and (max-width: 1150px) {
  .tag { min-width: 0; max-width: 52%; }
  .tag:nth-of-type(1), .tag:nth-of-type(3) { left: 0; }
  .tag:nth-of-type(2), .tag:nth-of-type(4) { right: 0; }
}

/* ═══ Bento ═══════════════════════════════════════════════════════ */
.bento {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(12px, 1.5vw, 20px);
  position: relative;
}
.bcard {
  background: var(--card);
  border: 1px solid var(--rule-dark);
  border-radius: 16px;
  padding: clamp(22px, 2.4vw, 32px);
  min-height: 208px;
  display: flex; flex-direction: column; justify-content: space-between;
  position: relative; overflow: hidden;
}
.bcard .num {
  position: absolute; top: 18px; right: 20px;
  font-family: 'Montserrat', sans-serif; font-size: .74rem;
  color: var(--on-dark-fa); letter-spacing: .08em;
}
.bcard .bico { width: 24px; color: var(--sky); }
.bcard h3 { margin: 22px 0 8px; font-size: 1.22rem; }
.bcard p { color: var(--on-dark-mu); font-size: .88rem; max-width: 40ch; }
/* the mark floating between the four cards */
.bento-core {
  position: absolute; left: 50%; top: 50%;
  width: clamp(94px, 11vw, 150px); aspect-ratio: 1;
  color: var(--sky); z-index: 2;
  pointer-events: none;
  display: grid; place-items: center;
  will-change: transform;
  transform: translate(-50%, -50%);
}
/* the plate that punches the mark out of the cards behind it */
.bento-core::before {
  content: ''; position: absolute; inset: -26%;
  background: radial-gradient(circle, var(--floor) 52%, transparent 78%);
}
.bento-core svg {
  position: relative; width: 100%;
  filter: drop-shadow(0 0 24px rgba(110, 151, 214, .3));
}

/* ═══ Steps ═══════════════════════════════════════════════════════ */
.steps { display: grid; gap: 0; }
.step {
  display: grid; grid-template-columns: 58px 1fr 1fr;
  gap: clamp(16px, 3vw, 44px);
  padding-block: clamp(24px, 3.4vh, 40px);
  border-top: 1px solid var(--rule-dark);
  align-items: start;
}
.step:last-child { border-bottom: 1px solid var(--rule-dark); }
.step .sn {
  font-family: 'Montserrat', sans-serif; font-size: .8rem;
  color: var(--on-dark-fa); letter-spacing: .08em; padding-top: 5px;
}
.step h3 { font-size: 1.3rem; }
.step p { color: var(--on-dark-mu); font-size: .92rem; }

/* ═══ Trust row ═══════════════════════════════════════════════════ */
.trust {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px; background: var(--rule-dark);
  border: 1px solid var(--rule-dark); border-radius: 14px; overflow: hidden;
}
.trust > div { background: var(--floor-2); padding: 24px 22px; }
.trust .tico { width: 21px; color: var(--sky); margin-bottom: 14px; }
.trust b {
  display: block; font-family: 'Montserrat', sans-serif;
  font-weight: 500; font-size: .96rem; margin-bottom: 6px;
}
.trust p { font-size: .82rem; color: var(--on-dark-mu); }

/* ═══ FAQ ═════════════════════════════════════════════════════════ */
.faq-grid {
  display: grid; grid-template-columns: .82fr 1.18fr;
  gap: clamp(28px, 5vw, 70px);
}
.faq-list { border-top: 1px solid var(--rule); }
.faq-list details { border-bottom: 1px solid var(--rule); }
.faq-list summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; padding: 19px 0; cursor: pointer;
  font-family: 'Montserrat', sans-serif; font-size: 1rem; font-weight: 500;
  list-style: none;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary .plus {
  flex: 0 0 auto; width: 15px; height: 15px; position: relative;
}
.faq-list summary .plus::before,
.faq-list summary .plus::after {
  content: ''; position: absolute; inset: 50% 0 auto; height: 1.5px;
  background: var(--ink-mu); transition: transform .35s var(--ease);
}
.faq-list summary .plus::after { transform: rotate(90deg); }
.faq-list details[open] summary .plus::after { transform: rotate(0deg); }
.faq-list .ans {
  padding: 0 42px 22px 0; color: var(--ink-mu); font-size: .92rem;
  max-width: 62ch;
}

/* ═══ Closing CTA ═════════════════════════════════════════════════ */
.close {
  background: var(--navy);
  color: #fff; text-align: center;
  position: relative; overflow: hidden;
  border-radius: clamp(20px, 3vw, 44px) clamp(20px, 3vw, 44px) 0 0;
}
/* the diagonal hatch motif, echoing the brand mark's circuit traces */
.close::before {
  content: ''; position: absolute; inset: 0;
  background: repeating-linear-gradient(58deg,
    rgba(255,255,255,.055) 0 1.5px, transparent 1.5px 34px);
  pointer-events: none;
}
.close .shell { position: relative; }
.close h2 { margin-bottom: 18px; }
.close .lede { color: rgba(255,255,255,.72); margin-inline: auto; }
.close .hero-cta { margin-top: 30px; }
.close .pill-solid { background: #fff; color: var(--navy); }
.close .pill-ghost { border-color: rgba(255,255,255,.34); color: #fff; }
.close .pill-ghost:hover { border-color: #fff; }
.close .scrub > span.w { color: rgba(255,255,255,.3); }
.close .scrub > span.w.lit { color: #fff; }

/* ═══ Footer ══════════════════════════════════════════════════════ */
.foot {
  background: var(--navy);
  color: rgba(255,255,255,.78);
  padding: clamp(46px, 6vh, 72px) 0 30px;
  border-top: 1px solid rgba(255,255,255,.1);
}
.foot-grid {
  display: grid; grid-template-columns: 1fr auto 1fr;
  gap: 34px; align-items: start;
}
.foot-nav { display: grid; gap: 9px; font-size: .87rem; }
.foot-nav a { opacity: .74; } .foot-nav a:hover { opacity: 1; }
.foot-mid { text-align: center; }
.foot-mid .brand { justify-content: center; color: #fff; }
.foot-mid p {
  margin-top: 12px; font-size: .82rem; color: rgba(255,255,255,.6);
  max-width: 38ch;
}
.foot-right { display: flex; justify-content: flex-end; }
/* the footer sits on navy, so the default near-black solid pill disappears */
.foot .pill-solid { background: #fff; color: var(--navy); }
.foot .pill-solid:hover { background: rgba(255, 255, 255, .88); }
.foot-bottom {
  margin-top: clamp(34px, 5vh, 56px); padding-top: 18px;
  border-top: 1px solid rgba(255,255,255,.1);
  display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  font-size: .76rem; color: rgba(255,255,255,.5);
}

/* ═══ Prototype banner ════════════════════════════════════════════ */
.proto {
  position: fixed; bottom: 0; inset-inline: 0; z-index: 95;
  background: #1B1205; color: #F5D7A1;
  border-top: 1px solid rgba(245, 215, 161, .28);
  font-size: .74rem; padding: 7px var(--gutter);
  display: flex; gap: 14px; align-items: center; justify-content: center;
  text-align: center;
}
.proto button {
  background: transparent; border: 1px solid rgba(245,215,161,.4);
  color: inherit; border-radius: 5px; padding: 2px 9px; cursor: pointer;
  font-size: .72rem;
}
body.proto-off .proto { display: none; }
/* lift the fixed furniture clear of the banner while it is showing */
body:not(.proto-off) .to-top { bottom: 58px; }
body:not(.proto-off) .corner-mark { bottom: 60px; }

/* ═══ Responsive ══════════════════════════════════════════════════ */
@media (max-width: 900px) {
  .pipe-grid, .faq-grid { grid-template-columns: 1fr; }
  .bento { grid-template-columns: 1fr; }
  .bento-core { display: none; }
  .stats { grid-template-columns: 1fr 1fr; }
  .stat:nth-child(3) { border-left: 0; }
  .stat:nth-child(-n+2) { border-bottom: 1px solid var(--rule); }
  .step { grid-template-columns: 40px 1fr; }
  .step p { grid-column: 2; }
  .foot-grid { grid-template-columns: 1fr; }
  .foot-right { justify-content: flex-start; }
  .foot-mid { text-align: left; } .foot-mid .brand { justify-content: flex-start; }
  .foot-mid p { max-width: none; }
  .tag { position: static; opacity: 1; transform: none; margin-top: 10px; }
  .pipe-art { aspect-ratio: 3 / 2; }
}
@media (max-width: 680px) {
  .nav-links { display: none; }
  .nav-burger {
    display: grid; place-items: center;
    width: 38px; height: 38px; border-radius: 9px;
    border: 1px solid var(--rule); background: transparent; cursor: pointer;
  }
  .nav.on-dark .nav-burger { border-color: var(--rule-dark); }
  .nav-open .nav-links {
    display: grid; position: absolute; top: 100%; inset-inline: 0;
    background: var(--paper); border-bottom: 1px solid var(--rule);
    padding: 16px var(--gutter) 22px; gap: 14px;
  }
  .nav.on-dark.nav-open .nav-links { background: var(--floor); border-color: var(--rule-dark); }
  /* the nav has three things and room for two: drop the company suffix and
     tighten the CTA rather than letting either wrap */
  .brand .mark { width: 21px; }
  .brand .wordmark { font-size: .95rem; }
  .brand .wordmark small { display: none; }
  .nav .pill { padding: 9px 15px; font-size: .8rem; gap: 6px; }
  .nav .pill .arr { display: none; }
    .stats { grid-template-columns: 1fr; }
  .stat { border-left: 0; border-bottom: 1px solid var(--rule); }
  .fcard { flex: 0 0 84vw; padding: 22px 22px 0; }
  .corner-mark { display: none; }
  .handoff-cap { flex-direction: column; align-items: center; gap: 6px; }
}

/* ═══ Reduced motion — the whole motion layer is optional ═════════ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
  #flight { display: none; }
  .rise { opacity: 1; transform: none; }
  .scrub > span.w { color: var(--ink); }
  .dark .scrub > span.w { color: var(--on-dark); }
  .tag { opacity: 1; transform: none; }
  .pin-stick { position: static; height: auto; }
  .track { flex-wrap: wrap; transform: none !important; }
  .fcard { flex: 1 1 320px; }
  .laptop { transform: none !important; }
}

/* ═══ STORY: pinned passenger journey ═══════════════════════════════
   The section is tall; `.story-stick` holds one viewport still while
   scroll position selects the beat and the phone's scene. Copy and order
   come from the narrated walkthrough script so the two agree.           */
.story { position: relative; }
.story-stick {
  position: sticky; top: 0;
  height: 100vh;
  display: flex; align-items: center;
  overflow: hidden;
}
.story-grid {
  display: grid; grid-template-columns: 1.05fr .95fr;
  gap: clamp(28px, 5vw, 76px); align-items: center;
}
.story-copy .h-section { margin-bottom: clamp(20px, 3vh, 34px); max-width: 20ch; }

.story-beats { list-style: none; margin: 0; padding: 0; }
.story-beats li {
  padding: 13px 0 13px 20px;
  border-left: 2px solid var(--rule-dark);
  opacity: .32;
  transition: opacity .45s var(--ease), border-color .45s, padding-left .45s var(--ease);
}
.story-beats li.is-on {
  opacity: 1; border-left-color: var(--sky); padding-left: 26px;
}
.story-beats li b {
  display: block; font-family: 'Montserrat', sans-serif;
  font-weight: 500; font-size: 1.02rem; margin-bottom: 3px;
}
.story-beats li span {
  display: block; font-size: .88rem; color: var(--on-dark-mu);
  max-width: 46ch;
  /* the body only earns its space on the active beat */
  max-height: 0; overflow: hidden; opacity: 0;
  transition: max-height .5s var(--ease), opacity .4s;
}
.story-beats li.is-on span { max-height: 9em; opacity: 1; }

/* ── the phone ──────────────────────────────────────────────────── */
.story-stage { display: grid; place-items: center; gap: 18px; }
/* Phone and laptop are stacked in the SAME cell and cross-faded, so the
   stage keeps one size across every beat and the beats beside it never
   jump. Only the frame that owns the live scene is visible. */
.story-stage .phone,
.story-stage .console-frame { grid-area: 1 / 1; transition: opacity .3s var(--ease-out), transform .4s var(--ease-out); }
.story-stage .console-frame { opacity: 0; transform: scale(.96); pointer-events: none; }
.story-stage.on-laptop .phone { opacity: 0; transform: scale(.96); pointer-events: none; }
.story-stage.on-laptop .console-frame { opacity: 1; transform: none; }
.story-dots { grid-area: 2 / 1; }

/* ── the officer's console ─────────────────────────────────────────
   Deliberately NOT named `.laptop`: the hero already owns that component
   (and its `-lid`/`-base`/`-screen` children), and reusing the name
   silently restyled the hero's machine. */
.console-frame { position: relative; width: clamp(300px, 34vw, 440px); }
.console-lid {
  position: relative; aspect-ratio: 16 / 10;
  padding: 10px 10px 11px; border-radius: 12px 12px 4px 4px;
  background: linear-gradient(168deg, #2b3648 0%, #151d2a 58%, #0d141e 100%);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.09) inset,
    0 40px 70px -30px rgba(0,0,0,.75),
    0 0 60px -20px rgba(110,151,214,.18);
}
.console-lid .scene {
  inset: 10px 10px 11px; border-radius: 5px; overflow: hidden;
  background: var(--floor-2);
}
/* the lid sits on a deck, drawn rather than fetched */
.console-deck {
  display: block; height: 9px; margin: 0 auto;
  width: 116%; border-radius: 0 0 7px 7px;
  background: linear-gradient(180deg, #27313f, #131a25);
  box-shadow: 0 0 0 1px rgba(255,255,255,.07) inset,
              0 18px 26px -16px rgba(0,0,0,.8);
  position: relative; left: -8%;
}
/* The officer stands at the machine, on the deck line, in the gap between the
   beats column and the console — never over either. Hidden below 900px, where
   the stage is sticky and that gap does not exist. */
.console-officer {
  position: absolute; right: calc(100% + clamp(10px, 3%, 26px)); bottom: 0;
  width: clamp(38px, 17%, 62px);
  color: var(--sky); opacity: .8;
  pointer-events: none;
}
.console-officer svg { display: block; width: 100%; height: auto; }

.console-deck::after {
  content: ""; position: absolute; top: 0; left: 50%; translate: -50% 0;
  width: 15%; height: 3px; border-radius: 0 0 4px 4px;
  background: rgba(255,255,255,.13);
}

.phone {
  position: relative;
  width: clamp(250px, 25vw, 320px);
  aspect-ratio: 41 / 84;
  padding: 11px;
  border-radius: 40px;
  background: linear-gradient(168deg, #2b3648 0%, #151d2a 52%, #0d141e 100%);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.09) inset,
    0 40px 70px -30px rgba(0,0,0,.75),
    0 0 60px -20px rgba(110,151,214,.18);
}
.phone-notch {
  position: absolute; top: 17px; left: 50%; translate: -50% 0;
  width: 34%; height: 6px; border-radius: 99px;
  background: #0a0f17; z-index: 3;
}
.phone-screen {
  position: relative; height: 100%;
  border-radius: 30px; overflow: hidden;
  background: var(--floor-2);
}
.scene {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  opacity: 0; transform: translateY(14px) scale(.985);
  /* the outgoing scene leaves faster than the incoming one arrives: the
     scenes are stacked, so an even cross-fade shows both at once whenever
     the reader scrolls quickly */
  transition: opacity .16s linear, transform .16s var(--ease-out);
  pointer-events: none;
}
.scene.is-on {
  opacity: 1; transform: none;
  transition: opacity .34s var(--ease-out) .05s, transform .4s var(--ease-out);
}

.sc-bar {
  flex: 0 0 auto; padding: 26px 16px 12px;
  background: var(--navy); color: #fff;
  font-family: 'Montserrat', sans-serif; font-size: .72rem; font-weight: 500;
  letter-spacing: .02em; text-align: center;
}
.sc-bar-air { background: #1F3A63; }
/* on the console the title bar is an app bar, not a phone status bar */
.sc-bar-wide {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 12px; text-align: left; font-size: .66rem;
}
.sc-crumb-mu { color: rgba(255,255,255,.6); font-family: 'IBM Plex Mono', monospace; }
.sc-desk {
  flex-direction: row; align-items: stretch; gap: 10px;
  padding: 11px; font-size: .95em;
}
.sc-desk-main { flex: 1 1 46%; display: flex; flex-direction: column; gap: 9px; }
.sc-desk-side { flex: 1 1 54%; display: flex; flex-direction: column; gap: 6px; }
.sc-desk .sc-verdict { flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; }
.sc-desk .sc-actions { margin-top: 0; }

/* the extra case context under the five matched factors */
.sc-meta {
  display: grid; grid-template-columns: 1fr 1fr; gap: 5px 8px;
  padding-top: 2px;
}
.sc-meta div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.sc-meta span { font-size: .58rem; letter-spacing: .08em; text-transform: uppercase; color: var(--on-dark-fa); }
.sc-meta b {
  font-size: .655rem; font-weight: 500; color: var(--on-dark-mu);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sc-body {
  flex: 1 1 auto; padding: 14px 14px 16px;
  display: flex; flex-direction: column; gap: 8px;
  overflow: hidden;
}
.sc-row, .sc-field {
  display: flex; align-items: baseline; gap: 8px;
  padding: 8px 10px; border-radius: 8px;
  background: rgba(255,255,255,.035);
  border: 1px solid var(--rule-dark);
  font-size: .72rem;
}
.sc-row span, .sc-field span { color: var(--on-dark-mu); flex: 0 0 42%; }
.sc-row b, .sc-field b { font-weight: 500; }
.sc-field i { margin-left: auto; color: #6FBF8E; font-style: normal; }
.sc-note { font-size: .68rem; color: var(--on-dark-fa); text-align: center; }
.mono { font-family: 'IBM Plex Mono', ui-monospace, monospace; }

.sc-btn {
  margin-top: auto; padding: 11px 14px; border-radius: 9px;
  border: 0; background: var(--sky); color: #0A1220;
  font-family: 'Montserrat', sans-serif; font-weight: 500; font-size: .78rem;
  cursor: default;
}
.sc-actions { margin-top: auto; display: flex; gap: 8px; }
.sc-actions .sc-btn { margin-top: 0; flex: 1; }
.sc-btn-ok { background: #3F8F63; color: #fff; }
.sc-btn-no { background: transparent; color: #E48B8B; border: 1px solid rgba(228,139,139,.45); }

.sc-alert, .sc-directive, .sc-verdict, .sc-seal {
  padding: 11px 12px; border-radius: 10px; border: 1px solid;
}
.sc-alert { background: rgba(200,80,80,.12); border-color: rgba(220,110,110,.35); }
.sc-alert b { display: block; font-family: 'IBM Plex Mono', monospace; font-size: .72rem; color: #F0A9A9; }
.sc-alert span { font-size: .7rem; color: var(--on-dark-mu); }
.sc-directive { background: rgba(110,151,214,.12); border-color: rgba(110,151,214,.35); }
.sc-directive b { display: block; font-size: .8rem; font-family: 'Montserrat', sans-serif; font-weight: 500; }
.sc-directive span { font-size: .7rem; color: var(--on-dark-mu); }

.sc-verdict { background: rgba(255,255,255,.04); border-color: var(--rule-dark); text-align: center; }
.sc-verdict span { display: block; font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; color: var(--on-dark-fa); }
.sc-verdict b { display: block; font-family: 'Montserrat', sans-serif; font-size: 1.5rem; font-weight: 500; margin: 4px 0 5px; color: var(--sky); }
.sc-verdict em { font-style: normal; font-size: .68rem; color: var(--on-dark-mu); }

.sc-seal { background: rgba(63,143,99,.13); border-color: rgba(63,143,99,.4); text-align: center; }
.sc-seal svg { width: 26px; height: 26px; margin: 2px auto 6px; color: #6FBF8E; }
.sc-seal b { display: block; font-family: 'Montserrat', sans-serif; font-size: 1.25rem; font-weight: 500; }
.sc-seal span { font-size: .68rem; color: var(--on-dark-mu); }
.sc-ok {
  margin-top: auto; text-align: center; padding: 9px;
  border-radius: 8px; background: rgba(63,143,99,.16);
  color: #9FD9B4; font-size: .72rem;
}

/* the conversation, built a bubble at a time */
.sc-chat { gap: 7px; justify-content: flex-end; }
.bub {
  font-size: .715rem; line-height: 1.45;
  padding: 8px 10px; border-radius: 11px;
  max-width: 86%;
  opacity: 0; transform: translateY(8px);
  transition: opacity .35s var(--ease-out), transform .35s var(--ease-out);
}
.bub.in { opacity: 1; transform: none; }
.bub.q { background: rgba(255,255,255,.07); border: 1px solid var(--rule-dark); align-self: flex-start; }
.bub.a { background: var(--navy-mid); align-self: flex-end; }
.bub.sys {
  align-self: center; max-width: 100%;
  background: transparent; border: 1px dashed var(--rule-dark);
  color: var(--on-dark-fa); font-size: .64rem;
  font-family: 'IBM Plex Mono', monospace;
}
.bub.sys em { font-style: normal; color: var(--sky); }

/* beat pips under the phone */
.story-dots { display: flex; gap: 7px; }
.story-dots i {
  width: 22px; height: 3px; border-radius: 3px;
  background: var(--rule-dark); transition: background-color .4s;
}
.story-dots i.is-on { background: var(--sky); }

@media (max-width: 900px) {
  /* Unpin, and read in the right order: heading, then the phone (which
     sticks while the beats scroll past it), then the beats. `display:
     contents` promotes the heading and the list to grid items so they can be
     ordered around the stage that sits between them. */
  .story-stick { position: static; height: auto; padding-block: 8px; }
  .story-grid { grid-template-columns: 1fr; gap: 22px; }
  .story-copy { display: contents; }
  .story-copy .h-section { order: 1; max-width: none; margin-bottom: 0; }
  .story-stage { order: 2; position: sticky; top: 72px; z-index: 2; }
  .story-beats { order: 3; }
  .story-beats li { opacity: 1; border-left-color: var(--rule-dark); }
  .story-beats li span { max-height: none; opacity: 1; }
  .story-beats li.is-on { border-left-color: var(--sky); }
  /* size by HEIGHT here: a 280px-wide phone is 574px tall and would eat most
     of a short viewport, leaving no room for the beat it illustrates */
  .phone { width: auto; height: min(50vh, 440px); }
  /* sized by WIDTH here — the console is landscape, so height-first sizing
     (which is what keeps the tall phone off a short viewport) would blow it
     past the column */
  .console-frame { width: min(100%, 420px); }
  .console-officer { display: none; }
  /* The phone is sized by HEIGHT here (440px at most), and the security-check
     scene fills that exactly with the five factors alone — measured, not
     guessed. The case-context block and its trailing line are the first
     things to go, rather than being silently clipped by the body's
     `overflow: hidden`. */
  .sc-meta,
  .sc-meta + .sc-note { display: none; }
  .story-dots { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .story-stick { position: static; height: auto; }
  .story-beats li, .story-beats li span { opacity: 1; max-height: none; }
  .bub { opacity: 1; transform: none; }
}

/* the hand-off code on the directive scene — a suggestion of a QR, not a
   real one: a scannable-looking code that resolves nowhere would be worse */
.sc-qr {
  width: 78px; aspect-ratio: 1; margin: 4px auto 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;
  padding: 7px; border-radius: 8px; background: #fff;
}
.sc-qr span { background: #0A1220; border-radius: 2px; }
.sc-qr span:nth-child(2),
.sc-qr span:nth-child(4),
.sc-qr span:nth-child(8) { background: rgba(10,18,32,.22); }

/* ── Utility classes replacing inline style= attributes ──────────────────────
   The page is served under `style-src 'self'` with NO unsafe-inline, so an
   inline `style="…"` attribute is silently dropped by the browser — it does
   not warn, the rule simply never applies. Every one-off declaration the
   prototype carried inline lives here instead. Keep it that way: if you add a
   `style=` attribute to templates/marketing.html it will not work in
   production even though it works when you open the file from disk. */
.u-abs           { position: absolute; }
.u-narrow26      { margin-inline: auto; max-width: 26ch; }
.u-narrow24      { margin-inline: auto; max-width: 24ch; }
.u-narrow24-gap  { max-width: 24ch; margin-bottom: 38px; }
.u-mt26          { margin-top: 26px; }
.u-mt24          { margin-top: 24px; }
.u-mt22          { margin-top: 22px; }
.u-mt20          { margin-top: 20px; }
.u-center        { text-align: center; }
.u-center-block  { margin: 26px auto 0; text-align: center; }
.u-pt-hero       { padding-top: clamp(70px, 9vh, 120px); }
.u-row           { display: flex; gap: 10px; align-items: center; }
