/* ============================================================
   flowscan — the rooms.
   ============================================================ */

/* ═══════════════ THE STAGE ═══════════════
   One canvas, fixed, behind everything, never torn down.
   Pointer-transparent so every CTA above it stays clickable. */
.stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.stage-canvas { width: 100%; height: 100%; pointer-events: none; }

.stage-field {
  position: absolute;
  inset: -10%;
  background:
    radial-gradient(58% 46% at 62% 34%, rgba(63, 175, 75, 0.10), transparent 70%),
    radial-gradient(52% 42% at 22% 68%, rgba(58, 108, 190, 0.11), transparent 72%);
  opacity: 0.55;
}

/* the warehouse floor grid — faint, and it fades before it meets the type */
.stage-grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: 74px 74px;
  -webkit-mask-image: radial-gradient(76% 62% at 50% 44%, #000 20%, transparent 78%);
          mask-image: radial-gradient(76% 62% at 50% 44%, #000 20%, transparent 78%);
  opacity: 0.75;
}

.no-gl .stage-canvas { display: none; }

/* ---------- atmosphere ----------
   Three cheap layers that do most of the work of making a real-time render
   read as photographed rather than rasterised. All of them sit in front of
   the canvas and none of them cost a draw call. */

/* the pointer carries a soft scan light across the page */
.stage-torch {
  position: absolute;
  left: 0; top: 0;
  width: 46vmax; height: 46vmax;
  margin: -23vmax 0 0 -23vmax;
  border-radius: 50%;
  background: radial-gradient(circle closest-side,
    rgba(47, 154, 63, 0.10), rgba(47, 154, 63, 0.04) 42%, transparent 72%);
  mix-blend-mode: multiply;
  opacity: 0;
  transition: opacity 0.6s ease;
  will-change: transform;
}
.stage-torch.is-on { opacity: 1; }

/* film grain — an inline SVG turbulence tile, so no request and no decode */
.stage-grain {
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  opacity: 0.035;
  mix-blend-mode: overlay;
}

/* vignette — pulls the eye to the middle and hides the grid's outer edge */
.stage-vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(74% 66% at 50% 44%,
    transparent 44%, rgba(23, 38, 66, 0.10) 100%);
}

@media (prefers-reduced-motion: reduce) {
  .stage-torch { display: none; }
}

/* ---------- page progress ----------
   A single hairline that fills as you scroll. It is the same green as the
   scan brackets, so the page reads as one long scan. */
.scanbar {
  position: fixed;
  left: 0; right: 0; top: 0;
  height: 2px;
  z-index: 70;
  pointer-events: none;
}
.scanbar i {
  display: block;
  height: 100%;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--green) 30%, transparent), var(--green-lit));
  transform-origin: left;
  transform: scaleX(0);
  box-shadow: 0 0 12px color-mix(in srgb, var(--green) 60%, transparent);
}

/* ═══════════════ NAV ═══════════════ */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 60;
  transition: background 0.3s ease, border-color 0.3s ease, backdrop-filter 0.3s ease;
  border-bottom: 1px solid transparent;
}
.nav.is-condensed {
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  border-bottom-color: var(--line);
}
.nav-in {
  max-width: var(--maxw);
  margin-inline: auto;
  padding: 14px var(--pad);
  display: flex;
  align-items: center;
  gap: 14px;
}
.nav-brand { display: flex; align-items: center; gap: 10px; margin-right: auto; }
.nav-mark {
  width: 32px; height: 32px;
  border-radius: 9px;
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--line);
  flex: none;
}
.nav-mark img { width: 100%; height: 100%; object-fit: cover; }
.nav-word {
  font-weight: 800;
  letter-spacing: -0.03em;
  font-size: 1.12rem;
}
.nav-word b { font-weight: 800; }
.nav-word i { color: var(--green); font-style: normal; padding: 0 1px; }

.nav-links { display: flex; gap: 4px; }
.nav-link {
  padding: 8px 12px;
  border-radius: 9px;
  font-size: 0.9rem;
  color: var(--muted);
  transition: color 0.2s ease, background 0.2s ease;
}
.nav-link:hover { color: var(--ink); background: var(--card); }
.nav-link.is-active { color: var(--ink); }

.nav-burger {
  display: none;
  width: 40px; height: 40px;
  border-radius: 11px;
  border: 1px solid var(--line);
  background: var(--card);
  padding: 11px 10px;
}
.nav-burger span {
  display: block; height: 2px; border-radius: 2px;
  background: var(--ink); margin: 0 auto;
  transition: transform 0.25s ease, opacity 0.2s ease;
}
.nav-burger span + span { margin-top: 5px; width: 70%; margin-right: 0; }

