/* ==========================================================================
   FRANCIS — DIGITAL SYSTEMS
   Dark-only. Palette per spec §34, values measured against #050505:
     --ink        #F5F1EA  18.10
     --ink-muted  #9A968D   6.91
     --ink-dim    #7C7972   4.69   (still AA for body)
     --signal     #E8A33D   9.45   (and 9.45 for #050505 ON the fill)
   The signal colour is used sparingly: it means signal, activity, conversion.
   ========================================================================== */

:root {
  color-scheme: dark;

  --bg: #050505;
  --bg-2: #090909;
  --ink: #f5f1ea;
  --ink-muted: #9a968d;
  /* Was #7c7972: 4.66:1 on pure black, so ANY background lift broke AA —
     over the backplate it measured 4.06:1. #8b8881 holds 4.5:1 at bound A. */
  --ink-dim: #8b8881;
  --signal: #e8a33d;
  --signal-hi: #ffc46b;
  --line: rgba(245, 241, 234, 0.10);
  --line-2: rgba(245, 241, 234, 0.18);

  /* BACKPLATE OPACITY — measured, not chosen.
     Every pixel of every frame of plate-network.mp4 was sampled and
     composited over #050505. Two bounds:
       A  99.5th-percentile pixel rgb(38,32,31) — what sits behind virtually
          all text virtually all the time. At .70: body --ink-muted 5.97:1
          (AA), headings 15.65:1.
       B  single brightest pixel rgb(224,188,160) — a node core a few px
          wide. At .70 headings still hold 3.08:1; at .80 they fall to 2.42
          and fail even the large-text floor.
     So .70 is the ceiling: body AA behind 99.5% of pixels, headings never
     below 3:1 anywhere. Body text under a node core is not AA at ANY visible
     opacity — that is inherent to a point-light plate, not a tuning problem.
     Re-run scratch plate_build.py before touching this. */
  --plate-opacity: .70;

  /* OVERLAY SCRIM ---------------------------------------------------------
     Every full-screen overlay — the menu, both modals, and the Ligna partner
     widget — uses these two values. They were three different treatments
     (5,5,5/.94/26px vs 3,3,3/.86/7px twice), which read as three different
     products the moment you opened two of them in a row. One definition, so
     they cannot drift apart again. */
  --scrim: rgba(5, 5, 5, .94);
  --scrim-blur: blur(26px) saturate(120%);

  --sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --t-display: clamp(2.75rem, 8.5vw, 8rem);
  --t-h2: clamp(1.9rem, 4.4vw, 4rem);
  --t-lede: clamp(1.05rem, 1.7vw, 1.5rem);
  --t-body: clamp(0.95rem, 1vw, 1.0625rem);
  --t-mono: clamp(0.66rem, 0.78vw, 0.75rem);

  --gutter: clamp(1.25rem, 5vw, 6rem);

  /* RHYTHM ----------------------------------------------------------------
     One padding value and one reveal timing for every band made the page
     metronomic. Both are now three-step scales, assigned per band by
     narrative role, so the page has tension and release instead of a
     constant pulse.

       tight  compressed, technical  — reads as urgency
       md     the working default
       vast   a held breath, used around the page's two big beats           */

  --band-tight: clamp(3rem, 7vh, 5.5rem);
  --band-md: clamp(5rem, 14vh, 11rem);
  --band-vast: clamp(8rem, 24vh, 18rem);

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --fast: 200ms;

  /* Reveal tempo. Inherited, so a band can retune every .reveal inside it. */
  --rv-dur: 640ms;
  --rv-step: 80ms;
  --rv-shift: 16px;
  --reveal: var(--rv-dur);
}

*, *::before, *::after { box-sizing: border-box; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  font-weight: 300;
  line-height: 1.65;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
strong { color: var(--ink); font-weight: 500; }

::selection { background: var(--signal); color: var(--bg); }

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

.shell { width: min(100%, 92rem); margin-inline: auto; padding-inline: var(--gutter); }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  padding: .75rem 1rem; background: var(--signal); color: var(--bg);
  font-family: var(--mono); font-size: var(--t-mono);
}
.skip-link:focus { left: var(--gutter); top: .75rem; }

