/* ============================================================
   MinervaXR — marketing site, round 3: alive, but disciplined.
   r2's structure (flat surfaces, one accent, four type sizes,
   real content only) with motion put back — every timing taken
   from a measurement of a shipping site, not from taste:

     hover   160ms cubic-bezier(.25,.46,.45,.94)  ← linear.app
     state   240ms                                 ← apple.com (opacity+transform, x171)
     reveal  420ms cubic-bezier(.16,1,.3,1)        ← vercel.com reveals (0.3s) widened
     rail     40s linear infinite, pause on hover  ← documented marquee practice

   MOVEMENT is opacity + transform only. STATE may also cross-fade
   colour (color / background-color / border-color / background-size)
   — paint-only, and what the measured references do too. Nothing
   that triggers LAYOUT is ever animated: no width/height/top/left,
   no `transition: all`. (Verified by reading computed styles across
   the whole page, not by intention.)
   No framework, no CDN.
   ============================================================ */

/* ─── Fonts (self-hosted, unchanged) ─────────────────────────── */

@font-face {
  font-family: 'Syne';
  src: url('../fonts/syne-var-latin.woff2') format('woff2') tech('variations'),
       url('../fonts/syne-var-latin.woff2') format('woff2');
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Syne';
  src: url('../fonts/syne-var-latin-ext.woff2') format('woff2') tech('variations'),
       url('../fonts/syne-var-latin-ext.woff2') format('woff2');
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Sans';
  src: url('../fonts/dm-sans-var-latin.woff2') format('woff2') tech('variations'),
       url('../fonts/dm-sans-var-latin.woff2') format('woff2');
  font-weight: 100 1000;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Sans';
  src: url('../fonts/dm-sans-var-latin-ext.woff2') format('woff2') tech('variations'),
       url('../fonts/dm-sans-var-latin-ext.woff2') format('woff2');
  font-weight: 100 1000;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Mono';
  src: url('../fonts/dm-mono-400-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Mono';
  src: url('../fonts/dm-mono-400-latin-ext.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'DM Mono';
  src: url('../fonts/dm-mono-500-latin.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'DM Mono';
  src: url('../fonts/dm-mono-500-latin-ext.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ─── Tokens ─────────────────────────────────────────────────
   Colour is r2's (contrast floors already proven: 7.15:1 dark,
   4.64:1 light). Motion is new and lives here so timing is ONE
   decision, not eighty.                                        */

:root {
  color-scheme: dark;
  --bg: #0b0d10;
  --surface: #10141a;
  --surface-2: #151a21;    /* panel chrome — one step up        */
  --ink: #eef2f7;
  --muted: #99a2ae;
  --hairline: #222933;
  --hairline-hi: #2f3846;  /* hairline on hover                 */
  --accent: #2bd4d4;
  --accent-fill: #2bd4d4;
  --on-accent: #0b0d10;
  --dot-ok: #10b981;
  --dot-warn: #f59e0b;
  --mark-filter: invert(1);   /* the black company mark, whitened for the dark ground */
  --band: #0e1218;            /* the alternating block ground — see main > *:nth-child(even) */

  /* ─── TYPE: THE SAME φ, IN HALF STEPS, AND IN REM ──────────────────────
     The spacing scale went first; this is the other half, because a φ rhythm
     running under an unrelated type scale is what still read as slightly off.

     🔴 FULL φ STEPS ARE THE WRONG RATIO FOR TYPE, and the arithmetic says so
     rather than taste. φ is a 61.8% jump. From this 20px root that puts small
     text on either 20px — identical to body, so the hierarchy disappears — or
     12.4px, a 24% cut on a page that sells to clinicians. It also shrinks the
     hero headline by 17% and the phone headline by the same.

     HALF steps are still φ: every value here is φ^(n/2), a 27.2% jump, which
     also clears the practitioner floor of "never less than 25% between steps"
     that came out of the spacing round's research. Measured against what the
     design already used, everything lands within ~5% except the display size,
     which grows 5.7% (63px → 66.6px) — the one visible change in this round.

     🪤 REM, NOT PX, AND THAT IS LOAD-BEARING. The header comment on `html`
     records why: the 125% root exists so a visitor who raises their own browser
     font size scales the whole page with it. Type tokens written in px would
     silently kill that, and it is the kind of regression nobody notices until
     someone who needs it does. The spacing scale is px because spacing is not
     what a reader enlarges; type is. */
  --fs-2xs: 0.618rem;      /* φ⁻¹    12.4px */
  --fs-xs: 0.786rem;       /* φ⁻⁰·⁵  15.7px */
  --fs-body: 1rem;         /* φ⁰     20px — the anchor */
  --fs-h3: 1.272rem;       /* φ⁰·⁵   25.4px */
  --fs-h2: 1.618rem;       /* φ¹     32.4px */
  --fs-h1: 2.058rem;       /* φ¹·⁵   41.2px */
  --fs-display: 3.330rem;  /* φ²·⁵   66.6px */

  /* Leading is φ too, and the values it replaced were already within a few
     percent of these: 1.6/1.55/1.7 → 1.618, 1.22 → 1.272, 1.14 → 1.128. Big
     type needs less leading, so the ladder tightens as size grows. */
  --lh-body: 1.618;        /* φ¹    */
  --lh-heading: 1.272;     /* φ⁰·⁵  */
  --lh-display: 1.128;     /* φ⁰·²⁵ */

  --r: 10px;
  --r-lg: 14px;

  /* Motion — measured, not invented. See the header comment. */
  --t-hover: 160ms;
  --t-state: 240ms;
  --t-reveal: 420ms;
  --e-hover: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --e-reveal: cubic-bezier(0.16, 1, 0.3, 1);
  --stagger: 70ms;
  --rail-duration: 40s;
  /* The reduced-motion reveal. Short on purpose: guidance is that a fade
     kept under ~200ms signals "this is new" without the sustained change
     that causes discomfort. No easing curve is needed for opacity alone. */
  --t-calm: 150ms;

  /* ─── SPACING: ONE GEOMETRIC SCALE, RATIO φ ─────────────────────────────
     Ofir: "spaces need to be golden ratio - we are perfectionists here".

     What it replaced, measured before touching anything: 103 margin/padding/gap
     declarations carrying 31 DISTINCT px values, of which five happened to land
     near a φ step and twenty-six did not — 6, 9, 10, 14, 16, 18, 22, 24, 26, 28,
     30, 36, 40, 44, 48, 56, 64, 72, 104, 120. That is not a system; it is 31
     separate decisions, and it is why the spacing read as arbitrary and the hero
     gap read as dead space.

     Every step is 20px × φⁿ. 20px is the root size, so the rhythm and the type
     share one origin instead of drifting apart. The values below are written out
     rather than computed in CSS because a stylesheet full of nested calc() is
     harder to read than the numbers themselves — and `site-spacing-scale.test.ts`
     RECOMPUTES each one from φ and fails if it drifts by more than 0.5px, so the
     proof lives in a test rather than in this comment.

     The steps are far apart on purpose: φ is a 61.8% jump, so no two are ever
     confusable, and a value that "feels between two steps" is a signal that the
     layout wants restructuring rather than a new number. Snapping is by RATIO,
     not by absolute distance — 40px is nearer 32.4 than 52.4 because proportion
     is what the eye reads.

     🪤 Honest note, so nobody has to re-derive it: there is no evidence that φ
     is perceptually special (NN/g is explicit about that). What is real and
     measurable is that ONE consistent ratio replaces 31 arbitrary numbers with
     nine principled ones. φ is the ratio the owner chose; the discipline is the
     part that does the work. */
  --s-4xs: 2.9px;    /* φ⁻⁴ */
  --s-3xs: 4.7px;    /* φ⁻³ */
  --s-2xs: 7.6px;    /* φ⁻² */
  --s-xs: 12.4px;    /* φ⁻¹ */
  --s-s: 20px;       /* φ⁰  — the root */
  --s-m: 32.4px;     /* φ¹  */
  --s-l: 52.4px;     /* φ²  */
  --s-xl: 84.7px;    /* φ³  */
  --s-2xl: 137.1px;  /* φ⁴  */
}

@media (prefers-color-scheme: light) {
  :root {
    color-scheme: light;
    --bg: #fafbfc;
    --surface: #ffffff;
    --surface-2: #f4f6f9;
    --ink: #0e1116;
    --muted: #55606e;
    --hairline: #e3e7ed;
    --hairline-hi: #cdd5e0;
    --accent: #0c7f81;
    --accent-fill: #0c7f81;
    --on-accent: #ffffff;
    --dot-ok: #0a7d58;
    --dot-warn: #b45309;
    --mark-filter: none;        /* the mark ships black */
    --band: #f3f5f8;
  }
}

:root[data-theme='dark'] {
  color-scheme: dark;
  --bg: #0b0d10; --surface: #10141a; --surface-2: #151a21;
  --ink: #eef2f7; --muted: #99a2ae;
  --hairline: #222933; --hairline-hi: #2f3846;
  --accent: #2bd4d4; --accent-fill: #2bd4d4; --on-accent: #0b0d10;
  --dot-ok: #10b981; --dot-warn: #f59e0b;
  --mark-filter: invert(1);
  --band: #0e1218;
}
:root[data-theme='light'] {
  color-scheme: light;
  --bg: #fafbfc; --surface: #ffffff; --surface-2: #f4f6f9;
  --ink: #0e1116; --muted: #55606e;
  --hairline: #e3e7ed; --hairline-hi: #cdd5e0;
  --accent: #0c7f81; --accent-fill: #0c7f81; --on-accent: #ffffff;
  --dot-ok: #0a7d58; --dot-warn: #b45309;
  --mark-filter: none;
  --band: #f3f5f8;
}

/* ─── Base ───────────────────────────────────────────────── */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/* THE BASE SIZE. 125% => a 20px root. Everything typographic below is
   expressed in rem, so this one line scales the whole page — and a
   visitor who raises their own browser font size scales with it. */
html {
  font-size: 125%;
  /* 🔴 `auto`, AND THIS ONE WORD IS WHY THE NAV LINKS FELT WRONG.
     This said `smooth`, and site.js drives the anchor glide by calling
     window.scrollTo(0, y) once per frame. The two-argument form obeys THIS
     property, so every one of those calls was a *smooth-scroll request*: each
     frame retargeted the one before it and the page barely moved, then the
     browser ran its own scroll at the end — which begins slowly and then
     rushes. Ofir: "start slowly to sweep then fast - its bad".

     Measured on the running page, driving the shipped glide frame by frame:
     as shipped the page had moved 0px at 100ms, 0px at 400ms and 0px in total;
     with this one word changed, 883px / 2907px / 4205px. The easing tuned in
     r18 was real and never once ran in a browser — it was masked here.

     🪤 And that is how it went unnoticed: the r18 harness set
     scroll-behavior:'auto' before measuring, to "isolate the JS curve". It
     isolated the curve from the very thing that was breaking it. Measure the
     shipped condition, not a cleaner one. */
  scroll-behavior: auto;
}
/* Without JavaScript nothing can drive a glide, so let the browser do it. The
   pre-paint script drops .no-js, so this applies to exactly the visitors whose
   anchor jumps would otherwise be instant. */
html.no-js { scroll-behavior: smooth; }

body {
  font-family: 'DM Sans', system-ui, sans-serif;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  background: var(--bg);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button { font: inherit; cursor: pointer; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Slides in on transform, not `top`: `top` would animate LAYOUT.
   (Caught by auditing computed styles — it was the page's only
   layout-triggering transition.) */
.skip-link {
  position: absolute; left: 16px; top: 12px; z-index: 200;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--hairline); border-radius: var(--r);
  padding: var(--s-xs) var(--s-s); font-size: var(--fs-xs);
  transform: translateY(-200%);
  transition: transform var(--t-state) var(--e-hover);
}
.skip-link:focus-visible { transform: none; }

.wrap { max-width: 1080px; margin-inline: auto; padding-inline: clamp(var(--s-s), 5vw, var(--s-m)); }

p { max-width: 68ch; }

.mono {
  font-family: 'DM Mono', monospace;
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* Links: animated underline. background-size is paint-only —
   no layout, so it stays cheap. */
.link {
  color: var(--accent);
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: background-size var(--t-state) var(--e-hover);
}
.link:hover, .link:focus-visible { background-size: 100% 1px; }

.arrow {
  display: inline-block;
  transition: transform var(--t-hover) var(--e-hover);
}
a:hover .arrow, button:hover .arrow { transform: translateX(3px); }

.ph-chip {
  display: inline-flex; align-items: center;
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted);
  border: 1px dashed var(--hairline); border-radius: 6px;
  padding: var(--s-4xs) var(--s-xs);
}

/* ─── Motion primitives ──────────────────────────────────── */

/* Arrival + reveal both run as ANIMATIONS, not transitions.
   🔴 Why this matters (caught by measuring, not by reading): a
   reveal built on `transition` is silently clobbered whenever the
   same element also owns a hover transition — `.tier` and
   `.ai-still` declare `transition: border-color, transform`, which
   REPLACED the reveal's opacity fade, so the pricing cards popped
   in with no fade at all. Animations occupy a different channel, so
   the two never fight.

   Then, because a finished `forwards` animation keeps ownership of
   the properties it animated (which would freeze the hover lift),
   JS swaps in `.is-shown` on animationend to hand `transform` back
   to the transition. */
.rise, .reveal {
  opacity: 0;
  transform: translateY(14px);
}
.rise, .reveal.is-in {
  animation: rise var(--t-reveal) var(--e-reveal) forwards;
  animation-delay: calc(var(--i, 0) * var(--stagger));
}
@keyframes rise { to { opacity: 1; transform: none; } }

/* The same arrival with the MOVEMENT taken out — opacity only, no travel.
   This is what the reduced-motion path runs instead of nothing at all;
   see the block at the end of this file for why that distinction matters. */
@keyframes fade-only { to { opacity: 1; } }

/* Settled: the element owns itself again.
   Both selectors carry TWO classes on purpose — `.is-shown` alone
   loses to `.reveal.is-in` on specificity, which left the finished
   animation still owning `transform` and the hover lift dead. */
.rise.is-shown,
.reveal.is-shown {
  opacity: 1;
  transform: none;
  animation: none;
}

/* No-JS safety: never leave content invisible because a script
   failed. (:has is widely supported; the reduced-motion block
   and the .is-in class cover the other paths.) */
html.no-js .reveal { opacity: 1; transform: none; }

/* ─── Buttons ────────────────────────────────────────────── */

.btn, .btn-quiet {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2xs);
  height: 2.875rem; padding: 0 1.5rem;
  border-radius: var(--r);
  font-weight: 560; font-size: var(--fs-body);
  transition:
    transform var(--t-hover) var(--e-hover),
    opacity var(--t-hover) var(--e-hover),
    border-color var(--t-hover) var(--e-hover);
}
.btn {
  border: none;
  background: var(--accent-fill); color: var(--on-accent);
}
.btn:hover { opacity: 0.92; transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn-quiet {
  border: 1px solid var(--hairline);
  background: transparent; color: var(--ink);
}
.btn-quiet:hover { border-color: var(--hairline-hi); transform: translateY(-1px); }
.btn-quiet:active { transform: translateY(0); }

/* ─── Nav ────────────────────────────────────────────────── */

.site-nav {
  position: sticky; top: 0; z-index: 100;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition:
    background-color var(--t-state) var(--e-hover),
    border-color var(--t-state) var(--e-hover);
}
/* Height NEVER changes — measured practice (linear.app 73px and
   vercel.com 64px both hold constant while scrolling); a
   condensing bar shifts layout and reads dated. What changes is
   the surface underneath it. */
/* Solid, not frosted: backdrop blur is decoration (and a real paint
   cost on long pages). The surface change alone says "you have left
   the top". */
.site-nav.is-scrolled {
  background: var(--bg);
  border-bottom-color: var(--hairline);
}
.site-nav .wrap {
  height: 3.75rem;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-s);
}

.brand { display: flex; align-items: center; gap: var(--s-xs); }
/* The company mark, flat. It replaces the teal rounded tile, so there
   is no tile to clip to any more — the silhouette sits on the page and
   inverts to white on the dark theme (it is a pure black shape, so a
   single invert is exact and saves shipping a second file). */
.brand-mark { display: flex; align-items: center; flex-shrink: 0; }
.brand-mark img {
  height: 1.4rem; width: auto;
  object-fit: contain;
  filter: var(--mark-filter);
}
.brand-name {
  font-family: 'Syne', 'DM Sans', sans-serif;
  font-weight: 700; font-size: var(--fs-xs); letter-spacing: 0.04em;
  white-space: nowrap;
}

.nav-links { display: flex; gap: var(--s-m); }
.nav-links a {
  font-size: var(--fs-xs); color: var(--muted);
  transition: color var(--t-hover) var(--e-hover);
}
.nav-links a:hover { color: var(--ink); }

.nav-actions { display: flex; align-items: center; gap: var(--s-s); }
.nav-signin {
  font-size: var(--fs-xs); color: var(--ink);
  transition: color var(--t-hover) var(--e-hover);
}
.nav-signin:hover { color: var(--accent); }

.theme-toggle {
  width: 32px; height: 32px; border-radius: 50%;
  border: none; background: transparent; color: var(--muted);
  display: inline-flex; align-items: center; justify-content: center;
  transition: color var(--t-hover) var(--e-hover), transform var(--t-hover) var(--e-hover);
  flex-shrink: 0;
}
.theme-toggle:hover { color: var(--ink); transform: rotate(12deg); }
.theme-toggle svg { width: 16px; height: 16px; }

/* The stop control: NO PIXELS until a keyboard reaches it, then a real button.
   Ofir asked for the nav icon to go; 2.2.2 still needs a mechanism, so it
   became a skip-link. Clipping (rather than `display: none` or
   `visibility: hidden`) is what keeps it focusable — a hidden element is
   removed from the tab order, which would leave the mechanism there in the
   markup and unreachable in practice, the exact failure r15 found. */
.motion-skip {
  position: absolute;
  width: 1px; height: 1px; padding: 0; margin: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  border: 0; background: transparent; color: inherit;
}
/* :focus, NOT :focus-visible. A skip-link must appear for ANY focus that
   lands on it — Chrome withholds :focus-visible from programmatic and
   pointer focus, which would leave the control focused and still invisible
   for a screen-reader user driving it that way. */
.motion-skip:focus {
  position: static;
  width: auto; height: auto; clip-path: none;
  align-self: center;
  margin: 0 0 var(--s-xs); padding: var(--s-2xs) var(--s-s);
  border-radius: 999px;
  border: 1px solid var(--hairline); background: var(--surface); color: var(--ink);
  font: inherit; font-size: var(--fs-xs);
}
.icon-sun { display: none; }
.icon-moon { display: block; }
:root[data-theme='light'] .icon-sun { display: none; }
:root[data-theme='light'] .icon-moon { display: block; }
:root[data-theme='dark'] .icon-sun { display: block; }
:root[data-theme='dark'] .icon-moon { display: none; }
@media (prefers-color-scheme: light) {
  .icon-sun { display: none; }
  .icon-moon { display: block; }
  :root[data-theme='dark'] .icon-sun { display: block; }
  :root[data-theme='dark'] .icon-moon { display: none; }
}
@media (prefers-color-scheme: dark) {
  .icon-sun { display: block; }
  .icon-moon { display: none; }
  :root[data-theme='light'] .icon-sun { display: none; }
  :root[data-theme='light'] .icon-moon { display: block; }
}

.nav-toggle {
  display: none; background: none; border: none; padding: var(--s-3xs);
  width: 34px; height: 34px;
  flex-direction: column; justify-content: center; gap: var(--s-3xs);
}
.nav-toggle span {
  display: block; height: 2px; width: 18px;
  background: var(--ink); border-radius: 2px;
  transition: transform var(--t-state) var(--e-hover), opacity var(--t-state) var(--e-hover);
}
.nav-toggle[aria-expanded='true'] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded='true'] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded='true'] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.mobile-menu {
  display: none;
  position: fixed; top: 3.75rem; left: 0; right: 0; z-index: 99;
  background: var(--bg);   /* solid — no frosted glass */
  border-bottom: 1px solid var(--hairline);
  padding: var(--s-2xs) var(--s-s) var(--s-s);
  flex-direction: column;
}
.mobile-menu.open { display: flex; }
.mobile-menu a {
  font-size: var(--fs-xs); color: var(--muted);
  padding: var(--s-xs) var(--s-3xs); border-bottom: 1px solid var(--hairline);
}
.mobile-menu a:last-of-type { border-bottom: none; }

/* ─── Alternating block grounds ──────────────────────────────
   Ofir: "i want a small difference color (bearly) between parts blocks", with
   the partner band and the Minerva AI block marked "a bit greyer". Those are
   the 2nd and 4th children of <main>, so the pattern he was pointing at is
   simple alternation — and doing it by position rather than by naming two
   sections means a block inserted later inherits the rhythm instead of
   breaking it.

   BARELY is the whole brief: #fafbfc -> #f3f5f8 in light, #0b0d10 -> #0e1218 in
   dark. Enough to separate two blocks that meet, not enough to read as a
   coloured panel. Nothing inside a block is restyled — the cards on top carry
   their own surface colour, so they keep their contrast against either ground. */
main > *:nth-child(even) { background: var(--band); }

/* ─── Hero — copy + the live fleet panel ─────────────────── */

/* Stacked, not side-by-side: the product shot is 2.3:1, and a
   column would squash the one thing worth looking at. Copy leads,
   the product fills the width beneath it. */
.hero { padding-block: clamp(var(--s-l), 9vw, var(--s-xl)) clamp(var(--s-l), 7vw, var(--s-xl)); }
.hero-copy { max-width: 50rem; }

/* The display voice. Syne IS a display face — it was the original
   design's heading font, and carrying everything in DM Sans is what
   made this headline read as default-bold-sans. Bigger, tighter, and
   split in two: the claim in ink, the punchline in the accent, so the
   sentence has a shape instead of being one grey slab. */
/* Headline face: DM Sans, not Syne. Measured on the actual sentence —
   Syne is wide enough to need four lines where DM Sans needs three, and
   the fourth line is what made the hero read as a slab. Confidence here
   comes from weight and tracking, not from a novelty face. */
h1, h2 { font-family: 'DM Sans', sans-serif; }

.hero h1 {
  font-size: clamp(var(--fs-h1), 4.4vw, var(--fs-display));
  font-weight: 700;
  /* 1.16, not the 1.02 I first set: Syne's ascenders and descenders
     draw ~1.2em of ink, so a 1.02 line box left every line 10px short
     and the rows visibly collided — it read as clipped text. 1.2 is
     the measured figure that fully contains it (ink 67px at 56px).
     Measured, not eyeballed. */
  /* Descenders still need room (they were being sliced at 1px of
     clearance), hence the padding — but DM Sans sits happily tighter
     than Syne did. Tracking does the work Syne's shapes used to. */
  line-height: var(--lh-display);
  padding-bottom: 0.2em;   /* descender room, independent of leading */
  letter-spacing: -0.035em;
  /* the break is authored in the markup, at a phrase boundary — balance
     would re-flow it and put "can / run" back on separate lines */
  text-wrap: initial;
}
.hero h1 .punch {
  display: block;
  margin-top: 0.18em;
  color: var(--accent);
}
.hero .sub {
  margin-top: var(--s-s);
  font-size: var(--fs-body); color: var(--muted);
  max-width: 52ch;
}
.hero-shot { margin-top: clamp(var(--s-l), 6vw, var(--s-l)); }
.hero .cta-row {
  margin-top: var(--s-m);
  display: flex; align-items: center; gap: var(--s-s); flex-wrap: wrap;
}
.hero .cta-quiet {
  font-size: var(--fs-body); color: var(--accent);
  display: inline-flex; align-items: center; gap: var(--s-3xs);
}

/* ─── The hero's product shot ────────────────────────────────
   r5: the drawing of the Hub is gone; this is the Hub. Captured
   from the running app in both of ITS themes, and swapped to match
   the visitor's — the same prefers-color-scheme + [data-theme]
   mechanism the sun/moon icons use. One frame style, used for every
   product image on the page. */

.shot {
  position: relative;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
  transition: border-color var(--t-hover) var(--e-hover);
}
.shot:hover { border-color: var(--hairline-hi); }
.shot img {
  width: 100%; height: auto;
  display: block;
}
/* theme-matched: exactly one is ever displayed.
   These class hooks moved from the <img> onto its <picture> wrapper when the
   phone crop landed — <picture> is display:inline by default, so the block
   below is what keeps the frame flush. */
.shot .is-dark { display: block; }
.shot .is-light { display: none; }
@media (prefers-color-scheme: light) {
  .shot .is-dark { display: none; }
  .shot .is-light { display: block; }
}
:root[data-theme='dark'] .shot .is-dark { display: block; }
:root[data-theme='dark'] .shot .is-light { display: none; }
:root[data-theme='light'] .shot .is-dark { display: none; }
:root[data-theme='light'] .shot .is-light { display: block; }

.shot-caption {
  margin-top: var(--s-xs);
  display: flex; align-items: center; gap: var(--s-xs); flex-wrap: wrap;
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.05em; color: var(--muted);
}

/* Facts, in his own words (all three are pricing copy). */
.facts {
  margin-top: var(--s-m);
  display: flex; flex-wrap: wrap; gap: var(--s-xs) var(--s-m);
  font-size: var(--fs-xs); color: var(--muted);
}
.facts li { list-style: none; display: flex; align-items: center; gap: var(--s-2xs); }
.facts .dot { background: var(--accent); }

/* The status dot, shared by the facts strip and the step mocks.
   (It lived inside the fleet-panel block until r5 retired that
   block — and took the border-radius with it, which turned every
   dot on the page into a square. Caught by looking at the render.) */
.dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--muted);
}
.dot-ready { background: var(--dot-ok); }
.dot-cast  { background: var(--accent); }
.dot-low   { background: var(--dot-warn); }