.menu {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: var(--bg);
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  padding: 90px var(--pad) 40px;
}
.menu.is-open { display: flex; }
.menu a {
  font-size: 1.9rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

@media (max-width: 900px) {
  .nav-links, .nav-cta { display: none; }
  .nav-burger { display: block; }
}

/* ═══════════════ RAIL ═══════════════ */
.rail {
  position: fixed;
  right: 22px; top: 50%;
  transform: translateY(-50%);
  z-index: 50;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
}
.rail-line {
  position: absolute; top: 0; bottom: 0; left: 50%;
  width: 1px; background: var(--line); transform: translateX(-0.5px);
}
.rail-fill {
  position: absolute; inset: 0;
  background: var(--green);
  transform-origin: top;
  transform: scaleY(0);
}
.rail-mark {
  position: relative;
  width: 22px; height: 22px;
  display: grid; place-items: center;
}
.rail-mark i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--line-2);
  transition: background 0.25s ease, transform 0.25s ease;
}
.rail-mark:hover i { background: var(--muted); }
.rail-mark.is-here i { background: var(--green-lit); transform: scale(1.5); }
.rail-mark em {
  position: absolute; right: 26px;
  font-family: var(--mono); font-style: normal; font-size: 0.62rem;
  color: var(--muted); opacity: 0;
  transition: opacity 0.25s ease;
  white-space: nowrap;
}
.rail-mark:hover em, .rail-mark.is-here em { opacity: 1; }
@media (max-width: 1100px) { .rail { display: none; } }

/* ═══════════════ SHARED SECTION FRAME ═══════════════ */
main { position: relative; z-index: 1; }

.beat { position: relative; }
.hold {
  position: sticky;
  top: 0;
  min-height: 100svh;
  display: flex;
  align-items: center;
}
.sec { position: relative; padding: clamp(90px, 13vh, 170px) 0; }
.sec-head { max-width: 62ch; margin-bottom: clamp(38px, 5vw, 64px); }
.sec-head h2 { margin: 14px 0 18px; }

/* ---------- the scan wipe ----------
   Every section heading is revealed by a green line travelling across it,
   left to right, driven by scroll. It is the brand's own gesture — the same
   sweep that reads the code in the hero — reused as the page's transition,
   so the site has ONE motion idea instead of a fade per section. */
[data-wipe] {
  --wipe: 0;
  position: relative;
  display: inline-block;
}
/* The mask goes on an inner span, NOT on the heading itself: a mask applies
   to an element's pseudo-elements too, so with it up here the ::after line
   sat exactly on the mask edge and was cut in half — invisible, which is the
   one part of the effect that has to be seen. bindWipes() inserts this. */
[data-wipe] .wipe-in {
  /* block, not inline: a mask on an inline box that wraps onto two lines is
     applied per line fragment, so the second line got cut at a completely
     different x from the first. As a block it is one box and the reveal is a
     single vertical edge across the whole heading. */
  display: block;
  -webkit-mask-image: linear-gradient(90deg, #000 calc(var(--wipe) * 118% - 9%), transparent calc(var(--wipe) * 118%));
          mask-image: linear-gradient(90deg, #000 calc(var(--wipe) * 118% - 9%), transparent calc(var(--wipe) * 118%));
}
[data-wipe]::after {
  content: '';
  position: absolute;
  top: -0.12em; bottom: -0.06em;
  left: calc(var(--wipe) * 118% - 1px);
  width: 2px;
  background: var(--green);
  box-shadow: 0 0 14px 1px color-mix(in srgb, var(--green) 75%, transparent);
  /* gone once it has run off the end, so a read heading is just a heading */
  opacity: calc(min(var(--wipe), 1 - var(--wipe)) * 6);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-wipe] .wipe-in { -webkit-mask-image: none; mask-image: none; }
  [data-wipe]::after { display: none; }
}

.sec-solid { background: var(--bg-2); border-block: 1px solid var(--line); }

/* ═══════════════ 01 · HERO ═══════════════ */
/* The scroll engine's progress for a pinned section spans (height − 100svh),
   so p = 1.0 is the frame the sticky stage lets go — NOT (H−100)/H, which is
   the fraction of the section's own height and is what an earlier pass got
   wrong. The choreography fills [0, 1] and the mark then rides out with the
   page, so there is no scroll anywhere in here with nothing on screen. */
#hero { height: 250svh; }
#hero .hold { align-items: center; }
.hero-in {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--pad);
}
/* Sized so "Scan the box." and "The count is done." each hold ONE line at
   every desktop width. At the global h1 scale the second line wrapped to
   four lines and pushed the eyebrow up behind the nav. */
.hero-copy { max-width: min(610px, 47vw); }
.hero-copy h1 { margin: 0 0 22px; font-size: clamp(2.3rem, 4.3vw, 3.95rem); }
.hero-copy h1 em {
  font-style: normal;
  color: var(--green);
  display: block;
}
.hero-copy .lede { margin-bottom: 26px; }
.hero-copy .row { margin-bottom: 22px; }

/* Act-driven bits start hidden — story.js owns opacity/transform from frame 0.
   Gated on .js-on (set by the bundle) so that if the script never runs, the
   hero is a normal, fully legible page instead of an empty screen. */