/* microscopic grain, §35 */
.grain {
  position: fixed; inset: 0; z-index: 1; pointer-events: none; opacity: .035;
  background-image: radial-gradient(circle at 1px 1px, #fff 1px, transparent 0);
  background-size: 3px 3px;
}

/* --- backplate -----------------------------------------------------------
   ONE fixed video behind the whole page. Every layer, 01 through 13, sits on
   the same element, so there is nothing per-layer to drift: same clip, same
   opacity, same timing, by construction.

   z-index -1 is what makes this work. A fixed element at z-index 0 paints
   ABOVE non-positioned text (positioned z:0 comes after inline content in
   the paint order), so it would cover the page. At -1 it paints above the
   body background and beneath everything in flow. body must not create a
   stacking context (no transform/filter/opacity) or -1 gets trapped above
   its background — it does not, and this comment is here so nobody adds one.

   The clip ships with no src; main.js sets it after the gates pass, so a
   rejected load costs zero bytes. .grain stays on top at z-index 1.        */
.plate {
  position: fixed; inset: 0; z-index: -1;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity 1400ms var(--ease);
  pointer-events: none;
}
.plate.is-live { opacity: var(--plate-opacity); }

/* ------------------------------------------------------------- type ----- */

.tag {
  font-family: var(--mono); font-size: var(--t-mono); font-weight: 500;
  letter-spacing: .24em; text-transform: uppercase; color: var(--ink-dim);
  margin-bottom: 1.5rem;
}

.display {
  font-size: var(--t-display); font-weight: 600; line-height: .92;
  letter-spacing: -.045em;
}
.display em { font-style: normal; color: var(--signal); }

/* Rotating headline word. min-width is set by JS to the widest option, after
   fonts load — measuring against the fallback face gives the wrong number and
   the line still shifts once Inter arrives. */
.rotor {
  display: inline-block;
  text-align: left;
  will-change: opacity, transform;
  transition: opacity 460ms var(--ease), transform 460ms var(--ease);
}

.rotor.is-out { opacity: 0; transform: translateY(-0.22em); }
.rotor.is-in  { opacity: 0; transform: translateY(0.22em); }
.rotor.is-instant { transition: none; }

@media (prefers-reduced-motion: reduce) {
  .rotor { transition: none; }
  .rotor.is-out, .rotor.is-in { opacity: 1; transform: none; }
}

.h2 {
  font-size: var(--t-h2); font-weight: 600; line-height: 1.02;
  letter-spacing: -.035em; margin-bottom: 1.25rem;
}

.lede { font-size: var(--t-lede); color: var(--ink); max-width: 40ch; }
.sub { color: var(--ink-muted); max-width: 56ch; margin-top: 1rem; }
.note { color: var(--ink-dim); font-family: var(--mono); font-size: var(--t-mono);
        letter-spacing: .14em; text-transform: uppercase; margin-top: 2rem; }

.stack { margin: 2rem 0 3rem; }
.stack p { color: var(--ink-muted); padding: .55rem 0; border-bottom: 1px solid var(--line); max-width: 62ch; }

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

.row { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 2.5rem; }

.btn {
  display: inline-flex; align-items: center; gap: .6em;
  padding: 1em 1.9em; border: 1px solid transparent; border-radius: 2px;
  font-family: var(--mono); font-size: var(--t-mono); font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; cursor: pointer;
  transition: background-color var(--fast) var(--ease),
              border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.btn--signal { background: var(--signal); color: var(--bg); }
.btn--signal:hover { background: var(--signal-hi); }
.btn--ghost { background: transparent; border-color: var(--line-2); color: var(--ink); }
.btn--ghost:hover { border-color: var(--signal); color: var(--signal); }

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

.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; padding: 1.4rem var(--gutter);
  background: linear-gradient(to bottom, rgba(5,5,5,.9), transparent);
}
.nav__mark { font-family: var(--mono); font-size: var(--t-mono); font-weight: 500; letter-spacing: .22em; }
.nav__mark span { color: var(--signal); }
.nav__label {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .24em;
  color: var(--ink-dim); text-transform: uppercase;
}
.nav__toggle {
  background: transparent; border: 1px solid var(--line-2); border-radius: 2px;
  color: var(--ink); font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .18em; text-transform: uppercase; padding: .6em 1.1em; cursor: pointer;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.nav__toggle:hover { border-color: var(--signal); color: var(--signal); }

@media (max-width: 860px) { .nav__label { display: none; } }

.menu {
  position: fixed; inset: 0; z-index: 55;
  display: grid; place-items: center;
  background: var(--scrim);
  -webkit-backdrop-filter: var(--scrim-blur); backdrop-filter: var(--scrim-blur);
  opacity: 0; transition: opacity var(--fast) var(--ease);
}
.menu[hidden] { display: none; }
.menu.is-open { opacity: 1; }
.menu__list { display: grid; gap: clamp(.5rem, 2vh, 1.25rem); text-align: left; }
.menu__item {
  display: flex; align-items: baseline; gap: 1.25rem;
  background: transparent; border: 0; cursor: pointer;
  font-family: var(--sans); font-size: clamp(1.6rem, 5vw, 3.25rem);
  font-weight: 500; letter-spacing: -.03em; color: var(--ink);
  padding: .2em 0; transition: color var(--fast) var(--ease);
}
.menu__item:hover { color: var(--signal); }
.menu__idx { font-family: var(--mono); font-size: var(--t-mono); color: var(--ink-dim); letter-spacing: .2em; }

/* The overlay showed all six rows at once, which read as a system dialog
   dropped on top of the page rather than part of it. Two changes: stagger
   the rows in, and give each row a rule that draws itself. --m is set per
   <li> in the markup and inherits down to the button. The delay lives only
   on the is-open state so CLOSING stays quick — a staggered exit would make
   the menu feel slow to dismiss. The backdrop itself is --scrim, shared with
   the modals, and set once at the top of this file. */
.menu__list { gap: 0; width: min(88vw, 40rem); }
.menu__list li { position: relative; }

.menu__list li + li::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 1px;
  background: var(--line);
  transform: scaleX(0); transform-origin: left;
  transition: transform 700ms var(--ease);
}
.menu.is-open .menu__list li + li::before {
  transform: scaleX(1);
  transition: transform 700ms var(--ease) calc(var(--m, 0) * 70ms + 90ms);
}

.menu__item {
  width: 100%; padding: clamp(.45rem, 1.5vh, .9rem) 0;
  opacity: 0; transform: translateY(20px);
  transition: opacity 300ms var(--ease), transform 300ms var(--ease),
              color var(--fast) var(--ease);
}
.menu.is-open .menu__item {
  opacity: 1; transform: none;
  transition:
    opacity 560ms var(--ease) calc(var(--m, 0) * 70ms),
    transform 560ms var(--ease) calc(var(--m, 0) * 70ms),
    color var(--fast) var(--ease);
}

/* The index lights before the label, so the eye lands on the number first —
   same read order as the layer spine in SYSTEMS. */
.menu__idx { transition: color var(--fast) var(--ease); }
.menu__item:hover .menu__idx,
.menu__item:focus-visible .menu__idx { color: var(--signal); }

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

.hero {
  position: relative; min-height: 100svh;
  display: flex; flex-direction: column; justify-content: center;
  padding: 7rem 0 5rem; overflow: hidden;
}
/* z-index 1, not 0: .grain is a fixed z-index:1 overlay declared earlier in
   the document, so the field needs to tie it and win on source order to sit
   ABOVE the dot grid while staying below .hero__inner (z-index 2). */
.hero__field { position: absolute; inset: 0; z-index: 1; pointer-events: none; }

.hero__canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block;
  pointer-events: none;
}
/* display:block matters: as an inline element the SVG sits on a text baseline
   and adds ~7px of descender space, which overflowed .hero (overflow:hidden)
   and clipped the bottom of the frame. */
.seed { width: 100%; height: 100%; display: block; }
.hero__inner { position: relative; z-index: 2; }
.hero__h { margin-bottom: 2rem; }
.hero__h span { display: block; }
.hero__scroll {
  position: absolute; bottom: 1.75rem; left: 50%; transform: translateX(-50%);
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .3em;
  color: var(--ink-dim); z-index: 2;
}