/* Proof band — the real logos, directly under the hero. */
.proof {
  padding-block: clamp(var(--s-m), 5vw, var(--s-l));
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}
/* `max-width: none` is load-bearing: the global `p { max-width: 68ch }`
   prose measure applies here too, so `text-align: center` was centring
   the text inside a 605px box pinned to the left of a 1080px wrap —
   measured 205px left of the row's centre on a 1920px screen. It read
   as "floating in the wrong place" because it was. */
.proof .partners-caption {
  max-width: none;
  text-align: center;
}
/* The caption sits well clear of the row — it was crowding it. */
.proof .rail { --rail-gap: 40px; }   /* the intent, not the arithmetic — see .rail */

/* ─── Sections ───────────────────────────────────────────── */

.section {
  /* so a native anchor jump (JS off) does not park the heading under
     the sticky bar — the JS glide measures the bar for the same reason */
  scroll-margin-top: 5rem;
  padding-block: clamp(var(--s-xl), 10vw, var(--s-2xl));
  border-bottom: 1px solid var(--hairline);
}
/* the footer brings its own top rule — two would read as a double line */
.section:last-of-type { border-bottom: none; }
.section h2 {
  /* The clamp lives HERE, not in the token, so --fs-h2 can stay a pure φ step
     and the responsive range is expressed where it applies — the same shape the
     hero h1 uses. It is also load-bearing: --fs-h2 used to BE a clamp
     (1.375rem…1.6875rem), and making it a flat step put this heading at 32.4px
     on a phone, where the phone h1 rule also resolves to 32.4px. Identical
     sizes, hierarchy gone. Floored at --fs-h3 it reads 25.4px there — exactly
     one half-step below the headline, which is the relationship it should have
     had all along. */
  font-size: clamp(var(--fs-h3), 2.6vw, var(--fs-h2));
  font-weight: 700; line-height: var(--lh-heading); letter-spacing: -0.028em;
  padding-bottom: 0.18em;   /* descender room — see the note on .hero h1 */
}
.section .lede { margin-top: var(--s-xs); color: var(--muted); }

