/* =====================================================================
   ENCODEX — LAB (MONOLITH v8.0)
   ---------------------------------------------------------------------
   The test bay. A live Three.js hero (one Sentinel unit on the
   calibration stand — src/lab.js) followed by an instrumented document:
   charter, the floor schematic, four active programs (each with an
   animated schematic plate), live telemetry, the two ledgers, the door.

   Extends site.css: same tokens, chrome, buttons, cards. Everything
   here is either the hero apparatus or lab-specific composition.
   Interactions for the document live in /lab-doc.js.
   ===================================================================== */

/* the hero runs full-bleed under the fixed top bar */
body.page-lab { padding-top: 0; }

/* =====================================================================
   HERO — the stand
   ===================================================================== */
.lab-hero {
  position: relative;
  height: 100vh;
  height: 100svh;
  /* The live capsule must fit the real viewport, including short landscape
     windows. A fixed 540px floor made the lower part of the hero fall below
     the fold and could crop the top halo from the visible slice. */
  min-height: 0;
  overflow: hidden;
  background: var(--bg);
}

/* the canvas mount — parallaxed + faded by src/lab.js while scrolling out */
.lab-stage {
  position: absolute;
  inset: 0;
  background: var(--bg);
  will-change: transform, opacity;
}
.lab-stage canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* NOTE: no touch-action lock — the page under the canvas must scroll */
}

/* no-WebGL fallback: the intro film's robot close-up stands in */
.lab-poster {
  display: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) brightness(0.72);
}
body.lab-nogl .lab-poster { display: block; }
body.lab-nogl .lab-stage { display: none; }

/* legibility scrim — the landing's cinematic vignette, verbatim */
.lab-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(3, 4, 6, 0.7) 0%, rgba(3, 4, 6, 0) 13%),
    radial-gradient(ellipse 100% 72% at 50% 105%, rgba(3, 4, 6, 0.85) 0%, rgba(3, 4, 6, 0.42) 48%, rgba(3, 4, 6, 0) 76%),
    linear-gradient(to top, rgba(3, 4, 6, 0.55) 0%, rgba(3, 4, 6, 0) 24%),
    var(--lens-vignette, none); /* the composer grade's lens vignette — see site.css */
}

/* ---- title card: centered, lower third — the landing's film-title
       reveal (rise out of a soft blur, slow and damped, staggered) ---- */
.lab-card {
  position: absolute;
  left: 50%;
  bottom: clamp(88px, 12vh, 150px);
  transform: translateX(-50%);
  width: min(880px, 92vw);
  text-align: center;
  z-index: 2;
}
.lab-card > * {
  transition:
    opacity 1.7s ease,
    transform 1.7s cubic-bezier(0.16, 0.6, 0.2, 1),
    filter 1.7s ease;
}
/* pre-state exists only while the controller is alive (html.js — see the
   watchdog in lab.html); no-JS and failed-JS render everything settled */
html.js .lab-card > * {
  opacity: 0;
  transform: translateY(16px);
  filter: blur(9px);
}
html.js body.lab-in .lab-card > * { opacity: 1; transform: none; filter: none; }
body.lab-in .lab-eyebrow { transition-delay: 0.1s; }
body.lab-in .lab-rule { transition-delay: 0.3s; }
body.lab-in .lab-title { transition-delay: 0.5s; }
body.lab-in .lab-sub { transition-delay: 0.95s; }
body.lab-in .lab-cta { transition-delay: 1.35s; }