/* ------------------------------------------------------------ bands ----- */

.band {
  --band-pad: var(--band-md);
  padding: var(--band-pad) 0;
  border-top: 1px solid var(--line);
  position: relative; z-index: 2;
}

/* --- rhythm modifiers ---------------------------------------------------- */

.band--tight { --band-pad: var(--band-tight); }
.band--vast  { --band-pad: var(--band-vast); }

/* Slow: deliberate, cinematic. Longer travel so the arrival is felt. */
.band--slow { --rv-dur: 920ms; --rv-step: 150ms; --rv-shift: 28px; }

/* Fast: technical, matter-of-fact. Barely moves — it just appears. */
.band--fast { --rv-dur: 360ms; --rv-step: 42ms; --rv-shift: 8px; }

/* Full-bleed chapter breaks. Two of these, deliberately — they only read as
   breaks because the surrounding bands do not do it. */
.band--bleed {
  /* No fill: an opaque --bg-2 here would black out the backplate on this one
     layer. The inset hairlines alone mark the section. */
  background: transparent;
  border-top-color: transparent;
  box-shadow: inset 0 1px 0 var(--line), inset 0 -1px 0 var(--line);
}

/* ---------------------------------------------------------- openers ----- */
/* Eleven of thirteen bands open tag > h2 > sub. The two chapter breaks get
   their own opening move so they read as structurally different, not merely
   slower — and the two moves differ from each other so they do not become a
   new pattern. */

/* A — Engine: outsized outline index in a left column. */
.opener--index {
  display: grid;
  grid-template-columns: minmax(0, 11rem) 1fr;
  gap: clamp(1.25rem, 4vw, 4rem);
  align-items: start;
}

.opener__num {
  font-size: clamp(4rem, 11vw, 9.5rem);
  font-weight: 600;
  line-height: .78;
  letter-spacing: -.06em;
  /* Fallback for engines without text-stroke: a very low-contrast fill. */
  color: rgba(245, 241, 234, .08);
  margin: 0;
}

@supports (-webkit-text-stroke: 1px red) {
  .opener__num { color: transparent; -webkit-text-stroke: 1px var(--line-2); }
}

.opener__body .tag { margin-bottom: 1rem; }

@media (max-width: 720px) {
  .opener--index { grid-template-columns: 1fr; gap: .5rem; }
  .opener__num { font-size: clamp(3rem, 16vw, 5rem); }
}

/* B — Loop: label, rule and heading on one baseline. */
.opener--line {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .9rem 1.5rem;
}

.opener__label {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: var(--t-mono);
  font-weight: 500;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--signal);
}

.opener__rule {
  flex: 0 0 clamp(2rem, 9vw, 9rem);
  height: 1px;
  background: var(--line-2);
  align-self: center;
}

.opener--line .h2 { flex: 1 1 18rem; margin-bottom: 0; }

/* ----------------------------------------------------------- engine ----- */

.engine {
  display: grid; grid-template-columns: 1fr 22rem; gap: clamp(1.5rem, 3vw, 3rem);
  align-items: center; padding: 2.5rem var(--gutter) 0;
  width: min(100%, 92rem); margin-inline: auto;
}
.engine__svg { width: 100%; height: auto; display: block; }
.engine__list { display: none; }

.engine__read {
  border-left: 1px solid var(--line-2); padding-left: 1.5rem; min-height: 14rem;
}
.engine__read-title {
  font-size: 1.5rem; font-weight: 600; letter-spacing: -.02em; color: var(--signal);
  margin-bottom: .5rem;
}
.engine__read-line { color: var(--ink-muted); margin-bottom: 1.25rem; }
.engine__flow { display: grid; gap: .4rem; }
.engine__flow li {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-dim); padding-left: 1.25rem; position: relative;
}
.engine__flow li::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 6px; height: 1px; background: var(--signal);
}
.engine__more {
  display: inline-block; margin-top: 1.5rem;
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .18em;
  text-transform: uppercase; color: var(--signal); text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: .2em;
}
.engine__more:hover { color: var(--signal-hi); }
.engine__more[hidden] { display: none; }

/* SVG node styling */
.node { cursor: pointer; transition: opacity var(--fast) var(--ease); }
.node__ring { fill: none; stroke: var(--line-2); stroke-width: 1;
  transition: stroke var(--fast) var(--ease), r var(--fast) var(--ease); }
.node__dot { fill: var(--ink-dim); transition: fill var(--fast) var(--ease); }
.node__txt {
  fill: var(--ink-dim); font-family: var(--mono); font-size: 11px;
  letter-spacing: .14em; text-transform: uppercase; transition: fill var(--fast) var(--ease);
}

/* Amber now marks STATE, not decoration: the active node and the live path.
   Neighbours reached by a flow bridge get a partial lift so the chain reads. */
.node.is-active .node__dot { fill: var(--signal); }
.node.is-active .node__txt { fill: var(--ink); }
.node.is-active .node__ring { stroke: var(--signal); }
.node.is-near .node__dot { fill: var(--signal-hi); }
.node.is-near .node__txt { fill: var(--ink-muted); }
.node.is-near .node__ring { stroke: rgba(232, 163, 61, .45); }
.node.is-dim { opacity: .3; }

.edge { stroke: var(--line); stroke-width: 1; fill: none;
  transition: stroke var(--fast) var(--ease), opacity var(--fast) var(--ease); }

/* Signal visibility. Was `2 26` at .55 opacity = 3.9% effective ink, which
   rendered as sub-pixel noise. Now round-capped particles at 3/16 with full
   opacity — roughly 4x the presence — and per-edge tempo set in engine.js so
   they do not march in lockstep. */
.edge--live {
  stroke: var(--signal); stroke-width: 1.6; fill: none; opacity: .9;
  stroke-linecap: round; stroke-dasharray: 3 16;
  animation: flow 3.4s linear infinite;
  transition: opacity var(--fast) var(--ease), stroke-width var(--fast) var(--ease);
}
.edge--chord { stroke-width: 1.3; opacity: .7; }

@keyframes flow { to { stroke-dashoffset: -19; } }