/* ─── How it works ───────────────────────────────────────── */

.steps {
  margin-top: var(--s-l);
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-m);
}
.step .idx { font-family: 'DM Mono', monospace; font-size: var(--fs-xs); color: var(--muted); }
.step h3 { margin-top: var(--s-xs); font-size: var(--fs-body); font-weight: 620; }
.step p { margin-top: var(--s-3xs); font-size: var(--fs-xs); line-height: var(--lh-body); color: var(--muted); }

/* Step visuals — P1 had labelled frames here and r2 cut them as
   clutter. They were INFORMATION (what each step looks like), so
   they return as small mocks in the product's own tokens rather
   than as dashed empty rectangles. */
.step-shot {
  margin-bottom: var(--s-s);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  overflow: hidden;
  transition: border-color var(--t-hover) var(--e-hover), transform var(--t-hover) var(--e-hover);
}
.step:hover .step-shot { border-color: var(--hairline-hi); transform: translateY(-2px); }
.shot-label {
  padding: var(--s-2xs) var(--s-xs);
  border-bottom: 1px solid var(--hairline);
  background: var(--surface-2);
  /* rem, not px: these three mono labels were the ONLY type on the page that
     opted out of the 125% base size, so the one lever the owner asked to drive
     the site with did not reach them - and 10px is under the floor for fine
     print, let alone on a prospect's first screen. 0.6rem = 12px at the 20px
     root, and scales with it from now on. */
  font-family: 'DM Mono', monospace; font-size: var(--fs-2xs);
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted);
}
.shot-body {
  aspect-ratio: 16 / 9;
  padding: var(--s-xs);
  display: flex; flex-direction: column; justify-content: center; gap: var(--s-2xs);
}

