/* Homepage — the eight-section fluid page (Phase 4). Loaded ONLY on `/`
   (index.html links it via {% block extra_head %}), so :root and html rules
   here scope to the homepage.

   This dispatch styles the top four sections: #reach, #why, #engine, #sampling.

   The merge (spec §5.5), applied here rather than ported literally:
     - NO device-frame chrome. There is no #top wrapper; sections sit in <main>.
       The mockups' fake phone frame (fixed 390px column, a 3D transform, a 44px
       radius, a drop shadow) is gone — the 3D transform especially would
       re-parent the fixed nav and trap it.
     - `body` stays --paper (Phase 1's .oi-body-bg), never the frame surround.
     - Every container-query unit becomes a viewport unit. Section heights ->
       svh; #engine is 155svh >1024 and 280svh <=1024 (D15); the rib card is
       86vw on phones.
     - The hero headline is a single fluid clamp() fitted through the three anchor
       sizes (30/40/60px at 390/834/1440) instead of the mockups' fixed-rem
       !important overrides, which step-function at the breakpoints.
     - The marquee gets the prefers-reduced-motion pause the design's CSS lacks.

   Breakpoints: 1024 (desktop|device split, per D14/D15) and 480 (phone). 768 is
   a synthesized intermediate that collapses #sampling to one column before its
   two-column layout gets too tight — flagged for the Task 6 responsive pass. */

:root {
  /* The sections read var(--nav-h) for their top padding; the #top wrapper that
     defined it is gone, so it lives here (homepage-only stylesheet). */
  --nav-h: 72px;
}

/* Section rhythm + snap. Every section is exactly one viewport (100svh), so the
   page reads as a vertical deck: `scroll-snap-type: y mandatory` on the scroller
   lands each section as one screen, on every device. This lives in home.css,
   which is linked ONLY on the homepage, so the `html` rule is homepage-scoped —
   subpages never inherit mandatory snap. The scrubs that once fought a touch user
   are gone (the pipeline + monitor now play on entry, not on scroll), so snap is
   on at every width.

   The nav is a fixed overlay on the homepage (it floats over the content), so the
   snap offset is ZERO: scroll-padding-top:0 on the scroller and scroll-margin-top:0
   on the sections, so each section snaps flush to the top and fills the viewport.
   Fragment jumps still land clear of the 72px nav because every section centres
   its content (align-items:center) mid-screen, well below the bar. */
/* No scroll-behavior:smooth — with mandatory snap it styles only programmatic
   jumps (user scrolling ignores it), and WebKit contests an animated
   programmatic scroll against snap: flights through the pinned sections
   stalled mid-way, and the contested-scroll loop was the last thing the
   crash black box recorded before Safari killed the page. All site code
   passes an explicit behavior. */
html { scroll-snap-type: y mandatory; scroll-padding-top: 0; }
[data-section] { scroll-margin-top: 0; scroll-snap-align: start; scroll-snap-stop: normal; }

/* The eyebrow spectrum bar sits in a flex column in every section head; zero the
   utility's own margins so only the column gap spaces it. */
.oi-hero__eyebrow-wrap .oi-spectrum-bar,
.oi-why__eyebrow-wrap .oi-spectrum-bar,
.oi-engine__eyebrow-wrap .oi-spectrum-bar,
.oi-sampling__eyebrow-wrap .oi-spectrum-bar,
.oi-assurance__eyebrow-wrap .oi-spectrum-bar,
.oi-monitor__eyebrow-wrap .oi-spectrum-bar,
.oi-monitor__cardhead-label .oi-spectrum-bar,
.oi-spoiler__eyebrow-wrap .oi-spectrum-bar,
.oi-close__eyebrow-wrap .oi-spectrum-bar { margin: 0; }
/* The live-monitor card head draws the bar a touch narrower (52px). */
.oi-spectrum-bar--sm { width: 52px; }

@keyframes oi-vmarquee { from { transform: translateY(0); } to { transform: translateY(-50%); } }
@keyframes oi-hmarquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

/* ============================================================ #reach (hero) */
/* A globe canvas fills a dark section; the headline sits over it. Phase 5 draws
   the globe — here the canvas is empty and correctly sized. That is correct. */
.oi-hero {
  position: relative; height: 100svh; overflow: hidden;
  background: transparent; color: var(--text-on-ink);
}
.oi-hero__sticky {
  position: sticky; top: 0; height: 100svh; overflow: hidden; z-index: 6;
}
.oi-hero__dark {
  position: absolute; inset: 0; z-index: 1; background: #0E1626; pointer-events: none;
}
.oi-hero__layer {
  position: absolute; inset: 0; z-index: 2; display: flex; align-items: center;
}
.oi-hero__canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; display: block;
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 calc(100% - 140px), transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0, #000 calc(100% - 140px), transparent 100%);
}
.oi-hero__scrim {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    radial-gradient(105% 92% at 3% 26%, rgba(8,13,24,0.97) 0%, rgba(8,13,24,0.68) 32%, rgba(8,13,24,0) 60%),
    linear-gradient(270deg, rgba(8,13,24,0.86) 0%, rgba(8,13,24,0.5) 26%, rgba(8,13,24,0) 50%);
}
.oi-hero__inner {
  position: relative; z-index: 3; width: 100%;
  max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter);
}
.oi-hero__scrub { max-width: 780px; padding: var(--nav-h) 0 14px; }
.oi-hero__eyebrow-wrap { display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px; }
.oi-hero__eyebrow {
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--amber-400);
}
/* Fluid through 390/834/1440 -> 30/~43/60px. One clamp, no step at the
   breakpoints (spec §5.5.6). The mockups' floor token (2.5rem) is too big for a
   phone, so this drops below it, hitting the design's 30px at 390. */
.oi-hero__headline {
  font-size: clamp(1.875rem, 2.86vw + 1.18rem, 3.75rem);
  letter-spacing: -.025em; margin: 0; color: #fff; line-height: 1.04;
  text-shadow: 0 2px 34px rgba(8,13,24,0.72), 0 1px 4px rgba(8,13,24,0.55);
}
.oi-hero__sub {
  font-size: var(--text-body-lg); line-height: 1.6; color: #d3dbe9;
  margin: 22px 0 0; max-width: 52ch; text-shadow: 0 1px 16px rgba(8,13,24,0.8);
}
.oi-hero__cta-row { display: flex; gap: 14px; margin-top: 30px; flex-wrap: wrap; }

/* ============================================================ #why (2×2) */
/* Never collapses to one column — 2×2 at every width (verified). */
.oi-why {
  position: relative; z-index: 1;
  background: var(--paper-2); border-top: 1px solid var(--line);
  height: 100svh; overflow: hidden; display: flex;
  /* safe center: centre when the content fits, but top-align (clearing the
     fixed nav) when it is too tall — the topmost element never tucks under. */
  align-items: safe center;
  /* Reserve the fixed nav so centered content never tucks under it (the nav is
     a 72px overlay). box-sizing:border-box keeps the section one viewport. */
  padding-block: var(--nav-h) clamp(16px, 3vh, 32px);
}
.oi-why__inner {
  width: 100%; max-width: var(--container); margin: 0 auto;
  /* Deck spacing: ~nav clearance up top, a slim floor. Was clamp(4.5rem,8vw,8rem)
     both sides (up to 230px total) — far too tall for a one-viewport section. */
  padding: clamp(76px, 10vh, 104px) var(--gutter) clamp(22px, 5vh, 48px);
}
.oi-why__head { max-width: 880px; margin-bottom: clamp(22px, 3.4vh, 52px); }
.oi-why__eyebrow-wrap { display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px; }
.oi-why__eyebrow {
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--amber-600);
}
/* Fluid through the design's per-device anchors: 24.8px @390 (mobile inline
   1.55rem), ~38px @834, 56px @1440 (desktop clamp ceiling 3.5rem). One
   continuous clamp — no step, and no frozen-too-big mobile size (the ported
   desktop floor was 2.3rem/36.8px, 12px over the mobile design). */
.oi-why__title {
  font-family: var(--font-sans); font-size: clamp(1.55rem, 2.97vw + 0.826rem, 3.5rem);
  font-weight: 700; letter-spacing: -.024em; line-height: 1.04;
  color: var(--navy-700); margin: 0; text-wrap: balance;
}
.oi-why__grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(20px, 2vw, 28px);
}
/* Tall viewports: lift the content modestly (trim the top padding) but keep the
   cards CONTENT-sized and centred. Stretching the cards to fill the height left
   them hollow with ~220px empty bands below the text — worse than the balanced
   whitespace of a compact centred grid. */
@media (min-height: 780px) {
  .oi-why__inner { padding-top: clamp(16px, 3vh, 40px); }
}
.oi-why__card {
  position: relative; overflow: hidden;
  background: var(--white); border: 1px solid var(--line);
  border-top: 3px solid var(--why-accent);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  padding: clamp(30px, 3vw, 44px);
  transition: transform .2s var(--ease-out), box-shadow .2s var(--ease-out);
}
.oi-why__card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
/* Per-card accent — kept in CSS so why_cards.py stays copy-only.
   amber / green / blue / terracotta, verbatim from the design. */