/* Propagation states (§37). */
.edge.is-hot { stroke: rgba(232, 163, 61, .5); }
.edge--live.is-hot { opacity: 1; stroke-width: 2.4; stroke: var(--signal-hi); }
/* Dimmed, not dead — the rest of the system idles in the background rather
   than switching off. */
.edge.is-dim { opacity: .4; }
.edge--live.is-dim { opacity: .28; }

.core__disc { fill: var(--bg-2); stroke: var(--signal); stroke-width: 1; }
.core__pulse {
  fill: none; stroke: var(--signal); stroke-width: 1.5;
  transform-box: fill-box; transform-origin: center;
  animation: corepulse 3s var(--ease) infinite;
}
@keyframes corepulse {
  0%, 100% { transform: scale(1);    opacity: .45; }
  50%      { transform: scale(1.32); opacity: 0;   }
}
.core__txt { fill: var(--ink); font-family: var(--mono); font-size: 12px; letter-spacing: .2em; }

@media (max-width: 900px) {
  .engine { grid-template-columns: 1fr; }

  /* The full labelled network shows on mobile. engine.js swaps to a PORTRAIT
     board (viewBox 720x1180) so there is room for it; these values are in SVG
     user units, which at 375px screen width render at ~13.5px. */
  .engine__svg { display: block; }
  .node__txt { font-size: 26px; letter-spacing: .10em; }
  .core__txt { font-size: 26px; letter-spacing: .18em; }
  .edge { stroke-width: 1.8; }
  .edge--live { stroke-width: 2.8; stroke-dasharray: 6 30; }
  .edge--chord { stroke-width: 2.2; }

  /* Tile grid, not a text list. Each node is a bordered tile; the selected
     one lights amber. The flow path lives in the read panel below, not in
     every row — that repetition was what made this section a wall of text. */
  .engine__list { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; margin-top: 1.5rem; }
  .engine__list li { border: 0; }
  .engine__pick {
    display: flex; gap: .7rem; align-items: center;
    width: 100%; min-height: 48px;
    padding: .7rem .9rem; border: 1px solid var(--line-2); border-radius: 6px;
    background: var(--bg-2);
    font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .14em;
    text-transform: uppercase; color: var(--ink-muted);
    text-align: left; cursor: pointer;
    transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease),
                box-shadow var(--fast) var(--ease);
  }
  .engine__pick b { color: var(--ink-dim); font-weight: 500; flex: 0 0 auto; letter-spacing: .2em; }
  .engine__pick span { opacity: 1; }
  .engine__pick[aria-pressed="true"] {
    border-color: var(--signal); color: var(--ink);
    box-shadow: 0 0 0 1px var(--signal), 0 0 18px rgba(232, 163, 61, .25);
  }
  .engine__pick[aria-pressed="true"] b { color: var(--signal); }

  .engine__read {
    border-left: 0; padding-left: 0;
    border-top: 1px solid var(--line-2); padding-top: 1.5rem; margin-top: 1.5rem;
  }
}

/* ----------------------------------------------------------- layers ----- */

/* ----------------------------------------------------------- layers ----- */
/* Was six identical rows separated by hairlines — a table, not a system.
   Now a descending spine: each layer is a node on one continuous line that
   assembles segment by segment as you scroll, and the content steps
   progressively right so the six read as depth rather than repetition. */

.layers { position: relative; margin-top: 2.5rem; }

.layer {
  position: relative;
  padding: 0 0 clamp(2rem, 5vh, 3.25rem) 3.25rem;
}

.layer:last-child { padding-bottom: 0; }

/* Node on the spine. Amber only once the layer has arrived — the dot is
   state, which is what the signal colour is reserved for. */
.layer__dot {
  position: absolute;
  left: 0; top: .45rem;
  width: 11px; height: 11px;
  border-radius: 50%;
  border: 1px solid var(--line-2);
  background: var(--bg);
  transition: background-color .55s var(--ease), border-color .55s var(--ease),
              box-shadow .55s var(--ease);
  transition-delay: calc(var(--i, 0) * 60ms);
}

.js .layer.is-visible .layer__dot {
  background: var(--signal);
  border-color: var(--signal);
  box-shadow: 0 0 0 5px rgba(232, 163, 61, .10);
}

/* Spine segment, drawn per layer so the line builds itself top-to-bottom
   without any scroll-position maths. */
.layer::before {
  content: "";
  position: absolute;
  left: 5px; top: 1.5rem; bottom: 0;
  width: 1px;
  background: linear-gradient(to bottom, var(--line-2), var(--line));
  transform: scaleY(0);
  transform-origin: top;
  transition: transform .9s var(--ease);
  transition-delay: calc(var(--i, 0) * 60ms + 120ms);
}

.js .layer.is-visible::before { transform: scaleY(1); }
.layer:last-child::before { display: none; }

/* Each layer steps further in: descent through the stack. */
.layer__body { margin-left: calc(var(--i, 0) * 10px); }

.layer__idx {
  font-family: var(--mono); font-size: var(--t-mono);
  color: var(--ink-dim); letter-spacing: .2em; margin-bottom: .35rem;
}

.layer__name {
  font-size: clamp(1.35rem, 2.6vw, 2.1rem);
  font-weight: 600; letter-spacing: -.03em; line-height: 1;
  margin-bottom: .9rem;
}

/* Chips cascade in rather than appearing as one block. --c is set per chip
   in main.js; without it every chip would land on the same frame. */
.js .layer .chips li {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  transition-delay: calc(var(--i, 0) * 60ms + var(--c, 0) * 45ms + 200ms);
}

.js .layer.is-visible .chips li { opacity: 1; transform: none; }

.layer__line { color: var(--ink-muted); max-width: 46ch; margin-bottom: .9rem; }
.layer__hit, .layer__tab { display: none; }

@media (max-width: 640px) {
  .layer { padding-left: 2.25rem; }
  .layer__body { margin-left: calc(var(--i, 0) * 4px); }
}

/* --- squeeze carousel (>= 900px) -------------------------------------------
   The six layers stand side by side; the active one takes about half the
   row and the rest compress to labelled strips. Below 900px the spine
   layout above stays exactly as it was: a horizontal squeeze has no room
   on a phone.

   Text is never allowed to reflow mid-squeeze: expanded content fades in
   only AFTER the panel has grown (transition-delay), so any rewrap happens
   at opacity 0. State (.is-active, aria-expanded) is owned by squeeze() in
   main.js; a transparent button covers each panel for click and keyboard. */
