/* ============================================================
   ZEALOUS BLOCK LIBRARY v1 (marker: zealous-blocks-v1)
   Companion to styles.css. Pages COMPOSE from these blocks; no page
   is ever "heading + paragraphs + card grid".

   Design system is INHERITED, not re-invented: Archivo 700/800 caps,
   Jost 200/300 thin caps, JetBrains Mono labels, Inter body, #050505
   ground, #F03E2F as punctuation, light/dark band rhythm, dashed
   frames with accent corner ticks.

   TWO LAWS every block obeys:
   1. FAIL-OPEN — a zero-JS page renders every block in its FINAL,
      fully-readable state. Initial/animating states live only under
      html.js-blocks, which blocks.js adds. If the JS dies, the page
      is complete, not blank.
   2. REDUCED MOTION — @media (prefers-reduced-motion: reduce) lands
      every block on its final state with no transition, no loop,
      no parallax, no autorotation.

   BANNED: the generic card grid (3-4 boxes of icon + title +
   paragraph). It has no class here on purpose. gate.cjs fails the
   build if the signature reappears.
   ============================================================ */

:root{
  --air: clamp(120px, 19vh, 260px);   /* between-block breathing room  */
  --air-s: clamp(28px, 5vh, 64px);    /* within-block                  */
}

/* one idea per viewport: blocks are taller than normal sections */
.b{ position:relative; padding:var(--air) var(--gutter); }
.b .wrap{ max-width:var(--maxw); margin:0 auto; }
.b-lbl{ font-family:var(--font-m); font-size:11.5px; letter-spacing:.24em;
  text-transform:uppercase; color:var(--muted); }
.b-lbl b{ color:var(--accent); font-weight:400; }
.light .b-lbl{ color:var(--l-muted); }
.b-cap{ margin-top:20px; font-family:var(--font-m); font-size:11px; letter-spacing:.14em;
  color:var(--muted); line-height:1.7; max-width:52ch; }
.light .b-cap{ color:var(--l-muted); }

/* =========================================================
   01 — HERO-SERVICE  (v5: FULL-BLEED ambient, TYPE ON TOP)
   Frank, 2026-07-16: "the hero section of main page is still an
   animation on the side. I think the feel I'm going for is text
   superimposing on the animation at the back."

   So the loop is no longer a masked box beside the words — it is the
   ENTIRE hero ground, edge to edge, and the colossal Archivo caps sit
   ON it. Rotation, the 01/03 index and the captions ride along.

   THE READABILITY LAW IS STILL ABSOLUTE, and it is served by THREE
   independent layers, so no single one has to do all the work:

     1. .b-herobg     the loop, DIMMED at source (brightness+opacity).
                      Type must dominate; the motion is atmosphere.
     2. .b-scrim      TWO crossed gradients, NOT a rectangle over the
                      video. A vertical ramp darkens the lower band
                      where the type lives and a horizontal ramp
                      darkens the left where it is set — they multiply
                      to near-solid under the words while the top-right
                      stays clear so the animation is genuinely visible.
                      A flat rectangle would kill the effect Frank asked
                      for; a gradient anchored to the text zone does not.
     3. #050505 base  the ground itself, so even a failed video decode
                      leaves the type on near-black.

   Verified by pixel-sampling the BRIGHTEST pixel of each real poster
   inside the measured text rect and compositing it through the computed
   filter/opacity/scrim — not by eyeballing a screenshot. See
   BLOCKS.md > "Hero contrast proof".
   ASSET: 1+ ambient loops (mp4 + poster each). 2+ => rotation.
   ========================================================= */
.b-hero{ position:relative; min-height:100vh; display:flex; align-items:flex-end; overflow:hidden;
  padding:clamp(150px,22vh,240px) var(--gutter) clamp(72px,11vh,132px); background:var(--bg); }

/* ---- layer 1: the loop, full-bleed, dimmed ---- */
.b-hero .b-herobg{ position:absolute; inset:0; z-index:0; pointer-events:none; overflow:hidden; }
.b-hero .b-vlayer{ position:absolute; inset:0; opacity:0; transition:opacity 1.4s var(--ease); }
.b-hero .b-vlayer.on{ opacity:1; }
/* no-JS: the rotator never runs, so the FIRST layer must be visible on
   its own. Only under .js-blocks do we hand opacity to the rotator. */
html:not(.js-blocks) .b-hero .b-vlayer{ opacity:0; }
html:not(.js-blocks) .b-hero .b-vlayer:first-child{ opacity:1; }
.b-hero .b-vlayer img,.b-hero .b-vlayer video{ position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:center; }
/* THE GAIN — not a dim. MEASURED 2026-07-16, do not "safely" lower it:
   these loops are ALREADY dark ambient assets (mean luminance of the
   three posters: 59.8 / 17.8 / 11.5 out of 255 — they were authored as
   light-in-the-black, not as bright footage). The first cut applied a
   generic brightness(.42) x opacity(.55) => a 0.23x multiplier => means
   of 14 / 4 / 2.6. The hero rendered as a black rectangle: perfectly
   readable, and no animation to superimpose the text ON, which was the
   entire request.
   So the loop runs at near-full strength and the CROSSED SCRIM does all
   the readability work, locally, where the type actually sits. Any change
   to these two numbers must re-run the contrast proof (BLOCKS.md). */
.b-hero .b-herobg{ opacity:.95; filter:brightness(1.22) saturate(1.05); }

/* ---- layer 2: the scrim — crossed gradients, only under the type ---- */
.b-hero .b-scrim{ position:absolute; inset:0; z-index:1; pointer-events:none; }
.b-hero .b-scrim::before{ content:''; position:absolute; inset:0;
  background:linear-gradient(180deg,
    rgba(5,5,5,.10) 0%, rgba(5,5,5,.16) 22%, rgba(5,5,5,.42) 36%,
    rgba(5,5,5,.86) 52%, rgba(5,5,5,.94) 72%, rgba(5,5,5,.97) 100%); }
.b-hero .b-scrim::after{ content:''; position:absolute; inset:0;
  background:linear-gradient(90deg,
    rgba(5,5,5,.74) 0%, rgba(5,5,5,.60) 30%, rgba(5,5,5,.30) 56%,
    rgba(5,5,5,.06) 78%, rgba(5,5,5,0) 100%); }

.b-hero .wrap{ position:relative; z-index:2; width:100%;
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,clamp(280px,26vw,400px));
  gap:clamp(30px,5vw,90px); align-items:end; }
.b-hero .b-eyebrow{ font-family:var(--font-m); font-size:11px; letter-spacing:.26em; text-transform:uppercase;
  color:var(--accent); margin-bottom:clamp(22px,3.4vh,40px); }
/* Colossal — but ONLY ever a 2-4 word display phrase (heroService throws
   otherwise). The long descriptive title is .b-subhead, below. */