.js-on #hero [data-act] { opacity: 0; will-change: transform, opacity; }

.hero-rule {
  height: 1px;
  background: linear-gradient(90deg, var(--green), transparent);
  margin-bottom: 22px;
  max-width: 300px;
}
.hero-meta {
  position: absolute;
  /* Centred by auto margins, NOT translateX(-50%): story.js writes an inline
     transform on every [data-act] element each frame, which would overwrite
     the centring and push the label a half-width to the right. */
  left: 0; right: 0; bottom: 12vh;
  margin-inline: auto;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}
.hero-meta small { display: block; margin-top: 10px; opacity: 0.6; letter-spacing: 0.3em; }
.hero-facts {
  display: flex; flex-wrap: wrap; gap: 8px 22px;
  font-family: var(--mono); font-size: 0.74rem;
  color: var(--faint);
}
.hero-facts b { color: var(--ink-2); font-weight: 500; }

/* keeps the mono line from running to both edges of a narrow screen */
.hero-meta { max-width: min(92vw, 460px); }


/* Reduced motion: the mark holds one assembled, parked composition and the
   copy is simply there. A 330svh section would otherwise be 2.5 screens of
   scrolling that does nothing. */
@media (prefers-reduced-motion: reduce) {
  #hero { height: 100svh; }
  .js-on #hero [data-act] { opacity: 1 !important; transform: none !important; }
  .hero-meta { display: none; }
}

@media (max-width: 900px) {
  #hero { height: 230svh; }
  /* The mark rises out of the top before the copy arrives (see mark.js
     `rise`), so the copy gets the middle of the screen to itself. */
  .hero-copy { max-width: none; }
  .hero-copy h1 { font-size: clamp(2.1rem, 9vw, 2.9rem); }
  .hero-copy .lede { font-size: 1rem; }
  .hero-facts { display: none; }
  .hero-meta { bottom: 6vh; }
}

/* ═══════════════ 02 · COUNT (editorial) ═══════════════ */
/* Tight at the top: the hero's own bottom half is already empty as it slides
   away, and stacking 20vh of section padding on top of that made the handover
   between the two a long blank band. */
#count { padding-block: clamp(70px, 10vh, 140px) clamp(90px, 13vh, 170px); }
.fill {
  font-size: clamp(1.5rem, 3.4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.25;
  max-width: 21ch;
}
.fill .w { transition: opacity 0.12s linear; }
.count-grid {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: clamp(32px, 6vw, 90px);
  align-items: end;
}
.count-notes { display: grid; gap: 14px; padding-bottom: 6px; }
.count-note {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 13px;
  background: var(--card);
}
.count-ic {
  width: 36px; height: 36px; flex: none;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--green-dim);
  color: var(--green);
}
.count-ic svg { width: 19px; height: 19px; }
.count-note b { font-size: 1rem; font-weight: 650; letter-spacing: -0.015em; }
@media (max-width: 860px) { .count-grid { grid-template-columns: 1fr; } }

/* ═══════════════ 03 · ONE (the bus) ═══════════════
   Eight departments, one rail. The rail is the page's scan line again — it
   fills left to right with scroll and each department lights as the fill
   passes under its stem, so "everyone reads the same record" is shown
   happening rather than asserted in a paragraph. */
.bus { --q: 0; }
.bus-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(10px, 1.6vw, 18px);
}
.dept {
  --lit: 0;
  position: relative;
  padding: 16px 15px;
  border-radius: 14px;
  background: var(--card);
  border: 1px solid color-mix(in srgb, var(--green) calc(var(--lit) * 45%), var(--line));
  transform: translateY(calc(var(--lit) * -3px));
  transition: border-color 0.35s ease, transform 0.35s ease, background 0.35s ease;
}
.dept-ic {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: 9px;
  background: var(--green-dim);
  color: var(--green);
  margin-bottom: 12px;
  opacity: calc(0.55 + var(--lit) * 0.45);
}
.dept-ic svg { width: 17px; height: 17px; }
.dept b { display: block; font-size: 0.97rem; letter-spacing: -0.015em; }
.dept em {
  display: block;
  margin-top: 5px;
  font-family: var(--mono); font-style: normal; font-size: 0.63rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--faint);
}
/* the stem down to (or up from) the rail — it is the connection, so it takes
   the green only once the fill has actually reached this column */
.dept::after {
  content: '';
  position: absolute;
  left: 50%; width: 2px; height: 19px;
  margin-left: -1px;
  background: color-mix(in srgb, var(--green) calc(var(--lit) * 100%), var(--line-2));
  transition: background 0.35s ease;
}
.bus-row:first-child .dept::after { bottom: -20px; }
.bus-row:last-child  .dept::after { top: -20px; }

.bus-rail {
  position: relative;
  height: 2px;
  margin: 20px 0;
  border-radius: 2px;
  background: var(--line);
}
.bus-rail i {
  position: absolute; inset: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--green), var(--green-lit));
  transform-origin: left;
  transform: scaleX(var(--q));
  box-shadow: 0 0 14px color-mix(in srgb, var(--green) 55%, transparent);
}
.bus-tag {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  padding: 5px 13px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--bg-2);
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--green);
  white-space: nowrap;
}