/* 01 — the portal login */
.mock-field {
  height: 22px; border-radius: 6px;
  border: 1px solid var(--hairline); background: var(--bg);
}
.mock-field.is-short { width: 62%; }
.mock-btn {
  height: 22px; width: 42%; border-radius: 6px;
  background: var(--accent); opacity: 0.9;
}

/* 02 — hubs found on the LAN */
.mock-row {
  display: flex; align-items: center; gap: var(--s-2xs);
  padding: var(--s-2xs) var(--s-2xs);
  border: 1px solid var(--hairline); border-radius: 6px;
  background: var(--bg);
}
.mock-row i { display: block; height: 5px; border-radius: 3px; background: var(--hairline-hi); flex: 1; }
.mock-row .dot { width: 5px; height: 5px; }

/* 03 — the cast grid */
.mock-grid { flex: 1; display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2xs); }
.mock-cell {
  border: 1px solid var(--hairline); border-radius: 6px;
  background: var(--bg);
  display: flex; align-items: flex-end; padding: var(--s-2xs);
}
.mock-cell i { display: block; height: 5px; width: 50%; border-radius: 3px; background: var(--hairline-hi); }
.mock-cell.is-live { border-color: var(--accent); }
.mock-cell.is-live i { background: var(--accent); }

.steps-note {
  margin-top: var(--s-s);
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.05em; color: var(--muted);
}