.oi-why__card:nth-child(1) { --why-accent: #C9842F; }
.oi-why__card:nth-child(2) { --why-accent: #3E9E73; }
.oi-why__card:nth-child(3) { --why-accent: #3E78AE; }
.oi-why__card:nth-child(4) { --why-accent: #C65F3C; }
.oi-why__ghost {
  position: absolute; top: clamp(6px, 1vw, 14px); right: clamp(14px, 2vw, 26px);
  font-family: var(--font-mono); font-size: clamp(72px, 7vw, 104px); font-weight: 700;
  line-height: 1; color: var(--why-accent); opacity: .09; pointer-events: none;
}
.oi-why__num {
  position: relative; font-family: var(--font-mono); font-size: 12px;
  font-weight: 600; letter-spacing: .1em; color: var(--why-accent);
}
.oi-why__card-title {
  position: relative; font-family: var(--font-sans);
  font-size: clamp(1.35rem, 1.9vw, 1.7rem); font-weight: 600;
  letter-spacing: -.017em; line-height: 1.15; color: var(--text-strong); margin: 16px 0 0;
  /* Reserve the ghost numeral's corner: one-line titles otherwise run straight
     through the giant "01"-"04" watermark ("...it gets hard." ending inside the
     numeral — audited at 574/1180/1440/1920). Sized to the numeral's clamp. */
  padding-right: clamp(58px, 5.8vw, 96px);
}
.oi-why__card-body {
  position: relative; font-size: 16.5px; line-height: 1.6;
  color: var(--text-muted); margin: 14px 0 0; max-width: 46ch;
}

/* ============================================================ #engine (pipeline) */
/* One viewport tall (100svh). The inner fills it and centres the pipeline; the
   4-up grid (desktop) / single stacked card (≤1024) flips in on entry, driven by
   an IntersectionObserver + rAF timer in scroll/spine.js (was scroll-scrubbed). */
.oi-engine {
  position: relative; z-index: 1; margin-top: 0; height: 100svh; overflow: hidden; background: transparent;
  /* Nav clearance on the SECTION, and the section itself centres the inner in the
     BALANCED remaining area (was padding-top on the inner + inner height:100%,
     which biased the centred content downward and left a big gap under the nav). */
  padding-block: var(--nav-h) clamp(16px, 3vh, 32px); box-sizing: border-box;
  display: flex; align-items: safe center;
}
.oi-engine__inner {
  position: relative; width: 100%;
  display: flex; flex-direction: column; overflow: hidden;
}
.oi-engine__wrap {
  width: 100%; max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter);
}
.oi-engine__head { max-width: 680px; margin-bottom: 8px; }
.oi-engine__eyebrow-wrap { display: flex; flex-direction: column; gap: 9px; margin-bottom: 12px; }
.oi-engine__eyebrow {
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-accent);
}
.oi-engine__title {
  font-size: clamp(1.65rem, 2.6vw, 2.2rem); letter-spacing: -.02em;
  margin: 0; color: var(--text-strong);
}
.oi-engine__lead {
  font-size: var(--text-body-lg); line-height: 1.55; color: var(--text-muted);
  margin: 18px 0 0; max-width: 58ch;
}
.oi-engine__rail-wrap { position: relative; margin-top: 54px; }
.oi-engine__rail {
  position: relative; height: 2px; background: var(--line);
  margin: 0 6px 36px; border-radius: 2px;
}
.oi-engine__rail-fill {
  position: absolute; left: 0; top: 0; height: 100%; width: 0%;
  background: var(--spectrum); border-radius: 2px;
}
.oi-engine__pulse {
  position: absolute; top: 50%; left: 0%; width: 13px; height: 13px;
  margin: -6.5px 0 0 -6.5px; border-radius: 50%; background: var(--amber-400);
  box-shadow: 0 0 0 6px rgba(226,162,76,.18); opacity: 0;
}
.oi-engine__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }

.oi-pipe {
  --pipe-text: var(--pipe-accent);
  position: relative; perspective: 1500px; -webkit-perspective: 1500px;
  display: flex; flex-direction: column;
}
.oi-pipe__inner {
  position: relative; flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  transform-style: preserve-3d; -webkit-transform-style: preserve-3d;
  transition: transform .72s cubic-bezier(.62,.04,.2,1);
  transform: rotateY(0deg); will-change: transform;
}
.oi-pipe__back {
  position: relative; flex: 1;
  display: flex; flex-direction: column; gap: 10px;
  padding: 20px; box-sizing: border-box;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); overflow: hidden;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  transform: rotateY(180deg);
}
.oi-pipe__front {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: 14px; padding: 22px; box-sizing: border-box;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); overflow: hidden;
  -webkit-backface-visibility: hidden; backface-visibility: hidden;
  transform: rotateY(0deg);
}
.oi-pipe__bar {
  position: absolute; left: 0; top: 0; height: 3px; width: 100%; background: var(--pipe-accent);
}
.oi-pipe__tag { display: flex; align-items: center; gap: 10px; }
.oi-pipe__dot {
  width: 10px; height: 10px; border-radius: 50%; background: var(--pipe-accent);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--pipe-accent) 15%, transparent);
  flex: 0 0 auto;
}
.oi-pipe__eyebrow {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--pipe-text);
}
.oi-pipe__title {
  font-size: var(--text-h4); font-weight: 700; color: var(--text-strong); letter-spacing: -.01em;
}
.oi-pipe__body { font-size: var(--text-body-sm); color: var(--text-muted); line-height: 1.5; }
.oi-pipe__stats {
  margin-top: auto; padding-top: 14px; border-top: 1px solid var(--line-soft);
  display: flex; gap: 20px;
}
.oi-pipe__stat { display: flex; flex-direction: column; gap: 2px; }
.oi-pipe__stat-value {
  font-family: var(--font-mono); font-size: 15px; font-weight: 500; color: var(--text-strong);
}
.oi-pipe__stat-label {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-faint);
}
.oi-pipe__explore {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  height: 38px; border-radius: var(--radius-md);
  border: 1.5px solid color-mix(in srgb, var(--pipe-text) 50%, transparent);
  color: var(--pipe-text); font-size: 13.5px; font-weight: 600; text-decoration: none;
  transition: background .16s, color .16s, border-color .16s;
}
.oi-pipe__explore:hover { background: var(--pipe-text); border-color: var(--pipe-text); color: #fff; }
.oi-pipe__arrow {
  position: absolute; top: 14px; right: 14px; width: 34px; height: 34px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--pipe-accent) 13%, transparent);
  color: var(--pipe-text); text-decoration: none;
  transition: transform .14s var(--ease-out), background .14s, color .14s;
}
.oi-pipe__arrow:hover { transform: translateX(2px); background: var(--pipe-text); color: #fff; }
.oi-pipe__icon {
  width: 76px; height: 76px; border-radius: 50%; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--pipe-accent) 13%, transparent);
  color: var(--pipe-text);
}
.oi-pipe__front-label { display: flex; flex-direction: column; gap: 5px; }
.oi-pipe__front .oi-pipe__eyebrow { font-size: 11px; letter-spacing: .14em; }

/* ============================================================ #sampling (marquee) */
.oi-sampling {
  background: transparent; position: relative;
  height: 100svh; overflow: hidden; display: flex;
  /* safe center: centre when the content fits, but top-align (clearing the
     fixed nav) when it is too tall — the topmost element never tucks under. */
  align-items: safe center;
  padding: var(--nav-h) 0 14px;
}
.oi-sampling__inner {
  width: 100%; max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter);
}
.oi-sampling__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 384px;
  gap: clamp(32px, 5vw, 72px); align-items: center;
}
.oi-sampling__copy { max-width: 560px; }
.oi-sampling__eyebrow-wrap { display: flex; flex-direction: column; gap: 9px; margin-bottom: 12px; }
.oi-sampling__eyebrow {
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-accent);
}
/* Fluid through the design's anchors: 20.8px @390 (mobile inline 1.3rem),
   ~27px @834, 35.2px @1440 (ceiling 2.2rem). Diverges from #engine on mobile —
   the design gives #sampling a smaller phone heading (1.3rem vs 1.65rem). */
.oi-sampling__title {
  font-size: clamp(1.3rem, 1.37vw + 0.966rem, 2.2rem); letter-spacing: -.02em;
  margin: 0; color: var(--text-strong);
}
/* --text-size-body (16px): the design writes var(--text-body), which is a COLOR
   token in ds.css (Phase 1 renamed the size). See site.css's same note. */
.oi-sampling__lead {
  font-size: var(--text-size-body); line-height: 1.5; color: var(--text-muted);
  margin: 14px 0 18px; max-width: 54ch;
}
.oi-sampling__points { display: flex; flex-direction: column; gap: 12px; }
.oi-sampling__point { display: flex; gap: 14px; }
.oi-sampling__bullet {
  margin-top: 7px; width: 7px; height: 7px; border-radius: 50%;
  background: var(--amber-500); flex: 0 0 auto;
}
.oi-sampling__point-title { font-weight: 600; color: var(--text-strong); margin-bottom: 2px; }
.oi-sampling__point-body { font-size: 15px; color: var(--text-muted); line-height: 1.55; }
.oi-sampling__cta-row { display: flex; align-items: center; gap: 16px; margin-top: 26px; flex-wrap: wrap; }

/* Navy pill CTA — the design's "Run free feasibility" / "Learn More" button.
   Distinct from .oi-btn-dark (42px, radius-md): this is 50px on the pill radius. */
.oi-btn-navy {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  height: 50px; padding: 0 26px;
  font-family: var(--font-sans); font-size: var(--text-size-body); font-weight: 600; line-height: 1;
  color: #fff; background: var(--navy-800); border: none; border-radius: var(--radius-pill);
  cursor: pointer; text-decoration: none;
  transition: background .18s ease, transform .18s ease;
}
.oi-btn-navy:hover { background: var(--navy-900); transform: translateY(-1px); }
.oi-btn-navy__arrow { font-size: 1.1em; line-height: 0; }