.bus-cap {
  margin-top: 30px;
  font-size: 1.02rem;
  color: var(--ink-2);
  max-width: 60ch;
}

@media (max-width: 860px) {
  .bus-row { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  /* stems only line up with the rail while each row is a single line of
     cards; wrapped, they dangle in mid-air */
  .dept::after { display: none; }
  .bus-rail { margin: 14px 0; }
}

/* ═══════════════ 04 · SCAN (three steps) ═══════════════ */
.steps-beam {
  height: 2px;
  background: var(--line);
  border-radius: 2px;
  margin-bottom: 34px;
  overflow: hidden;
}
.steps-beam i {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--green), var(--green-lit));
  transform-origin: left;
  transform: scaleX(0);
}
.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(18px, 2.6vw, 30px);
}
.step {
  --lit: 0;
  position: relative;
  padding: 26px 24px 28px;
  border-radius: var(--r);
  border: 1px solid var(--line);
  background: var(--card);
  transition: border-color 0.4s ease, background 0.4s ease, transform 0.4s ease;
}
.step.is-lit {
  border-color: color-mix(in srgb, var(--green) 45%, transparent);
  transform: translateY(-4px);
}
.step::after {
  content: '';
  position: absolute; inset: -1px;
  border-radius: inherit;
  box-shadow: var(--glow);
  opacity: calc(var(--lit) * 0.7);
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.step-n {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  color: var(--green);
  margin-bottom: 14px;
}
.step h3 { margin-bottom: 8px; }
.step p { color: var(--muted); font-size: 0.94rem; }
@media (max-width: 860px) { .steps { grid-template-columns: 1fr; } }

/* ---------- the step diagrams ----------
   Each one animates off the SAME --lit the step card uses, so the picture
   and the card light on the same scroll frame. All CSS, no extra canvas. */
.step-art {
  position: relative;
  height: 96px;
  margin: 4px 0 20px;
}

/* 01 — the reticle closes on the code */
.art-scan .code {
  position: absolute; left: 50%; top: 50%;
  width: 42px; height: 42px;
  transform: translate(-50%, -50%) scale(calc(0.86 + var(--lit) * 0.14));
  border-radius: 9px;
  border: 2px solid var(--ink-2);
  background:
    radial-gradient(circle at 30% 30%, currentColor 1.4px, transparent 1.5px) 0 0 / 9px 9px;
  color: var(--ink-2);
  opacity: calc(0.35 + var(--lit) * 0.65);
}
.art-scan .c {
  position: absolute;
  width: 15px; height: 15px;
  border: 2.5px solid var(--green);
  opacity: calc(0.25 + var(--lit) * 0.75);
}
/* the four corners travel inward as the step lights */
.art-scan .c1 { left: 50%; top: 50%; border-right: 0; border-bottom: 0; border-radius: 4px 0 0 0;
  transform: translate(calc(-50% - 46px + var(--lit) * 10px), calc(-50% - 46px + var(--lit) * 10px)); }
.art-scan .c2 { left: 50%; top: 50%; border-left: 0; border-bottom: 0; border-radius: 0 4px 0 0;
  transform: translate(calc(-50% + 46px - var(--lit) * 10px), calc(-50% - 46px + var(--lit) * 10px)); }
.art-scan .c3 { left: 50%; top: 50%; border-right: 0; border-top: 0; border-radius: 0 0 0 4px;
  transform: translate(calc(-50% - 46px + var(--lit) * 10px), calc(-50% + 46px - var(--lit) * 10px)); }
.art-scan .c4 { left: 50%; top: 50%; border-left: 0; border-top: 0; border-radius: 0 0 4px 0;
  transform: translate(calc(-50% + 46px - var(--lit) * 10px), calc(-50% + 46px - var(--lit) * 10px)); }

/* 02 — the fields fill themselves in */
.art-form {
  display: flex; flex-direction: column; justify-content: center; gap: 11px;
  padding: 0 6px;
}
.art-form i {
  position: relative;
  display: block; height: 9px; border-radius: 5px;
  background: var(--line-2);
  overflow: hidden;
}
.art-form i::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--green), var(--green-lit));
  transform-origin: left;
  transform: scaleX(var(--lit));
}
/* staggered, so it reads as three fields being filled rather than one bar */
.art-form i:nth-child(1) { width: 68%; }
.art-form i:nth-child(2) { width: 88%; }
.art-form i:nth-child(2)::after { transform: scaleX(calc(max(0, var(--lit) * 1.5 - 0.5))); }
.art-form i:nth-child(3) { width: 46%; }
.art-form i:nth-child(3)::after { transform: scaleX(calc(max(0, var(--lit) * 2 - 1))); }