/* ─── Minerva AI ─────────────────────────────────────────── */

/* The AI DEMONSTRATION — restored as one designed console.
   P1 stacked four literal chat bubbles; r2 cut the whole thing as
   decoration. It was neither: showing the assistant being TOLD
   something and DOING it is the only way to explain what it is. So:
   one console, two turns, cycling through the two real scenarios. */
.console {
  margin-top: var(--s-m);
  max-width: 600px;
  background: var(--surface);
  border: 1px solid var(--hairline); border-radius: var(--r-lg);
  overflow: hidden;
  transition: border-color var(--t-hover) var(--e-hover), transform var(--t-hover) var(--e-hover);
}
.console:hover { border-color: var(--hairline-hi); transform: translateY(-2px); }

.console-bar {
  display: flex; align-items: center; gap: var(--s-2xs);
  padding: var(--s-xs) var(--s-s);
  background: var(--surface-2);
  border-bottom: 1px solid var(--hairline);
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
}
.console-body { padding: var(--s-s) var(--s-s) var(--s-s); min-height: 11.75rem; }

.turn-label {
  /* rem, not px: these three mono labels were the ONLY type on the page that
     opted out of the 125% base size, so the one lever the owner asked to drive
     the site with did not reach them - and 10px is under the floor for fine
     print, let alone on a prospect's first screen. 0.6rem = 12px at the 20px
     root, and scales with it from now on. */
  font-family: 'DM Mono', monospace; font-size: var(--fs-2xs);
  letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted);
}
.turn-cmd {
  margin-top: var(--s-2xs);
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  line-height: var(--lh-body); color: var(--ink);
  min-height: 2.9em;   /* holds the row so a retype cannot jog layout */
}
.turn-cmd::before { content: '› '; color: var(--accent); }
/* the caret only exists while typing */
.turn-cmd.is-typing::after {
  content: '';
  display: inline-block;
  width: 7px; height: 1.05em;
  margin-left: var(--s-4xs); vertical-align: -0.18em;
  background: var(--accent);
  animation: caret 1s steps(1) infinite;
}
@keyframes caret { 50% { opacity: 0; } }