.lab-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 20px;
  font: 500 10px var(--f-mono);
  letter-spacing: 0.22em;
  color: #aeb6c2;
}
.lab-eyebrow::before {
  content: '';
  width: 5px;
  height: 5px;
  background: var(--hi);
  box-shadow: 0 0 7px rgba(237, 241, 245, 0.9);
  animation: pulse 3.6s ease-in-out infinite;
}
.lab-rule {
  width: 90px;
  height: 1px;
  margin: 0 auto 28px;
  background: linear-gradient(to right, transparent, #aeb6c2, transparent);
}
.lab-title {
  margin: 0 0 24px;
  font-family: var(--f-disp);
  font-weight: 400;
  font-size: clamp(23px, 3.6vw, 50px);
  line-height: 1.24;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  text-wrap: balance;
  background: linear-gradient(to bottom, #ffffff 30%, #8e99a7 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
.lab-sub {
  margin: 0 auto 36px;
  max-width: 56ch;
  font-family: var(--f-text);
  font-weight: 400;
  font-size: clamp(13.5px, 1.2vw, 16px);
  line-height: 1.7;
  letter-spacing: 0.05em;
  color: #97a1af;
}
.lab-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 13px;
  flex-wrap: wrap;
}
/* hero buttons sit on the live scene — give the ghost a touch of body */
.lab-cta .btn.ghost { background: rgba(10, 13, 18, 0.4); }

/* ---- scroll cue: a hairline the current travels (S1), then the word ---- */
.lab-cue {
  position: absolute;
  left: 50%;
  bottom: 58px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  z-index: 2;
  transition: opacity 1.4s ease 2.2s;
}
html.js .lab-cue { opacity: 0; }
html.js body.lab-in .lab-cue { opacity: 1; }
.lab-cue:hover span { color: #cfd6dd; }
.lab-cue i {
  position: relative;
  width: 1px;
  height: 30px;
  background: rgba(174, 182, 194, 0.28);
  overflow: hidden;
}
.lab-cue i::after {
  content: '';
  position: absolute;
  left: 0;
  top: -40%;
  width: 100%;
  height: 40%;
  background: linear-gradient(to bottom, transparent, var(--hi));
  animation: cueDrop 3.4s cubic-bezier(0.6, 0, 0.3, 1) infinite;
}
@keyframes cueDrop {
  0% { top: -42%; opacity: 0; }
  12% { opacity: 1; }
  46% { top: 102%; opacity: 1; }
  58%, 100% { top: 102%; opacity: 0; }
}
.lab-cue span {
  font: 500 8.5px var(--f-mono);
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  color: var(--txt-3);
}

@media (min-width: 720px) {
  /* Keep the centred capsule unobstructed while preserving the complete
     hero narrative as a quiet, direction-aware editorial rail. */
  .lab-card {
    left: auto;
    inset-inline-start: var(--gutter);
    bottom: clamp(82px, 12vh, 120px);
    transform: none;
    width: min(430px, 34vw);
    text-align: start;
  }
  .lab-eyebrow { margin-bottom: 14px; font-size: 9px; }
  .lab-rule { width: 64px; margin: 0 0 18px; }
  .lab-title {
    margin-bottom: 16px;
    font-size: clamp(25px, 3vw, 38px);
    line-height: 1.22;
  }
  .lab-sub { margin: 0 0 22px; font-size: 13px; line-height: 1.55; }
  .lab-cta { justify-content: flex-start; }
}

@media (max-width: 719px) {
  /* Preserve the capsule as the mobile hero's unobstructed centrepiece.
     The primary action stays; supporting copy continues immediately below. */
  .lab-card {
    bottom: 68px;
    width: 92vw;
  }
  .lab-rule, .lab-sub, .lab-cta .btn.ghost, .lab-cue { display: none; }
  .lab-eyebrow { margin-bottom: 12px; font-size: 8.5px; }
  .lab-title {
    margin-bottom: 16px;
    font-size: clamp(20px, 6vw, 26px);
    line-height: 1.2;
  }
  .lab-cta { gap: 0; }
}

@media (max-height: 620px) {
  .lab-cue { display: none; }

  /* Short landscape windows give the capsule the centre and move the title
     into a compact editorial rail instead of laying it over the robot. */
  .lab-card {
    left: auto;
    inset-inline-start: var(--gutter);
    bottom: 58px;
    transform: none;
    width: min(430px, 38vw);
    text-align: left;
  }
  .lab-eyebrow { margin-bottom: 9px; font-size: 8.5px; }
  .lab-rule { width: 58px; margin: 0 0 12px; }
  .lab-title { margin-bottom: 11px; font-size: clamp(18px, 2.2vw, 28px); }
  .lab-sub { margin: 0 0 13px; font-size: 11.5px; line-height: 1.45; }
  .lab-cta { justify-content: flex-start; gap: 8px; }
}

@media (max-height: 620px) and (max-width: 719px) {
  .lab-card {
    left: 50%;
    inset-inline-start: auto;
    bottom: 52px;
    transform: translateX(-50%);
    width: 92vw;
    text-align: center;
  }
  .lab-eyebrow, .lab-rule, .lab-sub, .lab-cta { display: none; }
  .lab-title { margin: 0; font-size: clamp(17px, 5vw, 22px); }
}

/* =====================================================================
   DOCUMENT — shared rhythm for the sections below the fold
   ===================================================================== */

/* scroll-staged reveal (site.css .rise is load-staged; these are
   viewport-staged — settled by src/lab.js as each enters). The hidden
   pre-state is gated on html.js so copy is never trapped. */
.io-rise {
  transition:
    opacity 1.1s ease,
    transform 1.1s cubic-bezier(0.16, 0.6, 0.2, 1),
    filter 1.1s ease;
}
html.js .io-rise:not(.is-in) {
  opacity: 0;
  transform: translateY(18px);
  filter: blur(8px);
}
.io-rise.d1 { transition-delay: 0.1s; }
.io-rise.d2 { transition-delay: 0.2s; }
.io-rise.d3 { transition-delay: 0.3s; }

/* section separator that runs the current (S1) once, on arrival */
.rule-x {
  position: relative;
  height: 1px;
  margin: clamp(56px, 9vh, 84px) 0;
  border: 0;
  background: var(--line);
  overflow: hidden;
}
.rule-x::after {
  content: '';
  position: absolute;
  top: 0;
  left: -22%;
  width: 22%;
  height: 100%;
  background: linear-gradient(to right, transparent, #cfd6dd, transparent);
  opacity: 0;
}
.rule-x.is-in::after { animation: ruleCurrent 1.2s cubic-bezier(0.5, 0, 0.3, 1) 1 forwards; }
@keyframes ruleCurrent {
  0% { left: -22%; opacity: 1; }
  100% { left: 102%; opacity: 1; }
}

/* anchor jumps land clear of the fixed top bar */
main section[id] { scroll-margin-top: 78px; }

/* section header — mono label left, quiet spec meta right */
.sec-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 18px;
  margin: 0 0 26px;
}
.sec-head .section-label { margin: 0; }
.sec-head .sec-meta {
  font: 400 9px var(--f-mono);
  letter-spacing: 0.16em;
  color: var(--txt-4);
  white-space: nowrap;
}
@media (max-width: 560px) { .sec-head .sec-meta { display: none; } }

/* lab display line — between page-title and card-title in the scale */
.lab-disp {
  margin: 0 0 22px;
  font-family: var(--f-disp);
  font-weight: 400;
  font-size: clamp(22px, 3.2vw, 40px);
  line-height: 1.3;
  letter-spacing: 0.03em;
  text-wrap: balance;
  background: linear-gradient(to bottom, #ffffff 34%, #8e99a7 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
.lab-disp .dim { -webkit-text-fill-color: #4d5765; }

/* the charter display line sits a step above the standard lab-disp */
.lab-disp.xl {
  font-size: clamp(25px, 4.2vw, 52px);
  margin-bottom: 30px;
}

/* schematic-plate animations idle until their section has entered */
.p-anim, .p-anim::before { animation-play-state: paused; }
.io-rise.is-in .p-anim, .io-rise.is-in .p-anim::before { animation-play-state: running; }

/* =====================================================================
   01 — CHARTER
   ===================================================================== */
.charter-lead { max-width: 64ch; margin: 0 0 46px; }

.laws {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 3vw, 34px);
}
.law {
  border-top: 1px solid var(--line);
  padding: 22px 4px 6px 0;
}
.law .n {
  font: 400 9.5px var(--f-mono);
  letter-spacing: 0.18em;
  color: var(--txt-4);
}
.law .t {
  display: block;
  margin-top: 14px;
  font-family: var(--f-disp);
  font-size: 13.5px;
  letter-spacing: 0.05em;
  color: var(--txt);
}
.law .d {
  display: block;
  margin-top: 11px;
  font-size: 12.5px;
  line-height: 1.68;
  color: var(--txt-3);
}
@media (max-width: 820px) { .laws { grid-template-columns: 1fr; gap: 0; } .law { padding-bottom: 22px; } }

/* =====================================================================
   02 — THE FLOOR (facility schematic)
   ===================================================================== */
.floor-intro { max-width: 56ch; margin: 0 0 36px; }

.floor-wrap {
  display: grid;
  grid-template-columns: minmax(0, 13fr) minmax(0, 7fr);
  gap: clamp(24px, 4vw, 52px);
  align-items: center;
}
@media (max-width: 900px) { .floor-wrap { grid-template-columns: 1fr; } }

.floor-map { position: relative; }
.floor-map svg { display: block; width: 100%; height: auto; }
/* on small screens the drawing stays legible and pans like a blueprint */
@media (max-width: 680px) {
  .floor-map { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .floor-map svg { min-width: 640px; }
}

/* ---- linework ---- */
.floor-map .fm-wall { fill: none; stroke: #333b47; stroke-width: 1.5; }
.floor-map .fm-part { fill: none; stroke: #232a35; stroke-width: 1; }
.floor-map .fm-fill { fill: transparent; transition: fill 0.5s ease; }
.floor-map .fm-hatched { opacity: 0.55; pointer-events: none; }
.floor-map text {
  font: 500 10px var(--f-mono);
  letter-spacing: 0.16em;
  fill: var(--txt-4);
  transition: fill 0.4s ease;
}
.floor-map .fm-name { font-size: 11.5px; fill: var(--txt-3); }
.floor-map .fm-tiny { font-size: 8.5px; letter-spacing: 0.24em; }
.floor-map .fm-here { font-size: 9px; letter-spacing: 0.2em; fill: var(--txt-3); }
.floor-map .fm-glyph * {
  fill: none;
  stroke: #2c3542;
  stroke-width: 1;
  transition: stroke 0.4s ease;
}
.floor-map .fm-glyph .fm-dot { fill: #5f6b79; stroke: none; }
.floor-map .fm-here-dot {
  fill: var(--hi);
  animation: pulse 3.6s ease-in-out infinite;
}
.floor-map .fm-bay { pointer-events: none; }
.floor-map .fm-hit { fill: transparent; pointer-events: all; cursor: pointer; }
.floor-map .fm-hit:focus-visible { outline: none; stroke: #3a4553; stroke-width: 1.5; }

/* sim-array dot lattice is stamped by lab-doc.js into .fm-dots */
.floor-map .fm-dots circle { fill: #2c3542; stroke: none; transition: fill 0.4s ease; }

/* ---- spine current (signature S1) ---- */
.floor-map .fm-rail { stroke: #1b212b; stroke-width: 1; }
.floor-map .fm-current {
  stroke: #cfd6dd;
  stroke-width: 1;
  stroke-dasharray: 7 100;
  stroke-dashoffset: 7;
  animation: fmCurrent 6.5s linear infinite;
}
@keyframes fmCurrent {
  from { stroke-dashoffset: 7; }
  to { stroke-dashoffset: -100; }
}

/* ---- draw-in: linework traces itself as the section arrives ---- */
.fm-draw {
  stroke-dasharray: 1;
  transition: stroke-dashoffset 1.6s cubic-bezier(0.5, 0, 0.3, 1) 0.15s;
}
.fm-part.fm-draw { transition-delay: 0.55s; }
html.js .floor-wrap.io-rise:not(.is-in) .fm-draw { stroke-dashoffset: 1; }
.fm-bay, .fm-spine { transition: opacity 1s ease; }
html.js .floor-wrap.io-rise:not(.is-in) .fm-bay,
html.js .floor-wrap.io-rise:not(.is-in) .fm-spine { opacity: 0; }
.fm-bay.b1 { transition-delay: 0.7s; }
.fm-bay.b2 { transition-delay: 0.8s; }
.fm-bay.b3 { transition-delay: 0.9s; }
.fm-bay.b4 { transition-delay: 1.0s; }
.fm-bay.b5 { transition-delay: 1.1s; }
.fm-bay.b6 { transition-delay: 1.2s; }
.fm-spine { transition-delay: 1.3s; }

/* ---- active bay (data-active on .floor-wrap, set by lab-doc.js) ---- */
.floor-wrap[data-active='b1'] .fm-bay.b1 .fm-fill,
.floor-wrap[data-active='b2'] .fm-bay.b2 .fm-fill,
.floor-wrap[data-active='b3'] .fm-bay.b3 .fm-fill,
.floor-wrap[data-active='b4'] .fm-bay.b4 .fm-fill,
.floor-wrap[data-active='b5'] .fm-bay.b5 .fm-fill,
.floor-wrap[data-active='b6'] .fm-bay.b6 .fm-fill { fill: rgba(237, 241, 245, 0.03); }
.floor-wrap[data-active='b1'] .fm-bay.b1 text,
.floor-wrap[data-active='b2'] .fm-bay.b2 text,
.floor-wrap[data-active='b3'] .fm-bay.b3 text,
.floor-wrap[data-active='b4'] .fm-bay.b4 text,
.floor-wrap[data-active='b5'] .fm-bay.b5 text,
.floor-wrap[data-active='b6'] .fm-bay.b6 text { fill: #8e99a7; }
.floor-wrap[data-active='b1'] .fm-bay.b1 .fm-name,
.floor-wrap[data-active='b2'] .fm-bay.b2 .fm-name,
.floor-wrap[data-active='b3'] .fm-bay.b3 .fm-name,
.floor-wrap[data-active='b4'] .fm-bay.b4 .fm-name,
.floor-wrap[data-active='b5'] .fm-bay.b5 .fm-name,
.floor-wrap[data-active='b6'] .fm-bay.b6 .fm-name { fill: var(--txt); }
.floor-wrap[data-active='b1'] .fm-bay.b1 .fm-glyph *,
.floor-wrap[data-active='b2'] .fm-bay.b2 .fm-glyph *,
.floor-wrap[data-active='b3'] .fm-bay.b3 .fm-glyph *,
.floor-wrap[data-active='b4'] .fm-bay.b4 .fm-glyph *,
.floor-wrap[data-active='b5'] .fm-bay.b5 .fm-glyph *,
.floor-wrap[data-active='b6'] .fm-bay.b6 .fm-glyph * { stroke: #97a1af; }
.floor-wrap[data-active='b1'] .fm-bay.b1 .fm-dots circle { fill: #97a1af; }
.floor-wrap[data-active='b1'] .fm-bay.b1 .fm-glyph .fm-dot,
.floor-wrap[data-active='b2'] .fm-bay.b2 .fm-glyph .fm-dot,
.floor-wrap[data-active='b5'] .fm-bay.b5 .fm-glyph .fm-dot { fill: var(--txt); stroke: none; }

/* ---- bay index ---- */
.bay-index { margin: 0; padding: 0; list-style: none; }
.bay-row {
  position: relative;
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 14px;
  padding: 15px 10px 15px 2px;
  border-top: 1px solid var(--line);
  cursor: pointer;
  outline: none;
  transition: background-color 0.35s ease;
}
.bay-row:last-child { border-bottom: 1px solid var(--line); }
.bay-row::before {
  content: '';
  position: absolute;
  left: 0;
  top: -1px;
  height: 1px;
  width: 0;
  background: linear-gradient(to right, #cfd6dd, transparent);
  transition: width 0.9s cubic-bezier(0.5, 0, 0.3, 1);
}
.bay-row .bn {
  font: 400 9.5px var(--f-mono);
  letter-spacing: 0.18em;
  color: var(--txt-4);
  padding-top: 2px;
  transition: color 0.35s ease;
}
.bay-row .bt {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--f-disp);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  color: var(--txt-2);
  transition: color 0.35s ease;
}
.bay-row .bd {
  display: block;
  margin-top: 7px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--txt-3);
}
.here-chip {
  font: 500 7.5px var(--f-mono);
  font-style: normal;
  letter-spacing: 0.18em;
  color: #aeb6c2;
  border: 1px solid #313a47;
  padding: 3px 7px;
  clip-path: polygon(0 0, calc(100% - 4px) 0, 100% 4px, 100% 100%, 4px 100%, 0 calc(100% - 4px));
}
.floor-wrap[data-active='b1'] .bay-row[data-bay='b1'],
.floor-wrap[data-active='b2'] .bay-row[data-bay='b2'],
.floor-wrap[data-active='b3'] .bay-row[data-bay='b3'],
.floor-wrap[data-active='b4'] .bay-row[data-bay='b4'],
.floor-wrap[data-active='b5'] .bay-row[data-bay='b5'],
.floor-wrap[data-active='b6'] .bay-row[data-bay='b6'] { background: rgba(255, 255, 255, 0.025); }
.floor-wrap[data-active='b1'] .bay-row[data-bay='b1']::before,
.floor-wrap[data-active='b2'] .bay-row[data-bay='b2']::before,
.floor-wrap[data-active='b3'] .bay-row[data-bay='b3']::before,
.floor-wrap[data-active='b4'] .bay-row[data-bay='b4']::before,
.floor-wrap[data-active='b5'] .bay-row[data-bay='b5']::before,
.floor-wrap[data-active='b6'] .bay-row[data-bay='b6']::before { width: 100%; }
.floor-wrap[data-active='b1'] .bay-row[data-bay='b1'] .bt,
.floor-wrap[data-active='b2'] .bay-row[data-bay='b2'] .bt,
.floor-wrap[data-active='b3'] .bay-row[data-bay='b3'] .bt,
.floor-wrap[data-active='b4'] .bay-row[data-bay='b4'] .bt,
.floor-wrap[data-active='b5'] .bay-row[data-bay='b5'] .bt,
.floor-wrap[data-active='b6'] .bay-row[data-bay='b6'] .bt { color: var(--hi); }

/* =====================================================================
   03 — ACTIVE PROGRAMS
   ===================================================================== */
.progs { border-bottom: 1px solid var(--line); }
.prog {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  gap: clamp(26px, 4.5vw, 60px);
  align-items: center;
  padding: clamp(36px, 6vh, 58px) 0;
  border-top: 1px solid var(--line);
}
.prog.flip .plate { order: -1; }

/* ghost numeral — outlined watermark behind the dossier text column */
.prog-num {
  position: absolute;
  top: 10px;
  left: -8px;
  font-family: var(--f-disp);
  font-size: clamp(64px, 9vw, 112px);
  line-height: 1;
  color: rgba(237, 241, 245, 0.04);
  -webkit-text-stroke: 1px rgba(151, 161, 175, 0.11);
  -webkit-text-fill-color: transparent;
  pointer-events: none;
  user-select: none;
}
.prog.flip .prog-num { left: auto; right: -8px; }

.prog-head {
  display: flex;
  align-items: center;
  gap: 16px;
}
.prog-head .pref {
  font: 400 9.5px var(--f-mono);
  letter-spacing: 0.18em;
  color: var(--txt-4);
}
.prog-head .pcycle {
  margin-left: auto;
  font: 400 9px var(--f-mono);
  letter-spacing: 0.18em;
  color: var(--txt-4);
}
.state {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: 500 9px var(--f-mono);
  letter-spacing: 0.18em;
  color: var(--txt-3);
}
.state::before {
  content: '';
  width: 4px;
  height: 4px;
  background: var(--hi);
  box-shadow: 0 0 6px rgba(237, 241, 245, 0.8);
}
.state.live::before { animation: pulse 3.6s ease-in-out infinite; }

.prog-title {
  margin: 20px 0 8px;
  font-family: var(--f-disp);
  font-weight: 400;
  font-size: clamp(19px, 2.6vw, 28px);
  letter-spacing: 0.06em;
  background: linear-gradient(to bottom, #ffffff 34%, #8e99a7 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
.prog-sub {
  display: block;
  margin-bottom: 18px;
  font: 500 9.5px var(--f-mono);
  letter-spacing: 0.22em;
  color: var(--txt-4);
}
.prog-thesis {
  margin: 0;
  max-width: 52ch;
  font-size: 14px;
  line-height: 1.72;
  color: var(--txt-2);
}
.prog-ticks { margin: 20px 0 0; padding: 0; list-style: none; }
.prog-ticks li {
  display: flex;
  gap: 10px;
  padding: 8px 0;
  border-top: 1px solid var(--line);
  font: 400 11px var(--f-mono);
  letter-spacing: 0.06em;
  color: var(--txt-3);
}
.prog-ticks li::before { content: '—'; color: var(--txt-4); }

@media (max-width: 900px) {
  .prog { grid-template-columns: 1fr; gap: 28px; align-items: start; }
  .prog.flip .plate { order: 0; }
  .prog-num, .prog.flip .prog-num { font-size: 56px; top: 24px; left: auto; right: 0; }
}

/* ---- schematic plate — shared frame for the four figures ---- */
.plate {
  position: relative;
  margin: 0;
  border: 1px solid var(--line);
  background-color: #080b10;
  background-image:
    linear-gradient(rgba(27, 33, 43, 0.5) 1px, transparent 1px),
    linear-gradient(90deg, rgba(27, 33, 43, 0.5) 1px, transparent 1px);
  background-size: 28px 28px;
  clip-path: polygon(0 0, calc(100% - var(--cut)) 0, 100% var(--cut), 100% 100%, var(--cut) 100%, 0 calc(100% - var(--cut)));
  padding: 22px 20px 46px;
}
.plate svg { display: block; width: 100%; height: auto; overflow: visible; }
.plate-cap {
  position: absolute;
  left: 20px;
  bottom: 15px;
  font: 500 8.5px var(--f-mono);
  letter-spacing: 0.18em;
  color: var(--txt-4);
}
.plate-ref {
  position: absolute;
  top: 12px;
  right: 18px;
  font: 400 9px var(--f-mono);
  letter-spacing: 0.12em;
  color: var(--txt-4);
}

/* ---- FIG.01 — Sentinel pose solver ---- */
.plate .sk polyline, .plate .sk line, .plate .sk circle {
  fill: none;
  stroke: #8e99a7;
  stroke-width: 1.3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.plate .sk-back polyline { stroke: #3a4553; }
.plate .sk-joints circle { fill: #edf1f5; stroke: none; opacity: 0.22; }
.pl-halo { fill: none; stroke: #2c3542; stroke-width: 1; }
.pl-halo.dim { stroke: #20262f; }
.sk-torso { animation: skBreathe 4.8s ease-in-out infinite alternate; }
@keyframes skBreathe { from { transform: translateY(0); } to { transform: translateY(2.2px); } }
.sk-armF {
  transform-box: fill-box;
  transform-origin: 0% 0%;
  animation: armSway 5.6s ease-in-out infinite alternate;
}
.sk-armB {
  transform-box: fill-box;
  transform-origin: 100% 0%;
  animation: armSway 6.4s ease-in-out infinite alternate-reverse;
}
@keyframes armSway { from { transform: rotate(-2.4deg); } to { transform: rotate(2.6deg); } }
.sk-joints .j1 { animation: jointPulse 3.9s ease-in-out infinite 0s; }
.sk-joints .j2 { animation: jointPulse 3.9s ease-in-out infinite 0.35s; }
.sk-joints .j3 { animation: jointPulse 3.9s ease-in-out infinite 0.7s; }
.sk-joints .j4 { animation: jointPulse 3.9s ease-in-out infinite 1.05s; }
.sk-joints .j5 { animation: jointPulse 3.9s ease-in-out infinite 1.4s; }
.sk-joints .j6 { animation: jointPulse 3.9s ease-in-out infinite 1.75s; }
@keyframes jointPulse { 0%, 55%, 100% { opacity: 0.22; } 8% { opacity: 1; } }
.pl-read text { font: 500 10px var(--f-mono); letter-spacing: 0.08em; fill: var(--txt-4); }
.pl-read tspan[data-osc] { fill: #aeb6c2; }
.pl-scan {
  stroke: rgba(237, 241, 245, 0.13);
  stroke-width: 1;
  animation: plScan 7s cubic-bezier(0.45, 0, 0.4, 1) infinite;
}
@keyframes plScan {
  0% { transform: translateY(16px); opacity: 0; }
  8% { opacity: 1; }
  92% { opacity: 1; }
  100% { transform: translateY(212px); opacity: 0; }
}

/* ---- FIG.02 — Envoy decision trail ---- */
.dt-e { fill: none; stroke: #232a35; stroke-width: 1; }
.dt-e.ret { stroke: #1e2430; stroke-dasharray: 3 4; }
.dt-p {
  fill: none;
  stroke: #edf1f5;
  stroke-width: 1.3;
  stroke-dasharray: 14 100;
  stroke-dashoffset: 14;
  opacity: 0;
  animation: dtPulse 7.2s linear infinite;
}
.dt-p.p1 { animation-delay: 0s; }
.dt-p.p2 { animation-delay: 0.85s; }
.dt-p.p3 { animation-delay: 1.7s; }
.dt-p.p4 { animation-delay: 2.55s; }
.dt-p.p5 { animation-delay: 3.5s; stroke: #8e99a7; }
@keyframes dtPulse {
  0% { stroke-dashoffset: 14; opacity: 0; }
  2% { opacity: 0.95; }
  12% { stroke-dashoffset: -100; opacity: 0.95; }
  12.5%, 100% { stroke-dashoffset: -100; opacity: 0; }
}
.dt-nodes circle { fill: #0a0d12; stroke: #3a4553; stroke-width: 1; animation: dtNode 7.2s linear infinite; }
.dt-nodes .n1 { animation-delay: 0s; }
.dt-nodes .n2 { animation-delay: 0.8s; }
.dt-nodes .n3 { animation-delay: 1.65s; }
.dt-nodes .n4 { animation-delay: 2.5s; }
.dt-nodes .n5 { animation-delay: 3.35s; }
@keyframes dtNode {
  0%, 100% { fill: #0a0d12; stroke: #3a4553; }
  3% { fill: #edf1f5; stroke: #edf1f5; }
  10% { fill: #0a0d12; stroke: #3a4553; }
}
.dt-labels text { font: 500 8.5px var(--f-mono); letter-spacing: 0.14em; fill: #4d5765; }
.dt-labels .dt-ret { font-size: 7.5px; letter-spacing: 0.2em; fill: var(--txt-4); }

/* FIG. 02 (Redline, the hunt) and FIG. 04 (Forge, the training loop)
   are v8 plates — their styles live in the v8 block at the end. */

/* =====================================================================
   04 — LIVE TELEMETRY
   ===================================================================== */
.tele-intro { max-width: 56ch; margin: 0 0 32px; }
.tele { padding: 30px 28px 16px; }
.tele-grid { display: grid; grid-template-columns: repeat(6, 1fr); }
.tile { min-width: 0; padding: 2px 20px 8px; border-left: 1px solid var(--line); }
.tile:first-child { border-left: 0; padding-left: 0; }
.tile .k {
  display: block;
  font: 500 8.5px var(--f-mono);
  letter-spacing: 0.2em;
  color: var(--txt-4);
}
.tile .v {
  display: block;
  margin-top: 12px;
  font: 500 clamp(15px, 1.7vw, 21px) var(--f-mono);
  letter-spacing: 0.04em;
  color: var(--txt);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tile .v .u { font-size: 10px; letter-spacing: 0.12em; color: var(--txt-3); }
.tile .s {
  display: block;
  margin-top: 10px;
  font: 400 8px var(--f-mono);
  letter-spacing: 0.14em;
  color: var(--txt-4);
}
.spark { display: block; margin-top: 10px; width: 100%; max-width: 132px; height: 26px; }
.tele-foot {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  margin-top: 24px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font: 400 8.5px var(--f-mono);
  letter-spacing: 0.16em;
  color: var(--txt-4);
}
#tl-clock { color: var(--txt-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (max-width: 980px) {
  .tele-grid { grid-template-columns: repeat(3, 1fr); }
  .tile:nth-child(3n + 1) { border-left: 0; padding-left: 0; }
  .tile:nth-child(n + 4) { border-top: 1px solid var(--line); margin-top: 16px; padding-top: 14px; }
}
@media (max-width: 560px) {
  .tele { padding: 24px 20px 14px; }
  .tele-grid { grid-template-columns: repeat(2, 1fr); }
  .tile { border-left: 0; padding-left: 0; }
  .tile:nth-child(even) { border-left: 1px solid var(--line); padding-left: 18px; }
  .tile:nth-child(n + 3) { border-top: 1px solid var(--line); margin-top: 14px; padding-top: 14px; }
  .tele-foot { flex-direction: column; gap: 8px; }
}

/* =====================================================================
   05 — THE RECORD (two ledgers)
   ===================================================================== */
.record-intro { max-width: 56ch; margin: 0 0 32px; }
.record-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  align-items: start;
}
@media (max-width: 900px) { .record-grid { grid-template-columns: 1fr; } }
.ledger { padding: 24px 26px 12px; }
.ledger-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}
.ledger-head .lt {
  font-family: var(--f-disp);
  font-size: 12.5px;
  letter-spacing: 0.14em;
  color: var(--txt);
}
.ledger-head .ls {
  font: 400 8px var(--f-mono);
  letter-spacing: 0.16em;
  color: var(--txt-4);
  text-align: right;
}
.led-row {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: 16px;
  padding: 16px 0;
  border-bottom: 1px solid var(--line);
  transition: background-color 0.2s ease;
}
.led-row:last-child { border-bottom: 0; }
.led-row:hover { background: rgba(255, 255, 255, 0.02); }
.led-row .lr {
  font: 400 9px var(--f-mono);
  letter-spacing: 0.12em;
  color: var(--txt-3);
  line-height: 1.5;
}
.led-row .lr i {
  display: block;
  margin-top: 3px;
  font-style: normal;
  color: var(--txt-4);
}
.led-row p { margin: 0; font-size: 13px; line-height: 1.65; color: var(--txt-2); }

/* =====================================================================
   06 — THE DOOR
   ===================================================================== */
/* site.css only stamps refs on cards — panels here carry them too */
.panel > .ref {
  position: absolute;
  top: 12px;
  right: 20px;
  font: 400 9px var(--f-mono);
  letter-spacing: 0.12em;
  color: var(--txt-4);
}
.lab-close {
  padding: clamp(44px, 7vh, 72px) clamp(24px, 6vw, 72px);
  text-align: center;
}
.lab-close .lab-disp { margin-bottom: 16px; }
.lab-close .lead {
  max-width: 52ch;
  margin: 0 auto 8px;
}
.lab-close .cta-row {
  justify-content: center;
  margin-top: 34px;
}
.lab-close .foot-note {
  display: block;
  margin-top: 30px;
  font: 400 9px var(--f-mono);
  letter-spacing: 0.2em;
  color: var(--txt-4);
}
/* the lit seam — a thin echo of the gate on the contact page */
.door-seam {
  display: block;
  width: 1px;
  height: 44px;
  margin: 4px auto 26px;
  background: linear-gradient(to bottom, transparent, #edf1f5, transparent);
  box-shadow: 0 0 12px rgba(237, 241, 245, 0.35);
  animation: pulse 3.6s ease-in-out infinite;
}

/* =====================================================================
   ===== v8 ============================================================
   Two new schematic plates for 03 · ACTIVE PROGRAMS:
     FIG. 02 — Redline · THE HUNT, ONE SORTIE   (hk-)
     FIG. 04 — Forge   · THE TRAINING LOOP      (tr-)
   CSS-only. Structural linework draws in via io-rise transitions
   (the fm-draw pattern, so no-JS shows every figure settled); loop
   transients carry .p-anim and idle until their article enters. Both
   loops run a 14 s master cycle, phase-locked by animation-delay.
   Damped throughout, nothing bouncy.
   ===================================================================== */

/* ---- FIG. 02 — the hunt, one sortie -------------------------------
   Master cycle (14 s): crews fan out ~2 s · probes travel ~3–6 s ·
   flash ~6.2 s · walk-back + seal ~6.6–8.4 s · stamp ~8.1 s ·
   hold to ~12.3 s · reset. Structural linework rides the io-rise
   gate as a transition (fm-draw pattern), so no-JS shows it drawn. */

/* the codebase — an irregular, tree-ish lattice, drawn in by depth */
.hk-lat line { stroke: #232a35; stroke-width: 1; }
.hk-e {
  stroke-dasharray: 1;
  transition: stroke-dashoffset 1.4s cubic-bezier(0.5, 0, 0.3, 1) 0.15s;
}
.hk-e.e2 { transition-delay: 0.5s; }
.hk-e.e3 { transition-delay: 0.85s; }
.hk-e.e4 { transition-delay: 1.2s; }
html.js .prog.io-rise:not(.is-in) .hk-e { stroke-dashoffset: 1; }
.hk-nodes circle { fill: #2c3542; }
.hk-n { transition: opacity 0.9s ease 0.55s; }
.hk-n.n2 { transition-delay: 0.9s; }
.hk-n.n3 { transition-delay: 1.25s; }
.hk-n.n4 { transition-delay: 1.6s; }
html.js .prog.io-rise:not(.is-in) .hk-n { opacity: 0; }

/* crew base */
.hk-base { transition: opacity 1.2s ease 0.1s; }
html.js .prog.io-rise:not(.is-in) .hk-base { opacity: 0; }
.hk-base circle { fill: none; stroke: #5f6b79; stroke-width: 1; }
.hk-base .hk-bdot { fill: #8e99a7; stroke: none; }
.hk-base text { font: 500 8.5px var(--f-mono); letter-spacing: 0.16em; fill: #4d5765; }

/* three crews (tiny triangles) fan out along their first edges */
.hk-agent path { fill: #cfd6dd; stroke: none; }
.hk-agent {
  opacity: 0;
  animation: hkFan 14s cubic-bezier(0.35, 0, 0.25, 1) infinite;
}
.hk-agent.a1 { --dx: 55px; --dy: -48px; animation-delay: 2s; }
.hk-agent.a2 { --dx: 62px; --dy: 4px; animation-delay: 2.9s; }
.hk-agent.a3 { --dx: 49px; --dy: 51px; animation-delay: 3.8s; }
@keyframes hkFan {
  0% { transform: translate(0, 0); opacity: 0; }
  2% { opacity: 0.9; }
  11% { transform: translate(var(--dx), var(--dy)); opacity: 0.9; }
  16%, 100% { transform: translate(var(--dx), var(--dy)); opacity: 0; }
}

/* probes — each crew's pulse works its route; only a2 connects */
.hk-p {
  fill: none;
  stroke: #8e99a7;
  stroke-width: 1.2;
  stroke-dasharray: 14 100;
  stroke-dashoffset: 14;
  opacity: 0;
  animation: hkProbe 14s linear infinite;
}
.hk-p.a1 { animation-delay: 3.1s; }
.hk-p.a2 { animation-delay: 4s; }
.hk-p.a3 { animation-delay: 4.9s; }
@keyframes hkProbe {
  0% { stroke-dashoffset: 14; opacity: 0; }
  2% { opacity: 0.85; }
  16% { stroke-dashoffset: -100; opacity: 0.85; }
  18%, 100% { stroke-dashoffset: -100; opacity: 0; }
}

/* the flawed node flashes when crew a2 reaches it */
.hk-f {
  fill: #2c3542;
  opacity: 0.6;
  transform-box: fill-box;
  transform-origin: center;
  animation: hkFlash 14s ease-in-out infinite;
}
@keyframes hkFlash {
  0%, 42% { fill: #2c3542; opacity: 0.6; transform: scale(1); }
  44.5% { fill: #edf1f5; opacity: 1; transform: scale(1.9); }
  50% { fill: #edf1f5; opacity: 0.95; transform: scale(1.15); }
  88% { fill: #edf1f5; opacity: 0.95; transform: scale(1.15); }
  94%, 100% { fill: #2c3542; opacity: 0.6; transform: scale(1); }
}

/* the path back to base re-draws, brighter */
.hk-ret {
  fill: none;
  stroke: #cfd6dd;
  stroke-width: 1.2;
  stroke-dasharray: 100 100;
  stroke-dashoffset: 100;
  opacity: 0;
  animation: hkRet 14s cubic-bezier(0.4, 0, 0.3, 1) infinite;
}
@keyframes hkRet {
  0%, 47% { stroke-dashoffset: 100; opacity: 0; }
  49% { opacity: 0.9; }
  60% { stroke-dashoffset: 0; opacity: 0.9; }
  87% { stroke-dashoffset: 0; opacity: 0.9; }
  93%, 100% { stroke-dashoffset: 0; opacity: 0; }
}

/* seal ring closes around the find */
.hk-seal {
  fill: none;
  stroke: #edf1f5;
  stroke-width: 1.2;
  stroke-dasharray: 100 100;
  stroke-dashoffset: 100;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  transform: rotate(-90deg);
  animation: hkSeal 14s linear infinite;
}
@keyframes hkSeal {
  0%, 45% { stroke-dashoffset: 100; opacity: 0; }
  47% { opacity: 1; }
  55% { stroke-dashoffset: 0; opacity: 1; }
  88% { stroke-dashoffset: 0; opacity: 1; }
  94%, 100% { stroke-dashoffset: 0; opacity: 0; }
}

/* the finding stamps in, filed */
.hk-stamp { opacity: 0; transform: translateY(4px); animation: hkStamp 14s cubic-bezier(0.3, 0, 0.2, 1) infinite; }
.hk-stamp path { fill: rgba(237, 241, 245, 0.03); stroke: #3a4553; stroke-width: 1; }
.hk-stamp text { font: 500 7.5px var(--f-mono); letter-spacing: 0.14em; fill: #aeb6c2; }
@keyframes hkStamp {
  0%, 58% { opacity: 0; transform: translateY(4px); }
  64% { opacity: 1; transform: translateY(0); }
  88% { opacity: 1; transform: translateY(0); }
  94%, 100% { opacity: 0; transform: translateY(4px); }
}

/* ---- FIG. 04 — the training loop -----------------------------------
   Master cycle (14 s, delay 2.4 s after the ring draws): pulse orbits
   DATA → TRAIN → EVAL → SHIP · loss re-draws on the TRAIN→EVAL leg ·
   MODEL grows past HUMAN on the EVAL→SHIP leg, settles, resets.
   Ring, axes, baseline and bars are structural — io-rise transitions. */

/* the ring — drawn once, then a slow pulse orbits it */
.tr-ring {
  fill: none;
  stroke: #232a35;
  stroke-width: 1;
  stroke-dasharray: 1;
  transition: stroke-dashoffset 2.2s cubic-bezier(0.5, 0, 0.3, 1) 0.2s;
}
html.js .prog.io-rise:not(.is-in) .tr-ring { stroke-dashoffset: 1; }
.tr-pulse {
  fill: none;
  stroke: #cfd6dd;
  stroke-width: 1.2;
  stroke-dasharray: 10 90;
  stroke-dashoffset: 5;
  transition: opacity 1.2s ease 2.4s;
  animation: trOrbit 14s linear 2.4s infinite;
}
html.js .prog.io-rise:not(.is-in) .tr-pulse { opacity: 0; }
@keyframes trOrbit {
  from { stroke-dashoffset: 5; }
  to { stroke-dashoffset: -95; }
}

/* stations brighten as the pulse passes */
.tr-n {
  fill: #0a0d12;
  stroke: #3a4553;
  stroke-width: 1;
  transition: opacity 0.8s ease 0.4s;
  animation: trNode 14s linear 2.4s infinite;
}
.tr-n.n2 { transition-delay: 0.9s; animation-delay: 5.9s; }
.tr-n.n3 { transition-delay: 1.4s; animation-delay: 9.4s; }
.tr-n.n4 { transition-delay: 1.9s; animation-delay: 12.9s; }
html.js .prog.io-rise:not(.is-in) .tr-n { opacity: 0; }
@keyframes trNode {
  0% { fill: #0a0d12; stroke: #3a4553; }
  2% { fill: #edf1f5; stroke: #edf1f5; }
  8%, 100% { fill: #0a0d12; stroke: #3a4553; }
}
.tr-labels text { font: 500 8.5px var(--f-mono); letter-spacing: 0.14em; fill: #4d5765; }

/* inside the ring — the loss comes down, every cycle */
.tr-ax {
  fill: none;
  stroke: #2c3542;
  stroke-width: 1;
  stroke-dasharray: 1;
  transition: stroke-dashoffset 1.6s cubic-bezier(0.5, 0, 0.3, 1) 1s;
}
html.js .prog.io-rise:not(.is-in) .tr-ax { stroke-dashoffset: 1; }
.tr-tiny { font: 500 7px var(--f-mono); letter-spacing: 0.2em; fill: #4d5765; }
.tr-loss {
  fill: none;
  stroke: #aeb6c2;
  stroke-width: 1.2;
  stroke-dasharray: 100 100;
  stroke-dashoffset: 100;
  opacity: 0;
  animation: trLoss 14s cubic-bezier(0.4, 0, 0.3, 1) 2.4s infinite;
}
@keyframes trLoss {
  0%, 25% { stroke-dashoffset: 100; opacity: 0; }
  27% { opacity: 0.9; }
  46% { stroke-dashoffset: 0; opacity: 0.9; }
  90% { stroke-dashoffset: 0; opacity: 0.9; }
  97%, 100% { stroke-dashoffset: 0; opacity: 0; }
}

/* the judgement — model vs trained intuition, monthly */
.tr-basel {
  stroke: #232a35;
  stroke-width: 1;
  stroke-dasharray: 1;
  transition: stroke-dashoffset 1.4s cubic-bezier(0.5, 0, 0.3, 1) 1.3s;
}
html.js .prog.io-rise:not(.is-in) .tr-basel { stroke-dashoffset: 1; }
.tr-refline {
  stroke: #2c3542;
  stroke-width: 1;
  stroke-dasharray: 3 4;
  transition: opacity 1s ease 1.9s;
}
html.js .prog.io-rise:not(.is-in) .tr-refline { opacity: 0; }
.tr-bar { transform-box: fill-box; transform-origin: 50% 100%; }
.tr-human {
  fill: rgba(237, 241, 245, 0.05);
  stroke: #5f6b79;
  stroke-width: 1;
  transition: transform 1.6s cubic-bezier(0.3, 0, 0.2, 1) 1.7s;
}
html.js .prog.io-rise:not(.is-in) .tr-human { transform: scaleY(0); }
.tr-model {
  fill: rgba(237, 241, 245, 0.1);
  stroke: #aeb6c2;
  stroke-width: 1;
  transform: scaleY(0.4);
  animation: trModel 14s cubic-bezier(0.3, 0, 0.2, 1) 2.4s infinite;
}
@keyframes trModel {
  0%, 50% { transform: scaleY(0.4); }
  68% { transform: scaleY(1); }
  94% { transform: scaleY(1); }
  100% { transform: scaleY(0.4); }
}
.tr-blabel { font: 500 8px var(--f-mono); letter-spacing: 0.16em; fill: #4d5765; }

/* =====================================================================
   ===== v8.1 ==========================================================
   02 · THE FLOOR — the living floor. Six per-bay micro-systems, each
   nested inside its .fm-bay group (so it inherits the reveal fade and
   the data-active focus brightening), plus root-level spine traffic:
   ghost stubs docking each bay to the rail and one 20 s choreographed
   loop — MDL packet B2→B5 · probe attack B3→B5 · seal tick · the
   graduate exits through the GRADUATES → wall port · a faint SCN
   packet feeds B1→B2. CSS only (opacity / transform / dash-offset);
   the existing fm-current keeps running underneath as the carrier hum.
   Every traffic element runs a 20 s cycle and all of them resume
   together when .is-in lands on .floor-wrap, so the clock stays locked.
   ===================================================================== */

/* ---- B1 · SIM ARRAY — the lattice computes in slow ripple waves ---- */
.floor-map .fm-dots circle {
  opacity: 0.4;
  animation: fmRipple 5s ease-in-out infinite;
}
.floor-map .fm-dots circle:nth-child(5n + 2) { animation-delay: 0.35s; }
.floor-map .fm-dots circle:nth-child(5n + 3) { animation-delay: 0.7s; }
.floor-map .fm-dots circle:nth-child(5n + 4) { animation-delay: 1.05s; }
.floor-map .fm-dots circle:nth-child(5n + 5) { animation-delay: 1.4s; }
@keyframes fmRipple {
  0%, 55%, 100% { opacity: 0.4; }
  12% { opacity: 1; }
}

/* one row completes per ripple pass — a tick flashes and holds; the
   board wipes clean at the end of the 20 s master cycle */
.floor-map .fm-b1-tick { fill: none; stroke: #5f6b79; stroke-width: 1.2; opacity: 0; animation: fmB1T1 20s linear infinite; }
.floor-map .fm-b1-tick.t2 { animation-name: fmB1T2; }
.floor-map .fm-b1-tick.t3 { animation-name: fmB1T3; }
.floor-map .fm-b1-tick.t4 { animation-name: fmB1T4; }
@keyframes fmB1T1 { 0%, 6% { opacity: 0; } 9% { opacity: 1; } 13% { opacity: 0.55; } 95% { opacity: 0.55; } 99%, 100% { opacity: 0; } }
@keyframes fmB1T2 { 0%, 31% { opacity: 0; } 34% { opacity: 1; } 38% { opacity: 0.55; } 95% { opacity: 0.55; } 99%, 100% { opacity: 0; } }
@keyframes fmB1T3 { 0%, 56% { opacity: 0; } 59% { opacity: 1; } 63% { opacity: 0.55; } 95% { opacity: 0.55; } 99%, 100% { opacity: 0; } }
@keyframes fmB1T4 { 0%, 81% { opacity: 0; } 84% { opacity: 1; } 88% { opacity: 0.55; } 95% { opacity: 0.55; } 99%, 100% { opacity: 0; } }

/* ---- B2 · MODEL FORGE — training arc orbits, loss steps downward --- */
.floor-map .fm-b2-arc {
  fill: none;
  stroke: #5f6b79;
  stroke-width: 1;
  stroke-dasharray: 16 84;
  opacity: 0.85;
  animation: fmB2Arc 9s linear infinite;
}
@keyframes fmB2Arc {
  from { stroke-dashoffset: 0; }
  to { stroke-dashoffset: -100; }
}
.floor-map .fm-b2-loss { stroke: #8e99a7; stroke-width: 1.2; opacity: 0; animation: fmB2Loss 6.5s linear infinite; }
.floor-map .fm-b2-loss.l2 { animation-delay: 0.55s; }
.floor-map .fm-b2-loss.l3 { animation-delay: 1.1s; }
@keyframes fmB2Loss {
  0%, 5% { opacity: 0; }
  12% { opacity: 0.95; }
  55% { opacity: 0.5; }
  80%, 100% { opacity: 0; }
}

/* ---- B3 · RED RANGE — sweep beam + probes aimed at the spine ------- */
.floor-map .fm-b3-sweep {
  transform-box: view-box;
  transform-origin: 760px 155px;
  animation: fmB3Sweep 11s linear infinite;
}
.floor-map .fm-b3-sweep line { stroke: #3a4553; }
@keyframes fmB3Sweep {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
/* two fires per master cycle (t≈5.1 s and t≈6.6 s) — the spine tracers
   pick the attack up moments later */
.floor-map .fm-b3-tracer {
  fill: none;
  stroke: #dfe5ec;
  stroke-width: 1.1;
  stroke-dasharray: 18 100;
  stroke-dashoffset: 18;
  opacity: 0;
  animation: fmB3Tracer 20s linear infinite;
}
@keyframes fmB3Tracer {
  0%, 25.5% { stroke-dashoffset: 18; opacity: 0; }
  26.5% { opacity: 0.9; }
  30% { stroke-dashoffset: -100; opacity: 0.9; }
  30.5% { stroke-dashoffset: -100; opacity: 0; }
  32.9% { stroke-dashoffset: -100; opacity: 0; }
  33% { stroke-dashoffset: 18; opacity: 0; }
  34% { opacity: 0.9; }
  37.5% { stroke-dashoffset: -100; opacity: 0.9; }
  38%, 100% { stroke-dashoffset: -100; opacity: 0; }
}

/* ---- B4 · FAB — the machine turns, feeds pulse, parts pile up ------ */
.floor-map .fm-b4-rot {
  transform-box: view-box;
  transform-origin: 146px 432px;
  animation: fmB4Rot 8s linear infinite;
}
@keyframes fmB4Rot {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
.floor-map .fm-b4-feed {
  fill: none;
  stroke: #cfd6dd;
  stroke-width: 1;
  stroke-dasharray: 45 100;
  stroke-dashoffset: 45;
  opacity: 0.9;
  animation: fmB4Feed 2.8s linear infinite;
}
.floor-map .fm-b4-feed.f2 { animation-delay: 0.9s; }
.floor-map .fm-b4-feed.f3 { animation-delay: 1.8s; }
@keyframes fmB4Feed {
  from { stroke-dashoffset: 45; }
  to { stroke-dashoffset: -100; }
}
.floor-map .fm-b4-part {
  fill: #10141b;
  stroke: #8e99a7;
  stroke-width: 1;
  opacity: 0;
  animation: fmB4Part 6.6s cubic-bezier(0.16, 0.6, 0.2, 1) infinite;
}
@keyframes fmB4Part {
  0%, 8% { transform: translate(0, 0); opacity: 0; }
  14% { transform: translate(2px, 0); opacity: 0.9; }
  52% { transform: translate(34px, 0); opacity: 0.9; }
  74% { transform: translate(34px, 0); opacity: 0.9; }
  84%, 100% { transform: translate(34px, 0); opacity: 0; }
}

/* ---- B5 · TEST BAY — sonar ping + the traffic flashes -------------- */
.floor-map .fm-b5-sonar {
  fill: none;
  stroke: #8e99a7;
  stroke-width: 1;
  transform-box: view-box;
  transform-origin: 460px 430px;
  opacity: 0;
  animation: fmB5Sonar 5s cubic-bezier(0.16, 0.6, 0.2, 1) infinite;
}
@keyframes fmB5Sonar {
  0% { transform: scale(0.5); opacity: 0; }
  10% { opacity: 0.65; }
  70%, 100% { transform: scale(3.2); opacity: 0; }
}
/* the outer ring lights when the MDL packet docks (15%), twice as the
   probes hit (39% / 44%) */
.floor-map .fm-b5-flash {
  fill: none;
  stroke: #dfe5ec;
  stroke-width: 1.2;
  opacity: 0;
  animation: fmB5Flash 20s linear infinite;
}
@keyframes fmB5Flash {
  0%, 14.8% { opacity: 0; }
  15.8% { opacity: 0.9; }
  18.5% { opacity: 0; }
  37.8% { opacity: 0; }
  39% { opacity: 0.85; }
  41% { opacity: 0; }
  42.5% { opacity: 0; }
  43.8% { opacity: 0.85; }
  46%, 100% { opacity: 0; }
}
.floor-map .fm-b5-seal {
  fill: none;
  stroke: #edf1f5;
  stroke-width: 1.2;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: fmB5Seal 20s cubic-bezier(0.16, 0.6, 0.2, 1) infinite;
}
@keyframes fmB5Seal {
  0%, 47% { opacity: 0; transform: scale(0.4); }
  50% { opacity: 1; transform: scale(1); }
  71% { opacity: 1; transform: scale(1); }
  75%, 100% { opacity: 0; transform: scale(1); }
}

/* ---- B6 · SEALED WING — presence behind the hatch, one slow wink --- */
.floor-map .fm-b6-p {
  fill: #5f6b79;
  stroke: none;
  opacity: 0.14;
  animation: fmB6Drift1 26s ease-in-out infinite alternate;
}
.floor-map .fm-b6-p.p2 { opacity: 0.11; animation-name: fmB6Drift2; animation-duration: 31s; }
@keyframes fmB6Drift1 {
  from { transform: translate(0, 0); }
  to { transform: translate(26px, 12px); }
}
@keyframes fmB6Drift2 {
  from { transform: translate(0, 0); }
  to { transform: translate(-22px, -9px); }
}
.floor-map .fm-b6-na { animation: fmB6Na 20s ease-in-out infinite; }
@keyframes fmB6Na {
  0%, 88% { opacity: 1; }
  92% { opacity: 0.15; }
  96%, 100% { opacity: 1; }
}

/* ---- SPINE TRAFFIC — root level, capped, never fights a focused bay - */
.floor-map .fm-traffic {
  opacity: 0.8;
  pointer-events: none;
  transition: opacity 1s ease 1.45s;
}
html.js .floor-wrap.io-rise:not(.is-in) .fm-traffic { opacity: 0; }
.floor-map .fm-stub { fill: none; stroke: #1b212b; stroke-width: 1; }
.floor-map .fm-stub-port { fill: #232a35; stroke: none; }
.floor-map .fm-port line { stroke: #333b47; stroke-width: 1.5; }
.floor-map .fm-grad-label { font-size: 9px; letter-spacing: 0.2em; fill: var(--txt-4); }

/* packets — a small labelled slug that walks its route corner by corner */
.floor-map .fm-tp rect { fill: #0a0d12; stroke: #8e99a7; stroke-width: 1; }
.floor-map .fm-tp text { font-size: 9px; letter-spacing: 0.16em; fill: #7f8996; }
.floor-map .fm-tp-grad rect { fill: #dfe5ec; stroke: #edf1f5; }

/* t 0–3 s · MDL leaves the forge, rides the spine, docks at the stand */
.floor-map .fm-tp-mdl { opacity: 0; animation: fmTpMdl 20s linear infinite; }
@keyframes fmTpMdl {
  0% { transform: translate(430px, 250px); opacity: 0; }
  1.5% { opacity: 1; }
  5% { transform: translate(430px, 280px); }
  12% { transform: translate(490px, 280px); }
  15% { transform: translate(490px, 310px); opacity: 1; }
  17%, 100% { transform: translate(490px, 310px); opacity: 0; }
}
/* t 6–8.7 s · two probe tracers ride B3's stub into the test bay */
.floor-map .fm-tp-probe {
  fill: none;
  stroke: #dfe5ec;
  stroke-width: 1.2;
  stroke-dasharray: 8 100;
  stroke-dashoffset: 8;
  opacity: 0;
  animation: fmTpProbe 20s linear infinite;
}
.floor-map .fm-tp-probe.pr2 { animation-delay: 0.9s; }
@keyframes fmTpProbe {
  0%, 29.9% { stroke-dashoffset: 8; opacity: 0; }
  30.7% { opacity: 0.9; }
  39% { stroke-dashoffset: -100; opacity: 0.9; }
  39.6%, 100% { stroke-dashoffset: -100; opacity: 0; }
}
/* t 11–14 s · the packet leaves brighter and exits the wall port */
.floor-map .fm-tp-grad { opacity: 0; animation: fmTpGrad 20s linear infinite; }
@keyframes fmTpGrad {
  0%, 54.9% { transform: translate(490px, 310px); opacity: 0; }
  56% { opacity: 1; }
  57.5% { transform: translate(490px, 280px); }
  69% { transform: translate(898px, 280px); opacity: 1; }
  70.5%, 100% { transform: translate(898px, 280px); opacity: 0; }
}
.floor-map .fm-tp-exitflash {
  fill: none;
  stroke: #edf1f5;
  stroke-width: 1.5;
  opacity: 0;
  animation: fmTpExit 20s linear infinite;
}
@keyframes fmTpExit {
  0%, 68.4% { opacity: 0; }
  69.6% { opacity: 1; }
  73%, 100% { opacity: 0; }
}
/* t 14.4–18 s · a faint SCN packet feeds the forge from the sim array */
.floor-map .fm-tp-scn { opacity: 0; animation: fmTpScn 20s linear infinite; }
.floor-map .fm-tp-scn rect { stroke: #5f6b79; }
.floor-map .fm-tp-scn text { fill: #545f6d; }
@keyframes fmTpScn {
  0%, 71.9% { transform: translate(170px, 250px); opacity: 0; }
  73% { opacity: 0.55; }
  76% { transform: translate(170px, 280px); }
  86% { transform: translate(430px, 280px); }
  88.5% { transform: translate(430px, 250px); opacity: 0.55; }
  90.5%, 100% { transform: translate(430px, 250px); opacity: 0; }
}

/* gate — the whole floor idles until .is-in arrives, then every loop
   resumes on the same clock (declared last so it beats the shorthands) */
.floor-map .p-anim,
.floor-map .fm-dots circle { animation-play-state: paused; }
.floor-wrap.is-in .p-anim,
.floor-wrap.is-in .fm-dots circle { animation-play-state: running; }

/* =====================================================================
   Reduced motion — everything settles instantly
   ===================================================================== */
@media (prefers-reduced-motion: reduce) {
  .lab-card > *, .io-rise, .fm-draw, .fm-bay, .fm-spine,
  .bay-row::before, .hk-e, .hk-n, .hk-base, .tr-ring, .tr-pulse,
  .tr-n, .tr-ax, .tr-basel, .tr-refline, .tr-human { transition: none; }
  .lab-cue { display: none; }
  .rule-x.is-in::after { animation: none; }
  .lab-stage { transform: none !important; opacity: 1 !important; }
  .p-anim, .p-anim::before, .fm-current, .fm-here-dot, .door-seam,
  .sk-torso, .sk-armF, .sk-armB, .sk-joints circle, .pl-scan,
  .dt-p, .dt-nodes circle { animation: none !important; }
  .pl-scan { opacity: 0; }
  .dt-p { opacity: 0; }
  /* v8 — FIG. 02 freezes at the filed finding, FIG. 04 at the shipped
     model: loops off, transients resolved to their verdict state */
  .hk-agent, .hk-p, .hk-f, .hk-ret, .hk-seal, .hk-stamp,
  .tr-pulse, .tr-n, .tr-loss, .tr-model { animation: none !important; }
  .hk-agent, .hk-p { opacity: 0; }
  .hk-f { fill: #edf1f5; opacity: 0.95; }
  .hk-ret { stroke-dashoffset: 0; opacity: 0.6; }
  .hk-seal { stroke-dashoffset: 0; opacity: 1; }
  .hk-stamp { opacity: 1; transform: none; }
  .tr-pulse { opacity: 0; }
  .tr-loss { stroke-dashoffset: 0; opacity: 0.9; }
  .tr-model { transform: scaleY(1); }
}

/* =====================================================================
   Reduced motion — v8.1 · the floor freezes at its most legible state:
   lattice fully lit, row board complete, training arc and sweep parked,
   machine still, seal tick shown, packets / probes / flashes hidden,
   stubs and the GRADUATES → port and label stay visible
   ===================================================================== */
/* 06 · THE FOUNDER ---------------------------------------------------- */
.founder-panel {
  position: relative;
  padding: clamp(36px, 6vh, 60px) clamp(24px, 5vw, 64px);
}
.founder-panel .lab-disp { margin: 0 0 16px; }
.founder-panel .lead { max-width: 640px; margin: 0; }
.founder-sig {
  margin: 26px 0 0;
  font: 500 10px var(--f-mono, monospace);
  letter-spacing: 0.22em;
  color: var(--txt-3, #7f8996);
}

@media (prefers-reduced-motion: reduce) {
  .floor-map .fm-dots circle,
  .floor-map .fm-b1-tick,
  .floor-map .fm-b2-arc,
  .floor-map .fm-b2-loss,
  .floor-map .fm-b3-sweep,
  .floor-map .fm-b3-tracer,
  .floor-map .fm-b4-rot,
  .floor-map .fm-b4-feed,
  .floor-map .fm-b4-part,
  .floor-map .fm-b5-sonar,
  .floor-map .fm-b5-flash,
  .floor-map .fm-b5-seal,
  .floor-map .fm-b6-p,
  .floor-map .fm-b6-na,
  .floor-map .fm-tp-mdl,
  .floor-map .fm-tp-scn,
  .floor-map .fm-tp-grad,
  .floor-map .fm-tp-probe,
  .floor-map .fm-tp-exitflash { animation: none !important; }
  .floor-map .fm-dots circle { opacity: 1; }
  .floor-map .fm-b1-tick { opacity: 0.55; }
  .floor-map .fm-b2-loss { opacity: 0.6; }
  .floor-map .fm-b3-tracer,
  .floor-map .fm-b4-feed,
  .floor-map .fm-b4-part,
  .floor-map .fm-b5-sonar,
  .floor-map .fm-b5-flash,
  .floor-map .fm-tp-mdl,
  .floor-map .fm-tp-scn,
  .floor-map .fm-tp-grad,
  .floor-map .fm-tp-probe,
  .floor-map .fm-tp-exitflash { opacity: 0; }
  .floor-map .fm-b5-seal { opacity: 0.8; transform: none; }
  .floor-map .fm-b6-p { transform: none; }
  .floor-map .fm-b6-na { opacity: 1; }
  .floor-map .fm-traffic { transition: none; }
}