/* 03 — one write, pushed to every device */
.art-push .core {
  position: absolute; left: 50%; top: 50%;
  width: 13px; height: 13px; margin: -6.5px 0 0 -6.5px;
  border-radius: 50%;
  background: var(--green);
}
.art-push .ring {
  position: absolute; left: 50%; top: 50%;
  width: 28px; height: 28px; margin: -14px 0 0 -14px;
  border-radius: 50%;
  border: 1.5px solid var(--green);
  opacity: 0;
}
.art-push .r1 { transform: scale(calc(0.4 + var(--lit) * 1.5)); opacity: calc(var(--lit) * 0.75); }
.art-push .r2 { transform: scale(calc(0.4 + var(--lit) * 2.4)); opacity: calc(var(--lit) * 0.42); }
.art-push .r3 { transform: scale(calc(0.4 + var(--lit) * 3.3)); opacity: calc(var(--lit) * 0.2); }
.art-push .dev {
  position: absolute; top: 50%;
  width: 13px; height: 20px; margin-top: -10px;
  border-radius: 3px;
  border: 1.5px solid var(--ink-2);
  opacity: calc(0.25 + var(--lit) * 0.75);
}
.art-push .d1 { left: 8%; }
.art-push .d2 { right: 8%; }
.art-push .d3 { left: 50%; margin-left: -6.5px; top: 86%; }

/* ═══════════════ 05 · FLOW (order → report) ═══════════════
   One rail, eight beads, and a packet that rides down it with the scroll.
   The rail passes BEHIND the number chips, which are opaque — that is what
   makes it read as one continuous thread rather than eight cards. */
.flow {
  --q: 0;
  position: relative;
  padding-left: 78px;
  max-width: 860px;
}
.flow-rail {
  position: absolute;
  left: 25px; top: 12px; bottom: 12px;
  width: 2px;
  border-radius: 2px;
  background: var(--line);
}
.flow-rail i {
  position: absolute; inset: 0;
  border-radius: inherit;
  background: linear-gradient(180deg, var(--green), var(--green-lit));
  transform-origin: top;
  transform: scaleY(var(--q));
}
.flow-dot {
  position: absolute;
  left: 50%; top: calc(var(--q) * 100%);
  width: 11px; height: 11px;
  margin: -5.5px 0 0 -5.5px;
  border-radius: 50%;
  background: var(--green-lit);
  box-shadow: 0 0 0 4px var(--green-dim),
              0 0 18px color-mix(in srgb, var(--green) 70%, transparent);
  /* invisible at the very ends, so a section you have not reached (or have
     long left) is not sitting there with a stray green dot on it */
  opacity: calc(min(var(--q), 1 - var(--q)) * 8);
}

.flow-list {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  gap: clamp(20px, 2.6vw, 30px);
}
.flow-step {
  --lit: 0;
  position: relative;
  opacity: calc(0.4 + var(--lit) * 0.6);
  transition: opacity 0.35s ease;
}
.flow-n {
  position: absolute;
  left: -78px; top: -5px;
  width: 52px; height: 52px;
  display: grid; place-items: center;
  border-radius: 15px;
  font-family: var(--mono);
  font-size: 0.8rem;
  background: var(--bg-2);
  border: 1px solid color-mix(in srgb, var(--green) calc(var(--lit) * 55%), var(--line));
  color: color-mix(in srgb, var(--green-lit) calc(var(--lit) * 100%), var(--muted));
  transition: border-color 0.35s ease, color 0.35s ease;
}
.flow-step b {
  display: block;
  font-size: 1.06rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-bottom: 5px;
}
.flow-step p { color: var(--muted); font-size: 0.95rem; max-width: 58ch; }

@media (max-width: 620px) {
  .flow { padding-left: 58px; }
  .flow-rail { left: 19px; }
  .flow-n { left: -58px; top: -2px; width: 40px; height: 40px; border-radius: 12px; font-size: 0.72rem; }
}

/* ═══════════════ 06 · MODULES (dealt) ═══════════════ */
.mods {
  display: grid;
  /* the cards are a name and a tag now, so they can be half the width and
     twice as many per row — the grid itself carries the "ten modules" idea */
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 12px;
}
.mod {
  padding: 18px 16px 16px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--card);
  opacity: 0;
  will-change: transform, opacity;
  transition: border-color 0.25s ease, background 0.25s ease;
}
.mod:hover { border-color: var(--line-2); background: var(--card-2); }
.mod-ic {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 10px;
  background: var(--green-dim);
  color: var(--green);
  margin-bottom: 14px;
}
.mod-ic svg { width: 18px; height: 18px; }
.mod b { display: block; font-size: 0.98rem; letter-spacing: -0.015em; }
.mod em {
  display: block;
  margin-top: 6px;
  font-family: var(--mono); font-style: normal; font-size: 0.65rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--faint);
}

/* ═══════════════ 07 · DEMO (the capture) ═══════════════ */
/* Two rows in the left column and the phone spanning both, so that on one
   column the running order becomes heading → phone → chapters. Stacking the
   whole left column first put a six-item chapter list between the promise
   and the thing it is describing. */
