/* self-hosted: a third-party font request paints the page in a fallback face first,
   then re-flows everything when the real font lands. these ship with the page. */
@font-face {
  font-family: 'IBM Plex Sans';
  src: url('fonts/plex-sans.woff2') format('woff2');
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('fonts/plex-mono-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'IBM Plex Mono';
  src: url('fonts/plex-mono-600.woff2') format('woff2');
  font-weight: 500 600;
  font-style: normal;
  font-display: swap;
}

:root {
  --navy-900: #071B3A;
  --navy-800: #082147;
  --navy-700: #0C2C5E;
  --navy-600: #14489C;
  --blue-500: #2563EB;
  --blue-400: #3B82F6;
  --blue-200: #BFD3F5;
  --ember: #E8541E;
  --ember-soft: #F97316;
  --ember-deep: #C4410F;

  --ink: #0F172A;
  --ink-muted: #475569;
  --ink-faint: #64748B;
  --paper: #FFFFFF;
  --paper-tint: #F4F7FC;
  --rule: #DCE3ED;

  --on-navy: #FFFFFF;
  --on-navy-muted: #A9BDDC;
  /* recessive but still legible: 5.27:1 on the lightest point of the page gradient.
     dimming --on-navy-muted with opacity instead lands at 3.68:1 and fails AA */
  --on-navy-faint: #8FA3C4;
  --on-navy-rule: rgba(255, 255, 255, 0.14);

  --radius: 4px;
  --radius-lg: 10px;
  /* the page is simply as wide as the window. no vw units here: vw counts the
     scrollbar, so anything sized in vw ends up wider than the space you can see. */
  --shell: 100%;
  --gutter: clamp(20px, 4%, 72px);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --z-nav: 50;
  /* the pill nav's own height plus its 12px inset — section heads pin just below it */
  --nav-h: 80px;
}

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  /* hold the scrollbar's space so locking scroll during the intro can't jolt the width */
  scrollbar-gutter: stable;
  /* html owning a background stops body's from propagating to the canvas, so the
     gradient below is measured against the whole document instead of the viewport */
  background: var(--navy-900);
}

body {
  margin: 0;
  /* the hero animation moves elements above the fold; without this, Chrome "helpfully"
     re-scrolls the page to compensate, which looks like the page scrolling on its own */
  overflow-anchor: none;
  /* one unbroken descent from top to bottom — sections no longer paint their own
     backgrounds, so there is no seam anywhere to read as a slide boundary */
  background: linear-gradient(
    180deg,
    #071B3A 0%,
    #082147 20%,
    #0C2C5E 46%,
    #0A2450 70%,
    #071B3A 100%
  );
  background-repeat: no-repeat;
  color: var(--on-navy);
  /* Plex Sans covers Latin + Vietnamese; the rest catch CJK, which Plex doesn't ship */
  font-family: 'IBM Plex Sans', system-ui, -apple-system, 'Hiragino Sans', 'Yu Gothic',
    'Noto Sans CJK SC', 'Microsoft YaHei', 'PingFang SC', 'Malgun Gothic', 'Apple SD Gothic Neo', sans-serif;
  font-size: clamp(17px, 1.05vw, 19px);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img, svg { max-width: 100%; display: block; }

h1, h2, h3, h4 {
  margin: 0;
  line-height: 1.12;
  letter-spacing: -0.022em;
  font-weight: 700;
}

h1 { font-size: clamp(2.6rem, 6.2vw, 4.6rem); }
h2 { font-size: clamp(2rem, 4vw, 3.4rem); }
h3 { font-size: clamp(1.15rem, 2vw, 1.42rem); letter-spacing: -0.012em; }

p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

::selection { background: rgba(232, 84, 30, 0.22); }

:focus-visible {
  outline: 3px solid var(--blue-500);
  outline-offset: 3px;
  border-radius: 2px;
}

.skip-link {
  position: absolute;
  left: 50%;
  top: -100px;
  transform: translateX(-50%);
  z-index: 100;
  background: var(--navy-800);
  color: #fff;
  padding: 12px 20px;
  border-radius: var(--radius);
  transition: top 200ms var(--ease);
}
.skip-link:focus { top: 12px; }

/* the animation is the heading — this one is for search engines and screen readers */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- layout ---------- */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* the pinned title is what now marks where a section starts, so the old slab of
   padding at every seam is no longer doing that job — it just left dead space */
section {
  padding-block: clamp(48px, 6vw, 84px);
  scroll-margin-top: var(--nav-h);
}

.wv {
  color: var(--ember);
  font-weight: 600;
}
/* never step *down* from a heading's own weight */
.tier-name .wv { font-weight: inherit; }

/* every section now opens on a statement and nothing else. the rule is the only
   thing left marking a new section, so it draws itself out as the heading arrives */
.section-title h2::before {
  content: '';
  display: block;
  width: 52px;
  height: 3px;
  margin-bottom: clamp(18px, 1.8vw, 26px);
  background: var(--ember);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 620ms var(--ease) 120ms;
}
.section-title.is-visible h2::before { transform: none; }

.closing .section-title h2::before { margin-inline: auto; }

/* section heads carry the hero's type: same size, same weight, same gap to the text */
.lede {
  font-size: clamp(1.12rem, 1.55vw, 1.5rem);
  color: var(--on-navy-muted);
  max-width: 62ch;
  line-height: 1.55;
}
.lede + .lede { margin-top: 0.9em; }

/* the statement is the section's whole opening now, so it carries the weight the
   label used to share: larger, tighter, and held to a measure that lands it on
   one or two lines rather than trailing off across the full width */
.section-title h2 {
  font-size: clamp(2rem, 4.4vw, 5rem);
  letter-spacing: -0.03em;
  line-height: 1.06;
}

/* ---------- how it works: the flow stage ---------- */

/* the sequence only reads if the whole stage is on screen at once, so the section
   fits the viewport and the drawing is capped to what's left after the heading */
#how {
  min-height: 100svh;
  display: flex;
  align-items: center;
}
#how .section-title { margin-bottom: clamp(20px, 2.5vw, 36px); }