.turn-answer {
  margin-top: var(--s-s); padding-top: var(--s-s);
  border-top: 1px solid var(--hairline);
  transition: opacity var(--t-state) var(--e-hover);
}
.turn-answer[data-pending='true'] { opacity: 0; }
.turn-answer p {
  margin-top: var(--s-2xs);
  font-size: var(--fs-xs); line-height: var(--lh-body); color: var(--muted);
}
.turn-result {
  display: inline-flex; align-items: center; gap: var(--s-2xs);
  margin-top: var(--s-xs);
  /* rem, not px - same reason as the two labels above. 0.65rem = 13px. */
  font-family: 'DM Mono', monospace; font-size: var(--fs-2xs);
  letter-spacing: 0.04em; color: var(--accent);
}
.turn-result svg { width: 12px; height: 12px; flex-shrink: 0; }

.ai-caption {
  margin-top: var(--s-xs);
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.05em; color: var(--muted);
}

/* ─── Demo checklist (restored) ──────────────────────────────
   Three lines saying what a visitor actually gets on the call —
   information, so it comes back. A crisp accent tick, not the
   ✦/✓ text-glyph parade r2 was right to kill. */
.demo-points { list-style: none; margin-top: var(--s-s); display: flex; flex-direction: column; gap: var(--s-xs); }
.demo-points li { display: flex; gap: var(--s-xs); font-size: var(--fs-xs); line-height: var(--lh-body); color: var(--ink); }
.demo-points svg { width: 14px; height: 14px; flex-shrink: 0; margin-top: var(--s-4xs); color: var(--accent); }

/* ─── Pricing ────────────────────────────────────────────── */

.pricing-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-s); flex-wrap: wrap; }

.tiers {
  margin-top: var(--s-l);
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-s);
}
.tier {
  background: var(--surface);
  border: 1px solid var(--hairline); border-radius: var(--r-lg);
  padding: var(--s-m);
  display: flex; flex-direction: column;
  transition: border-color var(--t-hover) var(--e-hover), transform var(--t-hover) var(--e-hover);
}
.tier:hover { border-color: var(--hairline-hi); transform: translateY(-2px); }
.tier .name { font-size: var(--fs-body); font-weight: 620; }
.tier .price { margin-top: var(--s-xs); font-size: var(--fs-h2); font-weight: 650; line-height: 1; }
.tier .price small {
  font-size: var(--fs-xs); font-weight: 400; color: var(--muted);
  margin-left: var(--s-3xs); letter-spacing: 0;
}
.tier .desc { margin-top: var(--s-xs); font-size: var(--fs-xs); line-height: var(--lh-body); color: var(--muted); }
.tier .group {
  margin-top: var(--s-s);
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
  padding-bottom: var(--s-3xs); border-bottom: 1px solid var(--hairline);
}
.tier ul { list-style: none; margin-top: var(--s-xs); display: flex; flex-direction: column; gap: var(--s-2xs); }
.tier li { font-size: var(--fs-xs); line-height: var(--lh-body); color: var(--ink); }
.tier li.soon { color: var(--muted); }
.tier .note { margin-top: var(--s-s); font-size: var(--fs-xs); color: var(--muted); }
.tier .cta { margin-top: var(--s-m); }
.tier .cta .btn, .tier .cta .btn-quiet { width: 100%; }

/* ─── Trusted by — the rail that slides ──────────────────── */

.rail {
  --visible: 4;          /* how many fit the frame at once   */
  --chip-gap: 18px;
  --chips: 12;           /* six partners, carried twice      */
  /* 🔴 THE FADE IS SIZED IN CARDS, NOT IN FRAME-PERCENT, AND THAT IS THE
     WHOLE FIX. It used to be a flat `4%`: 40px at a 1000px frame, against a
     232px chip. Measured with the loop frozen part-way (the state a hover
     pause leaves behind), a chip sat at x = -10..222 — so ~190px of FULLY
     OPAQUE card ended in a hard vertical cut, because a 40px ramp is only 17%
     of a card and far too steep to dissolve one. Moving, that reads as
     motion. Stopped, it reads as a broken card, which is exactly what Ofir
     saw: "the block is cutted when stops".

     Half a chip is the smallest fade that always works: any sliver up to half
     a card is inside the ramp and dissolves completely, so the frame's cut is
     never visible — moving or frozen. Deriving it from --visible rather than
     hard-coding a percentage means it stays half a chip when the breakpoints
     change the count (4 -> 3 -> 2, so 12.5% -> 16.7% -> 25%); a hand-tuned
     percentage would be correct at one width and wrong at the other two. */
  --rail-fade: calc(50% / var(--visible));
  /* 🔴 THE CLIP BOX NEEDS HEADROOM, BECAUSE THE CHIPS RISE INSIDE IT.
     `overflow: hidden` clips BOTH axes — it is here for the horizontal loop,
     but it was also cutting the top off every hovered card. Measured settled
     (transition disabled, two frames later): a hovered chip's top sits 2px
     ABOVE the rail's box, and with zero vertical padding the box ended exactly
     at the chip, so the top border and both upper corners were the 2px that
     got cut. Ofir: "on hover the cube goes up (good) but the top part gets cut
     out (bad)".

     `overflow-x: hidden; overflow-y: visible` is NOT the fix — per spec, one
     axis visible against a clipping axis computes to `auto`, which makes this
     a scroll container and clips anyway. The fix is to give the clip box room:
     pad it by more than any chip can rise, then pull the padding back out of
     the flow so nothing above or below moves a pixel.

     --rail-gap is the spacing the rail WANTS above it; the arithmetic lives
     here once, so a context that needs different spacing overrides the intent
     (`.proof .rail { --rail-gap: 40px }`) and never the calculation. */
  --rail-lift: 8px;
  --rail-gap: 24px;
  padding-block: var(--rail-lift);
  margin-top: calc(var(--rail-gap) - var(--rail-lift));
  margin-bottom: calc(-1 * var(--rail-lift));
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--rail-fade), #000 calc(100% - var(--rail-fade)), transparent);
          mask-image: linear-gradient(90deg, transparent, #000 var(--rail-fade), #000 calc(100% - var(--rail-fade)), transparent);
}
/* The track is as many frames wide as it needs to be, so each chip is
   exactly one Nth of the visible frame — change --visible and the
   sizing, the count and the loop all follow. Adding partners later
   means adding markup and bumping --chips; nothing else moves. */