.b-hero h1{ font-size:clamp(2.6rem,6.4vw,6.6rem); max-width:12ch; color:var(--fg); }
.b-hero h1 .it{ color:var(--accent); }
/* the full keyword-bearing title, at reading scale, adjacent to the h1 */
.b-hero .b-subhead{ margin-top:clamp(18px,2.4vh,30px); font-family:var(--font-d); font-weight:200;
  text-transform:uppercase; letter-spacing:.04em; line-height:1.24;
  font-size:clamp(15px,1.5vw,21px); color:var(--fg-2); max-width:30ch; }
.b-hero .b-herotxt{ min-width:0; }
.b-hero .b-heroside{ min-width:0; padding-bottom:.6vh; }
.b-hero .b-heroside p{ color:var(--fg-2); font-size:clamp(15.5px,1.15vw,17.5px); line-height:1.78; max-width:42ch; }
.b-hero .b-stackline{ margin-top:26px; font-family:var(--font-m); font-size:10.5px; letter-spacing:.16em;
  text-transform:uppercase; color:var(--muted); line-height:2.1; }
.b-hero .b-pills{ display:flex; flex-wrap:wrap; gap:12px; margin-top:30px; }
.b-hero .b-pills a{ display:inline-flex; align-items:center; min-height:46px; padding:13px 24px; border-radius:999px;
  text-decoration:none; font-family:var(--font-m); font-size:11px; letter-spacing:.18em; text-transform:uppercase;
  transition:background .3s var(--ease),color .3s var(--ease),border-color .3s var(--ease); }