.flowstage {
  position: relative;
  margin: 0 auto;
  /* the reserve went up when this section's heading stopped being screen-reader-only
     and became a full statement like every other section's */
  max-width: calc((100svh - 460px) * 1.9);
}
.flowstage svg { width: 100%; height: auto; display: block; }

/* the labels live over the drawing, not in it — HTML text reflows on a phone, SVG text just shrinks */
.fs-label {
  position: absolute;
  display: block;
  /* the card hugs its own text. the English labels carry their own line breaks, and
     max-content sizes the box to the longest of those lines — so a short label like
     "You ask in / plain language" gets a small box, not one padded out to the width
     of the longest label. a translated label has no breaks, so max-width steps in and
     wraps it near 19 characters a line (em, so it tracks the clamped font-size). */
  width: max-content;
  max-width: 11.5em;
  opacity: 0;
  padding: 9px 14px;
  border-radius: 10px;
  /* the same card the hero's signals sit on */
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.16);
  backdrop-filter: blur(3px);
  font-size: clamp(0.8rem, 1.15vw, 1.05rem);
  font-weight: 600;
  line-height: 1.35;
  color: var(--on-navy);
  text-wrap: balance;
}
.fs-label.t1 { top: 30%; left: 0;   --lx: -14px; }
.fs-label.t2 { top: 20%; right: 0;  --lx: 14px; }
.fs-label.t3 { top: 0;   left: 0; right: 0; margin-inline: auto; --ly: -12px; }
/* These two wait beside the search bar and the answer tray before riding up with them.
   They used to share one start (68%), which worked while both were a single line. At a
   fixed width they are 65px and 109px tall, so a shared top pushed the taller one 46px
   down into the tray it is naming. The starts differ instead, chosen so both cards end
   at about the same bottom edge — each sitting just above the thing it points at.
   --top-end is where they settle once the corpus clears. */
.fs-label.t4 { top: 66%; left: 0;   --ly: 12px; --top-end: 15%; }
.fs-label.t5 { top: 54%; right: 0;  --ly: 12px; --top-end: 15%; }