@media (min-width: 900px) {
  .layers { display: flex; gap: .6rem; height: clamp(24rem, 46vh, 31rem); margin-top: 3rem; }
  .layer {
    flex: 1 1 0; min-width: 0; padding: 0; position: relative;
    border: 1px solid var(--line-2); border-radius: 6px;
    background: rgba(9, 9, 9, .78); overflow: hidden;
    transition: flex-grow .7s var(--ease), border-color .3s var(--ease), background-color .3s var(--ease);
  }
  .layer.is-active { flex-grow: 4.6; border-color: var(--signal); background: rgba(9, 9, 9, .92); }
  .layer:not(.is-active):hover { border-color: rgba(232, 163, 61, .45); }
  .layer:focus-within { outline: 1px solid var(--signal); outline-offset: 3px; }
  .layer__dot, .layer::before { display: none; }

  .layer__hit {
    display: block; position: absolute; inset: 0; z-index: 2;
    background: transparent; border: 0; cursor: pointer; padding: 0;
  }
  .layer.is-active .layer__hit { cursor: default; }
  .layer__hit:focus { outline: none; }

  .layer__body {
    margin: 0; position: absolute; inset: 0; padding: 1.4rem 1.5rem;
    display: flex; flex-direction: column;
  }
  .layer__idx { margin-bottom: auto; }

  /* strip label, read bottom-to-top like a book spine */
  .layer__tab {
    display: block; position: absolute; left: 50%; bottom: 1.4rem; transform: translateX(-50%);
    writing-mode: vertical-rl; rotate: 180deg;
    font-size: clamp(1.05rem, 1.4vw, 1.35rem); font-weight: 600; letter-spacing: -.02em;
    color: var(--ink-muted); white-space: nowrap;
    transition: opacity .25s var(--ease), color .3s var(--ease);
  }
  .layer:not(.is-active):hover .layer__tab { color: var(--ink); }
  .layer.is-active .layer__tab { opacity: 0; }

  .layer__name, .layer__line, .layer .chips { opacity: 0; transition: opacity .3s var(--ease); }
  .layer__name { white-space: nowrap; }
  .layer.is-active .layer__name, .layer.is-active .layer__line, .layer.is-active .chips {
    opacity: 1; transition-delay: .32s;
  }
  .layer.is-active .layer__idx { color: var(--signal); }
  .js .layer.is-active.is-visible .chips li { opacity: 1; transform: none; }

  /* Panel art. One still per layer, cover-cropped from the top so the motif
     sits in the upper two-thirds and the empty lower third of each image
     lands under the text. A slow scale+drift (18 s, alternating) makes the
     still read as motion at zero runtime cost. Strips keep a faint slice so
     the row has texture; the active panel shows it in full. */
  .layer__art {
    position: absolute; inset: 0; z-index: 0; pointer-events: none;
    /* background-image is set inline on the element: a relative url() inside a
       custom property resolves against the STYLESHEET in Chrome, not the page. */
    background-size: cover; background-position: center top; background-repeat: no-repeat;
    opacity: .28; transform-origin: 50% 30%;
    transition: opacity .5s var(--ease);
    animation: artdrift 18s ease-in-out infinite alternate;
  }
  .layer.is-active .layer__art { opacity: 1; }
  @keyframes artdrift {
    from { transform: scale(1.06) translate(0, 0); }
    to   { transform: scale(1.14) translate(-2%, -3%); }
  }
  /* Contrast guard: the text block sits on a scrim regardless of what the
     image does behind it. Transparent above 75% so the motif stays clear. */
  .layer__body {
    z-index: 1;
    background: linear-gradient(to top, rgba(9,9,9,.96) 0%, rgba(9,9,9,.86) 42%, rgba(9,9,9,0) 75%);
  }
}

/* ------------------------------------------------------------ chips ----- */

.chips { display: flex; flex-wrap: wrap; gap: .45rem; }
.chips li {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-muted);
  border: 1px solid var(--line); border-radius: 2px; padding: .45em .8em;
}
.chips--loose { margin-top: 1rem; }

/* ------------------------------------------------------------- flow ----- */

.flow { display: flex; flex-wrap: wrap; gap: .35rem .9rem; margin-top: 1rem; }
.flow li {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-muted); position: relative; padding-right: 1rem;
}
.flow li:not(:last-child)::after {
  content: "→"; position: absolute; right: 0; color: var(--ink-dim);
}
.flow--wide { gap: .5rem 1.2rem; }
.flow--tall { display: grid; gap: .5rem; margin-top: 1rem; }
.flow--tall li { padding-right: 0; padding-left: 1.25rem; }
/* :not(:last-child) matches the .flow arrow rule's specificity, or the arrows survive. */
.flow--tall li:not(:last-child)::after { content: none; }
.flow--tall li::before {
  content: ""; position: absolute; left: 0; top: .55em; width: 6px; height: 1px; background: var(--signal);
}

.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 5rem); margin-top: 3rem; }
@media (max-width: 760px) { .split { grid-template-columns: 1fr; } }

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

/* Forced 2x2. auto-fit resolved to 3 columns at desktop, orphaning the 4th
   card beside a dead cell. */
.cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
         gap: 1px; background: var(--line); margin-top: 3rem; }
@media (max-width: 720px) { .cards { grid-template-columns: 1fr; } }
.card { background: var(--bg); padding: clamp(1.5rem, 2.5vw, 2.25rem); }
.card__idx { font-family: var(--mono); font-size: var(--t-mono); color: var(--signal); letter-spacing: .2em; margin-bottom: 1rem; }
.card__h { font-size: 1.35rem; font-weight: 500; letter-spacing: -.02em; margin-bottom: .5rem; }
.card__p { color: var(--ink-muted); }

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