.rail-track {
  display: flex;
  width: calc(100% * var(--chips) / var(--visible));
  animation: rail var(--rail-duration) linear infinite;
}
/* Continuous motion the user cannot control is the failure mode
   the practice warns about — so it stops for hover AND focus. */
.rail:hover .rail-track,
.rail:focus-within .rail-track { animation-play-state: paused; }

/* 🔴 THE ACTUAL WCAG 2.2.2 MECHANISM. The two rules above are courtesies, not
   compliance. The criterion is Level A, and its Understanding page rejects a
   hover/focus-held pause in its own words: one that stops "only so long as a
   user has focus on it (where it restarts as soon as the user moves the focus
   away) would not be considered a 'mechanism for the user to pause' BECAUSE IT
   MAKES THE PAGE UNUSABLE IN THE PROCESS". The reason is that it ties the
   visitor up, not — as an earlier version of this comment claimed — that touch
   devices lack hover. (They do, and that is a second problem, but it is my
   observation and not the standard's rationale.)

   This is the switch that genuinely stops it, driven by the nav control and
   reachable from a keyboard.

   ⚠️ MOTION DEFAULTS TO **ON**, INCLUDING UNDER prefers-reduced-motion, AND
   THAT IS ON PURPOSE. 2.2.2 requires a MECHANISM; it does not require the
   content to start paused. Defaulting to paused here would reverse an owner
   decision — Ofir asked, explicitly and twice, that this rail keep rolling for
   reduced-motion visitors — so the fix is purely additive: nothing moves
   differently than before the control existed, it is simply stoppable now.
   Published guidance would prefer a paused default for someone who asked their
   OS for calm; that remains a real tension and it is HIS call, and with this
   switch in place it is a one-word change in index.html's pre-paint script.
   (An earlier version of this comment described that reversed default as if it
   had shipped. It never did. Do not "fix" the code to match a stale note.) */
:root[data-motion='off'] .rail-track { animation-play-state: paused; }

/* 🔴 NO JAVASCRIPT ⇒ NO AUTOMATIC MOTION, AND NO CONTROL PRETENDING OTHERWISE.
   Measured on a script-free copy of this site: `data-motion` was unset (the
   pre-paint script sets it), the button had NO accessible name (JS applies the
   label), it rendered the PAUSED glyph and announced `aria-pressed="false"` —
   all while the rail was measurably still `running`, and pressing it changed
   nothing. A control that misreports the state it cannot change is worse than
   no control; it is the "decorative compliance" this file warns about
   elsewhere, and it fails 2.2.2 outright because no mechanism exists.

   So without JS the rail simply does not animate, and the control is not shown.

   Why not a pure-CSS checkbox toggle, which would have kept BOTH the motion and
   a working control here: reaching this rail from a control in the nav needs
   `:has()`, which is a 2023 feature — and the people browsing without
   JavaScript overlap heavily with the people on old, policy-managed, locked-down
   browsers. The clever version would fail silently on exactly the browser it
   exists to serve. Nothing moving is honest and provably compliant everywhere. */
html.no-js .rail-track { animation: none; }
html.no-js .motion-skip { display: none; }
@keyframes rail { to { transform: translateX(-50%); } }

.partner-chip {
  flex: 0 0 calc(100% / var(--chips) - var(--chip-gap));
  margin-right: var(--chip-gap);
  height: 132px;
  background: #ffffff;              /* the logos are dark marks on white */
  border: 1px solid var(--hairline); border-radius: var(--r-lg);
  display: flex; align-items: center; justify-content: center;
  padding: var(--s-s) var(--s-m);
  transition: transform var(--t-hover) var(--e-hover), border-color var(--t-hover) var(--e-hover);
}
.partner-chip:hover { transform: translateY(-2px); border-color: var(--hairline-hi); }
.partner-chip img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
.partners-caption { font-size: var(--fs-xs); color: var(--muted); }

/* ─── Contact ────────────────────────────────────────────── */

.partners { margin-bottom: clamp(var(--s-l), 8vw, var(--s-xl)); }

.contact-grid {
  margin-top: var(--s-l);
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-l);
  align-items: start;
}
.contact-aside p { font-size: var(--fs-body); color: var(--muted); }
.contact-aside .email { margin-top: var(--s-s); font-size: var(--fs-xs); color: var(--muted); }

.field { margin-bottom: var(--s-xs); }
.field label {
  display: block;
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
  margin-bottom: var(--s-3xs);
}
.field input, .field textarea {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--hairline); border-radius: var(--r);
  color: var(--ink);
  font-family: inherit; font-size: var(--fs-xs);
  height: 2.625rem; padding: 0 0.75rem;
  transition: border-color var(--t-hover) var(--e-hover);
}
.field textarea { height: 5.25rem; padding: 0.625rem 0.75rem; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: var(--muted); opacity: 0.75; }
.field input:hover, .field textarea:hover { border-color: var(--hairline-hi); }
.field input:focus-visible, .field textarea:focus-visible { outline-offset: 0; border-color: var(--accent); }
.form-actions { margin-top: var(--s-s); }
.form-actions .btn { width: 100%; }
.form-note {
  margin-top: var(--s-xs);
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.04em; color: var(--muted);
}

/* ─── Footer ─────────────────────────────────────────────── */

footer { border-top: 1px solid var(--hairline); padding-block: var(--s-m) var(--s-m); }
.foot-row { display: flex; align-items: center; gap: var(--s-xs) var(--s-m); flex-wrap: wrap; }
.foot-row .brand-name { font-size: var(--fs-2xs); }
.foot-row a {
  font-size: var(--fs-xs); color: var(--muted);
  transition: color var(--t-hover) var(--e-hover);
}
.foot-row a:hover { color: var(--ink); }
.foot-row .legal {
  margin-left: auto;
  font-family: 'DM Mono', monospace; font-size: var(--fs-xs);
  letter-spacing: 0.04em; color: var(--muted);
}