/* the document, and the fragments it comes apart into */
.fs-page { fill: #fff; }
.fs-line { fill: #CBD5E1; }
.fs-frag { fill: #94A3B8; }
.fs-neighbour rect { fill: #64748B; }
.fs-pip { fill: var(--ember); }

/* the fragments that answer the question — beats `.fs-neighbour rect`, which is more specific */
.fs-match,
.fs-neighbour .fs-match { fill: var(--ember); }

.fs-link {
  fill: none;
  stroke: rgba(96, 165, 250, 0.45);
  stroke-width: 1.5;
}
.fs-link.is-match { stroke: var(--ember); stroke-width: 2; }

/* the search — it has a home at the bottom of the stage */
.fs-searchbar {
  fill: rgba(255, 255, 255, 0.06);
  stroke: rgba(255, 255, 255, 0.24);
}
.fs-glass circle { fill: none; stroke: #fff; stroke-width: 2.2; }
.fs-glass path { stroke: #fff; stroke-width: 2.2; stroke-linecap: round; }
.fs-typed { fill: rgba(255, 255, 255, 0.35); }
.fs-ripple { fill: none; stroke: var(--ember); stroke-width: 2; }

/* the tray the answer lands in, right beside what was asked */
.fs-tray {
  fill: rgba(232, 84, 30, 0.1);
  stroke: rgba(232, 84, 30, 0.5);
}

@media (max-width: 720px) {
  .fs-label {
    font-size: 0.78rem;
    /* each card still hugs its own text (width: max-content, inherited). the cap is a
       share of the stage rather than a fixed em, so a translated label — which has no
       line breaks of its own — wraps inside its half instead of crossing the drawing.
       the earlier rule filled the whole half with the box and let the text ride one
       edge, which is the wide empty strip this replaces. */
    max-width: 46%;
    padding: 7px 10px;
  }
  /* each card keeps only the anchor on its own side (from the base rules), so it grows
     inward from that edge and hugs. right-side cards align their short lines right, to
     the edge they hang from. the bottom pair sits higher to clear the search bar and tray. */
  .fs-label.t2 { text-align: right; }
  .fs-label.t4 { top: 52%; --top-end: 14%; }
  .fs-label.t5 { top: 52%; text-align: right; --top-end: 14%; }
}

.section-head { max-width: 68ch; margin-bottom: clamp(40px, 5vw, 64px); }
/* the title box itself runs the full shell width so its scrim can reach both window
   edges — the measure is held by the h2 instead */
.section-title h2 { max-width: 22ch; }

/* ---------- pinned section heads ---------- */

/* the title holds under the nav while the rest of its section scrolls beneath it,
   then releases when the next section pushes it off — that hand-off is what makes
   the page read as one continuous argument rather than a stack of slides.
   only the statement pins: pinning the ledes too would hold most of the viewport
   and leave the content underneath with nowhere to go */
.section-title {
  position: sticky;
  top: var(--nav-h);
  z-index: 2;
  padding-block: 18px 16px;
  margin-bottom: clamp(20px, 2.4vw, 32px);
}

/* content passing underneath is occluded rather than colliding with the pinned type.
   blurring the backdrop rather than painting a flat panel keeps this readable at
   every point of the background gradient */
.section-title::before {
  content: '';
  position: absolute;
  /* bleed out by exactly the shell's gutter so the scrim reaches both window edges
     without adding scrollable width the way a vw-sized bleed would */
  inset: 0 calc(var(--gutter) * -1) -62px;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  transition: opacity 240ms var(--ease);
  background: rgba(7, 27, 58, 0.92);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  -webkit-mask-image: linear-gradient(180deg, #000 80%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 80%, transparent 100%);
}

/* the scrim is only wanted while the title is actually pinned. in normal flow it
   would darken and blur whatever happens to sit above the heading. when it is
   pinned it runs up behind the nav, so the held block has no visible top edge */
.section-title[data-stuck='true']::before {
  opacity: 1;
  top: calc((var(--nav-h) + 26px) * -1);
}

/* the statement arrives a word at a time, each rising out of its own mask. the mask
   is the point: the words climb into place rather than fading up as one block */
.section-title h2 .w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* without the extra room the mask shaves the descenders off g, y and p */
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.section-title h2 .w > span {
  display: inline-block;
  transform: translateY(112%);
  transition: transform 760ms var(--ease);
  transition-delay: calc(var(--i, 0) * 40ms);
}
.section-title.is-visible h2 .w > span { transform: none; }

/* a statement that is never observed would otherwise stay hidden for good, which is
   how the closing heading went missing once already */
.section-title:not(.reveal) h2 .w > span { transform: none; }

/* the flow stage is built to hold one screen at a time and the closing block is
   centred — a pinned title fights both, so they keep the ordinary flow */
#how .section-title,
.closing .section-title {
  position: static;
  padding-block: 0;
}
#how .section-title::before,
.closing .section-title::before { display: none; }

/* a pinned title eats too much of a short viewport to be worth it */
@media (max-width: 900px) {
  .section-title { position: static; padding-block: 0; }
  .section-title::before { display: none; }
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 48px;
  padding: 13px 24px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-size: 0.98rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 200ms var(--ease), color 200ms var(--ease),
              border-color 200ms var(--ease), box-shadow 200ms var(--ease);
}

/* white needs the deeper orange to clear 4.5:1 — on the brand ember it lands at 3.6 */
.btn-primary {
  background: var(--ember-deep);
  color: #fff;
  box-shadow: 0 1px 2px rgba(7, 27, 58, 0.3);
}
.btn-primary:hover { background: #A9370B; box-shadow: 0 6px 20px rgba(232, 84, 30, 0.32); }

.btn-ghost {
  border-color: rgba(255, 255, 255, 0.28);
  color: #fff;
  background: rgba(255, 255, 255, 0.02);
}
.btn-ghost:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.5); }

.btn-line {
  border-color: rgba(255, 255, 255, 0.28);
  color: #fff;
  background: rgba(255, 255, 255, 0.04);
}
.btn-line:hover { border-color: rgba(255, 255, 255, 0.5); background: rgba(255, 255, 255, 0.1); }

.btn svg { width: 18px; height: 18px; flex: none; }
.btn-primary svg { transition: transform 240ms var(--ease); }
.btn-primary:hover svg { transform: translateX(3px); }

/* ---------- nav ---------- */

.nav {
  position: fixed;
  inset: 12px 12px auto;
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  gap: 20px;
  /* line the logo up with the page content instead of a fixed pill width */
  padding: 10px max(12px, calc(var(--gutter) - 20px));
  border: 1px solid transparent;
  border-radius: 999px;
  transition: background-color 320ms var(--ease), border-color 320ms var(--ease),
              box-shadow 320ms var(--ease), backdrop-filter 320ms var(--ease);
}
/* the nav is an inset pill, so there is clear page either side of it and above it.
   a title releasing from its pin scrolls up through that strip in full view — this
   covers the strip once the page has moved, without touching the nav over the hero */
.nav::before {
  content: '';
  position: absolute;
  inset: -12px -12px auto;
  height: 92px;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(180deg, var(--navy-900) 42%, rgba(7, 27, 58, 0) 100%);
  transition: opacity 320ms var(--ease);
}
.nav[data-stuck='true']::before { opacity: 1; }

.nav[data-stuck='true'] {
  background: rgba(8, 33, 71, 0.82);
  border-color: rgba(255, 255, 255, 0.12);
  box-shadow: 0 10px 40px rgba(7, 27, 58, 0.4);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
}

.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  color: #fff;
  font-weight: 700;
  letter-spacing: 0.24em;
  font-size: 0.86rem;
  /* the mark is 26px, so the link needs padding to reach a 44px tap target.
     vertical only — horizontal padding would shift the logo off the page gutter */
  padding-block: 9px;
}
.brand .mark { width: 26px; height: 26px; }

/* the house Woven belongs to — quieter than the product it sits beside */
.brand-parent {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 auto 0 14px;
  padding-left: 14px;
  border-left: 1px solid var(--on-navy-rule);
  color: var(--on-navy-muted);
  font-size: 0.8rem;
  font-weight: 600;
}
.mamot-mark { height: 20px; width: auto; }

.nav-links {
  display: flex;
  align-items: center;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links a {
  display: flex;
  align-items: center;
  /* 44px tall, the smallest comfortable tap target — 9px padding left these at 42 */
  min-height: 44px;
  padding: 9px 14px;
  border-radius: 999px;
  color: rgba(255, 255, 255, 0.72);
  text-decoration: none;
  font-size: 0.92rem;
  font-weight: 500;
  transition: color 180ms var(--ease), background-color 180ms var(--ease);
}
.nav-links a:hover { color: #fff; background: rgba(255, 255, 255, 0.1); }
.nav-links a[aria-current='true'] { color: #fff; background: rgba(255, 255, 255, 0.14); }

.nav .btn { min-height: 44px; padding: 9px 18px; font-size: 0.9rem; }

.nav-toggle {
  display: none;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 999px;
  color: #fff;
  cursor: pointer;
}
.nav-toggle svg { width: 20px; height: 20px; }

/* ---------- language switch ---------- */
.lang-switch { position: relative; margin-left: 4px; }
.lang-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* 44px is the smallest comfortable touch target */
  min-height: 44px;
  padding: 8px 12px;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 999px;
  color: rgba(255, 255, 255, 0.82);
  font: inherit;
  font-size: 0.86rem;
  font-weight: 600;
  cursor: pointer;
  transition: color 180ms var(--ease), background-color 180ms var(--ease);
}
.lang-btn:hover { color: #fff; background: rgba(255, 255, 255, 0.1); }
.lang-globe { width: 16px; height: 16px; flex: none; opacity: 0.75; }

.lang-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 168px;
  margin: 0;
  padding: 6px;
  list-style: none;
  background: rgba(8, 33, 71, 0.98);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 14px;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 18px 40px rgba(2, 11, 28, 0.5);
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity 160ms var(--ease), transform 160ms var(--ease);
  z-index: 60;
}
.lang-switch[data-open='true'] .lang-menu { opacity: 1; transform: none; pointer-events: auto; }
.lang-menu li {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 10px 12px;
  border-radius: 9px;
  color: rgba(255, 255, 255, 0.78);
  font-size: 0.92rem;
  cursor: pointer;
}
.lang-menu li:hover { color: #fff; background: rgba(255, 255, 255, 0.1); }
.lang-menu li[aria-selected='true'] { color: #fff; background: rgba(232, 84, 30, 0.22); }

/* below this the Woven mark, the mamot.ai lockup, the language pill and the menu
   button stop fitting on one line and start overlapping. the parent brand is the
   one to drop — it is secondary here, and it still appears in the hero note and
   again in the footer, so the attribution is not lost */
@media (max-width: 620px) {
  .brand-parent { display: none; }
}


@media (max-width: 900px) {
  .nav-links, .nav > .btn { display: none; }
  .nav-toggle { display: flex; }

  .nav .brand { order: 0; }
  .lang-switch { order: 1; margin-left: auto; }
  .nav-toggle { order: 2; margin-left: 8px; }
  .nav-links { order: 3; }
  .nav > .btn { order: 4; }
  .nav[data-open='true'] {
    flex-wrap: wrap;
    border-radius: 20px;
    background: rgba(8, 33, 71, 0.96);
    border-color: rgba(255, 255, 255, 0.14);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
  }
  .nav[data-open='true'] .nav-links {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    padding: 8px 0 4px;
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    margin-top: 8px;
  }
  .nav[data-open='true'] .nav-links a { padding: 13px 8px; border-radius: 8px; }
  .nav[data-open='true'] > .btn { display: inline-flex; width: 100%; margin-bottom: 4px; }
}

/* ---------- hero ---------- */

/* only the glow of its own — the base colour underneath is the page gradient, so the
   hero no longer ends on an edge where the next section starts */
.hero {
  position: relative;
  background: radial-gradient(120% 90% at 88% 8%, rgba(20, 72, 156, 0.55) 0%, rgba(8, 33, 71, 0) 62%);
  color: var(--on-navy);
  padding-block: clamp(120px, 12vw, 164px) clamp(64px, 7vw, 96px);
  overflow: hidden;
}

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(40px, 4vw, 64px);
  align-items: start;
}
@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; }
}

/* the tagline IS the title — sized so the three phrases hold one line on a desktop */
.hero-headline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.42em;
  margin: 0 0 clamp(28px, 3vw, 44px);
  font-size: clamp(1.6rem, 3.5vw, 4rem);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.14;
  color: #fff;
}
.hero-headline .dot {
  flex: none;
  width: 0.17em;
  height: 0.17em;
  border-radius: 50%;
  background: var(--ember);
}

/* "Connected" carries the logo's orange — it's the word the whole line is driving at */
.hero-headline span:last-child { color: var(--ember); }

/* one line can't hold three phrases on a phone — stack them and drop the separators
   rather than wrapping mid-line with a dangling dot */
@media (max-width: 700px) {
  .hero-headline { flex-direction: column; align-items: flex-start; gap: 0; }
  .hero-headline .dot { display: none; }
}

.hero-lede {
  font-size: clamp(1.12rem, 1.55vw, 1.5rem);
  line-height: 1.55;
  color: var(--on-navy-muted);
  max-width: 52ch;
  margin-bottom: 32px;
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; }

.hero-note {
  margin-top: 30px;
  padding-top: 22px;
  border-top: 1px solid var(--on-navy-rule);
  font-size: 0.86rem;
  color: rgba(169, 189, 220, 0.8);
  max-width: 44ch;
}

/* the only proof we have — it doesn't get to sit in the muted grey with everything else */
.pilot { color: #fff; }
.pilot strong,
.moat-item strong,
.tabpanel strong { color: var(--ember); font-weight: 600; }
.tabpanel strong { color: var(--ember-soft); }

/* hero visual */

.hero-visual {
  position: relative;
  aspect-ratio: 5 / 4.4;
  width: 100%;
}
.hero-visual svg { width: 100%; height: 100%; overflow: visible; }

.doc-page { fill: #fff; }
.doc-line { fill: #CBD5E1; }
.doc-line.hot { fill: #FBCFB8; }

.sig-card rect { fill: rgba(255, 255, 255, 0.06); stroke: rgba(255, 255, 255, 0.16); }
.sig-card .sig-label {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  fill: #fff;
}
.sig-card .sig-cite {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 8px;
  fill: #8FA9CF;
}
.sig-card .pip { fill: var(--ember); }

.thread {
  fill: none;
  stroke: url(#threadGrad);
  stroke-width: 1.6;
  stroke-linecap: round;
}

.scanline { fill: url(#scanGrad); }

/* ---------- stats ---------- */

.stat-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin-top: clamp(40px, 5vw, 60px);
}
@media (max-width: 780px) { .stat-row { grid-template-columns: repeat(2, 1fr); } }

.stat {
  padding: 26px 22px;
  /* translucent, not a fixed navy — an opaque block would read as a patch sitting
     on top of the page gradient instead of part of the same descent */
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-lg);
  transition: transform 300ms var(--ease), border-color 300ms var(--ease);
}
.stat:hover { border-color: rgba(232, 84, 30, 0.5); }

.stat-value {
  font-size: clamp(2rem, 3.4vw, 2.7rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  color: #fff;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat-label {
  margin-top: 12px;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--on-navy-muted);
  line-height: 1.5;
}

/* where the number came from — small, but never hidden. the recession comes from
   the colour, not from opacity: dimming the muted blue to 62% dropped these to
   3.68:1, which is the whole page failing contrast on the one thing that backs
   up every number it quotes */
.stat-source {
  margin-top: 8px;
  font-size: 0.72rem;
  line-height: 1.45;
  color: var(--on-navy-faint);
}
.lede-source {
  display: block;
  margin-top: 10px;
  font-size: 0.78rem;
  color: var(--on-navy-faint);
}

/* ---------- pull quote ---------- */

.pull {
  margin-top: clamp(44px, 5vw, 64px);
  padding: 26px 30px;
  border-left: 3px solid var(--ember);
  background: rgba(255, 255, 255, 0.04);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.pull p {
  font-size: clamp(1.1rem, 2vw, 1.4rem);
  font-weight: 500;
  letter-spacing: -0.015em;
  color: #fff;
}
.pull cite {
  display: block;
  margin-top: 10px;
  font-style: normal;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--on-navy-muted);
}

/* ---------- capability cards ---------- */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 18px;
}

.card {
  position: relative;
  padding: 30px 26px 32px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--on-navy-rule);
  border-radius: var(--radius-lg);
  transition: border-color 260ms var(--ease), background-color 260ms var(--ease);
}
.card:hover {
  border-color: var(--blue-400);
  background: rgba(255, 255, 255, 0.07);
}
.card::after {
  content: '';
  position: absolute;
  left: 26px;
  right: 26px;
  bottom: 0;
  height: 2px;
  background: var(--ember);
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 320ms var(--ease);
}
.card:hover::after { transform: scaleX(1); }

.card-step {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--blue-200);
}
.card h3 { margin: 10px 0 10px; }
.card p { color: var(--on-navy-muted); font-size: 0.96rem; }
.card-icon {
  width: 34px;
  height: 34px;
  margin-bottom: 18px;
  color: var(--ember);
}
.card-icon svg { width: 100%; height: 100%; }

/* ---------- split feature ---------- */

.split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: clamp(24px, 4vw, 48px);
  margin-top: clamp(40px, 5vw, 60px);
}

.feature-list { list-style: none; margin: 18px 0 0; padding: 0; }
.feature-list li {
  position: relative;
  padding: 0 0 0 26px;
  margin-bottom: 14px;
  color: var(--on-navy-muted);
  font-size: 0.97rem;
}
.feature-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 9px;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--ember);
}
.feature-list strong { color: #fff; font-weight: 600; }

/* ---------- tiers ---------- */

.tiers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
  margin-top: 8px;
}
.tier {
  padding: 26px 24px;
  border: 1px solid var(--on-navy-rule);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.04);
  transition: border-color 260ms var(--ease), background-color 260ms var(--ease);
}
.tier:hover { border-color: var(--blue-400); background: rgba(255, 255, 255, 0.07); }
.tier-name {
  font-size: 1.12rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 8px;
}
/* the plan name is what people scan the row for — it doesn't get to be the faintest thing on the
   card. `p.` because `.tier p` below would otherwise win on specificity and grey it out */
.tier p.tier-num {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--ember-soft);
  text-transform: uppercase;
}
.tier p { color: var(--on-navy-muted); font-size: 0.94rem; margin-top: 8px; }

/* ---------- pipeline ---------- */

.pipeline {
  margin-top: clamp(40px, 5vw, 56px);
  border: 1px solid var(--on-navy-rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: rgba(255, 255, 255, 0.025);
}

.pipe-track {
  position: relative;
  display: grid;
  grid-template-columns: repeat(10, minmax(0, 1fr));
}
.pipe-rail {
  position: absolute;
  left: 0;
  top: 0;
  height: 2px;
  width: 100%;
  background: rgba(255, 255, 255, 0.1);
}
.pipe-fill {
  position: absolute;
  left: 0;
  top: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, var(--blue-400), var(--ember));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 900ms var(--ease);
}
.pipeline.is-visible .pipe-fill { transform: scaleX(1); }

.pipe-step {
  padding: 24px 12px 22px;
  border-right: 1px solid rgba(255, 255, 255, 0.07);
  text-align: center;
  opacity: 0.4;
  transition: opacity 500ms var(--ease), background-color 300ms var(--ease);
}
.pipe-step:last-child { border-right: 0; }
.pipeline.is-visible .pipe-step { opacity: 1; }
.pipe-step:hover { background: rgba(255, 255, 255, 0.05); }

.pipe-dot {
  width: 9px;
  height: 9px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: var(--blue-400);
  box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.16);
}
/* +2 offsets the rail and fill divs: this lights Signals → Store */
.pipe-step:nth-child(n + 9) .pipe-dot {
  background: var(--ember);
  box-shadow: 0 0 0 4px rgba(232, 84, 30, 0.18);
}
.pipe-name {
  font-size: 0.85rem;
  font-weight: 600;
  color: #fff;
  margin-bottom: 6px;
}
.pipe-model {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  color: var(--on-navy-muted);
  line-height: 1.5;
  word-break: break-word;
}

@media (max-width: 900px) {
  .pipe-track { grid-template-columns: repeat(2, 1fr); }
  .pipe-step { border-bottom: 1px solid rgba(255, 255, 255, 0.07); text-align: left; padding: 18px 16px; }
  .pipe-dot { margin: 0 0 10px; }
  .pipe-rail, .pipe-fill { display: none; }
}

/* ---------- industry tabs ---------- */

.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 26px;
}
.tab {
  padding: 11px 18px;
  min-height: 44px;
  border: 1px solid var(--on-navy-rule);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.04);
  color: var(--on-navy-muted);
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 200ms var(--ease), color 200ms var(--ease), border-color 200ms var(--ease);
}
.tab:hover { border-color: rgba(255, 255, 255, 0.4); color: #fff; }
.tab[aria-selected='true'] {
  background: #fff;
  border-color: #fff;
  color: var(--navy-900);
}

.tabpanel {
  padding: clamp(26px, 4vw, 40px);
  border: 1px solid var(--on-navy-rule);
  border-left: 3px solid var(--ember);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  background: rgba(255, 255, 255, 0.04);
  animation: panel-in 400ms var(--ease);
}
.tabpanel[hidden] { display: none; }
.tabpanel h3 { margin-bottom: 12px; }
.tabpanel p { color: var(--on-navy-muted); max-width: 60ch; }

@keyframes panel-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* ---------- moat ---------- */

.moat {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1px;
  background: var(--on-navy-rule);
  border: 1px solid var(--on-navy-rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-top: clamp(36px, 4vw, 52px);
}
.moat-item {
  padding: 30px 26px;
  background: rgba(255, 255, 255, 0.04);
  transition: background-color 280ms var(--ease);
}
.moat-item:hover { background: rgba(255, 255, 255, 0.08); }
.moat-item h3 { color: #fff; margin-bottom: 10px; }
.moat-item p { color: var(--on-navy-muted); font-size: 0.95rem; }

/* ---------- investor track ---------- */

.investor-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  margin-top: clamp(32px, 4vw, 48px);
  padding: clamp(26px, 3vw, 38px);
  border: 1px solid var(--on-navy-rule);
  border-left: 3px solid var(--ember);
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  background: rgba(255, 255, 255, 0.04);
}
.investor-cta h3 { color: #fff; margin-bottom: 8px; }
.investor-cta p { color: var(--on-navy-muted); max-width: 52ch; font-size: 0.97rem; }

/* ---------- closing ---------- */

.closing {
  position: relative;
  overflow: hidden;
  text-align: center;
}
.closing .shell { position: relative; z-index: 1; }

.closing-watermark {
  position: absolute;
  right: -6%;
  bottom: -14%;
  width: min(760px, 70%);
  opacity: 0.06;
  pointer-events: none;
  transform: translate3d(0, 24px, 0);
  transition: transform 1400ms var(--ease), opacity 1400ms var(--ease);
}
.closing.is-visible .closing-watermark { transform: none; opacity: 0.08; }
body[data-motion='off'] .closing-watermark { transform: none; opacity: 0.08; }
.closing h2 {
  max-width: 20ch;
  margin: 0 auto clamp(28px, 3vw, 44px);
  font-size: clamp(1.6rem, 3.5vw, 4rem);
}
.closing .lede { margin: 0 auto 34px; text-align: center; }
.closing .hero-actions { justify-content: center; }

.contact-line {
  margin-top: 34px;
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  color: var(--on-navy-muted);
}
.contact-line a {
  color: #fff;
  text-decoration-color: var(--ember);
  text-underline-offset: 4px;
}

/* ---------- footer ---------- */

.footer {
  padding: 34px 0;
  background: var(--navy-900);
  color: var(--on-navy-muted);
  border-top: 1px solid var(--on-navy-rule);
}
.footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  justify-content: space-between;
  font-size: 0.82rem;
}

.company-lockup {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  border: 1px solid var(--on-navy-rule);
  border-radius: 999px;
  color: var(--on-navy-muted);
  text-decoration: none;
  transition: border-color 200ms var(--ease), color 200ms var(--ease);
}
.company-lockup:hover { border-color: var(--ember); color: #fff; }
.company-lockup strong { color: #fff; font-weight: 600; }
.company-lockup img { width: 34px; height: auto; }
.footer .brand { margin-right: 0; }

/* ---------- reveal ---------- */

.reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 700ms var(--ease), transform 700ms var(--ease);
  transition-delay: var(--delay, 0ms);
  will-change: opacity, transform;
}
.reveal.is-visible { opacity: 1; transform: none; }

/* ---------- motion off ---------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  .section-title h2 .w > span { transform: none; }
  .section-title h2::before { transform: none; }
}

/* ---------- one card per row on narrow screens ---------- */

/* These grids are auto-fit, so a second column appears the moment two tracks clear
   their minimum — around 565px. That is arithmetic, not a judgement: it admits two
   columns at ~267px each, too narrow for a card carrying an icon, a label, a
   heading and a paragraph. One per row until there is real room for two.
   This has to sit after the grid definitions above: a media query adds no
   specificity, so earlier in the file it would simply lose to them. */
@media (max-width: 720px) {
  .cards, .tiers, .moat { grid-template-columns: 1fr; }
}

/* the stats hold a short number, so they take a narrower track than the cards do —
   but by phone width their labels were wrapping to five lines, so they stack too */
@media (max-width: 520px) {
  .stat-row { grid-template-columns: 1fr; }
}

body[data-motion='off'] .reveal { opacity: 1; transform: none; }
body[data-motion='off'] .section-title h2 .w > span { transform: none; }
body[data-motion='off'] .section-title h2::before { transform: none; }
body[data-motion='off'] .pipe-step { opacity: 1; }
body[data-motion='off'] .pipe-fill { transform: scaleX(1); }