.b-hero .b-pills a.solid{ background:var(--fg); color:#000; border:1px solid var(--fg); }
.b-hero .b-pills a.solid:hover{ background:var(--accent); border-color:var(--accent); color:#000; }
.b-hero .b-pills a.ghost{ border:1px solid var(--hair); color:var(--fg-2); }
.b-hero .b-pills a.ghost:hover{ border-color:var(--accent); color:var(--accent); }
/* trust line under the pills — the site's strongest asset, above the fold */
.b-hero .b-trust{ margin-top:20px; max-width:44ch; font-size:12.5px; line-height:1.6; color:var(--fg-2);
  padding-left:14px; border-left:2px solid var(--accent); }

/* ---- the HUD: index + caption + dots, over the clear top-right ---- */
.b-hero .b-hud{ position:absolute; z-index:3; right:var(--gutter); top:clamp(96px,15vh,170px);
  display:flex; flex-direction:column; align-items:flex-end; gap:14px; text-align:right; }
.b-hero .b-hud .b-idx{ font-family:var(--font-m); font-size:10.5px; letter-spacing:.2em; color:var(--muted); }
.b-hero .b-hud .b-idx b{ color:var(--accent); font-weight:400; }
.b-hero .b-hud .b-stagecap{ font-family:var(--font-m); font-size:10px; letter-spacing:.14em;
  color:var(--fg-2); max-width:30ch; line-height:1.8;
  /* the caption sits over the CLEAR part of the video, so it carries its
     own local scrim — a small one, sized to the words, not the frame */
  background:rgba(5,5,5,.72); border:1px solid var(--hair); padding:9px 13px; }
.b-hero .b-hud .b-stagecap b{ color:var(--accent); font-weight:400; }
.b-hero .b-hud .b-dots{ display:flex; gap:9px; }
.b-hero .b-hud .b-dots button{ width:34px; height:34px; padding:0; border:0; background:none; cursor:pointer;
  display:grid; place-items:center; }  /* 34px is the visual; 44px tap target below */
.b-hero .b-hud .b-dots button::after{ content:''; width:26px; height:2px; background:rgba(245,240,230,.26);
  transition:background .3s var(--ease); }
.b-hero .b-hud .b-dots button.on::after{ background:var(--accent); }
.b-hero .b-hud .b-dots button:hover::after{ background:var(--fg-2); }

@media (max-width:900px){
  .b-hero{ min-height:92vh; padding-top:clamp(120px,18vh,170px); }
  .b-hero .wrap{ grid-template-columns:1fr; gap:clamp(26px,4vh,40px); align-items:start; }
  .b-hero h1{ max-width:14ch; }
  /* On a narrow screen the type occupies nearly the whole frame, so the
     horizontal ramp has no clear side to spare — go vertical-only and
     start the dark band higher. */
  .b-hero .b-scrim::before{ background:linear-gradient(180deg,
      rgba(5,5,5,.24) 0%, rgba(5,5,5,.52) 18%, rgba(5,5,5,.88) 34%,
      rgba(5,5,5,.95) 60%, rgba(5,5,5,.97) 100%); }
  .b-hero .b-scrim::after{ background:linear-gradient(90deg,
      rgba(5,5,5,.42) 0%, rgba(5,5,5,.30) 60%, rgba(5,5,5,.20) 100%); }
  .b-hero .b-hud{ top:auto; bottom:18px; right:var(--gutter); left:var(--gutter);
    flex-direction:row; align-items:center; justify-content:space-between; gap:12px; }
  .b-hero .b-hud .b-stagecap{ display:none; }   /* the words already say it */
  .b-hero .b-hud .b-dots button{ width:44px; height:44px; }   /* touch target */
}

/* =========================================================
   02 — SPLIT-MEDIA
   Text one side, artifact the other. ALTERNATES: .alt flips the
   media to the left. gate.cjs fails the build if two consecutive
   split-media land on the same side.
   ASSET: 1 artifact per instance (rendered panel, diagram or
   masked loop). No visual => the block cannot ship.
   ========================================================= */
.b-split .wrap{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(34px,5.5vw,96px); align-items:center; }
.b-split .b-txt{ min-width:0; }
.b-split .b-media{ min-width:0; }
.b-split.alt .b-txt{ order:2; }
.b-split.alt .b-media{ order:1; }
.b-split h2{ margin-top:22px; font-family:var(--font-h); font-weight:700; text-transform:none;
  letter-spacing:-.01em; font-size:clamp(1.6rem,3.1vw,2.6rem); line-height:1.1; }
.b-split p{ margin-top:22px; color:var(--fg-2); font-size:16.5px; line-height:1.8; max-width:54ch; }
.light .b-split p{ color:var(--l-ink-2); }
.b-split .b-pts{ list-style:none; margin-top:28px; }
.b-split .b-pts li{ display:flex; gap:14px; align-items:baseline; padding:13px 0;
  border-top:1px solid var(--hair); color:var(--fg-2); font-size:14.5px; line-height:1.7; }
.b-split .b-pts li .d{ color:var(--accent); font-size:9px; flex:none; }
.light .b-split .b-pts li{ border-color:var(--l-hair); color:var(--l-ink-2); }
.b-split a.inline{ color:inherit; text-decoration:underline; text-decoration-color:var(--accent);
  text-underline-offset:3px; }
.b-split a.inline:hover{ color:var(--accent); }
@media (max-width:860px){
  .b-split .wrap{ grid-template-columns:1fr; gap:clamp(28px,4.5vh,44px); }
  .b-split.alt .b-txt,.b-split.alt .b-media{ order:0; }
}

/* =========================================================
   03 — ARTIFACT-SHOWCASE  (the signature block)
   Full-bleed dark ambient ground + REAL product panels floating
   at different depths with soft shadows and scroll parallax.
   We SHOW the artifact; we never describe it.
   ASSET: 1 ambient loop (background layer only) + 1-2 rendered
   panels (HTML/CSS — costs zero credits, always on-brand).
   ========================================================= */
.b-artifact{ background:#030303; overflow:hidden; padding-block:clamp(140px,20vh,250px); }
.b-artifact .b-amb{ position:absolute; inset:0; z-index:0; opacity:.34; mix-blend-mode:screen; pointer-events:none;
  -webkit-mask-image:radial-gradient(ellipse 74% 68% at 50% 48%,#000 0%,#000 34%,rgba(0,0,0,.45) 62%,transparent 84%);
          mask-image:radial-gradient(ellipse 74% 68% at 50% 48%,#000 0%,#000 34%,rgba(0,0,0,.45) 62%,transparent 84%); }
.b-artifact .b-amb img,.b-artifact .b-amb video{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.b-artifact .b-glow{ position:absolute; z-index:0; inset:auto 0 -30% 0; height:70%; pointer-events:none;
  background:radial-gradient(ellipse 50% 60% at 50% 100%, rgba(240,62,47,.20), transparent 70%); }
.b-artifact .wrap{ position:relative; z-index:2; }
.b-artifact .b-head{ max-width:58ch; margin-bottom:clamp(46px,7vh,86px); }
.b-artifact h2{ margin-top:20px; font-size:clamp(1.8rem,4vw,3.4rem); }
.b-artifact .b-stack{ position:relative; }

/* the floating panels */
.b-panel{ position:relative; background:rgba(10,10,10,.92); border:1px solid rgba(245,240,230,.14);
  box-shadow:0 40px 90px -20px rgba(0,0,0,.9), 0 0 0 1px rgba(0,0,0,.5);
  overflow:hidden; will-change:transform; }
.b-panel .b-bar{ display:flex; align-items:center; gap:10px; padding:11px 14px;
  border-bottom:1px solid rgba(245,240,230,.10); background:rgba(255,255,255,.03); }
.b-panel .b-dot{ width:8px; height:8px; border-radius:50%; background:rgba(245,240,230,.18); flex:none; }
.b-panel .b-file{ font-family:var(--font-m); font-size:10.5px; letter-spacing:.1em; color:var(--fg-2); }
.b-panel .b-file .ext{ color:var(--accent); }
.b-panel .b-tag{ margin-left:auto; font-family:var(--font-m); font-size:9px; letter-spacing:.18em;
  text-transform:uppercase; color:var(--muted); }

/* code panel: line numbers + syntax colours */
.b-code{ display:grid; grid-template-columns:auto minmax(0,1fr); font-family:var(--font-m);
  font-size:clamp(9.5px,.83vw,12px); line-height:1.95; }
.b-code .b-ln{ padding:16px 12px 18px; text-align:right; color:rgba(245,240,230,.22);
  border-right:1px solid rgba(245,240,230,.07); user-select:none; white-space:pre; }
.b-code pre{ padding:16px 18px 18px; overflow-x:auto; color:var(--fg-2); }
.b-code .k{ color:#7FB7FF; }        /* key            */
.b-code .s{ color:#B7E08A; }        /* string         */
.b-code .p{ color:var(--muted); }   /* punctuation    */
.b-code .h{ color:var(--accent); }  /* the money line */

/* AI answer card: the citation chip lights up */
.b-answer{ padding:clamp(18px,1.6vw,26px); }
.b-answer .b-q{ font-family:var(--font-m); font-size:10px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--muted); }
.b-answer .b-qt{ margin-top:10px; color:var(--fg); font-size:clamp(13px,1.05vw,15.5px); line-height:1.6; }
.b-answer .b-a{ margin-top:20px; padding-top:18px; border-top:1px solid rgba(245,240,230,.10);
  color:var(--fg-2); font-size:clamp(12px,.95vw,14px); line-height:1.85; }
.b-answer .b-a b{ color:var(--fg); font-weight:500; }
.b-answer .b-cites{ display:flex; flex-wrap:wrap; gap:8px; margin-top:20px; }
.b-answer .b-chip{ display:inline-flex; align-items:center; gap:7px; padding:7px 12px; border-radius:999px;
  border:1px solid rgba(245,240,230,.16); font-family:var(--font-m); font-size:9.5px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--muted); }
.b-answer .b-chip::before{ content:''; width:5px; height:5px; border-radius:50%;
  background:rgba(245,240,230,.24); flex:none; }
/* fail-open: the brand chip is lit by default; JS only animates the arrival */
.b-answer .b-chip.on{ border-color:var(--accent); color:var(--accent);
  box-shadow:0 0 0 1px rgba(240,62,47,.35), 0 0 26px -4px rgba(240,62,47,.65); }
.b-answer .b-chip.on::before{ background:var(--accent); }
.js-blocks .b-artifact.in .b-answer .b-chip.on{ animation:b-chip-lit 2.4s var(--ease) .5s 1 both; }
@keyframes b-chip-lit{
  0%{ box-shadow:0 0 0 1px rgba(240,62,47,.35), 0 0 0 0 rgba(240,62,47,0); }
  40%{ box-shadow:0 0 0 1px rgba(240,62,47,.6), 0 0 40px 2px rgba(240,62,47,.9); }
  100%{ box-shadow:0 0 0 1px rgba(240,62,47,.35), 0 0 26px -4px rgba(240,62,47,.65); }
}

/* depth composition: panel A back-left, panel B front-right, overlapping */
.b-stack .b-p1{ width:min(64%,680px); transform:translate3d(0,var(--py1,0px),0); }
.b-stack .b-p2{ width:min(46%,460px); margin-left:auto; margin-top:clamp(-140px,-9vw,-60px);
  transform:translate3d(0,var(--py2,0px),0); z-index:3;
  box-shadow:0 60px 120px -24px rgba(0,0,0,.95); }
@media (max-width:820px){
  .b-stack .b-p1,.b-stack .b-p2{ width:100%; margin-left:0; transform:none !important; }
  .b-stack .b-p2{ margin-top:24px; }
}

/* ---------- ARTIFACT: ambient media box ----------
   For a split-media column. The radial mask dissolves all four edges,
   so this reads as light in the black — NEVER "a video in a box".
   The poster paints first and stays if the mp4 never arrives. */
.b-ambbox{ position:relative; width:100%; aspect-ratio:4/3; overflow:hidden; }
.b-ambbox .b-amb{ position:absolute; inset:0; opacity:.66; mix-blend-mode:screen; z-index:0;
  -webkit-mask-image:radial-gradient(ellipse 60% 60% at 50% 50%,#000 0%,#000 32%,rgba(0,0,0,.5) 58%,rgba(0,0,0,.12) 70%,transparent 79%);
          mask-image:radial-gradient(ellipse 60% 60% at 50% 50%,#000 0%,#000 32%,rgba(0,0,0,.5) 58%,rgba(0,0,0,.12) 70%,transparent 79%);
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat; -webkit-mask-size:100% 100%; mask-size:100% 100%; }
.b-ambbox .b-amb img,.b-ambbox .b-amb video{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.b-ambbox .b-idx{ position:absolute; top:0; left:0; z-index:2; font-family:var(--font-m); font-size:10.5px;
  letter-spacing:.2em; color:var(--muted); }
.b-ambbox .b-idx b{ color:var(--accent); font-weight:400; }

/* ---------- ARTIFACT: entity disambiguation, before/after ----------
   The page's claim, shown: three colliding "Acme"s with no winner, then
   one resolved node. Rendered in HTML/CSS — on-brand, zero credits. */
.b-dis{ padding:clamp(16px,1.5vw,24px); font-family:var(--font-m); }
.b-dis-h{ font-size:9.5px; letter-spacing:.2em; text-transform:uppercase; color:var(--muted);
  margin:18px 0 10px; }
.b-dis-h:first-child{ margin-top:0; }
.b-dis-h.win{ color:var(--accent); }
.b-dis-row{ display:flex; gap:12px; align-items:baseline; padding:10px 0;
  border-top:1px solid rgba(245,240,230,.08); font-size:clamp(10.5px,.86vw,12.5px);
  line-height:1.6; color:var(--muted); }
.b-dis-row .m{ flex:none; color:rgba(245,240,230,.3); font-size:11px; }
.b-dis-row.ok{ color:var(--fg); }
.b-dis-row.ok .m{ color:var(--accent); }
.b-dis-row .sub{ color:var(--muted); }

/* =========================================================
   04 — DIAGRAM-SCRUB
   A scroll-scrubbed SVG that DEMONSTRATES the concept instead of
   describing it. Stages advance with scroll; the brand node lights
   accent when the graph resolves.
   ASSET: none external — the diagram IS the asset (SVG, on-brand,
   infinitely reusable, zero credits).
   FAIL-OPEN: default CSS = the FINAL resolved graph. Scattered
   start states exist only under html.js-blocks.
   ========================================================= */
/* THE PIN CONTRACT — read before touching:
   .b-scrubinner IS the scroll distance. .b-sticky is a STRICT height:100vh
   pin. A sticky element TALLER than the viewport cannot pin: it scrolls
   with the page until its bottom meets the viewport bottom, so the stage
   never holds and the block reads as a giant black void. Shipped that bug
   2026-07-16 (min-height:100vh grew the pin to 1450px on a 778px viewport).
   RULES: height:100vh exactly. overflow:hidden. Content lives in two
   columns that fit INSIDE the pin. Never min-height. */
.b-scrub{ background:var(--bg); padding:0; }
.b-scrub .b-scrubinner{ position:relative; height:240vh; }   /* scrub distance: 240vh - 100vh pin = 140vh of scrub */
.b-scrub .b-sticky{ position:sticky; top:0; height:100vh; overflow:hidden;
  display:grid; grid-template-columns:minmax(0,0.92fr) minmax(0,1.08fr);
  gap:clamp(24px,3.6vw,68px); align-items:center;
  max-width:var(--maxw); margin:0 auto;
  padding:clamp(88px,12vh,128px) var(--gutter) clamp(36px,6vh,64px); }
.b-scrub .b-scol{ min-width:0; }
.b-scrub .b-shead{ width:100%; }
.b-scrub h2{ margin-top:16px; font-size:clamp(1.5rem,2.7vw,2.5rem); max-width:20ch; }
/* prose that EXPLAINS the diagram — narrow by design, never a wall, and
   clamped so it can never grow the pin past 100vh */
.b-scrub .b-sbody{ margin-top:18px; max-width:52ch; }
/* 14px floor. The clamp's 13.5px minimum only ever bound on NARROW
   viewports — i.e. it shipped the smallest body text of the whole site to
   the readers least able to read it (measured 13.5px at 390px). Body copy
   never goes under 14px. The scrub pin has room: on mobile it is not a
   pin at all. */
.b-scrub .b-sbody p{ color:var(--fg-2); font-size:clamp(14px,1.02vw,15px); line-height:1.7; }
.b-scrub .b-sbody p + p{ margin-top:12px; }
.b-scrub .b-stagewrap{ min-width:0; display:flex; align-items:center; justify-content:center;
  height:100%; }
/* the SVG scales to whatever the pin can spare — viewBox + meet keeps it
   centred and whole at every size, so it is ALWAYS visible during the scrub */
.b-scrub svg{ width:100%; height:100%; max-height:100%; display:block; }
.b-scrub .b-steplbl{ width:100%; margin-top:clamp(16px,2.4vh,28px);
  font-family:var(--font-m); font-size:11px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--fg-2); min-height:3.4em; }
.b-scrub .b-steplbl b{ color:var(--accent); font-weight:400; }

/* --- SVG piece styling: FINAL state is the default (fail-open) --- */
.b-node{ transition:transform 1.1s var(--ease), opacity .7s var(--ease); }
.b-node circle{ fill:#0C0C0C; stroke:rgba(245,240,230,.34); stroke-width:1.2;
  transition:stroke .8s var(--ease), fill .8s var(--ease); }
.b-node text{ font-family:var(--font-m); font-size:11px; letter-spacing:.08em; fill:var(--fg-2);
  text-anchor:middle; transition:opacity .7s var(--ease); }
.b-node .b-dotc{ fill:rgba(245,240,230,.4); transition:fill .8s var(--ease); }
.b-node.brand circle{ stroke:var(--accent); stroke-width:2; fill:#140705; }
.b-node.brand text{ fill:var(--accent); }
.b-node.brand .b-dotc{ fill:var(--accent); }
.b-halo{ fill:none; stroke:var(--accent); stroke-width:1; opacity:.34; transition:opacity .8s var(--ease); }
.b-edge{ stroke:rgba(245,240,230,.22); stroke-width:1; fill:none;
  stroke-dasharray:var(--len,400); stroke-dashoffset:0;
  transition:stroke-dashoffset 1.1s var(--ease), opacity .6s var(--ease); }
.b-edge.acc{ stroke:rgba(240,62,47,.55); }
.b-noise{ opacity:0; transition:opacity .6s var(--ease); }

/* --- animating states: ONLY when JS is alive ---
   STAGE/LABEL SYNC — each stage must SHOW what its caption claims:
     0 scattered + noise ...... "Scattered strings, none identified"
     1 nodes take their seats .. "The nodes resolve into one graph. Still nameless."
     2 labels attach .......... "Each node is declared and labelled"
     3 edges draw, brand lit .. "sameAs connects the graph"
   Scatter therefore ends at stage 0. (Held nodes scattered through stage 1
   at first, so the caption announced a resolve the eye couldn't see.) */
.js-blocks .b-scrub[data-stage="0"] .b-node{ transform:translate(var(--sx,0px),var(--sy,0px)); }
.js-blocks .b-scrub[data-stage="0"] .b-node circle,
.js-blocks .b-scrub[data-stage="1"] .b-node circle,
.js-blocks .b-scrub[data-stage="0"] .b-node.brand circle,
.js-blocks .b-scrub[data-stage="1"] .b-node.brand circle{ stroke:rgba(245,240,230,.14); stroke-width:1.2; fill:#0C0C0C; }
.js-blocks .b-scrub[data-stage="0"] .b-node.brand .b-dotc,
.js-blocks .b-scrub[data-stage="1"] .b-node.brand .b-dotc,
.js-blocks .b-scrub[data-stage="2"] .b-node.brand .b-dotc{ fill:rgba(245,240,230,.4); }
.js-blocks .b-scrub[data-stage="0"] .b-node text,
.js-blocks .b-scrub[data-stage="1"] .b-node text{ opacity:0; }
.js-blocks .b-scrub[data-stage="0"] .b-noise{ opacity:1; }
.js-blocks .b-scrub[data-stage="0"] .b-edge,
.js-blocks .b-scrub[data-stage="1"] .b-edge,
.js-blocks .b-scrub[data-stage="2"] .b-edge{ stroke-dashoffset:var(--len,400); opacity:0; }
.js-blocks .b-scrub[data-stage="0"] .b-halo,
.js-blocks .b-scrub[data-stage="1"] .b-halo,
.js-blocks .b-scrub[data-stage="2"] .b-halo{ opacity:0; }
.js-blocks .b-scrub[data-stage="2"] .b-node.brand circle{ stroke:rgba(245,240,230,.34); stroke-width:1.2; fill:#0C0C0C; }
.js-blocks .b-scrub[data-stage="2"] .b-node.brand text{ fill:var(--fg-2); }
/* Narrow screens: two columns cannot fit a 100vh pin, so we DROP the pin
   entirely rather than ship a broken one. Static block, resolved graph.
   blocks.js sees a track shorter than the viewport and lands on stage 3. */
@media (max-width:900px){
  .b-scrub .b-scrubinner{ height:auto; }
  .b-scrub .b-sticky{ position:static; height:auto; overflow:visible;
    grid-template-columns:1fr; gap:clamp(26px,4vh,40px);
    padding-block:clamp(80px,12vh,120px); }
  .b-scrub .b-stagewrap{ height:auto; }
  .b-scrub svg{ height:auto; }
  .b-scrub .b-steplbl{ min-height:0; }
}

/* =========================================================
   05 — ROTATING-GALLERY
   Auto-rotating artifact set with a mono index. Pausable, keyboard
   reachable, never a carousel of stock photos.
   ASSET: 2+ artifacts or images per instance.
   ========================================================= */
.b-gallery .b-ghead{ display:flex; justify-content:space-between; align-items:baseline; gap:20px;
  border-bottom:1px solid var(--hair); padding-bottom:14px; margin-bottom:clamp(30px,5vh,54px); }
.light .b-gallery .b-ghead{ border-color:var(--l-hair); }
.b-gallery .b-frames{ position:relative; }
/* fail-open: without JS every frame is stacked and readable */
.b-gallery .b-frame{ transition:opacity .9s var(--ease); }
.b-gallery .b-frame + .b-frame{ margin-top:20px; }
.js-blocks .b-gallery .b-frame{ position:absolute; inset:0; opacity:0; margin-top:0; pointer-events:none; }
.js-blocks .b-gallery .b-frame.on{ position:relative; opacity:1; pointer-events:auto; }
.b-gallery .b-gfoot{ display:flex; align-items:center; gap:16px; margin-top:22px; }
.b-gallery .b-gidx{ font-family:var(--font-m); font-size:11px; letter-spacing:.2em; color:var(--muted); }
.b-gallery .b-gidx b{ color:var(--accent); font-weight:400; }
.b-gallery .b-gpause{ margin-left:auto; background:none; border:1px dashed var(--muted); color:var(--muted);
  font-family:var(--font-m); font-size:9.5px; letter-spacing:.18em; text-transform:uppercase; padding:9px 14px;
  min-height:38px; cursor:pointer; transition:color .3s var(--ease),border-color .3s var(--ease); }
.b-gallery .b-gpause:hover{ color:var(--accent); border-color:var(--accent); }
.b-gallery .b-gdots{ display:flex; gap:8px; }
.b-gallery .b-gdots button{ width:26px; height:2px; border:0; padding:0; cursor:pointer;
  background:rgba(245,240,230,.26); transition:background .3s var(--ease); }
.b-gallery .b-gdots button.on{ background:var(--accent); }

/* =========================================================
   06 — STATEMENT
   One sentence, full-bleed, colossal, surrounded by air. The
   page's argument, not decoration.
   ASSET: none — the typography is the visual. Never more than
   twice per page.
   ========================================================= */
.b-statement{ padding-block:clamp(150px,26vh,300px); }
.b-statement .wrap{ max-width:min(1180px,90vw); }
.b-statement p{ font-family:var(--font-h); font-weight:800; text-transform:uppercase; letter-spacing:-.015em;
  line-height:1.02; font-size:clamp(1.9rem,5.4vw,4.6rem); color:var(--fg); }
.b-statement .it{ color:var(--accent); }
.light .b-statement p{ color:var(--l-ink); }
.b-statement .b-lbl{ margin-bottom:clamp(28px,4.6vh,54px); display:block; }
.b-statement .b-attr{ margin-top:clamp(28px,4vh,48px); font-family:var(--font-m); font-size:11px;
  letter-spacing:.18em; text-transform:uppercase; color:var(--muted); }

/* =========================================================
   07 — STEPPER
   Numbered process timeline. Horizontal on desktop, vertical on
   mobile. The rail fills as it enters view.
   ASSET: none required — the rail + numerals are the visual.
   ========================================================= */
.b-stepper .b-sintro{ max-width:66ch; margin-bottom:clamp(6px,1vh,14px); }
.b-stepper .b-sintro p{ color:var(--fg-2); font-size:16px; line-height:1.78; }
.light .b-stepper .b-sintro p{ color:var(--l-ink-2); }
.b-stepper .b-sintro a.inline{ color:inherit; text-decoration:underline;
  text-decoration-color:var(--accent); text-underline-offset:3px; }
.b-stepper .b-steps{ display:grid; grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(14px,2vw,30px); margin-top:clamp(34px,5.5vh,60px); position:relative; }
.b-stepper .b-rail{ position:absolute; top:26px; left:0; right:0; height:1px;
  background:var(--hair); overflow:hidden; }
.light .b-stepper .b-rail{ background:var(--l-hair); }
.b-stepper .b-rail i{ display:block; height:100%; width:100%; background:var(--accent); transform-origin:0 50%; }
.js-blocks .b-stepper .b-rail i{ transform:scaleX(0); transition:transform 2s var(--ease); }
.js-blocks .b-stepper.in .b-rail i{ transform:scaleX(1); }
.b-stepper .b-step{ position:relative; padding-right:clamp(10px,1.6vw,24px); }
.b-stepper .b-num{ position:relative; z-index:2; display:inline-flex; align-items:center; justify-content:center;
  width:52px; height:52px; border-radius:50%; background:var(--bg); border:1px solid var(--hair);
  font-family:var(--font-m); font-size:13px; letter-spacing:.06em; color:var(--muted);
  transition:border-color .6s var(--ease), color .6s var(--ease); }
.light .b-stepper .b-num{ background:var(--l-bg); border-color:var(--l-border); color:var(--l-muted); }
.b-stepper .b-step.on .b-num{ border-color:var(--accent); color:var(--accent); }
.b-stepper .b-step h3{ margin-top:24px; font-family:var(--font-h); font-weight:700; text-transform:none;
  letter-spacing:-.005em; font-size:clamp(1.05rem,1.5vw,1.35rem); line-height:1.2; }
.b-stepper .b-step p{ margin-top:14px; color:var(--fg-2); font-size:14px; line-height:1.7; }
.light .b-stepper .b-step p{ color:var(--l-ink-2); }
.b-stepper .b-when{ margin-top:16px; font-family:var(--font-m); font-size:9.5px; letter-spacing:.18em;
  text-transform:uppercase; color:var(--accent); }
@media (max-width:900px){
  .b-stepper .b-steps{ grid-template-columns:1fr; gap:38px; }
  .b-stepper .b-rail{ top:0; bottom:0; left:26px; right:auto; width:1px; height:auto; }
  .b-stepper .b-rail i{ width:100%; height:100%; transform-origin:50% 0; }
  .js-blocks .b-stepper .b-rail i{ transform:scaleY(0); }
  .js-blocks .b-stepper.in .b-rail i{ transform:scaleY(1); }
  .b-stepper .b-step{ padding-left:76px; padding-right:0; }
  .b-stepper .b-num{ position:absolute; left:0; top:0; }
  .b-stepper .b-step h3{ margin-top:0; }
}

/* =========================================================
   08 — STAT-BAND
   2-4 large numerals. HARD RULE: only real, sourced, verifiable
   numbers. No number => the block is UNAVAILABLE. It is never
   filled with invented figures; blocks-render.cjs THROWS if a
   stat arrives without a `source`.
   ASSET: none — the numerals are the visual.
   ========================================================= */
.b-stats .b-nums{ display:grid; grid-template-columns:repeat(auto-fit,minmax(min(230px,100%),1fr));
  gap:1px; background:var(--hair); border:1px solid var(--hair); margin-top:clamp(30px,5vh,54px); }
.light .b-stats .b-nums{ background:var(--l-hair); border-color:var(--l-hair); }
.b-stats .b-num-cell{ background:var(--bg); padding:clamp(26px,3vw,46px); }
.light .b-stats .b-num-cell{ background:var(--l-bg); }
.b-stats .b-fig{ font-family:var(--font-h); font-weight:800; letter-spacing:-.02em; line-height:.94;
  font-size:clamp(2.6rem,5vw,4.4rem); color:var(--fg); }
.light .b-stats .b-fig{ color:var(--l-ink); }
.b-stats .b-fig .u{ color:var(--accent); }
.b-stats .b-what{ margin-top:18px; color:var(--fg-2); font-size:14.5px; line-height:1.65; max-width:30ch; }
.light .b-stats .b-what{ color:var(--l-ink-2); }
.b-stats .b-src{ margin-top:16px; font-family:var(--font-m); font-size:9.5px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--muted); }

/* =========================================================
   09 — COMPARISON
   Designed before/after — two opposed columns with a slash mark.
   Explicitly NOT a table.
   ASSET: none required; each side may carry an artifact.
   ========================================================= */
.b-compare .b-cols{ display:grid; grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);
  gap:clamp(20px,3vw,56px); align-items:stretch; margin-top:clamp(34px,5.5vh,60px); }
.b-compare .b-slash{ font-family:var(--font-h); font-weight:800; font-size:clamp(2rem,4vw,3.4rem);
  color:var(--accent); align-self:center; opacity:.8; }
.b-compare .b-side{ border:1px dashed rgba(245,240,230,.2); padding:clamp(24px,2.8vw,42px); position:relative; }
.light .b-compare .b-side{ border-color:rgba(17,16,8,.22); }
.b-compare .b-side.win{ border-style:solid; border-color:var(--accent); background:rgba(240,62,47,.04); }
.b-compare .b-side .b-who{ font-family:var(--font-m); font-size:10px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--muted); }
.b-compare .b-side.win .b-who{ color:var(--accent); }
.b-compare .b-side h3{ margin-top:16px; font-family:var(--font-d); font-weight:300;
  font-size:clamp(1.15rem,1.9vw,1.6rem); }
.b-compare .b-side ul{ list-style:none; margin-top:22px; }
.b-compare .b-side li{ display:flex; gap:12px; align-items:baseline; padding:12px 0; border-top:1px solid var(--hair);
  color:var(--fg-2); font-size:14px; line-height:1.65; }
.light .b-compare .b-side li{ border-color:var(--l-hair); color:var(--l-ink-2); }
.b-compare .b-side li .m{ flex:none; font-size:10px; color:var(--muted); }
.b-compare .b-side.win li .m{ color:var(--accent); }
@media (max-width:860px){
  .b-compare .b-cols{ grid-template-columns:1fr; }
  .b-compare .b-slash{ text-align:center; }
}

/* =========================================================
   10 — LINK-INDEX
   Sanity's mono label + dashed arrow-link list. This is how we
   link internally — never a grid of boxes.
   ASSET: none.
   ========================================================= */
.b-linkindex .b-links{ display:grid; grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr));
  gap:14px; margin-top:clamp(30px,5vh,50px); }
.b-linkindex a{ display:flex; align-items:center; justify-content:space-between; gap:18px;
  border:1px dashed var(--muted); padding:20px 22px; min-height:64px; text-decoration:none; color:var(--fg);
  font-family:var(--font-d); font-weight:300; text-transform:uppercase; letter-spacing:.02em; font-size:14.5px;
  transition:border-color .3s var(--ease), color .3s var(--ease), transform .3s var(--ease); }
.b-linkindex a .ar{ font-family:var(--font-m); color:var(--accent); transition:transform .3s var(--ease); flex:none; }
.b-linkindex a:hover{ border-color:var(--accent); color:var(--accent); transform:translateX(6px); }
.b-linkindex a:hover .ar{ transform:translateX(5px); }
.light .b-linkindex a{ border-color:var(--l-muted); color:var(--l-ink); }
.light .b-linkindex a:hover{ border-color:var(--accent); color:var(--accent); }

/* =========================================================
   11 — CAPABILITY-ORBIT   (marker: zealous-blocks-v1)
   Frank, 2026-07-16, on the homepage's surviving 2x3 dashed card grid:
   "still have lazy card. here is where you can use creative 3D scroll
   effect to deliver wow factor."

   So: six capabilities as six PLANES stacked in real 3D depth. Scroll
   scrubs the stack toward you — the focused plane seats at z=0 with its
   name in colossal type and its own micro-artifact; the others recede,
   blur, tilt and dim behind it. One idea per viewport, literally.

   ASSET NEED: 1 inline SVG micro-artifact per panel. The emitter throws
   without one, and gate.cjs check 8 fails the build if a b-orbit panel
   ships without an <svg> — that is what stops a card grid from creeping
   back in wearing this block's class names.

   STRUCTURE IS LOAD-BEARING (the 2026-07-16 void bug — do not "tidy"):
     .b-orbitinner  IS the scroll distance (tall).
     .b-orbitpin    is a STRICT height:100vh sticky child. NEVER
                    min-height — a sticky taller than the viewport
                    cannot pin; it scrolls away and the block reads as
                    a giant black void.

   FAIL-OPEN: everything above is scoped under html.js-blocks. With no
   JS this is six full-width panels stacked vertically, every word and
   every artifact readable, no pin and no transform. Reduced motion
   lands on exactly that same static stack.
   ========================================================= */
.b-orbit{ padding:0; background:#040404; position:relative; }
.b-orbit .b-orbitinner{ position:relative; }
.b-orbit .b-orbitpin{ padding:var(--air) var(--gutter); }

/* the static, zero-JS, reduced-motion rendering: a readable stack */
.b-orbit .b-orbithead{ max-width:var(--maxw); margin:0 auto clamp(40px,6vh,72px);
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,clamp(280px,32%,460px));
  gap:clamp(18px,3vw,56px); align-items:end; }
.b-orbit .b-orbithead h2{ font-size:clamp(1.6rem,3.4vw,2.9rem); }
.b-orbit .b-orbithead h2 .it{ color:var(--accent); }
/* the intro is BODY, not a mono caption: at 11px mono in a narrow column
   it read as machine exhaust rather than the section's argument */
.b-orbit .b-orbitintro{ font-size:15.5px; line-height:1.75; color:var(--fg-2); margin:0; }
@media (max-width:900px){ .b-orbit .b-orbithead{ grid-template-columns:1fr; align-items:start; } }
.b-orbit .b-orbitstage{ max-width:var(--maxw); margin:0 auto; }
.b-orbit .b-orbpanel{ position:relative; display:grid; grid-template-columns:minmax(0,1fr) minmax(0,clamp(200px,24vw,320px));
  gap:clamp(24px,4vw,60px); align-items:center;
  padding:clamp(28px,4vh,44px) clamp(24px,3vw,44px); background:rgba(9,9,9,.92);
  border:1px solid var(--hair); }
.b-orbit .b-orbpanel + .b-orbpanel{ margin-top:20px; }
.b-orbit .b-orbnum{ font-family:var(--font-m); font-size:11px; letter-spacing:.2em; color:var(--accent); }
.b-orbit .b-orbpanel h3{ margin-top:14px; font-family:var(--font-h); font-weight:800; text-transform:uppercase;
  letter-spacing:-.01em; line-height:.94; font-size:clamp(1.9rem,4.6vw,4.2rem); color:var(--fg); }
.b-orbit .b-orbpanel p{ margin-top:18px; color:var(--fg-2); font-size:16px; line-height:1.75; max-width:46ch; }
.b-orbit .b-orbart{ min-width:0; }
.b-orbit .b-orbart svg{ width:100%; height:auto; display:block; }
.b-orbit .b-orblink{ display:inline-flex; align-items:center; gap:9px; min-height:44px; margin-top:18px;
  font-family:var(--font-m); font-size:10.5px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--muted); text-decoration:none; transition:color .3s var(--ease); }
.b-orbit .b-orblink:hover{ color:var(--accent); }
.b-orbit .b-orbrail{ display:none; }
/* MOBILE: one column. Without this the panel keeps its two-column desktop
   grid and the art's 200px floor eats the row — MEASURED at 390px:
   `grid-template-columns: 61px 200px`, i.e. a 61px-wide text column. The
   art goes UNDER the words, full width, where it can actually be read. */
@media (max-width:900px){
  .b-orbit .b-orbpanel{ grid-template-columns:1fr; gap:24px; align-items:start;
    padding:clamp(24px,4vw,34px) clamp(20px,4vw,30px); }
  .b-orbit .b-orbpanel h3{ font-size:clamp(1.7rem,7.4vw,2.6rem); }
  .b-orbit .b-orbart{ max-width:340px; }
}

/* ---- the 3D scrub: ONLY with JS, only with motion, only wide ---- */
@media (min-width:901px){
  html.js-blocks .b-orbit .b-orbitinner{ height:calc(var(--n,6) * 78vh + 100vh); }
  html.js-blocks .b-orbit .b-orbitpin{ position:sticky; top:0; height:100vh;  /* EXACT. never min-height */
    overflow:hidden; display:flex; flex-direction:column; justify-content:flex-start;
    padding:clamp(76px,10vh,110px) var(--gutter) clamp(40px,6vh,72px); }
  html.js-blocks .b-orbit .b-orbithead{ flex:none; margin-bottom:clamp(14px,2.4vh,30px);
    /* the head must WIN over the stack drifting behind it */
    position:relative; z-index:300; pointer-events:none; }
  html.js-blocks .b-orbit .b-orbithead a{ pointer-events:auto; }
  html.js-blocks .b-orbit .b-orbitstage{ position:relative; flex:1; min-height:0; width:100%;
    perspective:1600px; perspective-origin:50% 50%; transform-style:preserve-3d; }
  html.js-blocks .b-orbit .b-orbpanel{ position:absolute; inset:0; margin:0 !important;
    align-content:center; will-change:transform,opacity,filter;
    /* --o = signed distance from focus, --a = |--o|. Both set per frame
       by blocks.js: CSS abs() is too new to depend on, and doing the
       math in JS keeps every visual state declarative here.

       The Y drift is deliberately SMALL (3.2vh, was 6vh): at 6vh the
       already-seen panels climbed straight over the section heading and
       the stack read as clutter, not depth. Depth is carried by Z, blur
       and opacity — Y only hints at the stack. */
    transform:
      translate3d(0, calc(var(--o,0) * 3.2vh), calc(var(--o,0) * -340px))
      rotateX(calc(var(--o,0) * -6deg))
      scale(calc(1 - var(--a,0) * 0.03));
    /* Falloff is STEEP on purpose, and was tuned against the render twice.
       At 0.42/step five panels were legible at once and the colossal names
       overprinted into mush. At 0.62 the outgoing panel still ghosted at
       0.38 over the focused one — at this type size that is not depth, it
       is double-exposure. At 0.72 the neighbour sits at 0.28 behind 5.2px
       of blur: present as a receding plane, never competing to be read. */
    opacity:calc(1 - var(--a,0) * 0.72);
    filter:blur(calc(var(--a,0) * 5.2px));
    z-index:calc(100 - var(--a,0) * 10);
    pointer-events:none;
    transition:none; }
  html.js-blocks .b-orbit .b-orbpanel.focus{ pointer-events:auto; }
  /* panels far behind stop painting entirely — a couple of blurred planes
     is depth, six would be mud (and a compositor bill) */
  html.js-blocks .b-orbit .b-orbpanel[data-far="1"]{ visibility:hidden; }

  /* the progress rail — shows there IS a stack and where you are in it */
  html.js-blocks .b-orbit .b-orbrail{ display:flex; position:absolute; z-index:200;
    right:var(--gutter); top:50%; transform:translateY(-50%);
    flex-direction:column; gap:10px; align-items:flex-end; }
  html.js-blocks .b-orbit .b-orbrail i{ display:block; width:14px; height:2px;
    background:rgba(245,240,230,.22); transition:width .4s var(--ease),background .4s var(--ease); }
  html.js-blocks .b-orbit .b-orbrail i.on{ width:34px; background:var(--accent); }
}

/* =========================================================
   12 — STACK-BAND  ("BUILT ON")
   Frank, 2026-07-16: "Get rid of the marquee of logos... feels out of
   place, rather replace with all the logos of tech stacks that we're
   using... I emphasize, use their logos not made up fonts."

   STATIC. Not a marquee — a marquee is motion for its own sake and it
   read as filler. A calm band of REAL brand marks, monochromed to one
   ink so ten different brand palettes don't fight the page.

   HARD RULE, enforced in the emitter: every mark is a REAL logo file.
   A brand with no obtainable file is OMITTED — never redrawn as styled
   text. A wordmark we set in Archivo is a fabricated logo.
   ========================================================= */
.b-stackband{ padding:clamp(56px,9vh,96px) 0 0; }
.b-stackband .b-stacklbl{ font-family:var(--font-m); font-size:10.5px; letter-spacing:.24em;
  text-transform:uppercase; color:var(--muted); margin-bottom:clamp(26px,4vh,42px); }
.b-stackband .b-stackrow{ display:flex; flex-wrap:wrap; align-items:center;
  gap:clamp(26px,4.4vw,62px) clamp(30px,5vw,70px); }
/* --lh scales a single mark's optical height. Most of these lockups are
   wide (aspect 2.5-5.2); HeyGen's is STACKED (aspect 1.69), so at a
   common height its two lines of type set ~13px and turn to mush. The
   var keeps the mobile step proportional instead of an inline height
   fighting the media query. */
.b-stackband .b-stacklogo{ display:flex; align-items:center; height:calc(var(--lh,1) * 30px); }
.b-stackband .b-stacklogo img{ height:100%; width:auto; max-width:150px; object-fit:contain; display:block;
  /* one ink for every brand: crush to black, then lift to the page's
     bone white. Works on any source colour, incl. multi-fill SVGs. */
  filter:grayscale(1) brightness(0) invert(1) opacity(.58);
  transition:filter .4s var(--ease); }
.b-stackband .b-stacklogo img:hover{ filter:grayscale(1) brightness(0) invert(1) opacity(1); }
.light .b-stackband .b-stacklogo img{ filter:grayscale(1) brightness(0) opacity(.62); }
.light .b-stackband .b-stacklogo img:hover{ filter:grayscale(1) brightness(0) opacity(1); }
.light .b-stackband .b-stacklbl{ color:var(--l-muted); }
@media (max-width:700px){
  .b-stackband .b-stackrow{ gap:24px 30px; }
  .b-stackband .b-stacklogo{ height:calc(var(--lh,1) * 22px); }
  .b-stackband .b-stacklogo img{ max-width:104px; }
}

/* =========================================================
   REDUCED MOTION — every block lands on its final state.
   ========================================================= */
@media (prefers-reduced-motion: reduce){
  /* HERO: no rotation, no video. The first layer is the design. */
  .b-hero .b-vlayer{ transition:none !important; opacity:0 !important; }
  .b-hero .b-vlayer:first-child{ opacity:1 !important; }
  /* CAPABILITY-ORBIT: no pin, no 3D, no scrub — the readable stack,
     identical to the zero-JS rendering. Every selector that builds the
     3D scrub is undone here, including the ones scoped to .js-blocks. */
  html.js-blocks .b-orbit .b-orbitinner{ height:auto !important; }
  html.js-blocks .b-orbit .b-orbitpin{ position:static !important; height:auto !important;
    overflow:visible !important; display:block !important; padding:var(--air) var(--gutter) !important; }
  html.js-blocks .b-orbit .b-orbitstage{ position:static !important; perspective:none !important;
    transform-style:flat !important; flex:none !important; }
  html.js-blocks .b-orbit .b-orbpanel{ position:relative !important; inset:auto !important;
    transform:none !important; opacity:1 !important; filter:none !important;
    visibility:visible !important; pointer-events:auto !important; }
  html.js-blocks .b-orbit .b-orbpanel + .b-orbpanel{ margin-top:20px !important; }
  html.js-blocks .b-orbit .b-orbrail{ display:none !important; }
  .b-stackband .b-stacklogo img{ transition:none !important; }
  .b-node,.b-node circle,.b-node text,.b-edge,.b-halo,.b-panel,
  .b-gallery .b-frame,.b-stepper .b-rail i,.b-answer .b-chip{
    transition:none !important; animation:none !important; }
  .b-stack .b-p1,.b-stack .b-p2{ transform:none !important; }
  /* the scrub diagram shows its resolved graph: no sticky, no scrub */
  .js-blocks .b-scrub .b-node{ transform:none !important; }
  .js-blocks .b-scrub .b-node circle{ stroke:rgba(245,240,230,.34) !important; }
  .js-blocks .b-scrub .b-node.brand circle{ stroke:var(--accent) !important; stroke-width:2 !important; fill:#140705 !important; }
  .js-blocks .b-scrub .b-node.brand .b-dotc{ fill:var(--accent) !important; }
  .js-blocks .b-scrub .b-node text{ opacity:1 !important; }
  .js-blocks .b-scrub .b-node.brand text{ fill:var(--accent) !important; }
  .js-blocks .b-scrub .b-edge{ stroke-dashoffset:0 !important; opacity:1 !important; }
  .js-blocks .b-scrub .b-halo{ opacity:.34 !important; }
  .js-blocks .b-scrub .b-noise{ opacity:0 !important; }
  .b-scrub .b-scrubinner{ height:auto; }
  .b-scrub .b-sticky{ position:static; height:auto; overflow:visible; }
  .b-scrub .b-stagewrap{ height:auto; }
  .b-scrub svg{ height:auto; }
  .js-blocks .b-gallery .b-frame{ position:relative; opacity:1; pointer-events:auto; }
  .js-blocks .b-gallery .b-frame + .b-frame{ margin-top:20px; }
  .js-blocks .b-stepper .b-rail i{ transform:none !important; }
}