/* ─── The calm path ──────────────────────────────────────────
   Reduced motion is not "less animation" — it is NO transform
   or opacity choreography at all: content is simply present,
   the rail becomes a static wrapped grid, the pulse stops.     */

@media (prefers-reduced-motion: reduce) {
  /* Anchor scrolling stays SMOOTH here — owner's call, same reasoning
     as the partner rail: it is a short, user-initiated glide with no
     transform or parallax, and it only ever runs because someone
     clicked a nav link. Involuntary motion is what stops below. */

  /* 🔴 REDUCED MOTION MEANS REDUCED, NOT REMOVED — and this block used to
     get that backwards in the one way that mattered.

     It set `opacity:1; transform:none; animation:none` on all 21 reveals, so
     a visitor who asks their OS for less motion got a page with NO arrival at
     all: every element simply already there. That is not what the preference
     asks for. Vestibular discomfort comes from MOVEMENT — parallax, slide-ins,
     scaling, spinning. Opacity is the safe channel, and the standard treatment
     is a short fade (~150ms), not silence.

     🔬 WHY IT MATTERED HERE, measured rather than assumed: the machine this
     repo lives on has `prefers-reduced-motion: reduce` ON, so the owner has
     been judging a page with its entire motion system switched off — and said
     so, in the only vocabulary available to him: *"it look old and BAD so bad
     no advancing - even the 'Already in clinical simulation centers' isn't
     sliding like something relevant for today's sites."* The tell is that the
     two things he later demanded BY NAME — the partner rail sliding, and the
     nav links gliding — are exactly the two things this block was suppressing.
     Carving those out stopped those complaints. Everything still suppressed is
     everything he had not yet seen.

     So the fade comes back and the TRAVEL stays gone. `translateY` is dropped,
     the stagger delay is dropped (a queue of delays is itself a duration), and
     what is left is presence arriving softly. Everything genuinely
     motion-sickness-adjacent still stops dead below. */
  .rise,
  .reveal {
    opacity: 0;
    transform: none !important;
  }
  .rise,
  .reveal.is-in {
    animation: fade-only var(--t-calm) linear forwards !important;
    animation-delay: 0ms !important;
  }
  .is-shown {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }

  /* THE ONE EXCEPTION, and it is the owner's call (Ofir, explicitly,
     twice): the partner rail keeps rolling even here. It is a slow,
     linear, edge-masked drift of logos — no flashing, no parallax, no
     scale — and it stays pausable on hover and on keyboard focus,
     which is the mechanism WCAG 2.2.2 actually asks for. Everything
     genuinely motion-sickness-adjacent (arrival, scroll reveals, the
     typing console, hover lifts) still stops dead below. */


  /* The console shows one COMPLETE exchange: no typing, no caret,
     the answer simply present. JS never starts the cycle. */
  .turn-cmd.is-typing::after { display: none; }
  .turn-answer,
  .turn-answer[data-pending='true'] { opacity: 1; transition: none; }

  .step:hover .step-shot,
  .console:hover { transform: none; }

  /* The hamburger still becomes an X — it just does it instantly.
     The state change is feedback; the sweep between states is not. */
  .nav-toggle span { transition: none; }

  .btn:hover, .btn-quiet:hover,
  .tier:hover, .ai-still:hover, .partner-chip:hover,
  .theme-toggle:hover { transform: none; }

  .arrow, a:hover .arrow, button:hover .arrow { transition: none; transform: none; }
}

/* ─── Responsive ─────────────────────────────────────────── */


@media (max-width: 900px) {
  .rail { --visible: 3; }
  .partner-chip { height: 118px; padding: var(--s-s) var(--s-s); }
  .nav-links { display: none; }
  .nav-toggle { display: flex; }
  .steps { grid-template-columns: 1fr; gap: var(--s-m); }
  .tiers { grid-template-columns: 1fr; }
  .contact-grid { grid-template-columns: 1fr; gap: var(--s-m); }
}

@media (max-width: 600px) {
  /* At a 20px root the headline is tall enough to push the CTA off a
     phone screen; buy the space back from the hero's top padding. */
  .hero { padding-block: 2rem clamp(var(--s-m), 7vw, var(--s-l)); }

  /* 🔴 …AND THAT PADDING WAS TREATING THE SYMPTOM. The note above is r6's,
     and it is an accurate description of a headline that does not fit —
     the fix just moved the problem instead of removing it.

     Measured at 375x812 before this rule: the h1 computed to 42.5px and set
     as EIGHT line-fragments, with "run" stranded on a 65px line and "Your"
     on an 87px one against a 335px measure — 388px of headline, 48% of the
     first screen a phone visitor gets.

     The cause is `clamp(2.125rem, 4.4vw, 3.15rem)`: at 375px the 4.4vw term
     is only 16.5px, so the REM FLOOR governs — and the 125% base size Ofir
     asked for silently lifted that floor from 34px to 42.5px. His
     instruction was right; nobody re-tuned the floor underneath it.

     Scoped to <=600px on purpose: every wider composition — the one he has
     actually reviewed — is left byte-identical. */
  .hero h1 {
    font-size: clamp(var(--fs-h2), 8vw, var(--fs-h1));
    letter-spacing: -0.02em;
  }
  /* The authored break is a DESKTOP line-composition decision (r6 split the
     claim so it reads as three deliberate lines). On a 335px measure it is
     what strands "run" alone; let the text find its own breaks here.

     🪤 `display:none` DELETES the break and leaves NOTHING in its place, so the
     words on either side fuse unless the markup already holds whitespace — that
     is how the live headline shipped as "runa professional" to every phone
     (Ofir, screenshot 2026-08-23). Hiding it as an inline SPACE instead makes
     the rule self-sufficient: the markup's own space stays welcome, but the
     copy no longer depends on someone remembering to type it. */
  .hero h1 br {
    display: inline;
    content: " ";
    font-size: 0;
    white-space: pre-wrap;
  }
  .hero h1 br::after { content: " "; font-size: var(--fs-body); }
  .rail { --visible: 2; --chip-gap: 12px; }
  .partner-chip { height: 104px; padding: var(--s-xs) var(--s-s); }
  .nav-signin { display: none; }
  .hero .cta-row { flex-direction: column; align-items: stretch; }
  .hero .cta-quiet { justify-content: center; }
  .foot-row .legal { margin-left: 0; }
  /* the stage keeps its shape but drops the rail — at 375px a 84px
     rail would eat half the canvas */
  .console-body { min-height: 14.5rem; }
}