.demo-grid {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  grid-template-areas:
    'head  stage'
    'aside stage';
  gap: 0 clamp(30px, 5vw, 70px);
  align-items: center;
}
.demo-head { grid-area: head; }
.demo-aside { grid-area: aside; }
.demo-stage { grid-area: stage; }
.demo-head h2 { margin: 14px 0 18px; }
.demo-meta {
  margin-top: 16px;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

.chapters {
  list-style: none;
  margin: 26px 0 0;
  padding: 0;
  display: grid;
  gap: 2px;
  max-width: 460px;
}
.chapter {
  width: 100%;
  display: flex; align-items: center; gap: 13px;
  padding: 9px 14px;
  min-height: 44px;                 /* touch target */
  border-radius: 11px;
  border: 1px solid transparent;
  font-size: 0.9rem;
  color: var(--muted);
  text-align: left;
  transition: color 0.2s ease, background 0.2s ease, border-color 0.2s ease;
}
.chapter i {
  font-family: var(--mono); font-style: normal;
  font-size: 0.71rem;
  color: var(--faint);
  flex: none;
  /* a fixed column, so thirteen timecodes read as a ruler down the side
     rather than a ragged left edge */
  width: 3.2em;
}
.chapter:hover { color: var(--ink); background: var(--card); }
/* the chapter the video is actually in — set from timeupdate, so the list
   doubles as a transcript of what you are watching */
.chapter.is-now {
  color: var(--ink);
  background: var(--green-dim);
  border-color: color-mix(in srgb, var(--green) 38%, transparent);
}
.chapter.is-now i { color: var(--green); }

.demo-stage { display: grid; place-items: center; }
.phone {
  --q: 0;
  --sweep: 0;
  position: relative;
  width: min(320px, 74vw);
  aspect-ratio: 720 / 1600;
  padding: 8px;
  border-radius: 34px;
  border: 1px solid var(--line-2);
  background: var(--bg-3);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.demo-video {
  width: 100%; height: 100%;
  border-radius: 27px;
  object-fit: cover;
  background: #fff;
}

/* ---------- the play affordance ----------
   The whole poster is the button. It leaves for good on first play: the
   browser's controls are the right thing to scrub six minutes with, and two
   sets of transport controls on one video is one set too many. */
.phone-play {
  position: absolute;
  inset: 8px;
  z-index: 2;
  border-radius: 27px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  color: #fff;
  background: linear-gradient(180deg,
    rgba(7, 11, 18, 0.28), rgba(7, 11, 18, 0.62));
  backdrop-filter: blur(1px);
  -webkit-backdrop-filter: blur(1px);
  transition: opacity 0.3s ease, background 0.3s ease;
}
.phone-play svg {
  width: 26px; height: 26px;
  padding: 19px 17px 19px 21px;
  box-sizing: content-box;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 10px 34px -8px rgba(0, 0, 0, 0.6),
              0 0 0 1px rgba(255, 255, 255, 0.16) inset;
  transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1), background 0.2s ease;
}
.phone-play:hover svg { transform: scale(1.06); background: var(--green-lit); }
.phone-play:active svg { transform: scale(0.98); }
.phone-play span {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  font-size: 0.9rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.5);
}
.phone-play em {
  font-style: normal;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  opacity: 0.8;
}
.phone.is-playing .phone-play { display: none; }
/* the brand's scan line, run down the glass once as the section arrives */
.phone-sweep {
  position: absolute;
  left: 0; right: 0;
  top: calc(var(--q) * 100%);
  height: 20%;
  pointer-events: none;
  opacity: var(--sweep);
  background: linear-gradient(180deg,
    transparent,
    color-mix(in srgb, var(--green) 30%, transparent),
    transparent);
  mix-blend-mode: multiply;
}
.phone-glare {
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(122deg, transparent 34%, rgba(255, 255, 255, 0.07) 46%, transparent 57%);
  transition: opacity 0.4s ease;
}
/* a sheen on a still object is atmosphere; a sheen over a video somebody is
   trying to read is dirt on the lens */
.phone.is-playing .phone-glare { opacity: 0.3; }

@media (max-width: 900px) {
  .demo-grid {
    grid-template-columns: 1fr;
    grid-template-areas: 'head' 'stage' 'aside';
    gap: 30px 0;
  }
  .chapters { max-width: none; margin-top: 0; }
  .phone { width: min(290px, 78vw); }
}
@media (prefers-reduced-motion: reduce) {
  .phone-sweep { display: none; }
}

/* ═══════════════ 08 · REEL (pinned, horizontal) ═══════════════ */
#app { height: 420svh; }
/* The nav is fixed and 72px tall; a pinned section that centres its content
   in the full 100svh puts its heading underneath it. */