.oi-rib__head { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
.oi-rib__head-label {
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--amber-600); white-space: nowrap;
}
.oi-rib__head-rule { flex: 1; height: 1px; background: var(--line); }
.oi-rib__viewport {
  position: relative; height: min(680px, 78svh); overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 90px, #000 calc(100% - 90px), transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 90px, #000 calc(100% - 90px), transparent 100%);
}
.oi-rib__track {
  display: flex; flex-direction: column; gap: 16px;
  animation: oi-vmarquee 48s linear infinite; will-change: transform;
}
.oi-rib__card {
  flex: 0 0 auto; box-sizing: border-box; width: 100%; height: 222px; margin: 0;
  display: flex; flex-direction: column; gap: 13px; padding: 22px 22px 20px;
  background: var(--white); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.oi-rib__card-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.oi-rib__label {
  font-family: var(--font-mono); font-size: 10px; font-weight: 600;
  letter-spacing: .09em; color: var(--amber-600); white-space: nowrap;
}
.oi-rib__card-bar {
  width: 34px; height: 3px; border-radius: 2px; background: var(--spectrum); flex: 0 0 auto;
}
.oi-rib__audience {
  font-family: var(--font-sans); font-size: 19px; font-weight: 600;
  letter-spacing: -.01em; color: var(--text-strong); line-height: 1.2;
}
.oi-rib__qualifier {
  flex: 1; font-family: var(--font-mono); font-size: 11px; letter-spacing: .02em;
  color: var(--text-muted); line-height: 1.4;
}
.oi-rib__stats {
  display: flex; align-items: flex-end; gap: 26px;
  padding-top: 14px; border-top: 1px solid var(--line-soft);
}
.oi-rib__stat { display: flex; flex-direction: column; gap: 2px; }
.oi-rib__stat-value {
  font-family: var(--font-mono); font-size: 21px; font-weight: 600;
  letter-spacing: -.01em; color: var(--navy-800); line-height: 1;
}
.oi-rib__stat-label {
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 500;
  letter-spacing: .07em; color: var(--text-muted);
}

/* ============================================================ <=1024 (device) */
@media (max-width: 1024px) {
  /* #engine serial flip setup: stack the four cards absolutely; the entry-driven
     rAF in spine.js swings them 90→180→270 in sequence. The section is one
     viewport (100svh) — the height override the scroll runway needed is gone. */
  .oi-engine__grid {
    display: block; position: relative; height: 470px;
    max-width: 600px; margin-left: auto; margin-right: auto; gap: 0;
  }
  .oi-pipe { position: absolute; inset: 0; transition: none; }

  /* Tablet mirrors the user's MOBILE design. The tablet 2-col sampling, the
     interactive spoiler card, and the on-chart monitor labels all cramp/break at
     tablet widths; the mobile design already solves them (stack + simplify).
     Apply those treatments across the whole tablet range; >1024 keeps desktop. */

  /* #reach: drop the globe to the bottom 46% (mobile treatment) so the lead copy
     sits ABOVE it on clean dark ground instead of over the bright landmass with
     city labels bleeding through the paragraph. The mask gains a TOP fade the
     full-viewport desktop canvas doesn't need: at squat aspect ratios the
     sphere (R = min(.42w, .66h), cy = .62h) rides above the canvas top and the
     clip drew a hard horizontal line across the page. */
  .oi-hero__canvas {
    inset: auto 0 0 0; height: 46%;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 96px, #000 calc(100% - 120px), transparent 100%);
            mask-image: linear-gradient(to bottom, transparent 0, #000 96px, #000 calc(100% - 120px), transparent 100%);
  }

  /* The scrim exists to shield copy that sits OVER the globe — desktop only.
     Here the copy is above the canvas, so the scrim just murdered the globe's
     left-side colour and dimmed the city labels. */
  .oi-hero__scrim { background: none; }

  /* With the globe at the bottom, the copy anchors under the nav instead of
     keeping the desktop's vertical centering — centered copy left a dead band
     below the nav and dropped the CTA onto the landmass (seen at ~950x840). */
  .oi-hero__layer { align-items: flex-start; }
  .oi-hero__scrub { padding-top: calc(var(--nav-h) + clamp(26px, 7vh, 72px)); }

  /* The design's min-height model is restored in the FINAL @media block at the
     end of this file — it must come after every section's base rule to win the
     cascade (same specificity, source order decides). */

  /* Sampling: stack the copy over a HORIZONTAL marquee (was 2-col, which crushed
     the text column and left the marquee sparse with half-cut cards). Cap +
     centre the column so a WIDE-but-short landscape tablet (1024x768) doesn't
     strand the content in the left half with a dead right column. */
  .oi-sampling__grid { grid-template-columns: minmax(0, 1fr); gap: clamp(22px, 3.4vh, 40px); max-width: 820px; margin-inline: auto; }
  .oi-rib__viewport {
    width: 100%; max-width: 100%; height: auto;
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
  }
  .oi-rib__track { flex-direction: row; width: max-content; align-items: stretch; animation-name: oi-hmarquee; animation-duration: 64s; }
  .oi-rib__card { width: min(86vw, 360px); height: auto; min-height: 172px; }
  /* Spoiler + monitor mobile-mirror rules live in a LATER @media block (after the
     base .oi-spmini rules) so they actually win — see below. */
}

/* ============================================================ <=560 (phone) */
/* Only the narrowest phones collapse to one column — below this a 2-col marquee
   card is too cramped. The single-column content is short enough at phone width
   (tall viewport) that the eyebrow still clears the nav. */
@media (max-width: 560px) {
  .oi-sampling__grid { grid-template-columns: minmax(0, 1fr); }
}

/* ============================================================ <=480 (phone) */
@media (max-width: 480px) {
  /* Globe drops to the bottom 46% (empty until Phase 5; carried for it). */
  .oi-hero__canvas { inset: auto 0 0 0; height: 46%; }

  /* #why stays 2×2 — only the gap tightens. */
  .oi-why__grid { gap: 12px; }

  /* #engine stack shortens on the phone; slightly roomier card padding. The grid
     is a FIXED height (the flip stacks cards position:absolute, so it can't be
     content-sized) — keep it close to the card's real content height so the
     single device card doesn't leave a big empty void between body and stats. */
  .oi-engine__grid { height: 316px; }
  .oi-pipe__back { padding: 22px; }

  /* Audiences marquee goes horizontal: cards flow in a row, 86vw wide (capped),
     animating -50% on X. */
  .oi-rib__viewport {
    width: 100%; max-width: 100%; height: auto;
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 20px, #000 calc(100% - 20px), transparent 100%);
  }
  .oi-rib__track {
    flex-direction: row; width: max-content; align-items: stretch;
    animation-name: oi-hmarquee; animation-duration: 64s;
  }
  .oi-rib__card {
    width: 86vw; max-width: 330px; height: auto; min-height: 172px;
    padding: 14px 16px; gap: 8px;
  }
}

/* ============================================================ #assurance */
/* "Data you can defend." + the <signal-refine> graphic. The element paints
   NOTHING until Phase 5 — an empty box is the correct static state. Both
   orientation variants ship (attributes can't flip in CSS); one shows per
   breakpoint. */
.oi-assurance {
  position: relative; height: 100svh; background: var(--paper);
  overflow: hidden; display: flex; align-items: safe center;
  padding-block: var(--nav-h) clamp(16px, 3vh, 32px);  /* clear the fixed nav */
}
.oi-assurance__inner {
  position: relative; z-index: 3; width: 100%; max-width: var(--container);
  margin: 0 auto; padding: clamp(76px, 11vh, 104px) var(--gutter) clamp(20px, 4vh, 32px);
  display: flex; flex-direction: column; align-items: center; gap: clamp(18px, 2.6vh, 40px);
}
.oi-assurance__head { max-width: 660px; text-align: center; }
.oi-assurance__eyebrow-wrap {
  display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 20px;
}
.oi-assurance__eyebrow {
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-accent);
}
.oi-assurance__title {
  font-size: clamp(1.95rem, 3.2vw, 2.9rem); letter-spacing: -.025em;
  line-height: 1.04; margin: 0; color: var(--text-strong);
}
.oi-assurance__lead {
  font-size: var(--text-body-lg); line-height: 1.55; color: var(--text-muted);
  margin: 22px auto 0; max-width: 56ch;
}
.oi-assurance__cta-row {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  margin-top: 28px; flex-wrap: wrap;
}
.oi-assurance__graphic { position: relative; width: 100%; }
/* The graphic is the section's tallest element; cap it by viewport height so it
   shrinks on short/laptop screens rather than pushing the caption off-screen. */
.oi-assurance__graphic--h { height: clamp(180px, 34vh, 392px); }
.oi-assurance__graphic--v { display: none; height: clamp(230px, 40vh, 354px); }

/* Fill the height on tall viewports (tablet portrait, tall phones): stretch the
   inner to the full section and let the funnel graphic grow into the leftover
   space, so the section reads as composed to the viewport rather than a compact
   block stranded in the centre with dead bands above and below. Height-gated so
   short screens keep their scale-to-fit treatment. */
@media (min-height: 780px) {
  .oi-assurance { align-items: stretch; }
  .oi-assurance__inner { justify-content: center; padding-top: clamp(14px, 2.4vh, 30px); }
  .oi-assurance__graphic--h { flex: 1 1 auto; height: auto; min-height: 260px; max-height: 560px; }
  .oi-assurance__graphic--v { flex: 1 1 auto; height: auto; min-height: 280px; max-height: 520px; }
}
.oi-assurance__signal { display: block; }
.oi-assurance__caption {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center;
  gap: 9px 18px; font-family: var(--font-mono); font-size: 13px; letter-spacing: .02em;
  color: var(--text-muted); text-align: center;
}
.oi-assurance__caption-item { white-space: nowrap; }
.oi-assurance__caption-strong { color: var(--text-strong); font-weight: 600; }
.oi-assurance__caption-dot { color: var(--amber-600); }
/* The caption fits one line only above ~800px (its natural width is ~749px);
   below that the row wraps and the amber middots strand at line ends. When it
   can't be one line, stack the three items and drop the separators. */
@media (max-width: 820px) {
  .oi-assurance__caption { flex-direction: column; gap: 6px; }
  .oi-assurance__caption-dot { display: none; }
}

/* ============================================================ #monitor */
/* One viewport tall (100svh); the stage fills it. The chart + summary card play
   in on ENTRY (IntersectionObserver + rAF in graphics/monitor-scrub.js), not on
   scroll — the section is no longer taller than the viewport.
   Progressive enhancement: [data-mon-grow] ships the pre-scrub transform
   (scale(1 0.0001) — curves collapsed onto the baseline). Without JS we reset it
   so the hand-authored SVG reads in full. WITH JS, monitor-scrub.js drives that
   same transform ATTRIBUTE frame-by-frame — and a CSS `transform` property beats
   the SVG attribute, so this reset must be gated to no-JS or it would pin the
   chart flat forever (see the html:not(.js) selector below). */
.oi-monitor { position: relative; height: 100svh; overflow: hidden; background: var(--paper); }
.oi-monitor__stage { position: relative; height: 100svh; overflow: hidden; }
.oi-monitor__bg { position: absolute; inset: 0; z-index: 1; }
/* The chart's SVG text labels (THREATS FILTERED, LIVE MONITOR, QUOTA, VERIFIED
   COMPLETES) collide with each other and the copy, and clip at the viewport edge
   even on desktop ("QUOTA" -> "QUO A"). The growth chart reads fine as a pure
   line flourish, so drop the labels at every width. */
.oi-monitor__bg text { display: none; }
.oi-monitor__svg {
  position: absolute; inset: 0; display: block;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 120px, #000 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 120px, #000 100%);
}
html:not(.js) .oi-monitor__svg [data-mon-grow] { transform: none; }
.oi-monitor__scrim {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  /* Solid through the copy column (which ends ~46% at 1920), then release — the
     old 28%/46% stops let the dashed target line and the orange curve print
     through the paragraph at 1280-1920 (audited). The chart stays visible right
     of ~66%, where the dashboard card and its margin live. */
  background: linear-gradient(90deg, rgba(251,250,247,0.6) 0%, rgba(251,250,247,0.42) 55%, rgba(251,250,247,0) 82%);
}
.oi-monitor__copy {
  position: relative; z-index: 3; height: 100%; display: flex; align-items: center;
  /* The animated scrim's element opacity is JS-driven and CAPS AT 0.66, so no
     scrim gradient alone can fully shield the copy — the chart's dashed target
     line and curve printed through the paragraph at 1280-1920. The copy layer
     carries its own permanent paper backdrop instead. */
  /* Translucent, not solid: the chart's colour must survive behind the copy
     (client call 2026-07-22) — the wash keeps the text comfortably readable
     while the curves show through as soft traces. */
  background: linear-gradient(90deg, rgba(251,250,247,0.82) 0%, rgba(251,250,247,0.72) 46%, rgba(251,250,247,0) 70%);
}
.oi-monitor__panel {
  width: 100%; max-width: var(--container); margin: 0 auto; padding: var(--nav-h) var(--gutter) 0;
}
.oi-monitor__panel-inner { max-width: 530px; }
.oi-monitor__eyebrow-wrap { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.oi-monitor__eyebrow {
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-accent);
}
/* Fluid through the design's anchors: 24px @390 (mobile inline 1.5rem),
   ~33px @834, 46.4px @1440 (ceiling 2.9rem). Was frozen at 2rem/32px on the
   phone — 8px over the mobile design. */
.oi-monitor__title {
  font-size: clamp(1.5rem, 2.13vw + 0.98rem, 2.9rem); letter-spacing: -.025em;
  line-height: 1.04; margin: 0; color: var(--text-strong);
}
.oi-monitor__lead {
  font-size: var(--text-body-lg); line-height: 1.6; color: var(--text-muted);
  margin: 20px 0 0; max-width: 42ch;
}
.oi-monitor__panel-foot { margin-top: 30px; padding-top: 24px; border-top: 1px solid var(--line); }
.oi-monitor__cardwrap { position: absolute; inset: 0; z-index: 5; pointer-events: none; }
.oi-monitor__cardwrap-inner {
  height: 100%; max-width: var(--container); margin: 0 auto;
  padding: calc(var(--nav-h) + 10px) var(--gutter) 26px; box-sizing: border-box;
  display: flex; align-items: center; justify-content: flex-end;
}
.oi-monitor__summary {
  width: min(700px, calc(100% - 558px)); max-height: 100%; overflow: auto;
  pointer-events: auto; background: var(--paper-2); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: clamp(20px, 2.2vw, 28px);
}
.oi-monitor__cardhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.oi-monitor__cardhead-label { display: flex; flex-direction: column; gap: 9px; }
.oi-monitor__cardhead-eyebrow {
  font-family: var(--font-mono); font-size: 11.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-accent); white-space: nowrap;
}
.oi-monitor__cardhead-status {
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .05em; color: var(--text-muted);
}
.oi-monitor__status-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--verify-500);
  box-shadow: 0 0 0 3px rgba(47,125,91,.15); flex: 0 0 auto;
}
.oi-monitor__health { margin-top: 22px; }
.oi-monitor__section-label {
  display: block; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-faint); margin-bottom: 11px;
}
.oi-monitor__health-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px 18px; }
.oi-monitor__stat { display: flex; flex-direction: column; gap: 5px; }
.oi-monitor__stat--completes { gap: 7px; }
.oi-monitor__stat-value {
  font-family: var(--font-mono); font-size: clamp(1.4rem, 2.2vw, 1.8rem);
  font-weight: 500; color: var(--text-strong); line-height: 1;
}
.oi-monitor__stat-label {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted); line-height: 1.3;
}
/* Progress-track fill: the design's warm-grey "well" colour. --surface-inset is
   the semantic token for a sunken surface (its docs read "subtle wells") — the
   right token for a track, aliasing the same palette value the device-frame
   surround uses. Referencing the surround token directly is banned on the
   homepage (it must never be a page background), and a magic hex would drift
   from the token; the inset alias is neither. */