.pipeline { display: flex; flex-wrap: wrap; gap: 1px; background: var(--line); margin-top: 2.5rem; }
.pipeline li { flex: 1 1 8rem; background: var(--bg); padding: 1.5rem 1rem; text-align: center; position: relative; }
.pipeline li span {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-muted);
}
.pipeline li::before {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: var(--signal); transform: scaleX(0); transform-origin: left;
  transition: transform 700ms var(--ease); transition-delay: calc(var(--n, 0) * 110ms);
}
.pipeline.is-visible li::before { transform: scaleX(1); }

/* ------------------------------------------------------------- loop ----- */

.loop { display: grid; grid-template-columns: minmax(0, 26rem) 1fr; gap: clamp(2rem, 5vw, 5rem);
        align-items: center; margin-top: 3rem; }
.loop__svg { width: 100%; height: auto; }
.loop__list { display: grid; gap: .5rem; }
.loop__list li {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-muted); padding-left: 1.5rem; position: relative;
}
.loop__list li::before { content: ""; position: absolute; left: 0; top: .55em; width: 8px; height: 1px; background: var(--signal); }
@media (max-width: 760px) { .loop { grid-template-columns: 1fr; } .loop__svg { max-width: 20rem; } }

/* ---------------------------------------------------------- studies ----- */

.studies { margin-top: 3rem; }
.study { display: grid; grid-template-columns: 4rem 1fr; gap: 1.5rem; padding: 2rem 0; border-top: 1px solid var(--line); }
.study__idx { font-family: var(--mono); font-size: var(--t-mono); color: var(--signal); letter-spacing: .2em; }
.study__h { font-size: clamp(1.3rem, 2.6vw, 2rem); font-weight: 500; letter-spacing: -.025em; }
/* Client-site link on a study title. The global `a` has no hover, so a linked
   title would look inert; amber on hover + an arrow marks it as external. */
.study__link { transition: color var(--fast) var(--ease); }
.study__link:hover, .study__link:focus-visible { color: var(--signal); }
.study__ext { font-size: .6em; vertical-align: .25em; color: var(--ink-dim); transition: color var(--fast) var(--ease); }
.study__link:hover .study__ext { color: var(--signal); }
.study--empty { opacity: .42; }

.study__line { color: var(--ink-muted); max-width: 62ch; margin-top: .9rem; }

/* Verified figures get real weight — this is the only place on the page where
   a number is the loudest element, which is the point. */
.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1.5rem clamp(1.5rem, 4vw, 3.5rem);
  margin-top: 2rem;
  padding: 1.5rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.figures dt {
  font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-dim); margin-bottom: .5rem;
}

.figures dd {
  margin: 0;
  font-size: clamp(1.35rem, 2.6vw, 2rem);
  font-weight: 600; letter-spacing: -.03em; line-height: 1.1;
  display: flex; align-items: baseline; gap: .45em; flex-wrap: wrap;
}

.figures dd b { font-weight: 600; }
.figures dd .is-signal { color: var(--signal); }
.figures dd small {
  font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-dim); font-weight: 400;
}
.meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr)); gap: 1rem; margin-top: 1.5rem; }
.meta dt { font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .14em;
           text-transform: uppercase; color: var(--ink-dim); margin-bottom: .2rem; }
.meta dd { color: var(--ink-muted); }

/* ----------------------------------------------------------- matrix ----- */

/* --- capabilities accordion ------------------------------------------------
   Replaces the six-column keyword matrix. Height animates with the
   grid-template-rows 0fr -> 1fr technique: pure CSS, no JS measuring, and it
   follows the content if text wraps differently at another width.
   visibility (not just opacity) on the inner keeps collapsed text out of the
   tab order and away from screen readers; its delay lets the fade finish
   before it flips. State (.is-open, aria-expanded) is owned by caps() in
   main.js. With no JS every panel is simply open.                          */
.caps { border-top: 1px solid var(--line); margin-top: 2.5rem; }
.cap  { border-bottom: 1px solid var(--line); }
.cap__h { margin: 0; font: inherit; }
.cap__btn {
  width: 100%; display: flex; align-items: baseline; gap: 1.25rem;
  background: transparent; border: 0; color: var(--ink); cursor: pointer; text-align: left;
  padding: 1.4rem 0; font-family: var(--sans); font-size: clamp(1.25rem, 2.4vw, 2rem);
  font-weight: 500; letter-spacing: -.02em; transition: color var(--fast) var(--ease);
}
.cap__idx {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .2em;
  color: var(--ink-dim); transition: color var(--fast) var(--ease);
}
.cap__btn:hover, .cap.is-open .cap__btn,
.cap__btn:hover .cap__idx, .cap.is-open .cap__idx { color: var(--signal); }
.cap__btn:focus-visible { outline: 1px solid var(--signal); outline-offset: 4px; }

.cap__sign { margin-left: auto; position: relative; flex: 0 0 14px; width: 14px; height: 14px; align-self: center; }
.cap__sign::before, .cap__sign::after {
  content: ""; position: absolute; left: 0; top: 6px; width: 14px; height: 2px;
  background: currentColor; transition: transform .35s var(--ease);
}
.cap__sign::after { transform: rotate(90deg); }
.cap.is-open .cap__sign::after { transform: rotate(0deg); }

.cap__panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .45s var(--ease); }
.cap.is-open .cap__panel { grid-template-rows: 1fr; }
.cap__inner {
  overflow: hidden; min-height: 0; visibility: hidden; opacity: 0;
  transition: opacity .3s var(--ease), visibility 0s linear .45s;
}
.cap.is-open .cap__inner { visibility: visible; opacity: 1; transition-delay: .1s, 0s; }
.cap__inner > p { color: var(--ink-muted); max-width: 62ch; padding: 0 0 1.25rem 2.3rem; }
.cap__inner > p b { color: var(--ink); font-weight: 500; }
.cap__inner > .chips { padding: 0 0 1.75rem 2.3rem; }
@media (max-width: 640px) {
  .cap__inner > p, .cap__inner > .chips { padding-left: 0; }
}
.no-js .cap__panel { grid-template-rows: 1fr; }
.no-js .cap__inner { visibility: visible; opacity: 1; }

/* ------------------------------------------------------------ notes ----- */