#app .hold { flex-direction: column; justify-content: center; padding: 86px 0 26px; }
.reel-top {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding: 0 var(--pad) 30px;
  display: flex; align-items: flex-end; gap: 20px;
}
.reel-top h2 { flex: 1; }
.reel-counter {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--muted);
  text-align: right;
  white-space: nowrap;
}
.reel-counter b { color: var(--green); font-size: 1.5rem; font-weight: 500; display: block; }

/* Fade the lane out at both edges. Without it the reel ends in a hard
   vertical cut mid-phone, which reads as a clipping bug rather than a pan. */
.reel-view {
  width: 100%;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
}
.reel-lane {
  display: flex;
  gap: clamp(16px, 2.4vw, 34px);
  padding-inline: var(--pad);
  will-change: transform;
  /* the lane is a camera track, not a strip: each frame turns to face the
     middle, so the reel reads as a carousel you are moving through */
  perspective: 1600px;
  perspective-origin: 42% 50%;
  transform-style: preserve-3d;
}
.reel-frame {
  --turn: 0;                 /* -1 … 1, set per frame by sections.js */
  --near: 0;                 /* 1 when this frame is the one being read */
  flex: none;
  position: relative;
  /* A 9:20 phone at 268px is 595px tall — with the heading, bar and caption
     that overflowed 100svh and pushed the heading off the top of the pin. */
  width: clamp(160px, 16.5vw, 224px);
  border-radius: 22px;
  border: 1px solid var(--line);
  background: var(--bg-3);
  box-shadow: var(--shadow);
  opacity: calc(0.34 + var(--near) * 0.66);
  transform-style: preserve-3d;
  transform:
    rotateY(calc(var(--turn) * -26deg))
    translateZ(calc(var(--near) * 70px - 40px))
    scale(calc(0.94 + var(--near) * 0.06));
  border-color: color-mix(in srgb, var(--green) calc(var(--near) * 45%), var(--line));
}
.reel-frame img { width: 100%; height: auto; border-radius: 21px; }

/* a soft floor bounce under each phone — the cheapest "this is a real object"
   cue there is, and it costs no extra element */
.reel-frame::after {
  content: '';
  position: absolute;
  left: 8%; right: 8%; bottom: -26px;
  height: 22px;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--green) 34%, transparent), transparent);
  opacity: calc(var(--near) * 0.85);
  filter: blur(7px);
}

.reel-bar {
  width: min(520px, 72vw);
  height: 2px;
  background: var(--line);
  margin: 34px auto 0;
  border-radius: 2px;
  overflow: hidden;
}
.reel-bar i {
  display: block; height: 100%;
  background: var(--green);
  transform-origin: left;
  transform: scaleX(0);
}
.reel-cap {
  margin-top: 14px;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  min-height: 1.2em;
}
@media (max-width: 900px) {
  #app { height: 340svh; }
  .reel-top { flex-direction: column; align-items: flex-start; gap: 10px; }
  .reel-counter { text-align: left; }
}

/* ═══════════════ 09 · GAIN (what changes) ═══════════════ */
.gains {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(206px, 1fr));
  gap: 12px;
}
.gain {
  padding: 20px 18px 22px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--card);
  transition: border-color 0.25s ease, background 0.25s ease;
}
.gain:hover { border-color: var(--line-2); background: var(--card-2); }
.gain-n {
  display: block;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  color: var(--green);
  margin-bottom: 14px;
}
.gain b { display: block; font-size: 1.02rem; letter-spacing: -0.018em; margin-bottom: 6px; }
.gain p { color: var(--muted); font-size: 0.92rem; }

/* ---------- industries ----------
   Two rows that slide past each other as the section crosses the screen.
   It is a marquee that only moves when you do, which is the rule the rest
   of the page follows. */