.oi-monitor__bar-track {
  display: block; height: 5px; border-radius: 3px; background: var(--surface-inset);
  overflow: hidden; margin-top: 2px;
}
.oi-monitor__bar-fill { display: block; height: 100%; border-radius: 3px; background: var(--verify-500); }
.oi-monitor__stat-note { font-family: var(--font-mono); font-size: 10.5px; color: var(--text-faint); line-height: 1.3; }
.oi-monitor__week { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--line); }
.oi-monitor__week-rows { display: flex; flex-direction: column; gap: 8px; }
.oi-monitor__week-row {
  display: flex; align-items: flex-start; gap: 11px; padding: 9px 12px;
  background: var(--white); border: 1px solid var(--line-soft); border-radius: var(--radius-md);
}
.oi-monitor__week-dot {
  flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%;
  background: var(--amber-500); margin-top: 6px;
}
.oi-monitor__week-text { flex: 1; font-size: 13px; line-height: 1.45; color: var(--text-body); }
.oi-monitor__note {
  margin: 22px 0 0; font-family: var(--font-mono); font-size: 11px;
  letter-spacing: .03em; color: var(--text-faint);
}

/* ============================================================ #spoiler */
/* The demo card's two canvases ship empty (Phase 5 draws them). The FSM
   overlays ship in the DOM; the "proposed" coding frame renders at rest so the
   eleven rows + the data-driven total are visible now. */
.oi-spoiler {
  background: transparent; position: relative; height: 100svh; overflow: hidden;
  display: flex; align-items: safe center; padding: var(--nav-h) 0 14px;
}
.oi-spoiler__inner { width: 100%; max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter); }
.oi-spoiler__split { display: grid; grid-template-columns: 1fr 1.08fr; gap: var(--space-9); align-items: center; }
.oi-spoiler__eyebrow-wrap { display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px; }
.oi-spoiler__eyebrow {
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--amber-600);
}
.oi-spoiler__logo { display: block; width: min(330px, 80%); height: auto; aspect-ratio: 1150 / 1024; margin: 0 0 30px; }
/* Fluid through the design's anchors: 26.4px @390 (mobile #spoiler h2 override
   1.65rem !important), ~34px @834, 44.8px @1440 (ceiling 2.8rem). Was frozen at
   2rem/32px on the phone. */
.oi-spoiler__title {
  font-size: clamp(1.65rem, 1.75vw + 1.223rem, 2.8rem); font-weight: 700; letter-spacing: -.025em;
  line-height: 1.06; margin: 0; color: var(--text-strong);
}
.oi-spoiler__title em { font-style: italic; color: var(--text-strong); }
.oi-spoiler__tagline {
  margin-top: 14px; font-family: var(--font-mono); font-size: 12.5px; font-weight: 500;
  letter-spacing: .22em; text-transform: uppercase; color: var(--text-muted);
}
/* Keep "EVIDENCE · CONFIDENCE · CONTROL" on ONE line at narrow widths — when it
   wraps, a separator dot dangles at the line end. Tighter tracking + size fits
   it at 360. */