.notes { margin-top: 2.5rem; }
.note-row { padding: 1.5rem 0; border-top: 1px solid var(--line); }
.note-row__meta { font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .18em; color: var(--ink-dim); }
.note-row__h { font-size: clamp(1.15rem, 2.2vw, 1.6rem); font-weight: 500; letter-spacing: -.02em; margin-top: .35rem; }
.note-row__p { color: var(--ink-muted); max-width: 68ch; margin-top: .6rem; }
.note-row--empty { opacity: .42; }

/* -------------------------------------------------------------- end ----- */

.band--end { text-align: left; }
.end__h { margin-bottom: 1rem; }

/* ------------------------------------------------------- credentials ---- */
/* Typeset in the site's own system rather than embedding the issuer's
   certificate image — that artwork is bright green on a light plate and would
   fight the palette wherever it sat. */

.creds { margin-top: clamp(2rem, 5vh, 3rem); }
.creds .tag { margin-bottom: 1rem; }

.cred-list { display: grid; gap: .75rem; }

.cred {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .4rem 1rem;
  padding: .9rem 0 .9rem 1.25rem;
  border-left: 1px solid var(--line);
  position: relative;
}

/* The marker earns amber: it flags a held credential, which is state rather
   than decoration. */
.cred::before {
  content: ""; position: absolute; left: -1px; top: 1rem;
  width: 1px; height: 1.1rem; background: var(--signal);
}

.cred__name { font-size: 1.0625rem; font-weight: 500; color: var(--ink); letter-spacing: -.01em; }

.cred__by {
  font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-dim);
  border-bottom: 1px solid transparent;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
a.cred__by:hover { color: var(--signal); border-bottom-color: var(--signal); }

.cred__link {
  font-family: var(--mono); font-size: var(--t-mono);
  letter-spacing: .14em; text-transform: uppercase; color: var(--signal);
  border-bottom: 1px solid transparent; transition: border-color var(--fast) var(--ease);
}
.cred__link:hover { border-bottom-color: var(--signal); }

.foot { border-top: 1px solid var(--line); padding: 2.5rem 0 3.5rem; position: relative; z-index: 2; }
.foot__inner { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem; align-items: baseline; }
.foot__line { font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .28em; color: var(--signal); }
.foot__meta { display: flex; gap: 1.5rem; font-family: var(--mono); font-size: var(--t-mono);
              letter-spacing: .14em; color: var(--ink-dim); text-transform: uppercase; }
.foot__meta a:hover { color: var(--signal); }

.foot__partner { flex: 1 1 100%; order: 3; padding-top: 1.5rem; border-top: 1px solid var(--line); }