.drift {
  margin-top: clamp(34px, 5vw, 56px);
  display: grid;
  gap: 10px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.drift-row {
  display: flex;
  gap: 9px;
  width: max-content;
  will-change: transform;
}
.drift-row .chip { white-space: nowrap; }
.drift-cap {
  margin-top: 18px;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

/* ---------- the mid-page ask ---------- */
.band {
  margin-top: clamp(42px, 6vw, 72px);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px 30px;
  padding: clamp(22px, 3vw, 34px);
  border-radius: var(--r);
  border: 1px solid color-mix(in srgb, var(--green) 26%, var(--line));
  background: linear-gradient(118deg, var(--green-dim), transparent 68%), var(--card);
}
.band-copy { flex: 1 1 320px; }
.band-copy h3 { margin-bottom: 7px; }
.band-copy p { color: var(--ink-2); font-size: 0.98rem; }

/* ═══════════════ 10 · TRUST (isolation diagram) ═══════════════
   Three orgs in one database. Rows arrive in every lane as you scroll, but
   only your lane is ever legible — the other two stay locked and blurred.
   This replaced six paragraphs of prose and says the same thing faster. */
.iso {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(10px, 1.6vw, 20px);
}
.iso-lane {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--card);
  padding: 16px 14px 18px;
}
.iso-lane.is-you {
  border-color: color-mix(in srgb, var(--green) 45%, transparent);
  background: var(--green-dim);
  box-shadow: var(--glow);
}
.iso-head {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 16px;
}
.iso-lane.is-you .iso-head { color: var(--green); }

.iso-lock, .iso-you { width: 13px; height: 13px; flex: none; }
.iso-lock {
  border: 1.5px solid currentColor;
  border-radius: 3px;
  position: relative;
  margin-top: 3px;
}
.iso-lock::before {
  content: '';
  position: absolute; left: 50%; top: -6px;
  width: 7px; height: 7px; margin-left: -3.5px;
  border: 1.5px solid currentColor;
  border-bottom: 0;
  border-radius: 4px 4px 0 0;
}
.iso-you { border-radius: 50%; background: currentColor; }

.iso-rows { display: grid; gap: 9px; }
.iso-rows i {
  display: block;
  height: 10px;
  border-radius: 5px;
  background: var(--line-2);
  opacity: 0;
  transform: translate3d(0, 8px, 0);
  will-change: transform, opacity;
}
/* the locked lanes are present but unreadable — that IS the point */
.iso-lane:not(.is-you) .iso-rows i { filter: blur(2.5px); }
.iso-lane.is-you .iso-rows i { background: var(--green); }
.iso-rows i:nth-child(2) { width: 78%; }
.iso-rows i:nth-child(3) { width: 92%; }
.iso-rows i:nth-child(4) { width: 64%; }
.iso-rows i:nth-child(5) { width: 85%; }
.iso-rows i:nth-child(6) { width: 71%; }

.iso-cap {
  margin: 22px 0 26px;
  font-size: 1.02rem;
  color: var(--ink-2);
}

.chips { display: flex; flex-wrap: wrap; gap: 9px; }
.chip {
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 0.86rem;
  color: var(--ink-2);
}
.chip code {
  font-family: var(--mono);
  font-size: 0.82em;
  color: var(--green);
}

@media (max-width: 700px) {
  .iso { grid-template-columns: 1fr; }
  /* stacked, the locked lanes only need to be a hint */
  .iso-lane:not(.is-you) .iso-rows { grid-auto-rows: 10px; max-height: 29px; overflow: hidden; }
}

/* ═══════════════ 11 · GET ═══════════════ */
/* No min-height. Reserving 148svh for ~420px of content left three quarters
   of a screen blank, and the fly-in doesn't need it: because the section is
   SHORTER than the viewport, the scroll engine tracks it in pass mode and the
   entrance gets the whole approach to play on. */
#get { position: relative; display: flex; align-items: center; }
.get-grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: clamp(30px, 5vw, 70px);
  align-items: center;
  width: 100%;
}
/* the anchor the returning 3D mark is aimed at — it holds space, nothing else */
.get-mark {
  aspect-ratio: 1 / 1;
  max-width: 420px;
  margin-inline: auto;
  width: 100%;
}
/* store buttons — two platforms, equal weight */
.stores { gap: 12px; }
.store { padding: 12px 20px 12px 16px; }
.store svg { width: 21px; height: 21px; flex: none; }
.store span {
  display: flex; flex-direction: column; align-items: flex-start;
  line-height: 1.15; text-align: left;
}
.store em {
  font-style: normal;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.72;
  margin-top: 2px;
}

.get-note {
  margin-top: 20px;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--faint);
  line-height: 1.9;
}
.get-note b { color: var(--ink-2); font-weight: 500; }
@media (max-width: 900px) {
  .get-grid { grid-template-columns: 1fr; }
  /* the returning mark is centred on this box and overhangs it, so it needs
     clearance from the fixed nav */
  .get-mark { order: -1; max-width: 240px; margin-top: 34px; }
}

/* ═══════════════ FOOTER ═══════════════ */
.foot {
  border-top: 1px solid var(--line);
  padding: 46px 0 60px;
  background: var(--bg-2);
}
.foot-in {
  display: flex; flex-wrap: wrap; gap: 22px 40px; align-items: center;
}
.foot-brand { display: flex; align-items: center; gap: 10px; margin-right: auto; }
.foot-brand img { width: 30px; height: 30px; border-radius: 8px; }
.foot small { color: var(--faint); font-size: 0.82rem; }
.foot-links { display: flex; flex-wrap: wrap; gap: 12px 20px; font-size: 0.9rem; color: var(--muted); }
.foot-links a:hover { color: var(--ink); }

/* the mission, said once, quietly, where a mission statement belongs */
.foot-mission {
  max-width: 68ch;
  margin-top: 28px;
  color: var(--muted);
  font-size: 0.93rem;
}

/* the credit line — quiet, mono, under a hairline */
.foot-legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--faint);
}
.foot-dot { opacity: 0.5; }
.foot-legal a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ink-2);
  border-bottom: 1px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.foot-legal a:hover {
  color: var(--green);
  border-bottom-color: currentColor;
}
.foot-legal svg { width: 13px; height: 13px; }

/* visually hidden, still read out — the icon alone is not a link name */
.u-sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