@media (max-width: 700px) {
  .oi-spoiler__tagline { font-size: 11.5px; letter-spacing: .13em; white-space: nowrap; }
}
.oi-spoiler__lead {
  font-size: var(--text-size-body); line-height: 1.55; color: var(--text-muted);
  margin: 22px 0 22px; max-width: 46ch;
}
/* The red "Request a demo" button (design #C82828 → #A81F1F on hover). */
.oi-spoiler__demo {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  height: 48px; padding: 0 26px; font-family: var(--font-sans); font-size: var(--text-size-body);
  font-weight: 600; line-height: 1; border-radius: var(--radius-md);
  border: 1.5px solid #C82828; background: #C82828; color: #fff;
  box-shadow: var(--shadow-sm); cursor: pointer; transition: background .18s, transform .12s;
}
.oi-spoiler__demo:hover { background: #A81F1F; border-color: #A81F1F; }
.oi-spoiler__demo:active { transform: scale(.98); }

/* -- the demo card -- */
.oi-sp {
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md); overflow: hidden;
}
.oi-sp__head {
  display: flex; align-items: center; gap: 10px; padding: 15px 20px;
  border-bottom: 1px solid var(--line-soft); background: var(--paper-2);
}
.oi-sp__head-chip { width: 22px; height: 4px; border-radius: 2px; background: var(--spectrum); flex: 0 0 auto; }
.oi-sp__head-label { font-family: var(--font-mono); font-size: 12px; letter-spacing: .05em; color: var(--text-muted); }
.oi-sp__status {
  margin-left: auto; font-family: var(--font-mono); font-size: 10px; font-weight: 600;
  letter-spacing: .1em; color: var(--amber-700); background: var(--amber-100);
  padding: 4px 9px; border-radius: var(--radius-pill); min-width: 62px; text-align: center;
}
.oi-sp__body { position: relative; height: 454px; overflow: hidden; }
.oi-sp__canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; }
.oi-sp__analyzing {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 20px; padding: 0 40px; background: var(--white);
  opacity: 0; pointer-events: none;
}
.oi-sp__blink { display: flex; gap: 6px; align-items: center; height: 22px; }
.oi-sp__blink span { width: 6px; height: 18px; border-radius: 3px; background: var(--accent-strong); opacity: .3; }
.oi-sp__analyzing-text { font-family: var(--font-mono); font-size: 13.5px; color: var(--text-muted); letter-spacing: .03em; }
.oi-sp__ancount { color: var(--text-strong); font-weight: 500; }
.oi-sp__antrack { width: min(280px, 72%); height: 5px; border-radius: 3px; background: var(--surface-inset); overflow: hidden; }
.oi-sp__anprog { display: block; height: 100%; width: 0%; background: var(--spectrum); border-radius: 3px; }
.oi-sp__analyzing-note { font-family: var(--font-mono); font-size: 10.5px; color: var(--text-faint); letter-spacing: .05em; }
.oi-sp__proposed {
  position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--white);
  opacity: 1; /* resting frame (design ships 0 as a Phase-5 entrance state) */
}
.oi-sp__prompt { padding: 15px 20px 9px; font-size: 12px; color: var(--text-muted); }
.oi-sp__grid { flex: 1; display: grid; grid-template-columns: 1fr 1fr; min-height: 0; }
.oi-sp__rows { padding: 2px 6px 2px 14px; display: flex; flex-direction: column; justify-content: center; }
.oi-sp__prow {
  display: flex; align-items: center; gap: 10px; padding: 6px 10px; border-radius: 7px;
  cursor: default; transition: background .14s;
}
.oi-sp__prow-num { font-family: var(--font-mono); font-size: 10.5px; color: var(--text-faint); width: 16px; flex: 0 0 auto; }
.oi-sp__prow-name {
  flex: 1; font-size: 12.5px; font-weight: 500; color: var(--text-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.oi-sp__prow-count { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); }
.oi-sp__prow-swatch { width: 8px; height: 8px; border-radius: 2px; flex: 0 0 auto; }
/* The eleven code swatches — kept in CSS so spoiler_codes.py stays copy-only. */
.oi-sp__rows .oi-sp__prow:nth-child(1)  .oi-sp__prow-swatch { background: rgba(42,111,219,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(2)  .oi-sp__prow-swatch { background: rgba(62,120,174,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(3)  .oi-sp__prow-swatch { background: rgba(47,143,107,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(4)  .oi-sp__prow-swatch { background: rgba(62,158,115,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(5)  .oi-sp__prow-swatch { background: rgba(201,132,47,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(6)  .oi-sp__prow-swatch { background: rgba(226,162,76,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(7)  .oi-sp__prow-swatch { background: rgba(198,95,60,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(8)  .oi-sp__prow-swatch { background: rgba(168,83,58,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(9)  .oi-sp__prow-swatch { background: rgba(75,94,126,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(10) .oi-sp__prow-swatch { background: rgba(110,126,153,0.9); }
.oi-sp__rows .oi-sp__prow:nth-child(11) .oi-sp__prow-swatch { background: rgba(154,166,184,0.9); }
.oi-sp__verbatims {
  padding: 15px 20px 14px; border-left: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 10px; background: var(--paper);
}
.oi-sp__verbatims-label { font-family: var(--font-mono); font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--text-faint); }
.oi-sp__ex-name { font-size: 14px; font-weight: 600; color: var(--text-strong); }
.oi-sp__ex-quotes { display: flex; flex-direction: column; gap: 9px; }
.oi-sp__ex-count { margin-top: auto; padding-top: 8px; font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); }
.oi-sp__approve-row {
  display: flex; align-items: center; gap: 12px; padding: 12px 20px;
  border-top: 1px solid var(--line-soft); background: var(--paper-2);
}
.oi-sp__approve {
  margin-left: auto; display: inline-flex; align-items: center; gap: 8px; height: 40px;
  padding: 0 20px; font-family: var(--font-sans); font-size: 13.5px; font-weight: 600;
  border-radius: var(--radius-md); border: 1.5px solid var(--navy-800); background: var(--navy-800);
  color: #fff; cursor: pointer; transition: background .16s, transform .1s;
}
.oi-sp__approve:hover { background: var(--navy-950); }
.oi-sp__approve:active { transform: scale(.98); }
.oi-sp__chart {
  position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--white);
  opacity: 0; pointer-events: none;
}
.oi-sp__chart-head { padding: 14px 20px 9px; display: flex; align-items: center; gap: 12px; }
.oi-sp__chart-check {
  display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
  border-radius: 50%; background: var(--verify-100); flex: 0 0 auto;
}
.oi-sp__chart-titles { flex: 1; min-width: 0; }
.oi-sp__chart-title { font-size: 14px; font-weight: 600; color: var(--text-strong); }
.oi-sp__chart-sub { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; }
.oi-sp__modes { display: inline-flex; gap: 2px; background: var(--surface-inset); border-radius: var(--radius-pill); padding: 3px; flex: 0 0 auto; }
.oi-sp__mode {
  position: relative;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; letter-spacing: .03em;
  padding: 5px 10px; border: none; border-radius: var(--radius-pill); cursor: pointer;
  transition: background .15s, color .15s, box-shadow .15s; background: transparent; color: var(--text-muted);
}
/* Touch target: the segmented control is ~24px tall by design, under the 44px
   mandate. A transparent ::after grows the *hit area* to 44px without changing
   a pixel of the visible control. It extends ~10px above and below the button,
   which stays clear of the chart canvas below (measured: >=8px gap at 1440,
   ~29px at 834) — so it never steals the canvas's hover. Width is the button's
   own (already >=46px), separated from its neighbours by the 2px gap. */
.oi-sp__mode::after {
  content: ""; position: absolute; left: 0; right: 0;
  top: 50%; transform: translateY(-50%);
  height: 44px; min-height: 100%;
}
.oi-sp__mode--on { background: var(--white); color: var(--text-strong); box-shadow: var(--shadow-sm); }
.oi-sp__chart-canvas { flex: 1; width: 100%; display: block; min-height: 0; }
.oi-sp__ctip {
  padding: 10px 20px; border-top: 1px solid var(--line-soft); background: var(--paper-2);
  font-size: 12px; color: var(--text-muted); min-height: 18px;
}
.oi-sp__foot {
  display: flex; align-items: center; gap: 10px; padding: 13px 20px;
  border-top: 1px solid var(--line-soft); background: var(--paper-2);
}
.oi-sp__foot-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--verify-500); flex: 0 0 auto; }
.oi-sp__foot-text { font-size: 12px; color: var(--text-muted); }

/* -- the phone-only mini card (hidden >480; shown ≤480) -- */
.oi-spmini {
  display: none; margin: 2px 0 22px; background: var(--white); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-md); overflow: hidden;
}
.oi-spmini__head {
  display: flex; align-items: center; gap: 9px; padding: 10px 14px;
  border-bottom: 1px solid var(--line-soft); background: var(--paper-2);
}
.oi-spmini__chip { width: 20px; height: 4px; border-radius: 2px; background: var(--spectrum); flex: 0 0 auto; }
.oi-spmini__label { font-family: var(--font-mono); font-size: 11px; letter-spacing: .05em; color: var(--text-muted); }
.oi-spmini__pill {
  margin-left: auto; font-family: var(--font-mono); font-size: 9.5px; font-weight: 600;
  letter-spacing: .1em; color: var(--amber-700); background: var(--amber-100);
  padding: 3px 8px; border-radius: var(--radius-pill);
}
.oi-spmini__body { padding: 4px 14px 12px; }
.oi-spmini__row { padding: 10px 0 8px; border-bottom: 1px solid var(--line-soft); }
.oi-spmini__row--last { padding: 10px 0 2px; border-bottom: none; }
.oi-spmini__row-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.oi-spmini__row-title { font-size: 13.5px; font-weight: 600; color: var(--text-strong); }
.oi-spmini__row-n { font-family: var(--font-mono); font-size: 10.5px; color: var(--text-faint); }
.oi-spmini__row-pct { margin-left: auto; font-family: var(--font-mono); font-size: 11px; font-weight: 600; }
.oi-spmini__row-pct--good { color: #2F7F5B; }
.oi-spmini__row-pct--warn { color: var(--amber-700); }
.oi-spmini__track { display: block; height: 5px; border-radius: 3px; background: var(--surface-inset); overflow: hidden; }

/* Tablet (<=1024) mirrors the mobile design for #spoiler and #monitor. Placed
   AFTER the base .oi-spmini / .oi-sp rules so it wins (an earlier copy would be
   overridden by the base display:none on .oi-spmini). >1024 keeps the desktop
   interactive card + on-chart labels. */
@media (max-width: 1024px) {
  /* Spoiler: drop the interactive canvas card (its labels don't lay out at this
     width — the whole reason to mirror mobile) and show the static mini card. Cap
     + centre so a wide landscape tablet doesn't strand it in the left half. */
  .oi-spoiler__split { grid-template-columns: 1fr; gap: 22px; max-width: 560px; margin-inline: auto; }
  .oi-sp { display: none; }
  .oi-spmini { display: block; }
  /* Monitor: hide the background chart TEXT labels so they can't collide with the
     lead copy (THREATS FILTERED etc. were overlapping the paragraph). */
  .oi-monitor__bg text { display: none; }
}
.oi-spmini__fill { display: block; height: 100%; border-radius: 3px; }
.oi-spmini__fill--good { background: #3E9E73; }
.oi-spmini__fill--warn { background: var(--amber-500); }
.oi-spmini__foot {
  display: flex; align-items: center; gap: 6px; margin-top: 12px;
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .03em; color: var(--text-faint);
}

/* ============================================================ #close */
.oi-close {
  position: relative; background: var(--navy-900); overflow: hidden;
  height: 100svh; display: flex; align-items: safe center;
  padding-block: var(--nav-h) clamp(16px, 3vh, 32px);  /* clear the fixed nav */
}
.oi-close__glow {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(58% 92% at 88% 2%, rgba(226,162,76,0.16) 0%, rgba(226,162,76,0.05) 34%, rgba(226,162,76,0) 62%);
}
.oi-close__rule { position: absolute; top: 0; left: 0; right: 0; height: 2px; background: var(--spectrum); opacity: .55; z-index: 2; }
.oi-close__inner {
  position: relative; z-index: 3; width: 100%; max-width: var(--container); margin: 0 auto;
  padding: clamp(80px, 11vh, 116px) var(--gutter) clamp(24px, 5vh, 56px);
}
.oi-close__lead-row { display: flex; align-items: center; gap: clamp(28px, 5vw, 72px); flex-wrap: wrap; }
.oi-close__copy { flex: 1 1 440px; max-width: 840px; }
.oi-close__eyebrow-wrap { display: flex; flex-direction: column; gap: 11px; margin-bottom: 22px; }
.oi-close__eyebrow {
  font-family: var(--font-mono); font-size: 12px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--amber-400);
}
/* Fluid through all THREE of the design's explicit anchors: 25.6px @390 (mobile
   #close h2 override 1.6rem !important), 35.2px @834 (tablet override 2.2rem
   !important), 60px @1440 (desktop var(--text-display-l) ceiling 3.75rem). The
   linear term is fitted through the tablet+desktop pair; the 1.6rem floor holds
   across the phone band. Replaces the raw --text-display-l, which froze at
   2.5rem/40px on the phone — 14px over the mobile design. */
.oi-close__title {
  font-family: var(--font-sans); font-size: clamp(1.6rem, 4.09vw + 0.067rem, 3.75rem); font-weight: 700;
  letter-spacing: -.024em; line-height: 1.04; color: #fff; margin: 0; text-wrap: balance;
}
.oi-close__lead { font-size: var(--text-lead); line-height: 1.5; color: #d3dbe9; margin: 20px 0 0; max-width: 60ch; }
.oi-close__lead--mobile { display: none; }
.oi-close__grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(238px, 1fr)); gap: clamp(20px, 3vh, 40px) var(--space-8);
  margin-top: clamp(24px, 5vh, 64px); padding-top: clamp(22px, 4vh, 56px); border-top: 1px solid var(--border-ink);
}
/* auto-fit misfires in two bands: it yields exactly 2 columns from ~481-819
   (orphaning the 3rd card bottom-left — audited at 574) and again at 820-900
   (audited at 834, where 3x238px + gaps just misses). Single column through
   819, then an explicit 3-up until auto-fit can genuinely fit three. */
@media (min-width: 481px) and (max-width: 819px) {
  .oi-close__grid { grid-template-columns: 1fr; }
}
@media (min-width: 820px) and (max-width: 1024px) {
  .oi-close__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Tall viewports: lift the content modestly but keep it centred as one block.
   space-between distributed the rows into disjoint clumps with big voids between
   the lead, the icon rows, and the CTAs. */
@media (min-height: 780px) {
  .oi-close__inner { padding-top: clamp(18px, 3.2vh, 46px); }
}
.oi-close__card { display: flex; flex-direction: column; gap: 15px; }
.oi-close__icon {
  display: inline-flex; align-items: center; justify-content: center; width: 46px; height: 46px;
  border-radius: var(--radius-md); background: rgba(226,162,76,0.12); border: 1px solid rgba(226,162,76,0.28);
  color: var(--amber-400); flex: 0 0 auto;
}
.oi-close__card-title { font-family: var(--font-sans); font-size: var(--text-h4); font-weight: 600; letter-spacing: -.01em; color: #fff; margin: 0; }
.oi-close__card-body { font-size: var(--text-body-sm); line-height: 1.62; color: var(--text-on-ink-muted); margin: 9px 0 0; }
.oi-close__cta { display: flex; align-items: center; gap: 16px; margin-top: clamp(24px, 4.5vh, 64px); flex-wrap: wrap; }
.oi-close__ghost {
  display: inline-flex; align-items: center; gap: 9px; height: 54px; padding: 0 26px;
  font-family: var(--font-sans); font-size: var(--text-body-lg); font-weight: 600; line-height: 1;
  border-radius: var(--radius-md); border: 1.5px solid rgba(255,255,255,0.30); background: transparent;
  color: #fff; text-decoration: none; white-space: nowrap;
  transition: border-color .18s, background .18s, transform .12s;
}
.oi-close__ghost:hover { border-color: rgba(255,255,255,0.6); background: rgba(255,255,255,0.06); }
.oi-close__ghost:active { transform: scale(.98); }
/* The design pairs the phone with the team link at 54px (the shared ghost tag
   is 52px); nudge it up to match, in this section only. */
.oi-close__cta .oi-phone--ghost { height: 54px; padding: 0 26px; }

/* ============================================================ <=1024 (tablet, bottom 4) */
@media (max-width: 1024px) {
  /* The deck's `scroll-snap-type: y mandatory` assumes EVERY section is exactly
     one viewport. #monitor is now taller here (stacked copy + card), which breaks
     that assumption: a mandatory snapport skips over the oversized section (you
     could not scroll onto the monitor at all) or traps between its neighbours.
     Drop snapping to free scroll at these widths — sections still read as
     one-viewport blocks, they just no longer lock. */
  /* Mandatory snap stays ON here too. Oversized sections (360x640, landscape
     phones) do NOT trap: they keep scroll-snap-align:start, and the CSS
     covering rule lets the scroller rest anywhere the section covers the
     viewport (verified by forced-snap flick walks + in-section rest tests).
     The old unreachable-section bug was snap-align:none creating a GAP —
     never do that. */

  /* #monitor stops being a pinned one-viewport slide (the JS drops the scroll
     capture below 1025px too): the copy and the dashboard card stack in normal
     flow and the section grows past one viewport so BOTH stay fully visible —
     a phone can't fit them together without the card burying the copy. */
  .oi-monitor { height: auto; min-height: 100svh; overflow: visible; }
  .oi-monitor__stage { height: auto; min-height: 100svh; overflow: visible; }
  .oi-monitor__bg, .oi-monitor__scrim { height: 100svh; bottom: auto; }   /* chart stays one screen at the top, no stretch */
  /* Copy is stacked on TOP here, so the scrim must fade the chart from the TOP
     down (was the desktop left->right gradient), masking the chart lines behind
     the paragraph. Solid through ~34% (the copy runs past 60% of the chart zone
     on a 360 phone, where the curve was still crossing the last lines). */
  .oi-monitor__scrim { background: linear-gradient(to bottom, rgba(251,250,247,0.6) 0%, rgba(251,250,247,0.6) 34%, rgba(251,250,247,0.4) 55%, rgba(251,250,247,0) 82%); }
  .oi-monitor__copy {
    height: auto; display: block;
    /* Self-shielding paper under the stacked copy (the JS scrim caps at 0.66
       opacity; the orange curve printed through the last paragraph lines). */
    background: linear-gradient(to bottom, rgba(251,250,247,0.82) 0%, rgba(251,250,247,0.74) 86%, rgba(251,250,247,0) 100%);
  }
  .oi-monitor__panel-inner { max-width: 640px; }
  /* relative, not static: keeps the card in normal flow (below the copy) while
     preserving its z-index:5, so the opaque card paints ABOVE the absolute chart
     (z-1) instead of the chart bleeding through the stats. */
  .oi-monitor__cardwrap { position: relative; inset: auto; pointer-events: auto; }
  .oi-monitor__cardwrap-inner {
    height: auto; align-items: center; justify-content: center;
    padding: 8px var(--gutter) 56px;
  }
  .oi-monitor__summary { width: 100%; max-width: 600px; max-height: none; overflow: visible; }

  /* #spoiler stacks at <=1024 (the split's single-column rule lives in the
     mobile-mirror block). The old "tablet keeps two columns" rule that used to
     live here was a GHOST: it kept a 2-col grid whose second column (the hidden
     interactive card) was empty, stranding all content in a narrow left column
     across 574-1024. */
  .oi-spoiler__logo { width: min(220px, 58%); margin-bottom: 16px; }
}

/* ============================================================ <=480 (phone, bottom 4) */
/* #assurance flips the graphic to the vertical variant. Extended from <=480 to
   <=640: in the 481-640 band the horizontal funnel's gate labels no longer fit
   side by side (audited at 574: the SURVEY PLATFORM PROTECTIONS chip printed
   over DEVICE FORENSIQ IQ, hiding its "IQ"). */
@media (max-width: 640px) {
  .oi-assurance__graphic--h { display: none; }
  .oi-assurance__graphic--v { display: block; }
}

@media (max-width: 480px) {

  /* #monitor: card anchors bottom-centre, chart labels drop, scrim goes vertical.
     Solid through 38% — the copy runs past a third of the chart zone on a 360
     phone, where the orange curve was still printing through the last lines. */
  .oi-monitor__scrim {
    background: linear-gradient(to bottom, rgba(251,250,247,0.62) 0%, rgba(251,250,247,0.62) 38%, rgba(251,250,247,0.4) 60%, rgba(251,250,247,0) 86%);
  }
  .oi-monitor__copy { align-items: flex-start; }
  .oi-monitor__panel { padding: calc(var(--nav-h) + 6px) var(--gutter) 0; }
  .oi-monitor__panel-inner { max-width: 100%; }
  .oi-monitor__bg text { display: none; }
  .oi-monitor__svg {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 44px, #000 100%);
            mask-image: linear-gradient(to bottom, transparent 0, #000 44px, #000 100%);
  }
  .oi-monitor__cardwrap-inner {
    align-items: center; justify-content: center;
    padding: 4px var(--gutter) 40px;
  }
  .oi-monitor__summary { width: 100%; max-width: 520px; max-height: none; }
  .oi-monitor__cardhead { flex-direction: column; align-items: flex-start; gap: 10px; }

  /* #spoiler: one column; hide the interactive card, show the mini card. */
  .oi-spoiler__split { grid-template-columns: 1fr; gap: 22px; }
  .oi-spoiler__lead { font-size: 13.5px; line-height: 1.5; margin: 14px 0 18px; max-width: 100%; }
  .oi-sp { display: none; }
  .oi-spmini { display: block; }

  /* #close: mobile lead copy, single-column value grid, full-width CTAs, no team link. */
  .oi-close__lead--full { display: none; }
  .oi-close__lead--mobile { display: block; }
  .oi-close__lead-row { gap: 28px; }
  .oi-close__grid { grid-template-columns: 1fr; gap: 13px; margin-top: 20px; padding-top: 16px; }
  .oi-close__card { flex-direction: row; align-items: flex-start; gap: 12px; }
  .oi-close__icon { width: 34px; height: 34px; }
  .oi-close__icon svg { width: 17px; height: 17px; }
  .oi-close__card-title { font-size: 15px; }
  .oi-close__card-body { font-size: 12px; line-height: 1.45; margin-top: 4px; }
  .oi-close__cta { margin-top: 20px; gap: 10px; }
  .oi-close__cta > * { width: 100%; height: 48px; }
  .oi-close__ghost--team { display: none; }
}

/* ============================================================ one-viewport scale-to-fit */
/* Every section is exactly 100svh (a vertical deck), so long-form leads, generous
   gaps and the tall graphic/marquee/card blocks must scale DOWN on short and narrow
   viewports to fit ONE screen without clipping. Section HEADINGS keep their fluid
   clamps (the type-scale is fixed by design and asserted in check_chrome); every
   other measure — leads, card bodies, gaps, margins, and the oversized graphic /
   flip-card / logo blocks — flexes here by viewport HEIGHT (smooth, no width step)
   and, on phones, by width where the 2-up columns wrap tall. */

/* --- short-viewport compaction (laptops at 720/900 + phones): scale leads and
   vertical rhythm by height. Tablet portrait (>=1000px tall) keeps its roomy
   design spacing. --- */
@media (max-height: 920px) {
  .oi-hero__sub       { font-size: clamp(13.5px, 1.8vh, 18px); margin-top: clamp(12px, 2vh, 22px); }
  .oi-engine__lead    { font-size: clamp(12.5px, 1.6vh, 18px); margin-top: clamp(8px, 1.5vh, 18px); line-height: 1.45; }
  .oi-sampling__lead  { font-size: clamp(12.5px, 1.5vh, 16px); margin: clamp(8px, 1.3vh, 14px) 0 clamp(10px, 1.6vh, 18px); line-height: 1.45; }
  .oi-assurance__lead { font-size: clamp(13px, 1.65vh, 18px);  margin-top: clamp(10px, 1.7vh, 22px); line-height: 1.45; }
  .oi-monitor__lead   { font-size: clamp(13px, 1.65vh, 18px);  margin-top: clamp(10px, 1.7vh, 20px); line-height: 1.5; }
  .oi-spoiler__lead   { font-size: clamp(12.5px, 1.5vh, 16px); margin: clamp(10px, 1.7vh, 22px) 0; line-height: 1.45; }
  .oi-close__lead     { font-size: clamp(13px, 1.7vh, 22px);   margin-top: clamp(10px, 1.7vh, 20px); line-height: 1.45; }

  /* Eyebrow-wrap bottom margins tighten uniformly. */
  .oi-why__eyebrow-wrap, .oi-engine__eyebrow-wrap, .oi-sampling__eyebrow-wrap,
  .oi-assurance__eyebrow-wrap, .oi-monitor__eyebrow-wrap, .oi-spoiler__eyebrow-wrap,
  .oi-close__eyebrow-wrap { margin-bottom: clamp(10px, 1.5vh, 20px); }

  /* #why cards + grid. */
  .oi-why__grid { gap: clamp(10px, 1.6vh, 26px); }
  .oi-why__card { padding: clamp(15px, 2.2vh, 44px); }
  .oi-why__card-title { margin-top: clamp(6px, 1vh, 16px); }
  .oi-why__card-body  { margin-top: clamp(6px, 1vh, 14px); }

  /* #engine: shrink the ≤1024 single-card flip runway by height. */
  .oi-engine__rail-wrap { margin-top: clamp(18px, 3vh, 54px); }
  .oi-engine__rail { margin-bottom: clamp(16px, 2.6vh, 36px); }

  /* #assurance graphic is the tallest element — already vh-capped; tighten caption gap. */
  .oi-assurance__caption { gap: 6px 14px; }

  /* #close cards. */
  .oi-close__grid { gap: clamp(14px, 2.2vh, 40px) var(--space-8); }
  .oi-close__card { gap: clamp(9px, 1.4vh, 15px); }

  /* #spoiler logo scales with height (it drives the intro column's height). */
  .oi-spoiler__logo { width: min(300px, 62%); margin-bottom: clamp(12px, 2vh, 30px); }
  .oi-spoiler__tagline { margin-top: clamp(8px, 1.2vh, 14px); }
}

/* --- #engine ≤1024 single-card runway: sized close to the card's real content
   (was clamp(...,470px), which stretched the single card and left a big empty
   void between its body and stat row). --- */
@media (max-width: 1024px) {
  .oi-engine__grid { height: clamp(246px, 30vh, 300px); }
}
/* Tall tablet portrait / tall phone: grow the head->rail gap modestly so #engine
   fills more of the viewport without opening an awkward lead-to-card chasm. */
@media (max-width: 1024px) and (min-height: 820px) {
  .oi-engine__rail-wrap { margin-top: clamp(40px, 6vh, 84px); }
}

/* --- phones (≤480): the 2-up / stacked columns wrap tall, so scale the card and
   copy TYPE down (headings excepted). This is where 360×640 is won. --- */
@media (max-width: 480px) {
  /* #why: the four cards are ~150px-wide columns — shrink their type + chrome so
     all four + the heading fit one screen. */
  .oi-why__card { padding: 13px 13px 14px; }
  .oi-why__grid { gap: 8px; }
  .oi-why__ghost { font-size: 48px; top: 4px; right: 10px; }
  .oi-why__num { font-size: 10.5px; }
  .oi-why__card-title { font-size: 14px; line-height: 1.16; margin-top: 6px; }
  .oi-why__card-body  { font-size: 11.5px; line-height: 1.34; margin-top: 6px; max-width: 100%; }

  /* #engine: the long lead + stacked flip card. Shrink lead and card internals. */
  .oi-engine__lead { font-size: 12px; line-height: 1.4; margin-top: 8px; }
  .oi-engine__grid { height: clamp(280px, 44vh, 340px); }
  .oi-pipe__back { padding: 14px; gap: 7px; }
  .oi-pipe__body { font-size: 12px; line-height: 1.4; }
  .oi-pipe__stats { padding-top: 9px; gap: 14px; }
  .oi-pipe__explore { height: 34px; }

  /* #sampling: copy (lead + 3 points + CTA) stacked above the marquee. Tighten. */
  .oi-sampling__lead { font-size: 12px; line-height: 1.38; margin: 7px 0 10px; }
  .oi-sampling__points { gap: 6px; }
  .oi-sampling__point { gap: 9px; }
  .oi-sampling__point-title { font-size: 12.5px; margin-bottom: 1px; }
  .oi-sampling__point-body { font-size: 10.5px; line-height: 1.34; }
  .oi-sampling__cta-row { margin-top: 9px; }
  .oi-sampling__grid { gap: 9px; }
  .oi-rib__head { margin-bottom: 6px; }
  /* The horizontal phone marquee cards shrink so the strip clears the fold. */
  .oi-rib__card { min-height: 104px; padding: 10px 12px; gap: 4px; }
  .oi-rib__stats { padding-top: 7px; gap: 16px; }
  .oi-rib__audience { font-size: 15.5px; }
  .oi-rib__stat-value { font-size: 17px; }
  .oi-btn-navy { height: 42px; }

  /* #assurance: shrink the graphic + lead so the caption clears the fold. */
  .oi-assurance__graphic--v { height: clamp(150px, 30vh, 260px); }
  .oi-assurance__lead { font-size: 12.5px; line-height: 1.4; }
  .oi-assurance__caption { font-size: 11.5px; }

  /* #spoiler: shrink the logo (its aspect makes it the tallest intro element). */
  .oi-spoiler__logo { width: min(92px, 28%); margin-bottom: 8px; }
  .oi-spoiler__lead { font-size: 11.5px; line-height: 1.36; margin: 8px 0 10px; }
  .oi-spoiler__tagline { margin-top: 6px; letter-spacing: .13em; font-size: 11px; }
  .oi-spoiler__demo { height: 42px; }
  .oi-spmini { margin: 0 0 10px; }
  .oi-spmini__head { padding: 8px 14px; }
  .oi-spmini__body { padding: 2px 14px 8px; }
  .oi-spmini__row { padding: 5px 0 5px; }
  .oi-spmini__row--last { padding: 5px 0 0; }
  .oi-spmini__foot { margin-top: 7px; }

  /* #close: shrink the mobile lead + card body + spacers for 360-wide. */
  .oi-close__lead--mobile { font-size: 12.5px; line-height: 1.38; }
  .oi-close__grid { margin-top: 13px; padding-top: 12px; gap: 11px; }
  .oi-close__card-body { font-size: 11.5px; line-height: 1.4; }
  .oi-close__cta { margin-top: 14px; }
  .oi-close__cta > * { height: 45px; }
}

/* ============================================================ single-column #sampling (481–768) */
/* ≤768 collapses #sampling to one column, stacking the copy above the vertical
   marquee. On a portrait tablet (e.g. 768×1024) that stack is taller than the
   viewport, so the vertically-centred inner clips its top (the eyebrow/heading)
   AND the marquee bottom. Cap the marquee height and tighten the copy rhythm so
   the whole stack fits one screen. Scoped to the 481–768 band the original port
   flagged as "no design reference"; the protected 2-column desktop/tablet
   marquee (its intentionally-tall masked track) is untouched. */
@media (min-width: 481px) and (max-width: 768px) {
  .oi-sampling__lead { margin: 10px 0 12px; }
  .oi-sampling__points { gap: 10px; }
  .oi-sampling__cta-row { margin-top: 16px; }
  .oi-rib__viewport { height: min(680px, 32svh); }
}

/* ============================================================ short-viewport scale-to-fit (≤700 / ≤600 tall) */
/* The deck is 100svh per section, so on SHORT screens (small phones, short
   laptops) the content-heavy cards/graphics overflow the one-viewport box and
   get cropped by overflow:hidden. These tiers are HEIGHT-driven — the constraint
   is vertical — so tall phones (390×844, 414×896) and every viewport ≥701 tall
   (all tablets/desktops in the matrix) are untouched. Headings keep their fluid
   clamps (the type-scale is asserted in check_chrome); only leads, card bodies,
   gaps, paddings and the field-dashboard card's internals flex here. */
@media (max-height: 700px) {
  /* #monitor — the field-dashboard card is the tallest block on a short phone
     (94px clip @375×667, 121px @360×640, the priority fix). Shrink the stat
     numbers, the card padding, the row gaps and the THIS-WEEK note rows so the
     whole card + heading clears the fold. Height-gated so the tall-phone and
     desktop card layouts keep their roomy design spacing. */
  .oi-monitor__summary { padding: 13px 14px; }
  .oi-monitor__cardhead { gap: 8px; }
  .oi-monitor__cardhead-label { gap: 6px; }
  .oi-monitor__health { margin-top: 8px; }
  .oi-monitor__section-label { margin-bottom: 5px; }
  .oi-monitor__health-grid { gap: 6px 14px; }
  .oi-monitor__stat { gap: 3px; }
  .oi-monitor__stat--completes { gap: 4px; }
  .oi-monitor__stat-value { font-size: clamp(1.05rem, 4.6vw, 1.4rem); }
  .oi-monitor__stat-note { font-size: 10px; }
  .oi-monitor__week { margin-top: 8px; padding-top: 8px; }
  .oi-monitor__week-rows { gap: 4px; }
  .oi-monitor__week-row { padding: 5px 10px; }
  .oi-monitor__week-text { font-size: 12px; line-height: 1.3; }
  .oi-monitor__note { margin-top: 8px; }
}

/* ============================================================ nav-clear reclaim (≤760 tall) */
/* The content sections now reserve the fixed nav via `padding-block: var(--nav-h) …`,
   so each inner's OWN top padding (76–116px — it used to BE the nav clearance) is
   now redundant, doubled on top of the section's 72px. On short viewports that
   doubled top padding pushed the vertically-centred content past the nav-cleared
   area, tucking the heading under the nav and clipping the bottom. Reclaim the
   redundant inner padding (and a little deck rhythm) here — height-gated so the
   roomy tall/desktop layouts (≥761 tall: 390×844, 414×896, and every desktop
   ≥900 tall) are untouched. Wins the short phones (568/640/667) and the short
   laptop (1280×720). The ≤600 tier below overrides with its deeper squeeze at 568. */
@media (max-height: 760px) {
  .oi-why__inner       { padding-top: clamp(8px, 1.8vh, 18px); padding-bottom: clamp(10px, 2vh, 20px); }
  .oi-why__head        { margin-bottom: clamp(8px, 1.6vh, 16px); }
  .oi-assurance__inner { padding-top: clamp(8px, 1.8vh, 18px); padding-bottom: clamp(10px, 2vh, 20px); gap: clamp(10px, 1.8vh, 20px); }
  .oi-close__inner     { padding-top: clamp(8px, 1.8vh, 18px); padding-bottom: clamp(10px, 2vh, 20px); }
}

@media (max-height: 600px) {
  /* Smallest/oldest phones (320×568). Push the #monitor card further, and give
     #why / #sampling / #assurance / #spoiler / #close the small trims that clear
     568 (their ≤480 width tier gets them most of the way; this closes the gap). */

  /* #monitor card — deeper compaction (233px clip at 320×568; the card's own
     overflow:auto must not kick in — content must FIT, not scroll). Also reclaim
     a little card height from the bottom-anchor gap on the shortest phones. */
  .oi-monitor__cardwrap-inner { padding-bottom: 14px; }
  .oi-monitor__summary { max-height: calc(100% - var(--nav-h) - 14px); padding: 11px 13px; }
  .oi-monitor__cardhead { gap: 6px; }
  .oi-monitor__cardhead-eyebrow { font-size: 11px; }
  .oi-monitor__health { margin-top: 6px; }
  .oi-monitor__section-label { margin-bottom: 4px; }
  .oi-monitor__health-grid { gap: 4px 12px; }
  .oi-monitor__stat-value { font-size: clamp(0.9rem, 4.4vw, 1.15rem); }
  .oi-monitor__stat-label { font-size: 9px; }
  .oi-monitor__stat-note { font-size: 9.5px; }
  .oi-monitor__week { margin-top: 6px; padding-top: 6px; }
  .oi-monitor__week-rows { gap: 3px; }
  .oi-monitor__week-row { padding: 4px 9px; }
  .oi-monitor__week-text { font-size: 11px; line-height: 1.25; }
  .oi-monitor__note { margin-top: 5px; font-size: 10.5px; }

  /* #why — the 2×2 grid: tighten padding, gaps, ghost and body type. The inner's
     own top padding is now redundant with the section's nav-h padding-block, so
     it drops to a slim floor (the section clears the nav). */
  .oi-why__inner { padding-top: 10px; padding-bottom: 11px; }
  .oi-why__head { margin-bottom: 9px; }
  .oi-why__eyebrow-wrap { margin-bottom: 9px; }
  .oi-why__grid { gap: 6px; }
  .oi-why__card { padding: 9px 11px 10px; }
  .oi-why__ghost { font-size: 40px; }
  .oi-why__card-title { font-size: 13px; line-height: 1.13; margin-top: 4px; }
  .oi-why__card-body { font-size: 11px; line-height: 1.26; margin-top: 4px; }

  /* #sampling — the densest short-phone section (a long lead + 3 detailed points
     + the audiences strip). Squeeze the rhythm and the marquee card hard; primary
     reading text (lead 11.5) stays legible, the secondary micro-copy sits at the
     ≤480 design floor (point-body 10.5, mono qualifier 10.5). */
  .oi-sampling__eyebrow-wrap { gap: 6px; margin-bottom: 5px; }
  .oi-sampling__lead { font-size: 11.5px; line-height: 1.16; margin: 4px 0 5px; }
  .oi-sampling__points { gap: 2px; }
  .oi-sampling__point { gap: 8px; }
  .oi-sampling__point-title { font-size: 12px; margin-bottom: 0; }
  .oi-sampling__point-body { font-size: 10.5px; line-height: 1.14; }
  .oi-sampling__cta-row { margin-top: 3px; }
  .oi-btn-navy { height: 40px; }
  .oi-rib__head { margin-bottom: 2px; }
  .oi-rib__card { min-height: 74px; padding: 4px 10px; gap: 2px; }
  .oi-rib__audience { font-size: 12.5px; }
  .oi-rib__qualifier { font-size: 10.5px; line-height: 1.28; }
  .oi-rib__stats { padding-top: 4px; gap: 14px; }
  .oi-rib__stat-value { font-size: 14px; }
  .oi-rib__stat-label { font-size: 9px; }

  /* #assurance — the capped graphic + lead + caption. */
  .oi-assurance__inner { padding-top: 10px; padding-bottom: 12px; gap: 10px; }
  .oi-assurance__eyebrow-wrap { margin-bottom: 10px; }
  .oi-assurance__lead { font-size: 11.5px; line-height: 1.34; margin-top: 8px; }
  .oi-assurance__cta-row { margin-top: 14px; }
  .oi-assurance__graphic--v { height: clamp(120px, 24vh, 180px); }
  .oi-assurance__caption { font-size: 11px; gap: 4px 12px; }

  /* #spoiler — the intro column above the mini card. */
  .oi-spoiler__eyebrow-wrap { margin-bottom: 8px; }
  .oi-spoiler__logo { width: min(68px, 21%); margin-bottom: 5px; }
  .oi-spoiler__lead { font-size: 11px; line-height: 1.28; margin: 5px 0 6px; }
  .oi-spoiler__tagline { margin-top: 4px; }
  .oi-spoiler__demo { height: 40px; }
  .oi-spmini__head { padding: 7px 14px; }
  .oi-spmini__body { padding: 2px 14px 5px; }
  .oi-spmini__row { padding: 3px 0; }
  .oi-spmini__row--last { padding: 3px 0 0; }
  .oi-spmini__row-head { margin-bottom: 4px; }
  .oi-spmini__foot { margin-top: 5px; }

  /* #close — the lead + 3 value cards + both CTAs on navy. The inner's own top
     padding is now redundant with the section's nav-h padding-block. */
  .oi-close__inner { padding-top: 10px; padding-bottom: 11px; }
  .oi-close__eyebrow-wrap { margin-bottom: 10px; }
  .oi-close__lead--mobile { font-size: 11.5px; line-height: 1.3; margin-top: 9px; }
  .oi-close__grid { margin-top: 8px; padding-top: 8px; gap: 4px; }
  .oi-close__card { gap: 6px; }
  .oi-close__card-title { font-size: 14px; }
  .oi-close__card-body { font-size: 11px; line-height: 1.26; }
  .oi-close__cta { margin-top: 6px; gap: 8px; }
  .oi-close__cta > * { height: 42px; }
}

/* ============================================================ medium-width band (481–833) */
/* At 481–833px wide, #close leaves its wide single-row 3-card layout (the value
   grid auto-fits to two rows) but keeps the tall double-padded inner, so it
   overruns even a 900–1024-tall viewport (600×900 was ~189px over). Reclaim the
   redundant inner padding and tighten the value-grid + CTA rhythm. Phones ≤480
   (their own tier) and the design's ≥834 two-column layout are untouched. */
@media (min-width: 481px) and (max-width: 833px) {
  .oi-close__inner { padding-top: clamp(14px, 1.8vh, 24px); padding-bottom: clamp(14px, 1.8vh, 24px); }
  .oi-close__grid  { margin-top: clamp(12px, 1.8vh, 22px); padding-top: clamp(12px, 1.8vh, 22px); gap: clamp(12px, 1.6vh, 22px) var(--space-8); }
  .oi-close__card  { gap: clamp(9px, 1.3vh, 15px); }
  .oi-close__cta   { margin-top: clamp(12px, 1.8vh, 22px); }
}

/* #sampling single-column (481–768) on a SHORT-ish viewport (≤920 tall, e.g.
   600×900): the copy stack + vertical marquee overran the fold. Cap the marquee
   shorter and tighten the copy rhythm. Taller viewports in the band (700×1000,
   768×1024) keep the roomier single-column spacing. */
@media (min-width: 481px) and (max-width: 768px) and (max-height: 920px) {
  .oi-sampling__lead    { margin: 8px 0 10px; }
  .oi-sampling__points  { gap: 8px; }
  .oi-sampling__cta-row { margin-top: 12px; }
  .oi-rib__viewport     { height: min(680px, 26svh); }
}

/* ============================================================ reduced motion */
/* The design's @media only kills scroll-snap and lets the 48s marquee and the
   card hovers keep running (spec §3, §5.5.3). Pause the marquee and drop the
   motion here. */
@media (prefers-reduced-motion: reduce) {
  .oi-rib__track { animation-play-state: paused; }
  .oi-why__card { transition: box-shadow .2s var(--ease-out); }
  .oi-why__card:hover { transform: none; }
  .oi-pipe__inner { transition: none; }
}

/* ============================================================ reveal-on-scroll */
/* [data-reveal] content fades / rises in as it enters the viewport (JS adds
   `is-revealed` via IntersectionObserver in scroll/reveal.js). This is gated on
   `.js` — added by pages/home.js only when the module actually runs — so a no-JS
   user (or a failed module load) sees every [data-reveal] at its natural, final
   state rather than stranded at opacity:0. */
.js [data-reveal],
.js [data-reveal-group] > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .5s var(--ease-out), transform .5s var(--ease-out);
}
.js [data-reveal].is-revealed,
.js [data-reveal-group].is-revealed > * {
  opacity: 1;
  transform: none;
}
/* Reduced motion: show the final state immediately, from CSS alone, regardless
   of when (or whether) the JS runs — no fade, no rise. */
@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js [data-reveal-group] > * {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ============================================================ progress spine */
/* A fixed hairline on the right edge that tracks overall scroll (scroll/spine.js
   drives the fill / node / % / label). Ported from the design's [data-spine]
   inline styles; progressStyle frozen to 'spine' (D12 — no topbar).

   Pure scroll-motion, so shown only under `.js`: a static spine pinned at 0%
   would be a progress bar that lies. Hidden ≤820px (too narrow to earn the
   gutter); the label/% block hides ≤1400px, leaving just the rail + node. */
.oi-spine { display: none; }
/* Only on wide desktop (>=1440), where there is real margin room and the labelled
   rail reads as a deliberate progress indicator. Below that it looked like a stray
   line in the gutter (every critical reviewer flagged it), so it is hidden. */
@media (min-width: 1440px) {
  .js .oi-spine {
    display: block;
    position: fixed; right: clamp(40px, 3.4vw, 64px); left: auto; top: 22vh; bottom: 22vh;
    z-index: 60; pointer-events: none;
  }
}
.oi-spine__rail { position: relative; width: 2px; height: 100%; }
.oi-spine__track,
.oi-spine__fill { position: absolute; inset: 0; width: 2px; border-radius: 2px; }
.oi-spine__track { background: var(--line); }
.oi-spine__fill {
  background: linear-gradient(to bottom, #233351 0%, #3E78AE 48%, #E2A24C 100%);
  clip-path: inset(0 0 100% 0);
}
.oi-spine__node {
  position: absolute; left: 1px; top: 0%; width: 9px; height: 9px;
  margin: -4.5px 0 0 -4.5px; border-radius: 50%; background: #E2A24C;
  box-shadow: 0 0 0 4px rgba(226, 162, 76, 0.18);
  transition: background .4s var(--ease-out), box-shadow .4s var(--ease-out);
}
.oi-spine__info {
  position: absolute; right: 20px; left: auto; top: 0%;
  transform: translateY(-50%); display: flex; flex-direction: column;
  align-items: flex-end; gap: 4px; white-space: nowrap;
}
/* Show the section label wherever the margin has room for it (was 1400). Below
   that the pulled-in rail + node reads as an intentional progress marker on its own. */
@media (max-width: 1280px) { .oi-spine__info { display: none; } }
.oi-spine__label {
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .13em;
  text-transform: uppercase; color: var(--text-body);
  transition: color .4s var(--ease-out);
}
.oi-spine__pct { font-family: var(--font-mono); font-size: 10.5px; color: var(--text-muted); }
/* Reduced motion: the spine still tracks scroll (position, not animation), but
   the node/label colour hand-offs settle instantly. */
@media (prefers-reduced-motion: reduce) {
  .oi-spine__node,
  .oi-spine__label { transition: none; }
}

/* ============================================================ THE DESIGN'S HEIGHT MODEL (<=1024) */
/* LAST in the file on purpose: these must beat every section's base rule (equal
   specificity — source order decides). The user's design files give every
   section a one-viewport MINIMUM height: at least one screen, GROWING when
   content needs it. The port hard-boxed them (`height:100svh; overflow:hidden`), which
   amputated content at in-between sizes (574px wide: text wraps taller and the
   box guillotines the "Request a demo" / "Scope a study" CTAs). Scroll-snap is
   already off <=1024, so let every section grow like the design does. The hero
   is exempt (absolute layers would collapse an auto-height box; its content is
   short). */
@media (max-width: 1024px) {
  .oi-why, .oi-engine, .oi-sampling, .oi-assurance, .oi-spoiler, .oi-close,
  .oi-monitor, .oi-monitor__stage {
    height: auto; min-height: 100svh; overflow: visible;
  }
  .oi-engine__inner { overflow: visible; height: auto; }

  /* Engine card runway sized to the card's REAL content (measured: ~256px at
     phone widths, ~287px at tablet) — earlier clamps left a ~90px void between
     the card body and its stat row. Fixed height is required (the flip stacks
     cards position:absolute), so size it per band instead. */
  .oi-engine__grid { height: 268px; }

  /* Sections can now scroll UNDER the nav (min-height model). Over dark
     sections the nav is transparent by design, which desktop's one-viewport
     deck never exposed; here white copy would glide beneath white nav text.
     A soft navy gradient keeps the nav band legible while staying invisible
     against the dark sections it sits on. */
  .oi-nav--tint.oi-nav--dark:not(.oi-nav--solid) {
    background: linear-gradient(to bottom, rgba(14,22,38,0.94) 55%, rgba(14,22,38,0));
  }
}
@media (min-width: 601px) and (max-width: 1024px) {
  .oi-engine__grid { height: 296px; }
}


/* ============================================================ 481-680 compaction */
/* Small-window band (a Safari window floors at ~574px wide): the stacked
   layouts overshot the viewport by 34-98px, so a section's eyebrow was always
   sliding under the fixed nav mid-scroll. Levers: strip the DOUBLE top padding
   (sections carry nav clearance already; the design's big inner paddings were
   invisible under the old centered model but pure height under the growing
   model), lay the close promise cards icon-LEFT like the phone design, and
   tighten the tall blocks. Measured at 574x835 + 640x960: every section fits
   one viewport. */
@media (min-width: 481px) and (max-width: 680px) {
  .oi-why__inner { padding-top: 12px; padding-bottom: 16px; }
  .oi-why__head { margin-bottom: 12px; }
  .oi-why__card { padding: 14px 16px 16px; }
  .oi-why__card-title { margin-top: 8px; }
  .oi-why__card-body { margin-top: 8px; font-size: 14.5px; line-height: 1.5; }

  /* Engine: keep the TALLEST capability card compact enough that the JS-sized
     runway (max card height) still fits the section in one viewport. */
  .oi-engine__lead { margin-top: 12px; }
  .oi-engine__rail-wrap { margin-top: 28px; }
  .oi-engine__rail { margin-bottom: 24px; }
  .oi-pipe__back { padding: 16px; gap: 6px; }
  .oi-pipe__body { font-size: 13px; line-height: 1.45; }

  .oi-sampling { padding-bottom: 10px; }
  .oi-sampling__grid { gap: 12px; }
  .oi-sampling__eyebrow-wrap { margin-bottom: 8px; }
  .oi-rib__head { margin-bottom: 10px; }
  .oi-sampling__lead { margin: 10px 0 10px; }
  .oi-sampling__points { gap: 7px; }
  .oi-sampling__cta-row { margin-top: 10px; }
  .oi-rib__card { min-height: 0; padding: 10px 12px; gap: 4px; }

  .oi-close__inner { padding-top: 14px; padding-bottom: 20px; }
  .oi-close__lead { margin-top: 12px; }
  .oi-close__grid { margin-top: 16px; padding-top: 14px; gap: 12px; }
  /* Icon-left rows (the phone design's treatment) — icon-above-text columns
     cost ~450px for three promises; rows cost ~230px. */
  .oi-close__card { flex-direction: row; align-items: flex-start; gap: 13px; }
  .oi-close__icon { width: 38px; height: 38px; }
  .oi-close__icon svg { width: 19px; height: 19px; }
  .oi-close__card-title { font-size: 16px; }
  .oi-close__card-body { font-size: 13px; line-height: 1.5; margin-top: 5px; }
  .oi-close__cta { margin-top: 16px; }
}

/* Phones (<=480): the monitor stack ran 86-185px past the viewport (its card
   internals only had trims in the 481-768 band). Same treatment, phone-tuned,
   so the section fits one viewport on 390x844/414x896/480x854 and the
   conditional deck snap can engage. (360x640 stays free-scroll: that height
   genuinely cannot hold the why grid or this card at legible sizes.) */
@media (max-width: 480px) {
  .oi-monitor__panel { padding-bottom: 0; }
  .oi-monitor__lead { margin-top: 10px; }
  .oi-monitor__panel-foot { margin-top: 10px; padding-top: 8px; }
  .oi-monitor__summary { padding: 12px 14px; }
  .oi-monitor__cardhead { gap: 8px; }
  .oi-monitor__health { margin-top: 8px; }
  .oi-monitor__section-label { margin-bottom: 5px; }
  .oi-monitor__health-grid { gap: 8px 14px; }
  .oi-monitor__stat { gap: 3px; }
  .oi-monitor__week { margin-top: 8px; padding-top: 8px; }
  .oi-monitor__week-rows { gap: 5px; }
  .oi-monitor__week-row { padding: 8px 10px; }
  .oi-monitor__cardwrap-inner { padding-top: 4px; padding-bottom: 18px; }
}

/* Monitor + spoiler stacks run slightly tall through 768 as well (audited +39 /
   +16 at 700x900) — their trims cover the whole 481-768 band. */
@media (min-width: 481px) and (max-width: 768px) {
  .oi-monitor__panel { padding-bottom: 0; }
  .oi-monitor__lead { margin-top: 12px; }
  .oi-monitor__panel-foot { margin-top: 14px; padding-top: 10px; }
  .oi-monitor__summary { padding: 14px 16px; }
  .oi-monitor__health { margin-top: 10px; }
  .oi-monitor__health-grid { gap: 10px 16px; }
  .oi-monitor__week { margin-top: 10px; padding-top: 10px; }
  .oi-monitor__week-rows { gap: 6px; }
  .oi-monitor__cardwrap-inner { padding-top: 6px; padding-bottom: 26px; }

  .oi-spoiler__logo { width: min(150px, 38%); margin-bottom: 12px; }
  .oi-spoiler__lead { margin: 12px 0 14px; }
  .oi-spmini { margin-bottom: 14px; }
  .oi-spmini__row { padding: 7px 0 6px; }
  .oi-spmini__row--last { padding: 7px 0 0; }
  .oi-spmini__foot { margin-top: 8px; }
}

/* ============================================================ monitor labels */
/* The day-axis labels return whenever the full 1600x900 chart fits without
   horizontal cropping — below 16:9 the slice crop amputates the edge labels
   ("ay 1"), and below 1025px the copy stacks over the chart. The QUOTA and
   curve-name labels stay off at EVERY width: the dashboard card covers the
   curve names' anchor (x=858) and the LIVE MONITOR chip owns the quota line's
   right edge, so those two collide structurally, not situationally. */
@media (min-width: 1025px) and (min-aspect-ratio: 16/9) {
  .oi-monitor__bg .oi-mon-axis text { display: block; }
}