.foot__join {
  display: inline-flex; align-items: center; gap: .6em;
  background: transparent; border: 1px solid var(--line-2); border-radius: 2px;
  color: var(--ink); cursor: pointer;
  padding: .8em 1.4em;
  font-family: var(--mono); font-size: var(--t-mono); font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  transition: border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.foot__join:hover { border-color: var(--signal); color: var(--signal); }
.foot__join[aria-busy="true"] { opacity: .6; cursor: progress; }

/* ------------------------------------------------------------ modal ----- */

.modal { position: fixed; inset: 0; z-index: 120; display: flex; flex-direction: column;
         justify-content: flex-start; align-items: center;
         padding: clamp(1rem, 4vh, 3rem) 1rem; overflow-y: auto; overscroll-behavior: contain; }
.modal[hidden] { display: none; }
.modal__scrim { position: fixed; inset: 0; background: var(--scrim);
  -webkit-backdrop-filter: var(--scrim-blur); backdrop-filter: var(--scrim-blur);
  opacity: 0; transition: opacity var(--reveal) var(--ease); }
.modal__panel {
  position: relative; z-index: 1; width: min(100%, 52rem); margin: auto 0; flex: 0 0 auto;
  background: var(--bg-2); border: 1px solid var(--line-2);
  padding: clamp(1.75rem, 4vw, 3rem);
  opacity: 0; transform: translateY(16px) scale(.98);
  transition: opacity var(--reveal) var(--ease), transform var(--reveal) var(--ease);
}
.modal.is-open .modal__scrim { opacity: 1; }
.modal.is-open .modal__panel { opacity: 1; transform: none; }
.modal__x {
  position: absolute; top: 1rem; right: 1rem; width: 2.25rem; height: 2.25rem;
  display: grid; place-items: center; background: transparent;
  border: 1px solid var(--line-2); color: var(--ink-muted);
  font-size: 1.35rem; line-height: 1; cursor: pointer;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.modal__x:hover { color: var(--signal); border-color: var(--signal); }

/* --- Ligna v3 form shells (idea, access, pop-up) ---------------------------
   The forms render in an open shadow root and are skinned from main.js
   (lignaV3 / V3_SHEET); only the shell and its loading line live here.     */
.ligna { margin-top: 1.75rem; position: relative; min-height: 6rem; }
.ligna__wait {
  font-family: var(--mono); font-size: var(--t-mono); letter-spacing: .28em;
  color: var(--ink-dim); text-transform: uppercase;
}
.ligna.is-ready .ligna__wait { display: none; }
.ligna--access { margin-top: 2rem; }

/* Any overlay open — menu or any modal — and the chat launcher yields. */
.has-overlay #bv3-chat-widget-host { display: none !important; }





/* ==========================================================================
   PARTNER ONBOARDING — theme sync
   ==========================================================================
   The component renders into the LIGHT DOM (no iframe, no shadow root), so
   page CSS reaches it. 61 elements, all prefixed `bv3j-`.

   The selectors originally supplied do not exist in join.js — verified, zero
   occurrences of each. Mapping used instead:

     .bv3-join-container        ->  .bv3j            (the white panel)
     .bv3-onboard-modal         ->  .bv3j-modal / .bv3j-backdrop
     .bv3-join-container input  ->  .bv3j-input
     .bv3-join-container label  ->  .bv3j-label
     .bv3-btn-primary           ->  .bv3j-cta
     .bv3-progress-dot.is-active->  .bv3j-step--active .bv3j-step-dot

   Colours come from the site tokens, not standalone hexes, so this stays in
   sync if the palette moves. Notably --signal is #E8A33D; the #ffb800
   specified would have introduced a second, more acid gold.               */

.bv3j-backdrop {
  background: var(--scrim) !important;
  -webkit-backdrop-filter: var(--scrim-blur); backdrop-filter: var(--scrim-blur);
}

.bv3j {
  background-color: var(--bg-2) !important;
  color: var(--ink) !important;
  border: 1px solid var(--line-2) !important;
  border-radius: 2px !important;
  font-family: var(--sans) !important;
}

/* The header and footer carry their own white fills (rgba(255,255,255,.92)
   and #fff) independently of the panel, so setting .bv3j alone left two light
   bands top and bottom. */
.bv3j-header,
.bv3j-footer,
.bv3j-pane-head {
  background: var(--bg-2) !important;
  background-image: none !important;
  border-color: var(--line) !important;
}

.bv3j-body,
.bv3j-panes,
.bv3j-pane { background: transparent !important; }

.bv3j-header-title,
.bv3j-pane-title { color: var(--ink) !important; letter-spacing: -.02em; }

.bv3j-header-sub,
.bv3j-pane-sub,
.bv3j-footer-trust { color: var(--ink-muted) !important; }

.bv3j-label {
  color: var(--ink-dim) !important;
  font-family: var(--mono) !important;
  font-size: var(--t-mono) !important;
  letter-spacing: .14em; text-transform: uppercase;
}

.bv3j-input,
.bv3j select,
.bv3j textarea {
  background-color: var(--bg) !important;
  color: var(--ink) !important;
  border: 1px solid var(--line-2) !important;
  border-radius: 2px !important;
  font-family: var(--sans) !important;
}

.bv3j-input:focus,
.bv3j select:focus,
.bv3j textarea:focus {
  border-color: var(--signal) !important;
  outline: 2px solid var(--signal); outline-offset: 2px;
}

.bv3j-input::placeholder { color: var(--ink-dim) !important; }

/* Primary action. #050505 on #E8A33D measures 9.45:1. */
.bv3j-cta {
  background: var(--signal) !important;
  background-image: none !important;
  color: var(--bg) !important;
  border: 0 !important;
  border-radius: 2px !important;
  font-family: var(--mono) !important;
  letter-spacing: .16em; text-transform: uppercase; font-weight: 500 !important;
}
.bv3j-cta:hover { background: var(--signal-hi) !important; }
.bv3j-cta-label, .bv3j-cta-icon { color: var(--bg) !important; }

.bv3j-btn--ghost {
  background: transparent !important;
  color: var(--ink) !important;
  border: 1px solid var(--line-2) !important;
}

/* Step indicator — amber marks the active step, matching the engine's use of
   signal for state rather than decoration. */
.bv3j-step--active .bv3j-step-dot { background: var(--signal) !important; color: var(--bg) !important; }
.bv3j-step--pending .bv3j-step-dot { background: transparent !important; border: 1px solid var(--line-2) !important; color: var(--ink-dim) !important; }
.bv3j-connector { background: var(--line) !important; }
.bv3j-connector-fill { background: var(--signal) !important; }

/* The vendor draws a 3px accent bar as .bv3j::before, gradient from navy
   (rgb(14,22,41)). Cool-toned and the one remaining clash — retuned to the
   signal so it reads as the engine's own. */
.bv3j::before {
  background-image: linear-gradient(90deg, transparent, var(--signal), transparent) !important;
  opacity: .8;
}

.bv3j-close { color: var(--ink-muted) !important; border-color: var(--line-2) !important; }
.bv3j-close:hover { color: var(--signal) !important; border-color: var(--signal) !important; }

.bv3j-notice { background: var(--bg) !important; border: 1px solid var(--line) !important; color: var(--ink-muted) !important; }
.bv3j-toast { background: var(--bg-2) !important; color: var(--ink) !important; border: 1px solid var(--line-2) !important; }
.bv3j-error { color: #f4795b !important; }

.bv3j-strength-bar { background: var(--line) !important; }
.bv3j-strength-fill { background: var(--signal) !important; }
.bv3j-strength-label, .bv3j-pw-rule-label { color: var(--ink-dim) !important; }

/* ----------------------------------------------------------- cursor ----- */

.cursor { display: none; }
@media (hover: hover) and (pointer: fine) {
  .cursor {
    display: grid; place-items: center; position: fixed; top: 0; left: 0;
    width: 26px; height: 26px; border: 1px solid var(--line-2); border-radius: 50%;
    pointer-events: none; z-index: 300; transform: translate(-50%, -50%);
    transition: width 220ms var(--ease), height 220ms var(--ease),
                border-color 220ms var(--ease), background-color 220ms var(--ease);
  }
  .cursor.is-hot { width: 64px; height: 64px; border-color: var(--signal); }
  .cursor.is-node { width: 44px; height: 44px; border-color: var(--signal); }
  .cursor__label {
    font-family: var(--mono); font-size: 8px; letter-spacing: .16em;
    text-transform: uppercase; color: var(--signal); opacity: 0;
    transition: opacity 160ms var(--ease);
  }
  .cursor.is-hot .cursor__label { opacity: 1; }
}

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

/* Duration, stagger and travel all come from the band's tempo tokens, so a
   section reads slow or brisk as a whole rather than every section pulsing at
   the same 640ms / 80ms. */
.js .reveal {
  opacity: 0; transform: translateY(var(--rv-shift));
  transition: opacity var(--rv-dur) var(--ease), transform var(--rv-dur) var(--ease);
  transition-delay: calc(var(--i, 0) * var(--rv-step));
}
.js .reveal.is-visible { opacity: 1; transform: none; }
.js .study--empty.is-visible, .js .note-row--empty.is-visible { opacity: .42; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; transition-delay: 0ms !important;
  }
  .js .reveal { opacity: 1; transform: none; }
  .js .layer .chips li { opacity: 1; transform: none; }
  .js .layer::before { transform: scaleY(1); }
  .js .study--empty, .js .note-row--empty { opacity: .42; }
  .edge--live { animation: none; stroke-dasharray: none; opacity: .35; }
  .core__pulse { animation: none; opacity: 0; }
  .layer__art { animation: none; }
  .modal__panel { transform: none; }
  .cursor { display: none !important; }
  .pipeline li::before { transform: scaleX(1); }
  .menu__item { opacity: 1; transform: none; }
  .menu__list li + li::before { transform: scaleX(1); }
}
