/* V3 type system: Manrope for headings, Inter for body and UI. Self-hosted
   variable fonts — one file per family and subset carries every weight the
   site uses (the per-weight files were byte-identical copies of these). */

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/assets/fonts/manrope-latin.a30ddcd349.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/assets/fonts/manrope-latin-ext.3911b66d9f.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/inter-latin.3100e775e8.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/inter-latin-ext.34b9c504ca.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ============================================================
   Caretech AI — V3
   Hybrid editorial system: dark cinematic movements against light
   narrative ones. Every colour pair below was measured before use —
   see evidence/source-truth/PALETTE-CONSTRAINTS.md.
   ============================================================ */

:root {
  /* Darks */
  --obsidian: #061722;
  --atlantic: #0A2438;
  --deep-ocean: #0B3545;
  --clinical-teal: #0B7168;

  /* Intelligence */
  --intel-teal: #11998D;
  --signal-aqua: #32CBBE;
  --cobalt: #356FE6;

  /* Micro-accent. Measured 2.28:1 on white — dark surfaces only. */
  --gold: #C9A765;

  /* Light surfaces */
  --cloud: #F7FAFC;
  --ice: #EDF4F6;
  --surface: #FFFFFF;

  /* Text */
  --ink: #12212D;
  --slate: #536675;
  /* Slate at 5.35–5.96 is the floor for body copy on light. Nothing lighter. */

  /* On dark */
  --on-dark: #F7FAFC;
  --on-dark-2: #B9C7D2;   /* 10.6:1 on obsidian */
  --on-dark-3: #8FA3B4;   /* 6.4:1 — captions only */

  /* Lines. Surface fills are near-identical (1.048), so structure comes from
     rules and inversion, never from fill alone. */
  --line: rgba(18, 33, 45, 0.12);
  --line-strong: rgba(18, 33, 45, 0.22);
  --line-dark: rgba(247, 250, 252, 0.16);
  --line-dark-strong: rgba(247, 250, 252, 0.28);

  /* 8px system */
  --s1: 8px; --s2: 16px; --s3: 24px; --s4: 32px; --s5: 48px;
  --s6: 64px; --s7: 96px; --s8: 128px; --s9: 176px;

  --pad: clamp(64px, 8vw, 176px);
  --gutter: clamp(20px, 4vw, 56px);
  --canvas: min(92vw, 1760px);
  --measure: 68ch;

  --font-display: "Manrope", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-body: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --r1: 6px; --r2: 12px; --r3: 20px;

  --lift-1: 0 1px 2px rgba(6, 23, 34, 0.06), 0 4px 12px rgba(6, 23, 34, 0.05);
  --lift-2: 0 2px 6px rgba(6, 23, 34, 0.08), 0 18px 40px -12px rgba(6, 23, 34, 0.16);
}

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

html {
  scroll-behavior: smooth;
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--slate);
  font-family: var(--font-body);
  font-size: 1.0625rem; /* 17px at the 16px root; rides the ultrawide root steps (D2) */
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img, svg, video, canvas { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--cobalt); color: #fff; }

/* ---------- Type ---------- */

h1, h2, h3, h4, h5 {
  /* A single long word must wrap rather than widen the layout viewport —
     "acknowledgements" at mobile display size was wider than the narrow
     column and put 12px of overflow on the whole page. */
  overflow-wrap: break-word;
  font-family: var(--font-display);
  color: var(--ink);
  margin: 0 0 0.5em;
  letter-spacing: -0.022em;
  line-height: 1.08;
  text-wrap: balance;
}
h1 { font-size: clamp(2.7rem, 4.7vw, 5.8rem); font-weight: 700; letter-spacing: -0.032em; }
h2 { font-size: clamp(2rem, 3.1vw, 3.4rem); font-weight: 700; letter-spacing: -0.026em; }
h3 { font-size: clamp(1.15rem, 1.35vw, 1.4rem); font-weight: 600; line-height: 1.25; letter-spacing: -0.014em; }
h4 { font-size: 1.02rem; font-weight: 600; line-height: 1.3; }

p { margin: 0 0 1.1rem; max-width: var(--measure); }

.lead {
  font-size: clamp(1.1rem, 1.35vw, 1.4rem);
  line-height: 1.5;
  color: var(--slate);
  max-width: 60ch;
}
.lede-lg { font-size: clamp(1.25rem, 1.7vw, 1.7rem); line-height: 1.42; color: var(--slate); max-width: 46ch; }

/* Eyebrow: numbered editorial spine. Gold appears here only on dark. */
.eyebrow {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--font-display);
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--clinical-teal);
  margin-bottom: var(--s3);
}
.eyebrow::before {
  content: attr(data-index);
  font-variant-numeric: tabular-nums;
  color: var(--slate);
  font-weight: 600;
}
.eyebrow:not([data-index])::before { content: ""; width: 24px; height: 1px; background: var(--line-strong); }

/* ---------- Layout ---------- */

.wrap { width: var(--canvas); margin-inline: auto; padding-inline: var(--gutter); }
.wrap-narrow { width: min(92vw, 860px); margin-inline: auto; padding-inline: var(--gutter); }

.band { padding-block: var(--pad); position: relative; }
.band-tight { padding-block: calc(var(--pad) * 0.62); }

/* Light bands are separated by rules, not fills — the fills are 1.048 apart. */
.band-line { border-top: 1px solid var(--line); }
.band-ice { background: var(--ice); }

/* Dark movements. Depth comes from a light source, not a flat slab. */
.band-dark {
  background:
    radial-gradient(105% 70% at 8% -8%, #10304a 0%, transparent 62%),
    linear-gradient(180deg, #082130 0%, var(--obsidian) 58%, #04121b 100%);
  color: var(--on-dark-2);
}
.band-dark h1, .band-dark h2, .band-dark h3, .band-dark h4, .band-dark h5 { color: var(--on-dark); }
.band-dark .lead, .band-dark .lede-lg { color: var(--on-dark-2); }
.band-dark .eyebrow { color: var(--signal-aqua); }
.band-dark .eyebrow::before { color: var(--gold); }
.band-dark .eyebrow:not([data-index])::before { background: var(--line-dark-strong); }

.head { max-width: 780px; margin-bottom: clamp(40px, 5vw, 80px); }
.head .lead { margin-bottom: 0; }

.grid { display: grid; gap: clamp(20px, 2.2vw, 40px); }
.g2 { grid-template-columns: repeat(2, 1fr); }
.g3 { grid-template-columns: repeat(3, 1fr); }
.g4 { grid-template-columns: repeat(4, 1fr); }
/* Statement column + detail column. The workhorse. */
.split { display: grid; grid-template-columns: minmax(0, 4.6fr) minmax(0, 6fr); gap: clamp(32px, 5vw, 96px); align-items: start; }
@media (max-width: 1000px) { .g3, .g4 { grid-template-columns: repeat(2, 1fr); } .split { grid-template-columns: minmax(0, 1fr); } /* bare 1fr cannot shrink below min-content (Applier-B) */ }
@media (max-width: 640px) { .g2, .g3, .g4 { grid-template-columns: 1fr; } }

/* Ultrawide: the site must become MORE, not float in a sea of white. */
@media (min-width: 1900px) {
  :root { --pad: 200px; --canvas: min(92vw, 2000px); }
  .split { gap: 140px; }
}
@media (min-width: 2500px) {
  :root { --pad: 240px; font-size: 18px; --canvas: min(82vw, 2360px); }
  .lead { font-size: 1.5rem; }
}

.rule { height: 1px; background: var(--line); border: 0; margin: 0; }
.band-dark .rule { background: var(--line-dark); }

/* ---------- Header ---------- */

.hdr {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s, background 0.25s;
}
.hdr.is-scrolled { border-bottom-color: var(--line); }
.hdr-bar { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); height: 72px; }

.brand { display: flex; align-items: center; gap: 10px; flex: 0 1 auto; min-width: 0; color: var(--ink); }
.brand .mark { width: 38px; height: 38px; flex: 0 0 38px; }
@media (max-width: 429px) { .brand .mark { width: 34px; height: 34px; flex: 0 0 34px; } }
.brand-name {
  font-family: var(--font-display); font-weight: 800; font-size: 1.08rem;
  letter-spacing: -0.028em; min-width: 0; overflow: hidden; white-space: nowrap;
}

.nav { display: flex; align-items: center; gap: 2px; list-style: none; margin: 0; padding: 0; }
.nav > li { position: relative; }
.nav > li > a {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 14px; border-radius: var(--r1);
  font-size: 0.92rem; font-weight: 500; color: var(--slate);
  white-space: nowrap; /* "Why Caretech" wrapped to two lines 1081-1279 */
  transition: color 0.18s, background 0.18s;
}
.nav > li > a:hover { color: var(--ink); background: rgba(18, 33, 45, 0.05); }
.nav > li > a[aria-current] { color: var(--clinical-teal); }
.caret { width: 11px; height: 11px; opacity: 0.6; transition: transform 0.2s; }
.nav li:hover .caret, .nav li:focus-within .caret { transform: rotate(180deg); }

.menu {
  position: absolute; top: calc(100% + 6px); left: 0;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r2); box-shadow: var(--lift-2);
  opacity: 0; visibility: hidden; transform: translateY(5px);
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease), visibility 0.18s;
  z-index: 110;
}
li:hover > .menu, li:focus-within > .menu { opacity: 1; visibility: visible; transform: none; }
/* Touch without hover (iPad Pro landscape shows this nav): site.js turns
   the first tap into .tap-open instead of a navigation. */
.nav > li.tap-open > .menu { opacity: 1; visibility: visible; transform: none; }
.hdr.menus-off .menu { opacity: 0; visibility: hidden; }

.menu-1 { min-width: 268px; padding: 8px; }
.menu-1 a { display: block; padding: 9px 12px; border-radius: var(--r1); font-size: 0.9rem; color: var(--slate); }
.menu-1 a:hover { background: var(--ice); color: var(--ink); }
.menu-1 a[aria-current="page"] { color: var(--clinical-teal); font-weight: 600; }

.menu-wide {
  position: fixed; top: 78px; left: 50vw;
  transform: translateX(-50%) translateY(5px);
  width: min(1000px, calc(100vw - 48px));
  padding: 28px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 26px;
}
li:hover > .menu-wide, li:focus-within > .menu-wide { transform: translateX(-50%); }
.menu-col h5 {
  margin: 0 0 4px; font-size: 0.7rem; font-weight: 800;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink);
}
.menu-col .note { font-size: 0.78rem; color: var(--slate); margin: 0 0 12px; line-height: 1.4; }
.menu-col a { display: block; padding: 6px 8px; margin-inline: -8px; border-radius: var(--r1); font-size: 0.875rem; color: var(--slate); }
.menu-col a:hover { background: var(--ice); color: var(--ink); }
.menu-col a[aria-current="page"] { color: var(--clinical-teal); font-weight: 600; }

.hdr-cta { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }

.burger {
  display: none; place-items: center;
  min-width: 44px; min-height: 44px; padding: 0;
  background: none; border: 1px solid var(--line-strong); border-radius: 10px;
  color: var(--ink); cursor: pointer; flex: 0 0 auto;
}
.burger-bars { position: relative; display: block; width: 20px; height: 13px; flex: 0 0 20px; }
.burger-bars span {
  position: absolute; left: 0; width: 20px; height: 1.5px; border-radius: 2px; background: currentColor;
  transition: transform 0.26s var(--ease);
}
.burger-bars span:first-child { top: 2px; }
.burger-bars span:last-child { bottom: 2px; }
.burger.is-open .burger-bars span:first-child { transform: translateY(3.75px) rotate(45deg); }
.burger.is-open .burger-bars span:last-child { transform: translateY(-3.75px) rotate(-45deg); }

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font-display); font-weight: 600; font-size: 0.92rem;
  padding: 12px 22px; border-radius: 999px; border: 1px solid transparent;
  cursor: pointer; white-space: nowrap;
  transition: background 0.18s, color 0.18s, border-color 0.18s, transform 0.18s var(--ease), box-shadow 0.18s;
}
/* 320px screens: a nowrap pill like "Request the security and scope pack"
   is 304px wide and drags the whole page 17px sideways (iPhone sweep D2,
   /trust/ on SE — the only overflow on the site). Long labels wrap. */
@media (max-width: 374px) { .btn { white-space: normal; max-width: 100%; text-align: center; } }
.btn-solid { background: var(--ink); color: var(--surface); box-shadow: var(--lift-1); }
.btn-solid:hover { background: var(--clinical-teal); transform: translateY(-2px); box-shadow: var(--lift-2); }
.btn-solid:active { transform: none; }
.btn-line { border-color: var(--line-strong); color: var(--ink); }
.btn-line:hover { border-color: var(--ink); transform: translateY(-2px); }
.band-dark .btn-solid { background: var(--surface); color: var(--obsidian); }
.band-dark .btn-solid:hover { background: var(--signal-aqua); color: var(--obsidian); }
.band-dark .btn-line { border-color: var(--line-dark-strong); color: var(--on-dark); }
.band-dark .btn-line:hover { border-color: var(--on-dark); }

.arrow-link {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-display); font-weight: 600; font-size: 0.9rem;
  color: var(--clinical-teal);
}
.arrow-link svg { width: 15px; height: 15px; transition: transform 0.22s var(--ease); }
.arrow-link:hover svg { transform: translateX(4px); }
.band-dark .arrow-link { color: var(--signal-aqua); }

/* ---------- Rows (replaces card walls) ---------- */

.rows { border-top: 1px solid var(--line); }
.row {
  display: grid; grid-template-columns: 56px minmax(0, 1fr) auto;
  gap: var(--s3); align-items: baseline;
  padding: var(--s4) 4px;
  border-bottom: 1px solid var(--line);
  position: relative;
  transition: background 0.22s, padding-left 0.22s var(--ease);
}
a.row::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--clinical-teal); transform: scaleY(0); transform-origin: top;
  transition: transform 0.3s var(--ease);
}
a.row:hover { background: var(--ice); padding-left: 14px; }
a.row:hover::before { transform: scaleY(1); }
.row-n {
  font-family: var(--font-display); font-size: 0.75rem; font-weight: 700;
  color: var(--slate); font-variant-numeric: tabular-nums; letter-spacing: 0.06em;
}
.row-b h3 { margin-bottom: 6px; }
.row-b p { margin: 0; font-size: 0.96rem; color: var(--slate); max-width: 62ch; }
.row-go { color: var(--slate); transition: color 0.18s, transform 0.22s var(--ease); }
.row-go svg { width: 17px; height: 17px; }
a.row:hover .row-go { color: var(--clinical-teal); transform: translateX(4px); }
.band-dark .rows, .band-dark .row { border-color: var(--line-dark); }
.band-dark .row-b p, .band-dark .row-n, .band-dark .row-go { color: var(--on-dark-2); }
.band-dark a.row:hover { background: rgba(247, 250, 252, 0.05); }
.band-dark a.row::before { background: var(--signal-aqua); }
@media (max-width: 640px) {
  .row { grid-template-columns: 34px minmax(0, 1fr); gap: 12px; padding: var(--s3) 2px; }
  .row-go { display: none; }
}

/* ---------- Blocks ---------- */

.block { padding-top: var(--s3); position: relative; }
.block::before {
  content: ""; position: absolute; top: 0; left: 0; height: 2px; width: 100%;
  background: var(--ink); transform: scaleX(0); transform-origin: left;
  transition: transform 0.6s var(--ease);
}
.in .block::before, .block.in::before { transform: scaleX(1); }
.block h3 { margin-bottom: 8px; }
.block p { font-size: 0.95rem; color: var(--slate); margin: 0; }
.band-dark .block::before { background: var(--signal-aqua); }
.band-dark .block p { color: var(--on-dark-2); }
@media (prefers-reduced-motion: reduce) { .block::before { transform: scaleX(1); } }

/* ---------- Checks ---------- */

.checks { list-style: none; margin: 0; padding: 0; }
.checks li {
  display: grid; grid-template-columns: 20px 1fr; gap: 12px; align-items: start;
  padding: 12px 0; border-bottom: 1px solid var(--line);
  font-size: 0.96rem; color: var(--slate);
}
.checks li:last-child { border-bottom: 0; }
.checks svg { width: 15px; height: 15px; margin-top: 5px; color: var(--clinical-teal); }
.band-dark .checks li { border-color: var(--line-dark); color: var(--on-dark-2); }
.band-dark .checks svg { color: var(--signal-aqua); }

/* ---------- Pills ---------- */

.pills { display: flex; flex-wrap: wrap; gap: 8px; }
.pill {
  padding: 7px 14px; border-radius: 999px;
  border: 1px solid var(--line-strong); background: var(--surface);
  font-family: var(--font-display); font-size: 0.8rem; font-weight: 500; color: var(--ink);
}
.band-ice .pill { background: var(--surface); }
.band-dark .pill { border-color: var(--line-dark-strong); background: transparent; color: var(--on-dark); }

/* ---------- Statement ---------- */

.statement {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(1.7rem, 2.6vw, 2.9rem); line-height: 1.14;
  letter-spacing: -0.026em; color: var(--ink); max-width: 20ch;
}
.band-dark .statement { color: var(--on-dark); }
.statement em { font-style: normal; color: var(--clinical-teal); }
.band-dark .statement em { color: var(--signal-aqua); }

/* ---------- Hero ---------- */

.hero { padding-block: calc(72px + clamp(56px, 7vw, 128px)) clamp(56px, 7vw, 112px); overflow: hidden; }
.hero h1 { max-width: 17ch; }
.hero .lead { margin-top: var(--s3); }
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: var(--s5); }

.page-hero { padding-block: calc(72px + clamp(40px, 4.5vw, 72px)) clamp(28px, 3.5vw, 56px); }
.page-hero h1 { max-width: 19ch; }
.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; font-size: 0.83rem; color: var(--slate); margin-bottom: var(--s3); }
.crumbs a:hover { color: var(--clinical-teal); }
.crumbs .sep { color: var(--slate); opacity: 0.6; }
.band-dark .crumbs, .band-dark .crumbs .sep { color: var(--on-dark-3); }

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

.jump {
  position: sticky; top: 72px; z-index: 40;
  /* Opaque: a translucent bar over a dark band measured 2.73:1. */
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.jump-in { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; padding-block: 9px;
  /* Scrollable with hidden scrollbars needs an affordance (D4): fade the
     clipped edge so overflow reads as more content, not a dead end. */
  mask-image: linear-gradient(90deg, #000 calc(100% - 36px), transparent);
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 36px), transparent); }
.jump-in::-webkit-scrollbar { display: none; }
.jump a {
  flex: 0 0 auto; padding: 7px 14px; border-radius: 999px;
  font-family: var(--font-display); font-size: 0.82rem; font-weight: 500; color: var(--slate);
  white-space: nowrap;
}
.jump a:hover { color: var(--ink); background: var(--ice); }
.jump a[aria-current="true"] { background: var(--ink); color: var(--surface); }

/* ---------- CTA ---------- */

.cta { padding-block: clamp(64px, 8vw, 128px); }

/* ---------- Footer ---------- */

.ftr { background: var(--obsidian); color: var(--on-dark-2); padding-block: clamp(56px, 6vw, 88px) var(--s4); }
.ftr-grid { display: grid; grid-template-columns: 1.6fr repeat(3, 1fr); gap: var(--s5); }
@media (max-width: 940px) { .ftr-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .ftr-grid { grid-template-columns: 1fr; } }
.ftr h3 { margin: 0 0 var(--s2); font-size: 0.7rem; font-weight: 800; letter-spacing: 0.15em; text-transform: uppercase; color: var(--on-dark); }
.ftr ul { list-style: none; margin: 0; padding: 0; }
.ftr li { margin-bottom: 9px; }
.ftr a { font-size: 0.9rem; color: var(--on-dark-2); }
.ftr a:hover { color: var(--signal-aqua); }
.ftr .brand { color: var(--on-dark); }
.ftr-about { font-size: 0.9rem; color: var(--on-dark-2); max-width: 34ch; margin-top: var(--s2); }
.ftr-legal { margin-top: var(--s6); padding-top: var(--s3); border-top: 1px solid var(--line-dark); font-size: 0.8rem; color: var(--on-dark-3); line-height: 1.55; max-width: 100ch; }
.ftr-base { margin-top: var(--s3); display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px; font-size: 0.8rem; color: var(--on-dark-3); }
.geo { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.geo b { font-family: var(--font-display); font-size: 0.64rem; font-weight: 800; letter-spacing: 0.08em; padding: 2px 5px; border-radius: 4px; border: 1px solid var(--line-dark-strong); color: var(--on-dark-2); }

/* ---------- Forms ---------- */

.fgrid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
@media (max-width: 640px) { .fgrid { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field.wide { grid-column: 1 / -1; }
.field label { font-family: var(--font-display); font-size: 0.78rem; font-weight: 600; color: var(--ink); }
.field input, .field select, .field textarea {
  width: 100%; min-width: 0;
  background: var(--surface); color: var(--ink);
  /* 3.5:1 against white — a control boundary must be visible. Neutral
     slate, not the old warm tan (iPhone sweep: tan read off-palette). */
  border: 1px solid #86929E; border-radius: 10px; padding: 12px 14px;
  font-family: var(--font-body); font-size: 0.95rem;
  transition: border-color 0.18s, box-shadow 0.18s;
}
.field textarea { resize: vertical; }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
  outline: 2px solid var(--cobalt); outline-offset: 1px; border-color: var(--cobalt);
}
.field .hint { font-size: 0.82rem; color: var(--slate); }

/* ---------- Motion ---------- */

.js .rise { opacity: 0; transform: translateY(14px); transition: opacity 0.5s var(--ease), transform 0.5s var(--ease); }
.rise.in { opacity: 1; transform: none; }
.js .rise-set > * { opacity: 0; transform: translateY(14px); transition: opacity 0.45s var(--ease), transform 0.45s var(--ease); }
.rise-set.in > * { opacity: 1; transform: none; transition-delay: calc(var(--i, 0) * 45ms); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .rise, .js .rise-set > * { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ---------- Diagram ---------- */

.dgm { width: 100%; height: auto; overflow: visible; }
.dgm text { font-family: var(--font-display); }
.js .dgm-draw { stroke-dasharray: var(--len, 400); stroke-dashoffset: var(--len, 400); transition: stroke-dashoffset 1.2s var(--ease); }
.in .dgm-draw, .dgm.in .dgm-draw { stroke-dashoffset: 0; }
.js .dgm-fade { opacity: 0; transition: opacity 0.5s var(--ease); }
.in .dgm-fade { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .js .dgm-draw { stroke-dashoffset: 0; } .js .dgm-fade { opacity: 1; } }

/* Stacked equivalent for narrow screens — a 900-unit diagram scaled to a
   phone renders its labels at ~5px, which is not responsive design.
   Component styles sit outside the media query (inert while display:none)
   because the stack shows in two bands: ≤820 for every page, and ≤1280
   where it is data-swap-paired with a legacy 1000-unit diagram whose type
   breaks the 12px law across the tablet band (iPad sweep S1: 8.2px labels
   on 14 routes at 834-1194). */
.stack { display: none; }
.stack-step { position: relative; padding: 20px 0 20px 24px; border-left: 1px solid var(--line-dark); }
.stack-step::before { content: ""; position: absolute; left: -4.5px; top: 27px; width: 8px; height: 8px; border-radius: 50%; background: var(--signal-aqua); }
.stack-step:last-child { border-left-color: transparent; }
.stack-step .stack-h { font-size: 0.7rem; font-weight: 800; letter-spacing: 0.15em; text-transform: uppercase; color: var(--on-dark-3); margin: 0 0 10px; }
.stack-step ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
.stack-step li { font-size: 0.95rem; color: var(--on-dark); }
.stack-core { background: rgba(247, 250, 252, 0.05); border-radius: 0 12px 12px 0; padding-right: 16px; }
@media (max-width: 1280px) {
  .dgm[data-swap="1280"] { display: none; }
  .stack[data-swap="1280"] { display: grid; margin-top: var(--s4); }
}
@media (max-width: 820px) {
  .dgm { display: none; }
  .stack { display: grid; margin-top: var(--s4); }
}

/* ---------- Focus ---------- */

#main:focus, #main:focus-visible { outline: none; }
a:focus-visible, button:focus-visible, [tabindex]:focus-visible, summary:focus-visible {
  outline: 2px solid var(--cobalt); outline-offset: 2px;
}
.band-dark a:focus-visible, .band-dark button:focus-visible, .ftr a:focus-visible {
  outline-color: var(--signal-aqua);
}
.skip { position: absolute; left: -9999px; top: 0; z-index: 200; background: var(--ink); color: var(--surface); padding: 12px 18px; border-radius: 0 0 10px 0; }
.skip:focus { left: 0; }

/* ---------- Mobile navigation ---------- */

@media (max-width: 1080px) {
  /* The nav element itself must go, not just its list: as a flex child it
     keeps a gap slot that starves the brand of width (RM-03). */
  .hdr-bar > nav, .hdr-cta .btn-line { display: none; }
  .brand { flex-shrink: 0; }
  .burger { display: grid; }
  .mnav {
    position: fixed; inset: 72px 0 0 0; z-index: 99;
    background: var(--surface); display: none; flex-direction: column;
  }
  .mnav.open { display: flex; }
  .mnav-scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .mpanel { display: none; padding: 8px var(--gutter) 24px; }
  .mpanel[data-on] { display: block; animation: mslide 0.22s var(--ease); }
  @keyframes mslide { from { opacity: 0; transform: translateX(10px); } to { opacity: 1; transform: none; } }
  .mrow {
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    width: 100%; min-height: 62px; padding: 14px 2px;
    background: none; border: 0; border-bottom: 1px solid var(--line);
    color: var(--ink); font-family: var(--font-body); font-size: 1.05rem; text-align: left; cursor: pointer;
  }
  .mrow:last-child { border-bottom: 0; }
  .mrow b { display: block; font-weight: 500; }
  .mrow small { display: block; margin-top: 2px; font-size: 0.82rem; color: var(--slate); }
  .mchev { width: 15px; height: 15px; flex: 0 0 15px; color: var(--slate); }
  .mrow[aria-current="page"] b { color: var(--clinical-teal); }
  .mback { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 8px 8px 8px 0; background: none; border: 0; cursor: pointer; color: var(--clinical-teal); font-family: var(--font-display); font-size: 0.9rem; font-weight: 600; }
  .mback svg { width: 15px; height: 15px; }
  .mtitle { font-family: var(--font-display); font-weight: 700; font-size: 1.7rem; letter-spacing: -0.026em; margin: 4px 0 16px; }
  .mfoot { padding: 16px var(--gutter) calc(20px + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--line); }
  .mfoot .btn { width: 100%; }
  body.nav-open .hdr-cta .btn-solid { display: none; }
  .jump { top: 72px; }
}
@media (min-width: 1081px) { .mnav { display: none !important; } }
@media (max-width: 400px) { .hdr-cta .btn-solid { padding: 11px 15px; font-size: 0.86rem; } }

body.nav-open { position: fixed; width: 100%; overflow: hidden; }

/* ---------- Print ---------- */

@media print {
  .hdr, .mnav, .jump, .cta, .skip { display: none !important; }
  body { background: #fff; color: #000; font-size: 11pt; }
  .band-dark, .ftr { background: #fff !important; color: #000 !important; }
  .band-dark h1, .band-dark h2, .band-dark h3 { color: #000 !important; }
  .rise, .rise-set > * { opacity: 1 !important; transform: none !important; }
}

/* Below 360px the full brand, pill and burger cannot all fit. The pill
   used to yield — but interior pages carry no hero CTA, so a 320px
   visitor could land with no call to action anywhere near the fold
   (iPhone/Android sweeps). The WORDMARK yields instead: mark-only brand
   (36px) + compact pill (~106px) + burger (44px) = ~236px in a 320px bar.
   The wordmark returns at 360px and up. */
@media (max-width: 359px) {
  .brand .brand-text { display: none; }
  .hdr-cta .btn-solid { padding: 11px 14px; font-size: 0.85rem; }
}

/* At very large canvases the text column outgrows the hero diagram and
   top-alignment leaves a void under it (RM-17). Centre the pairing. */
@media (min-width: 1900px) {
  .hero .split { align-items: center; }
}

/* ============================================================================
   V4 · A. TOKENS
   Appended, never rewritten. Every existing class above still resolves —
   two copy waves are editing pages against them while this ships.
   Ratios below were computed with relative-luminance math (sRGB, WCAG 2.2),
   not estimated. Alpha colours are composited over their stated background
   before the ratio is taken, which is the step V2 skipped.
   ============================================================================ */

:root {
  /* Motion. --ease already exists above and is unchanged. */
  --ease-signal: cubic-bezier(0.45, 0, 0.15, 1);   /* travel along a path */
  --ease-node:   cubic-bezier(0.34, 1.28, 0.5, 1); /* activation, slight overshoot */
  --ease-exit:   cubic-bezier(0.4, 0, 1, 1);       /* anything leaving */

  /* Type ladder. Three of the four are the shipped values, named.
     Nothing already on the site changes size. */
  --display-0: clamp(3.4rem, 6.4vw, 118px); /* px ceiling: 13.5rem let the H1 hit 245px at 3840 and push the CTAs below the fold (Q) */   /* flagship / stage numeral. Archetype A & B only. */
  --display-1: clamp(2.7rem, 4.7vw, 5.8rem);    /* = today's h1 */
  --display-2: clamp(2rem, 3.1vw, 3.4rem);      /* = today's h2 */
  --display-3: clamp(1.7rem, 2.6vw, 2.9rem);    /* = today's .statement */

  /* Space. --pad already exists; these hang off it so the ultrawide steps
     carry through without restating them. */
  --pad-s:  calc(var(--pad) * 0.62);   /* = today's .band-tight */
  --pad-l:  calc(var(--pad) * 1.35);
  --pad-xl: calc(var(--pad) * 1.90);

  /* Signature. */
  --substrate:  rgba(50, 203, 190, 0.10); /* -> #0a2932, 1.19:1. Decorative field,
                                             aria-hidden, carries no information —
                                             the one exemption from the 3:1 floor. */
  --glow-halo:  rgba(50, 203, 190, 0.07); /* -> #09242d, 1.12:1. Decorative halo. */
  --glow-bloom: rgba(50, 203, 190, 0.16); /* -> #0d343b, 1.36:1. Decorative bloom. */

  --panel-dark:   #10202b;                    /* = rgba(247,250,252,0.04) over --obsidian.
                                                 --on-dark 15.86:1 · --on-dark-2 9.63:1 ·
                                                 --on-dark-3 6.38:1 · --signal-aqua 8.25:1 ·
                                                 --gold 7.28:1 · --cobalt 3.62:1 (non-text). */
  --panel-stroke: rgba(247, 250, 252, 0.28);  /* -> #515d66 on --panel-dark, 2.46:1.
                                                 DECORATIVE separators only. A container
                                                 that means something takes --line-dark-hi. */

  /* The control-boundary fix (D-06). --line-dark-strong measures 2.43:1 on
     --obsidian and was carrying .btn-line and .pill borders — below the 3:1
     non-text floor, and axe does not audit it. */
  --line-dark-hi: rgba(247, 250, 252, 0.40);  /* -> #667279 on --obsidian      3.68:1
                                                 -> #6c777f on --panel-dark    3.64:1
                                                 -> #687882 on hero top #082130 3.61:1
                                                 -> #6a7783 on legacy #0C2032  3.62:1 */

  /* Informative strokes that need to sit back but must still clear 3:1.
     rgba(143,163,180,0.50) measured 2.66:1 and would have shipped green. */
  --line-recede: rgba(143, 163, 190, 0.65);   /* -> #5f7281 on --obsidian, 3.65:1 */

  /* Signature timing. */
  --idle-start: 5000ms;   /* the intro finishes at ~5.0s; idle begins after it */
  --lane-delay: 0ms;      /* set per lane, 640ms apart, so <= 2 lanes are ever lit */
}

/* Ultrawide steps to 5120. --canvas grows sub-linearly and --measure steps
   DOWN in ch so the physical line stays near 62-68 characters: a bigger
   display gets a bigger site, not a longer line. 1900 / 2500 already exist. */
@media (min-width: 3200px) { :root { --pad: 280px; font-size: 19px; --canvas: min(80vw, 2720px); --measure: 66ch; } }
@media (min-width: 4200px) { :root { --pad: 320px; font-size: 20px; --canvas: min(78vw, 3100px); --measure: 64ch; } }
@media (min-width: 5120px) { :root { --pad: 360px; font-size: 21px; --canvas: min(76vw, 3480px); --measure: 62ch; } }

/* R-08. At 92px a 1.08 leading leaves a visible gutter inside a two-line h1;
   1.02 makes it a block. Declared here rather than edited above so the
   original rule and its comment stay intact. */
h1 { line-height: 1.02; }

p, .lead { text-wrap: pretty; }

/* R-04. Control boundaries on dark move off the 2.43:1 token.
   --line-dark-strong keeps its decorative jobs (.eyebrow::before, .rule). */
.band-dark .btn-line,
.band-dark .pill,
.geo b { border-color: var(--line-dark-hi); }

/* R-03 safety net. `stroke="#20415c"` on `fill="#0C2032"` measures 1.55:1 and
   is hardcoded as a presentation attribute in twenty un-migrated page SVGs.
   CSS beats a presentation attribute, so the failure is repaired from here
   without touching a page. Page builders must still stop copying it forward:
   author new panels with stroke="var(--line-dark-hi, #667279)". */
.dgm [stroke="#20415c"] { stroke: var(--line-dark-hi); }


/* ============================================================================
   V4 · B. SIGNATURE & MOTION — the luminous path system
   Every animated class has a prefers-reduced-motion fallback at the end of
   this block, and the fallback is always the FINISHED state, never a blank
   one. A .sig frozen at opacity 0 would erase the diagram.
   ============================================================================ */

/* --- The substrate field. Decorative, aria-hidden, exempt from 3:1 (it
   conveys nothing). One absolutely-positioned svg per dark band. */
.substrate {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  overflow: hidden;
}
.substrate path, .substrate line { fill: none; stroke: var(--substrate); stroke-width: 0.75; }
.substrate { z-index: 0; }
/* The band is already position:relative (.band). The wrap becomes positioned
   so it paints above the field without needing a z-index or a stacking
   context — DOM order does the work, and nothing existing is disturbed. */
.band-dark > .wrap, .band-dark > .wrap-narrow { position: relative; }

/* --- pathLength="1" backbone.
   Every animatable path declares pathLength="1", which normalises a 40-unit
   and a 900-unit path to the same length. All dash animation is then a pure
   CSS constant and getTotalLength() is never called (fixes D-08).
   Scoped under .js so a no-JS render shows the finished diagram, not a blank
   one — the same guard .dgm-draw already uses. */
.js .trace { stroke-dasharray: 1; stroke-dashoffset: 1; }
.js .in .trace, .js .dgm.in .trace {
  animation: trace-draw var(--dur, 900ms) var(--ease) var(--delay, 0ms) both;
}
@keyframes trace-draw { to { stroke-dashoffset: 0; } }

/* A short dash chases the path. 0.16 of a normalised length. */
.js .sig { stroke-dasharray: 0.16 0.84; stroke-dashoffset: 1; opacity: 0; }
.js .in .sig, .js .dgm.in .sig {
  animation: sig-travel var(--dur, 760ms) var(--ease-signal) var(--delay, 0ms) both;
}
@keyframes sig-travel {
  from { stroke-dashoffset: 1; opacity: 1; }
  to   { stroke-dashoffset: 0; opacity: 1; }
}

/* The idle loop. Travel occupies 2.4s of 5.2s; 2.8s is darkness. Lanes are
   staggered 640ms apart so at most two are lit at any instant. */
@keyframes sig-loop {
  0%   { stroke-dashoffset: 1; opacity: 0; }
  5%   { opacity: 1; }
  46%  { stroke-dashoffset: 0; opacity: 1; }
  53%  { stroke-dashoffset: 0; opacity: 0; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}
.js .sig-idle { stroke-dasharray: 0.16 0.84; stroke-dashoffset: 1; opacity: 0; }
.js .in .sig-idle, .js .dgm.in .sig-idle {
  animation: sig-loop 5.2s linear calc(var(--idle-start) + var(--lane-delay)) infinite;
}
/* Mobile plays the intro once and rests. Battery, and honesty. */
@media (max-width: 820px) {
  .js .in .sig-idle, .js .dgm.in .sig-idle { animation: none; opacity: 1; stroke-dasharray: none; }
}

/* --- Glow without filters. filter: url(#blur) forces an off-compositor
   raster pass per frame and is banned on anything animated. Glow is three
   coaxial copies of ONE path, shared via <use href="#id">: the markup cost
   is the path data once plus three 40-byte <use> elements. Only .core-line
   and .sig animate; .halo and .bloom are static and cost nothing per frame. */
.halo  { fill: none; stroke: var(--glow-halo);  stroke-width: 6;   stroke-linecap: round; }
.bloom { fill: none; stroke: var(--glow-bloom); stroke-width: 2.5; stroke-linecap: round; }
.core-line { fill: none; stroke-width: 1.1; stroke-linecap: round; }
/* Scale the stack on canvases narrower than the 1440 reference. */
.dgm[data-canvas="1200"] .halo { stroke-width: 5; }
.dgm[data-canvas="1200"] .bloom { stroke-width: 2.1; }
.dgm[data-canvas="1000"] .halo { stroke-width: 4.2; }
.dgm[data-canvas="1000"] .bloom { stroke-width: 1.8; }

/* --- Node activation. scale() on a fill-box origin, never r — r is not
   compositor friendly. Overshoot 1.28 is permitted only on objects <= 12px. */
.js .node { transform-box: fill-box; transform-origin: center; opacity: 0; transform: scale(0.55); }
.js .in .node, .js .dgm.in .node {
  animation: node-wake var(--dur, 380ms) var(--ease-node) var(--delay, 0ms) both;
}
@keyframes node-wake {
  from { opacity: 0; transform: scale(0.55); }
  to   { opacity: 1; transform: scale(1); }
}

/* --- The gate pulse. One ring, once, at the sequence's tempo change. */
.js .gate-pulse { transform-box: fill-box; transform-origin: center; opacity: 0; }
.js .in .gate-pulse, .js .dgm.in .gate-pulse {
  animation: gate-pulse var(--dur, 420ms) var(--ease-node) var(--delay, 2400ms) both;
}
@keyframes gate-pulse {
  0%   { opacity: 0; transform: scale(0.94); }
  55%  { opacity: 1; transform: scale(1.05); }
  100% { opacity: 0.55; transform: scale(1); }
}

/* --- A luminance sweep across a band (the hero's analysis band). */
.js .sweep { transform-box: fill-box; transform-origin: left center; transform: scaleX(0); }
.js .in .sweep, .js .dgm.in .sweep {
  animation: sweep-x var(--dur, 620ms) var(--ease-signal) var(--delay, 0ms) both;
}
@keyframes sweep-x { to { transform: scaleX(1); } }

/* --- Generic fade for static furniture inside an asset. */
.js .fade { opacity: 0; }
/* Delay capped at 700ms: long stagger chains left diagram cores empty for
   fast scrollers — staging is a garnish, never a gate (blind review). */
.js .in .fade, .js .dgm.in .fade { animation: fade-in var(--dur, 380ms) var(--ease) calc(min(var(--delay, 0ms), 700ms)) both; }
@keyframes fade-in { to { opacity: 1; } }

/* --- .reveal-mask. clip-path repaints its whole area: maximum one per
   viewport, reserved for the full-bleed asset in archetype B section 2.
   Never on text. */
.js .reveal-mask { clip-path: inset(0 100% 0 0); transition: clip-path 900ms var(--ease); }
.js .reveal-mask.in, .js .in .reveal-mask { clip-path: inset(0 0 0 0); }

/* --- Nothing off-screen animates. */
.dgm:not(.in) * { animation-play-state: paused; }

/* --- The rendered-type law, as three swap breakpoints.
   min_type_units >= 12 / (min_render_px / canvas_units). No SVG text may
   render below 12 CSS px at any viewport where the SVG is visible; below
   that viewport the SVG is display:none and its .journey sibling renders.
   .dgm with no data-swap keeps the shipped 820px rule for legacy
   900-unit diagrams. */
.journey { display: none; }
@media (max-width: 1280px) {
  .dgm[data-swap="1280"] { display: none; }
  .journey[data-swap="1280"] { display: grid; }
}
@media (max-width: 1080px) {
  .dgm[data-swap="1080"] { display: none; }
  .journey[data-swap="1080"] { display: grid; }
}
@media (max-width: 820px) {
  /* .dgm is already display:none here (shipped rule). */
  .journey, .journey[data-swap] { display: grid; }
}
/* Asset 04 is square: at full .wrap it would be 1213px tall, taller than the
   viewport. Capped, and centred in whatever column holds it. */
.dgm[data-cap="720"] { max-width: 720px; margin-inline: auto; }

/* --- The header over a dark hero (D-04). The most cinematic surface on the
   site was decapitated by an opaque white strip at 72px.
   build-chrome.js emits .hdr-over-dark when section 1 carries .band-dark. */
.hdr-over-dark {
  background: transparent;
  backdrop-filter: none;
  border-bottom-color: transparent;
  transition: background 250ms var(--ease), border-color 250ms var(--ease);
}
.hdr-over-dark .brand              { color: var(--on-dark);   }  /* 17.37:1 */
.hdr-over-dark .nav > li > a       { color: var(--on-dark-2); }  /* 10.55:1 */
.hdr-over-dark .nav > li > a:hover { color: var(--on-dark); background: rgba(247, 250, 252, 0.07); }
.hdr-over-dark .nav > li > a[aria-current] { color: var(--signal-aqua); }  /* 9.04:1 */
.hdr-over-dark .btn-solid          { background: var(--surface); color: var(--obsidian); }
.hdr-over-dark .burger             { color: var(--on-dark); border-color: var(--line-dark-hi); } /* 3.68:1 */
.hdr-over-dark a:focus-visible,
.hdr-over-dark button:focus-visible { outline-color: var(--signal-aqua); }
/* ONE palette per page, at every depth. The header used to cross-fade to
   white glass past the hero and flip its text ink in the same instant —
   but text cannot fade through a light/dark swap without a zero-contrast
   crossing, so the wordmark vanished for ~125ms on every scroll-direction
   change (Android sweep D1: 154/154 samples, every route, every device).
   Dark-hero pages now keep dark glass and on-dark text throughout; the
   on-dark rules above apply unconditionally. The dark-glass background for
   .is-scrolled lives with the glass rules (~line 1900). */
/* Without JavaScript .is-scrolled never applies — the bar must not rest
   transparent over whatever scrolls beneath it (blind review, no-JS pass). */
html:not(.js) .hdr-over-dark {
  background: rgba(6, 23, 34, 0.97);
  border-bottom: 1px solid var(--line-dark);
}
/* The drawer covers content, so it stays opaque unconditionally. */


/* ============================================================================
   V4 · C. ARCHETYPES & COMPONENTS
   ============================================================================ */

/* --- Four approved splits. .split (4.6:6) already exists above.
   No two consecutive .band sections may use the same split class. */
.split-wide   { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);  gap: clamp(32px, 5vw, 96px); align-items: start; }
.split-narrow { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 9fr);  gap: clamp(32px, 5vw, 96px); align-items: start; }
.split-offset { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 10fr); gap: clamp(32px, 5vw, 96px); align-items: start; }
@media (max-width: 1000px) {
  /* minmax(0,1fr), not 1fr: a bare 1fr track cannot shrink below its
     largest item's min-content, which inflated the whole column to 304px
     inside a 254px box at 320 (the V3 .split lesson, relearned). */
  .split-wide, .split-narrow, .split-offset { grid-template-columns: minmax(0, 1fr); }
}
/* HERO splits collapse a step earlier: a hero diagram squeezed into a
   ~350px column at 1001-1080 renders its type under the 12px law and
   reads cramped where the page should open big. Full-width below 1081. */
@media (max-width: 1080px) {
  .hero .split-wide { grid-template-columns: minmax(0, 1fr); }
}
@media (min-width: 1900px) { .split-wide, .split-narrow, .split-offset { gap: 140px; } }

/* Optical offset: a column-scale object in a right column drops 40px below
   the text baseline. Perfect top-alignment is the tell of a template.
   Does not apply to full-.wrap assets — those are their own movement. */
@media (min-width: 1001px) and (max-width: 1899px) {
  .offset-40 { margin-top: 40px; }
}

/* Band padding modifiers. Padding must change at least three times per page:
   a page with one padding value is a template regardless of the value. */
.pad-s  { padding-block: var(--pad-s); }
.pad-l  { padding-block: var(--pad-l); }
.pad-xl { padding-block: var(--pad-xl); }

/* One full-bleed per page maximum: archetype B section 2, archetype A
   section 3. It escapes --canvas to the viewport width. */
.bleed {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  max-width: none;
}
.bleed > .dgm { width: 100%; }

/* --- Display ladder helpers. --display-0 appears at most once per page and
   only in archetype A or B. */
.display-0 { font-family: var(--font-display); font-size: var(--display-0); font-weight: 700; line-height: 0.92; letter-spacing: -0.038em; color: var(--ink); text-wrap: balance; margin: 0 0 0.4em; }
.display-2 { font-family: var(--font-display); font-size: var(--display-2); font-weight: 700; line-height: 1.08; letter-spacing: -0.026em; color: var(--ink); text-wrap: balance; }
.band-dark .display-0, .band-dark .display-2 { color: var(--on-dark); }
/* Archetype B's stage numeral: --display-0 scale, used as a mark not a heading. */
.stage-num {
  font-family: var(--font-display); font-size: var(--display-0); font-weight: 700;
  line-height: 0.86; letter-spacing: -0.045em;
  /* 0.34, not 0.16: at 0.16 the numeral read as a smudge on phones; and a
     positive margin so the h1 cap line can never touch it (iPhone D10). */
  color: var(--on-dark); opacity: 0.34;
  font-variant-numeric: tabular-nums;
  display: block; margin-bottom: 0.14em;
}
.stage-num b { font-weight: 700; color: var(--signal-aqua); opacity: 1; }

/* --- .journey — the mobile transformation. NOT a shrink of the diagram:
   a full-width vertical descent carrying MORE content than the desktop
   asset, the same labels in the same order. Scroll is the propagation. */
.journey {
  grid-auto-flow: row;
  position: relative;
  margin-top: var(--s4);
  padding-left: 40px;
}
.journey-spine {
  position: absolute; left: 24px; top: 6px; bottom: 6px; width: 1px;
  background: linear-gradient(180deg, rgba(50,203,190,0.10), rgba(50,203,190,0.28), rgba(50,203,190,0.10));
  overflow: hidden;
}
/* The travelling light: one paint on a 1px column, the cheapest possible
   expression of the signature. Runs once, never looped. */
.js .journey-spine::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent, var(--signal-aqua), transparent);
  background-size: 100% 200%; background-position: 0 -100%;
  opacity: 0;
}
.js .in .journey-spine::after, .js .journey.in .journey-spine::after {
  animation: spine-light 5.2s linear 200ms 1 both;
}
@keyframes spine-light {
  0%   { background-position: 0 -100%; opacity: 0; }
  6%   { opacity: 1; }
  90%  { opacity: 1; }
  100% { background-position: 0 100%; opacity: 0; }
}
.journey-stop { position: relative; padding: 0 0 32px 0; }
.journey-stop:last-child { padding-bottom: 0; }
/* Station markers are CSS, not SVG. */
.journey-stop::before {
  content: ""; position: absolute; left: -20.5px; top: 7px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--signal-aqua);
}
.journey-h {
  font-family: var(--font-display);
  font-size: 0.7rem; font-weight: 800; letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--on-dark-3); margin: 0 0 8px;
}
.journey-stop p { font-size: 0.98rem; color: var(--on-dark); margin: 0; max-width: 46ch; }
.journey-stop ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.journey-stop li { font-size: 0.98rem; color: var(--on-dark); }
/* The platform stop: inset and raised, mirroring the shipped .stack-core. */
.journey-core {
  background: rgba(247, 250, 252, 0.05);
  border-radius: 0 12px 12px 0;
  margin-left: -16px; padding: 16px 16px 16px 16px;
}
.journey-core::before { left: -4.5px; }
/* The gate stop gets air: 56px before and after. That is how a reader knows
   it matters. Its marker is a gold diamond, and it is the only gold here. */
.journey-gate {
  margin-block: 24px;
  padding: 20px 16px 20px 16px;
  margin-left: -16px;
  background: rgba(201, 167, 101, 0.05);
  border-left: 1px solid var(--gold);      /* 7.98:1 on --obsidian */
  border-radius: 0 12px 12px 0;
}
.journey-gate + .journey-stop { padding-top: 32px; }   /* 24 + 32 = the 56px the gate is owed */
.journey-gate::before {
  /* the 1px gold border shifts the padding box, so the marker shifts with it */
  left: -5.5px; top: 22px; width: 10px; height: 10px;
  border-radius: 1px; background: var(--gold);
  transform: rotate(45deg);
}
.journey-gate .journey-h { color: var(--gold); }
.journey-return { padding-bottom: 0; }
.journey-return::before {
  background: none; border: 1px dashed var(--line-recede);   /* 3.65:1 */
  width: 9px; height: 9px; left: -21px;
}
.journey-return p { color: var(--on-dark-2); font-style: normal; }
/* Light-band journey: gold does not exist here, and aqua is invisible. */
/* Light-band journey and vignette. :not(.band-dark) is required: a dark
   section is class="band band-dark", so a bare `.band .journey-core` paints
   the platform stop white-on-obsidian. Watched fail at 390px. */
.band:not(.band-dark) .journey-spine, .band-ice:not(.band-dark) .journey-spine {
  background: linear-gradient(180deg, rgba(11,113,104,0.15), rgba(11,113,104,0.45), rgba(11,113,104,0.15));
}
.band:not(.band-dark) .journey-stop::before, .band-ice:not(.band-dark) .journey-stop::before { background: var(--clinical-teal); }  /* 5.87:1 */
.band:not(.band-dark) .journey-h, .band-ice:not(.band-dark) .journey-h { color: var(--slate); }
.band:not(.band-dark) .journey-stop p, .band-ice:not(.band-dark) .journey-stop p,
.band:not(.band-dark) .journey-stop li, .band-ice:not(.band-dark) .journey-stop li { color: var(--ink); }
.band:not(.band-dark) .journey-core, .band-ice:not(.band-dark) .journey-core {
  /* NOT --ice: on a .band-ice section that is a 1.048 step, i.e. invisible.
     Where a raised panel is needed on light, use a shadow, never a fill step. */
  background: var(--surface); box-shadow: var(--lift-1);
}
.band:not(.band-dark) .journey-gate, .band-ice:not(.band-dark) .journey-gate {
  background: rgba(11, 113, 104, 0.05); border-left-color: var(--clinical-teal);
}
.band:not(.band-dark) .journey-gate::before, .band-ice:not(.band-dark) .journey-gate::before { background: var(--clinical-teal); }
.band:not(.band-dark) .journey-gate .journey-h, .band-ice:not(.band-dark) .journey-gate .journey-h { color: var(--clinical-teal); }

/* --- .pullquote — archetype D's spine device and the site's primary warmth
   device. Weight 300 at display scale is the whole trick: one thin, large,
   quiet line in a wall of 600-800 reads as a human voice.
   Attribution law: a role and a moment, never a person or a company. */
.pullquote {
  font-family: var(--font-display);
  font-size: var(--display-1);
  font-weight: 300;
  line-height: 1.06;
  letter-spacing: -0.030em;
  color: var(--ink);
  max-width: 18ch;
  margin: 0 0 0 -0.055em;      /* the opening quote hangs into the gutter */
  text-wrap: balance;
}
.band-dark .pullquote { color: var(--on-dark); }
.pullquote-attr {
  font-family: var(--font-body);
  font-size: 0.86rem; font-weight: 500; color: var(--slate);
  margin-top: var(--s3); padding-top: var(--s2);
  border-top: 1px solid var(--line); max-width: 24ch;
}
.band-dark .pullquote-attr { color: var(--on-dark-3); border-color: var(--line-dark); }

/* --- .narrative — 60-90 words, second person, present tense, one real
   minute of one real job. No product, no feature, no claim.
   One per page maximum, archetypes B and D only. */
.narrative {
  font-family: var(--font-body);
  font-size: 1.15rem; line-height: 1.62; color: var(--ink);
  max-width: 54ch;
  padding-inline-start: var(--s4);
  border-inline-start: 2px solid var(--clinical-teal);   /* 5.87:1 on --surface */
}
.narrative p:last-child { margin-bottom: 0; }
.band-dark .narrative { color: var(--on-dark-2); border-color: var(--signal-aqua); }

/* --- .specimen — the artefact a person actually handles (a shift line, a
   handover note), set as TYPE, not as an interface. No frame, no chrome.
   Because it is unmistakably type it sits outside the product-imagery gate. */
.specimen { font-family: var(--font-body); font-variant-numeric: tabular-nums; border-top: 1px solid var(--line); }
.specimen-row {
  display: grid; grid-template-columns: minmax(84px, 0.6fr) minmax(0, 2fr) auto;
  gap: var(--s3); align-items: baseline;
  padding: 11px 2px; border-bottom: 1px solid var(--line);
  font-size: 0.92rem;
}
.specimen-row dt, .specimen-k { color: var(--slate); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; margin: 0; }
.specimen-row dd, .specimen-v { color: var(--ink); margin: 0; }
/* Phones: three columns leave the value 2-3 words a line with orphaned
   separators (iPhone sweep D5, 7 routes). Stack label over value. */
@media (max-width: 519px) {
  .specimen-row { grid-template-columns: minmax(0, 1fr); gap: 4px; padding: 12px 2px; }
}
.specimen-note { color: var(--slate); font-size: 0.82rem; }
.band-dark .specimen, .band-dark .specimen-row { border-color: var(--line-dark); }
.band-dark .specimen-row dt, .band-dark .specimen-k, .band-dark .specimen-note { color: var(--on-dark-3); }
.band-dark .specimen-row dd, .band-dark .specimen-v { color: var(--on-dark); }

/* --- .rail — archetype C's spine. A left column of label/value pairs that
   reads as a spec sheet. It is why a technical page looks technical without
   a single screenshot. */
.rail { border-top: 1px solid var(--line); font-variant-numeric: tabular-nums; }
.rail-item { display: grid; gap: 2px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.rail-k { font-family: var(--font-display); font-size: 0.75rem; /* 0.68 rendered 10.88px, under the 12px floor */ font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--slate); }
.rail-v { font-size: 0.82rem; color: var(--ink); }
@media (min-width: 1001px) { .rail-sticky { position: sticky; top: 112px; } }
.band-dark .rail, .band-dark .rail-item { border-color: var(--line-dark); }
.band-dark .rail-k { color: var(--on-dark-3); }
.band-dark .rail-v { color: var(--on-dark); }

/* --- .vig — the product-experience frame.
   A designed representation in the site's own vocabulary, framed as an
   illustration. NEVER a simulated screenshot: no window traffic-lights, no
   URL bar, no OS status bar, no phone bezel, no cursor, no notification
   badge, no avatar photo. Every one of those exists to say "this is a real
   running thing"; the frame must say the opposite.
   Depth on dark comes from the inset highlight and the hairline — never a
   shadow (shadows on dark read as dirt). Depth on light comes from --lift-2
   — never a fill step (--surface -> --ice is 1.048, invisible: the V2 defect). */
.vig { border-radius: var(--r2); overflow: hidden; position: relative; }
.vig svg { display: block; width: 100%; height: auto; }
.band-dark .vig {
  background: rgba(247, 250, 252, 0.04);   /* resolves to --panel-dark over --obsidian */
  border: 1px solid var(--line-dark);
  box-shadow: none;
}
.band-dark .vig::before {                  /* one inset top highlight — the whole depth cue */
  content: ""; position: absolute; inset: 0 0 auto 0; height: 40%;
  background: linear-gradient(180deg, rgba(247, 250, 252, 0.06), transparent);
  pointer-events: none;
}
.band:not(.band-dark) .vig, .band-ice:not(.band-dark) .vig {
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--lift-2);
}
/* The caption is mandatory and adjacent, never in a footer. */
.vig-cap {
  font-size: 0.78rem; line-height: 1.5; color: var(--slate); margin: 12px 0 0; max-width: 62ch;
}
.band-dark .vig-cap { color: var(--on-dark-3); }   /* 6.99:1 */

/* Type inside a vignette. One unit = one CSS pixel at full size: a
   vignette's viewBox width equals the maximum width it will ever occupy,
   so nothing is ever scaled down. Nothing smaller than 12 units is authored. */
.vig text { font-family: var(--font-display); }
.vig .v-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: 1.6px; fill: var(--on-dark-3); }
.vig .v-colh    { font-size: 12px; font-weight: 600; fill: var(--on-dark-3); }
.vig .v-row1    { font-size: 15px; font-weight: 500; fill: var(--on-dark); font-family: var(--font-body); }
.vig .v-row2    { font-size: 13px; font-weight: 400; fill: var(--on-dark-2); font-family: var(--font-body); }
.vig .v-num     { font-size: 13px; font-weight: 600; fill: var(--signal-aqua); font-variant-numeric: tabular-nums; }
.vig .v-num-gold{ font-size: 13px; font-weight: 600; fill: var(--gold); font-variant-numeric: tabular-nums; }
.vig .v-h       { font-size: 18px; font-weight: 600; fill: var(--on-dark); }
/* The one 880-unit vig canvas (the /platform/ working surface): in the
   tablet band it renders as narrow as ~698px (measured at 834 viewport
   inside band+wrap padding), a 0.79 scale that drags 11px type to 8.7px —
   under the 12px law. The 640-unit vigs pass everywhere.
   16px x (698/880) = 12.7px at the narrowest visible width. */
@media (min-width: 821px) and (max-width: 1280px) {
  .vig svg[viewBox^="0 0 880"] .v-eyebrow, .vig svg[viewBox^="0 0 880"] .v-colh,
  .vig svg[viewBox^="0 0 880"] .v-row2,    .vig svg[viewBox^="0 0 880"] .v-num,
  .vig svg[viewBox^="0 0 880"] .v-num-gold { font-size: 16px; }
  /* .v-row1 is 15px on this canvas — 11.91px rendered at 834, inside the
     gate's tolerance but under the stated law. 17px renders 13.5px. */
  .vig svg[viewBox^="0 0 880"] .v-row1 { font-size: 17px; }
}
/* The 520-unit hero diagram: below 1081 the hero split collapses and it
   renders full-width (safe); above 1280 the column is wide enough (15 x
   0.83 = 12.4px). In the 1081-1279 remainder the column bottoms out at
   ~400px, a 0.77 scale — one extra unit clears the 12px law (16 x 0.77 =
   12.3) without overflowing the record box, which a larger bump did.
   Attribute selectors keep the two-size hierarchy. */
@media (min-width: 1081px) and (max-width: 1279px) {
  svg[viewBox^="0 0 520"] text[font-size="15"] { font-size: 16px; }
  svg[viewBox^="0 0 520"] text[font-size="16"] { font-size: 17px; }
}
.band:not(.band-dark) .vig .v-eyebrow, .band-ice:not(.band-dark) .vig .v-eyebrow,
.band:not(.band-dark) .vig .v-colh,    .band-ice:not(.band-dark) .vig .v-colh    { fill: var(--slate); }
.band:not(.band-dark) .vig .v-row1,    .band-ice:not(.band-dark) .vig .v-row1,
.band:not(.band-dark) .vig .v-h,       .band-ice:not(.band-dark) .vig .v-h       { fill: var(--ink); }
.band:not(.band-dark) .vig .v-row2,    .band-ice:not(.band-dark) .vig .v-row2    { fill: var(--slate); }
.band:not(.band-dark) .vig .v-num,     .band-ice:not(.band-dark) .vig .v-num,
.band:not(.band-dark) .vig .v-num-gold,.band-ice:not(.band-dark) .vig .v-num-gold{ fill: var(--clinical-teal); }  /* gold does not exist on light */

/* Below 820px the vignette is replaced by an identical DOM list — same rows,
   same order, same caption, at real text sizes. */
.vig-list { display: none; }
@media (max-width: 820px) {
  .vig > svg { display: none; }
  .vig-list { display: grid; gap: 0; padding: 4px 16px 12px; }
  .vig-list > div { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 12px; align-items: baseline; padding: 12px 0; border-bottom: 1px solid var(--line); }
  .vig-list > div:last-child { border-bottom: 0; }
  .band-dark .vig-list > div { border-color: var(--line-dark); }
  .vig-list b { font-weight: 500; color: var(--ink); }
  .vig-list small { display: block; font-size: 0.82rem; color: var(--slate); }
  .band-dark .vig-list b { color: var(--on-dark); }
  .band-dark .vig-list small { color: var(--on-dark-2); }
}

/* --- Archetype scoping hooks. The archetypes are not skins: each differs on
   five structural axes and no two share a value on more than two of them.
   A: dark full-bleed hero, signature asset full-.wrap beneath.
   B: dark compressed hero, oversized stage numeral, one full-bleed asset.
   C: LIGHT hero with a specimen rail — the single highest-leverage change
      in the system, since today every route opens dark.
   D: dark typographic hero, pull-quote AS the h1, no asset at all. */
[data-arch="A"] .hero h1 { font-size: var(--display-0); line-height: 0.92; letter-spacing: -0.038em; max-width: 14ch; }
[data-arch="B"] .hero h1 { font-size: var(--display-1); max-width: 16ch; }
[data-arch="C"] .page-hero { background: var(--surface); }
[data-arch="C"] .page-hero h1 { font-size: var(--display-2); max-width: 22ch; }
[data-arch="D"] .page-hero h1 { font-weight: 300; letter-spacing: -0.030em; line-height: 1.06; max-width: 18ch; }
/* D has no signature asset, no diagram and no .rows — its entire visual
   weight is typographic, and after four diagram-led pages a page that is
   only words reads as confidence. That is enforced by the build lint, NOT
   by display:none here: silently hiding content a copy agent wrote is worse
   than failing the build for it. */


/* ============================================================================
   V4 · Reduced motion.
   The principle the shipped .dgm-draw fallback already gets right: reduced
   motion means the FINISHED state, never a blank one. A .sig frozen at
   opacity 0 would erase the diagram — that is the bug to watch for.
   This block comes last so it wins over everything above.
   ============================================================================ */

@media (prefers-reduced-motion: reduce) {
  .js .trace              { stroke-dasharray: none; stroke-dashoffset: 0; animation: none !important; }
  .js .sig,
  .js .sig-idle           { stroke-dasharray: none; stroke-dashoffset: 0; opacity: 1; animation: none !important; }
  .js .node               { opacity: 1; transform: none; animation: none !important; }
  .js .gate-pulse         { opacity: 0.55; transform: none; animation: none !important; }
  .js .sweep              { transform: none; animation: none !important; }
  .js .fade               { opacity: 1; animation: none !important; }
  .js .reveal-mask        { clip-path: none; transition: none; }
  .js .journey-spine::after { animation: none !important; opacity: 0; }
  .journey-spine          { background: rgba(50, 203, 190, 0.28); }
  .hdr-over-dark          { transition: none; }
}

/* ============================================================================
   V4 · D. THE LIGHT-BAND SUBSTITUTION
   The five signature assets are authored on dark. --gold measures 2.28:1 on
   white and --signal-aqua 2.02:1: both are illegal on a light surface, so an
   asset dropped into a light band would be an accessibility failure, not
   merely a subtle one. Rather than ship a second copy of every asset, the
   tokens the assets paint with are RE-POINTED inside a light band, which is
   the substitution table of ART-DIRECTION §3 expressed as CSS.

   Literal hexes, not var() references: --signal-aqua: var(--cobalt) and
   --cobalt: var(--slate) declared together would resolve the first through
   the second and collapse two hues into one, destroying the composition's
   argument. Every value below was measured on BOTH light surfaces.
   ============================================================================ */

/* :not(.band-dark) is load-bearing. A dark section carries BOTH classes
   (class="band band-dark"), so a bare `.band .dgm` repaints every dark asset
   in the light palette — ink labels on obsidian, invisible. Watched fail. */
.band:not(.band-dark) .dgm,
.band:not(.band-dark) .journey,
.band:not(.band-dark) .vig,
.band-ice:not(.band-dark) .dgm,
.band-ice:not(.band-dark) .journey,
.band-ice:not(.band-dark) .vig,
.page-hero:not(.band-dark) .dgm,
.page-hero:not(.band-dark) .journey {
  --signal-aqua:  #356FE6;   /* was aqua 2.02:1 -> cobalt  4.60:1 surface / 4.13:1 ice */
  --gold:         #0B7168;   /* was gold 2.28:1 -> teal    5.87:1 surface / 5.28:1 ice */
  --cobalt:       #536675;   /* process nodes  -> slate    5.96:1 surface / 5.35:1 ice */
  --intel-teal:   #0B7168;   /* 3.52:1 on white is large/non-text only      5.87 / 5.28 */
  --on-dark:      #12212D;   /* -> --ink                  16.39:1 / 14.73:1 */
  --on-dark-2:    #536675;   /* -> --slate                 5.96:1 /  5.35:1 */
  --on-dark-3:    #536675;   /* -> --slate                 5.96:1 /  5.35:1 */
  --panel-dark:   #FFFFFF;   /* NOT --ice: on a .band-ice section that would be ice
                                on ice, the 1.048 step that made the whole of V2 read as
                                one column. White + the 4.28:1 stroke carries it instead. */
  --panel-stroke: rgba(18, 33, 45, 0.22);
  --line-dark-hi: #6E7C88;   /* control boundary            4.28:1 /  3.85:1 */
  --line-recede:  #6E7C88;   /* informative but receding    4.28:1 /  3.85:1 */
  --substrate:    rgba(11, 113, 104, 0.10);   /* decorative, aria-hidden */
  --glow-halo:    rgba(53, 111, 230, 0.06);   /* decorative */
  --glow-bloom:   rgba(53, 111, 230, 0.12);   /* decorative */
}


/* ============================================================================
   V4.2 · E. CHOREOGRAPHY, STAGES, SEQUENCES, CINEMA
   Appended, never rewritten. Nothing above changes; no page renders
   differently until a page applier uses one of the class names below.

   Five laws hold for everything in this block.
   1. Compositor only — transform and opacity. No width, top, margin, or
      background-position on anything that runs more than once.
   2. Every animated class is scoped under `.js`, so a page with JS disabled
      renders the FINISHED state rather than an empty one.
   3. Every animated class has a `prefers-reduced-motion` rule AND a
      `[data-motion-freeze]` rule, both resolving to that same finished state.
   4. Any loop of 5s or longer carries a rest phase and pauses on hover/focus.
   5. Nothing changes faster than 3Hz, and no full-area luminance flashes.
   ============================================================================ */


/* --- E1. SEQUENCED SIGNAL CHOREOGRAPHY v2 -----------------------------------
   `initSignals` already writes --delay and --dur from data-delay / data-dur /
   data-seq. What it could not express was a NAMED PHASE: the hero sequence in
   ART-DIRECTION section 2.4 is eleven stages, and re-timing it meant editing
   delays scattered through 200 lines of SVG.

   `data-choreo` puts the whole tempo in one attribute on the asset:

     <svg class="dgm" data-choreo="origins 220 55 320, intake 520 70 760,
                                   areas 1400 110 300, gate 2400 0 420,
                                   outcome 2700 65 700, return 3900 0 1100">

   Each entry is  PHASE  START-ms  [STAGGER-ms]  [DURATION-ms].
   Elements carry `data-phase="intake"`; they are collected in DOM order and
   staggered within their own phase. A descendant carrying `data-lag="180"`
   starts that many ms after its phase unit — which is how one lane runs its
   .trace at t and its .sig at t+180 without either being hand-timed.
   An element with its own data-delay always keeps it.

   This block is documentation only: the mechanism lives in site.js and writes
   the same two custom properties the shipped primitives already read, so
   .trace .sig .node .sweep .gate-pulse .fade are untouched.

   Staggering is the point. A phase whose stagger is 0 fires as one beat, and
   more than two such beats overlapping is precisely what this attribute makes
   visible in review. */


/* --- E2. STEP-EMPHASIS CHAIN — .chain-live ----------------------------------
   Every step is visible and legible at all times. The cycle emphasises one
   step at a time and NEVER dims the text: --slate at 0.78 alpha over white
   composites to #798894, which measures 3.65:1 — below the body-copy floor.
   So opacity is spent only on decorative marks. Emphasis is the rule, the dot,
   and a 3px nudge. Understandable paused, because paused is its rest state. */

.chain-live { list-style: none; margin: 0; padding: 0; display: grid; }
.chain-step {
  position: relative;
  padding: clamp(16px, 1.6vw, 22px) 0 clamp(16px, 1.6vw, 22px) 26px;
  border-top: 1px solid var(--line);
}
.chain-step:last-child { border-bottom: 1px solid var(--line); }
.chain-step h3, .chain-step h4 { margin: 0 0 4px; }
.chain-step p { margin: 0; font-size: 0.95rem; max-width: 58ch; }
.chain-k {
  display: block; margin-bottom: 6px;
  font-family: var(--font-display); font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--slate); font-variant-numeric: tabular-nums;
}
/* The rule and the dot are decorative, so they may carry the whole emphasis. */
.chain-step::before {
  content: ""; position: absolute; left: 0; top: 14%; bottom: 14%; width: 2px;
  background: var(--clinical-teal);
  transform: scaleY(1); transform-origin: center;
}
.chain-step::after {
  content: ""; position: absolute; left: -3px; top: 50%; width: 8px; height: 8px;
  margin-top: -4px; border-radius: 50%; background: var(--clinical-teal);
  transform: scale(1);
}
.band-dark .chain-step { border-color: var(--line-dark); }
.band-dark .chain-step p { color: var(--on-dark-2); }
.band-dark .chain-k { color: var(--on-dark-3); }
.band-dark .chain-step::before, .band-dark .chain-step::after { background: var(--signal-aqua); }

/* Live state. site.js sets data-chain-live only when the cycle is actually
   running — never under reduced motion, never under freeze, never without JS.
   The de-emphasis rules below are therefore unreachable in all three cases,
   and the component's DEFAULT rendering is its finished state. */
.js .chain-live[data-chain-live] .chain-step {
  transform: translateX(0);
  transition: transform 480ms var(--ease);
}
.js .chain-live[data-chain-live] .chain-step::before {
  transform: scaleY(0.26); opacity: 0.4;
  transition: transform 480ms var(--ease), opacity 480ms var(--ease);
}
.js .chain-live[data-chain-live] .chain-step::after {
  transform: scale(0.5); opacity: 0.35;
  transition: transform 480ms var(--ease-node), opacity 480ms var(--ease);
}
.js .chain-live[data-chain-live] .chain-step.is-on { transform: translateX(3px); }
.js .chain-live[data-chain-live] .chain-step.is-on::before { transform: scaleY(1); opacity: 1; }
.js .chain-live[data-chain-live] .chain-step.is-on::after { transform: scale(1); opacity: 1; }


/* --- E3. SCROLL-LINKED PROGRESSION — .scrollstage ---------------------------
   A state machine, not a scroll animation. Native scroll is never touched: no
   wheel handler, no scrollTo, no pinning beyond `position: sticky`. site.js
   observes the cues with an IntersectionObserver band across the middle of the
   viewport and writes ONE attribute, data-active, plus --mark-x / --mark-y.
   Everything visible between two states is a CSS transition.

   With JS off there is no data-active: every environment renders lit and the
   record mark sits at the container's authored --mark-x, which by convention
   is its LAST position. The scene is then simply the completed journey. */

/* The scene and the cues are SIDE BY SIDE, never stacked with the scene
   sticky above them. Stacked-and-sticky was built first and watched fail:
   a cue scrolling upward passes straight through a sticky scene that has no
   background of its own, and two paragraphs of dark-band body copy render on
   top of the diagram. There is no honest fix from CSS - a sticky element
   cannot inherit the band gradient behind it at the right offset - so the
   layout changed instead. Below 1200px nothing is sticky and nothing can
   collide. */
.scrollstage { position: relative; }
.scrollstage-scene { position: static; }
.scrollstage-cues { display: grid; }
.scrollstage-cue {
  position: relative;
  padding: var(--s4) 0 var(--s4) 26px;
  display: flex; flex-direction: column; justify-content: center;
}
@media (min-width: 1200px) {
  /* No align-items here on purpose: a grid item that does not stretch has no
     room for its sticky child to travel in, and the scene unsticks instantly. */
  .scrollstage { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: clamp(32px, 5vw, 96px); }
  .scrollstage-scene { position: sticky; top: calc(72px + 24px); }
  .scrollstage-cue { min-height: 52vh; padding-block: 0; }
}
.scrollstage-cue h3 { margin-bottom: 8px; }
.scrollstage-cue p { margin: 0; }
.scrollstage-cue::before {
  content: ""; position: absolute; left: 0; top: 22%; bottom: 22%; width: 2px;
  background: var(--clinical-teal);
  transform: scaleY(1); transform-origin: center;
}
.band-dark .scrollstage-cue::before { background: var(--signal-aqua); }
.band-dark .scrollstage-cue p { color: var(--on-dark-2); }
.js .scrollstage[data-active] .scrollstage-cue::before {
  transform: scaleY(0.22); opacity: 0.4;
  transition: transform 520ms var(--ease), opacity 520ms var(--ease);
}
.js .scrollstage[data-active] .scrollstage-cue.is-on::before { transform: scaleY(1); opacity: 1; }

/* The scene. Environments are always drawn; only their emphasis moves, so a
   paused, frozen or reduced-motion render is still the whole journey. */
.stagescene { width: 100%; height: auto; overflow: visible; }
.stagescene text { font-family: var(--font-display); }
.stage-env .core-line { stroke: var(--line-dark-hi); }
.stage-env .stage-label { fill: var(--on-dark-2); }
.stage-env .stage-glow { opacity: 0; }
.js .scrollstage[data-active] .stage-env .core-line { transition: stroke 460ms var(--ease); }
.js .scrollstage[data-active] .stage-env .stage-label { transition: fill 460ms var(--ease); }
.js .scrollstage[data-active] .stage-env .stage-glow { transition: opacity 460ms var(--ease); }
.stage-env.is-on .core-line   { stroke: var(--signal-aqua); }
.stage-env.is-on .stage-label { fill: var(--on-dark); }
.stage-env.is-on .stage-glow  { opacity: 1; }

/* The record. One object, one transform, for the whole journey — which is the
   argument the scene exists to make. */
.stage-mark {
  transform: translate(calc(var(--mark-x, 0) * 1px), calc(var(--mark-y, 0) * 1px));
}
.js .scrollstage[data-active] .stage-mark {
  transition: transform 900ms var(--ease-signal);
}




/* --- E4. PRODUCT-EXPERIENCE SEQUENCES — .vig-seq ----------------------------
   A vignette whose rows change STATE in sequence: pending -> settled, one row
   at a time. Rows never appear or disappear — both state layers occupy the
   same cell from the first frame, so nothing reflows and the frame reads as a
   working surface rather than a diagram assembling itself.

   The whole vignette is driven by ONE animation on the container: a registered
   custom property --seq-t counts 0 -> 12, and each row's progress is
   clamp(0, (--seq-t - --seq-i) * 3, 1). That is why hovering pauses the
   sequence with a single animation-play-state, and why freezing it is one
   `animation: none` — --seq-t reverts to its initial value of 99, which is
   every row settled.

   Cycle shape at the default 18s: fill 0-38%, REST 38-82% (7.9s of stillness),
   unwind 82-100%. One row changes at a time; nothing approaches 3Hz. */

@property --seq-t { syntax: "<number>"; inherits: true; initial-value: 99; }

/* margin:0 because `.vig` is a <figure> and the UA gives it 40px of inline
   margin. On the shipped pages the vignette is full-width and the 80px it
   costs is invisible; in a `.split` right column at 1440 it takes the render
   from 646px to 566px, which drops a 12-unit label to 10.6 CSS px - under the
   rendered-type floor. Measured, both ways. A `.vig-seq` is authored for a
   full-width or `.split-narrow` slot; this is the belt as well as the braces. */
.vig-seq { --seq-cycle: 18s; margin: 0; }
.js .vig-seq.in { animation: seq-run var(--seq-cycle) linear infinite both; }
.js .vig-seq:hover, .js .vig-seq:focus-within, .js .vig-seq.is-rest { animation-play-state: paused; }
@keyframes seq-run {
  0%   { --seq-t: 0; }
  38%  { --seq-t: 12; }
  82%  { --seq-t: 12; }
  100% { --seq-t: 0; }
}

/* --seq-i is written by site.js in DOM order; both --seq-t and --seq-i carry a
   fallback so a browser without @property renders the settled state rather
   than an invalid opacity. */
.seq-step { --seq-p: clamp(0, calc((var(--seq-t, 99) - var(--seq-i, 0)) * 3), 1); }
/* Sequential, not simultaneous: the outgoing label is fully gone at half
   progress before the incoming one starts, so two strings never print in
   one slot (the blind review caught "Dra"+"Drafting"+"fting"). */
.seq-on   { opacity: clamp(0, calc(2 * var(--seq-p) - 1), 1); }
.seq-off  { opacity: clamp(0, calc(1 - 2 * var(--seq-p)), 1); }
.seq-mark {
  transform-box: fill-box; transform-origin: center;
  opacity: var(--seq-p);
  transform: scale(calc(0.55 + 0.45 * var(--seq-p)));
}
/* A settled row's own bar fills left-to-right rather than fading in. */
.seq-fill { transform-box: fill-box; transform-origin: left center; transform: scaleX(var(--seq-p)); }


/* --- E5. CINEMATIC REVEALS — .reveal-cine / .depth-soft ---------------------
   Built for photography that does not exist yet, and working today on the SVG
   scenes. Neither treatment assumes an <img>: both operate on whatever block
   they are put on.

   .reveal-cine is a soft-edged mask wipe, ONCE, on entry. Like .reveal-mask it
   repaints its own area, so the same law applies: maximum one per viewport,
   never on text. It is the cinematic sibling of .reveal-mask — a 105deg soft
   edge instead of a hard clip. */

.js .reveal-cine {
  opacity: 0;
  -webkit-mask-image: linear-gradient(105deg, #000 32%, rgba(0,0,0,0) 58%);
          mask-image: linear-gradient(105deg, #000 32%, rgba(0,0,0,0) 58%);
  -webkit-mask-size: 300% 100%;   mask-size: 300% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: 100% 0;  mask-position: 100% 0;
  transition: opacity 620ms var(--ease),
              mask-position 1150ms var(--ease),
              -webkit-mask-position 1150ms var(--ease);
}
.js .reveal-cine.in, .js .in > .reveal-cine {
  opacity: 1;
  -webkit-mask-position: 0 0; mask-position: 0 0;
}

/* .depth-soft separates a foreground from a background by at most 8px as the
   block crosses the viewport. Scroll-driven, so it costs NO JavaScript and no
   scroll listener; where view() timelines are unsupported the block is simply
   static, which is the finished state. 8px is the ceiling: past that it stops
   reading as depth and starts reading as a broken sticky element. */
.depth-soft { position: relative; }
.depth-back { position: absolute; inset: 0; pointer-events: none; }
.depth-fore { position: relative; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .js .depth-back, .js .depth-fore {
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    .js .depth-back { animation-name: depth-back; }
    .js .depth-fore { animation-name: depth-fore; }
  }
}
@keyframes depth-back { from { transform: translate3d(0, -8px, 0); } to { transform: translate3d(0, 8px, 0); } }
@keyframes depth-fore { from { transform: translate3d(0, 4px, 0); }  to { transform: translate3d(0, -4px, 0); } }


/* --- E6. CARE-SETTINGS STRIP — .strip ---------------------------------------
   Not an icon grid. A horizontally scrollable, snap-aligned row of compact
   luminous scene cards. No carousel JavaScript: CSS scroll-snap, native
   scrolling, native focus-scrolling for the keyboard. The default (no `.js`)
   and the reduced-motion rendering are both a WRAPPED GRID, so a visitor who
   cannot or will not scroll sideways still sees every setting. */

.strip { position: relative; }
.strip-track {
  display: grid; gap: var(--s2);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}
.js .strip-track {
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: minmax(248px, 304px);
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-padding-inline-start: 2px;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  padding-bottom: 6px;
  /* Hidden scrollbars need an affordance (D4). The clipped edge fades so the
     overflow reads as more content and not as a dead end — the same device
     .jump-in already uses. */
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 56px), transparent);
          mask-image: linear-gradient(90deg, #000 calc(100% - 56px), transparent);
}
.js .strip-track::-webkit-scrollbar { display: none; }
.js .strip-card { scroll-snap-align: start; }
.strip-card {
  display: flex; flex-direction: column; gap: 12px;
  min-width: 0; padding: 18px;
  border: 1px solid var(--line-strong); border-radius: var(--r2);
  background: var(--surface);
  transition: transform 220ms var(--ease), border-color 180ms;
}
.strip-card:hover { transform: translateY(-3px); border-color: var(--ink); }
.strip-card h3 { margin: 0; font-size: 1rem; }
.strip-card p { margin: 0; font-size: 0.88rem; max-width: none; }
.strip-scene { width: 100%; height: auto; overflow: visible; }
.band-dark .strip-card { background: var(--panel-dark); border-color: var(--line-dark-hi); }
.band-dark .strip-card p { color: var(--on-dark-2); }
.band-dark .strip-card:hover { border-color: var(--on-dark); }
/* The affordance in words as well as in the fade — and only when there is
   something to scroll, i.e. only when JS turned the grid into a track. */
.strip-hint { display: none; }
.js .strip-hint {
  display: flex; align-items: center; gap: 7px;
  margin-top: 12px;
  font-family: var(--font-display); font-size: 0.78rem; font-weight: 600;
  color: var(--slate);
}
.band-dark .strip-hint { color: var(--on-dark-3); }


/* ============================================================================
   V4.2 · F. REDUCED MOTION for everything in block E.
   The same principle the rest of the file obeys: the fallback is the FINISHED
   state, never a blank one. Deliberately last so it wins.
   ============================================================================ */

@media (prefers-reduced-motion: reduce) {
  /* .chain-live: site.js never sets data-chain-live under reduced motion, so
     the de-emphasis rules cannot match. Stated anyway, because a gate nobody
     has watched fail is not known to work. */
  .js .chain-live[data-chain-live] .chain-step,
  .js .chain-live[data-chain-live] .chain-step.is-on { transform: none; transition: none; }
  .js .chain-live[data-chain-live] .chain-step::before,
  .js .chain-live[data-chain-live] .chain-step::after { transform: none; opacity: 1; transition: none; }

  /* .scrollstage: site.js pins the LAST stage and stops observing. Every
     environment stays lit and the mark does not travel. */
  .js .scrollstage[data-active] .scrollstage-cue::before { transform: none; opacity: 1; transition: none; }
  .js .scrollstage[data-active] .stage-env .core-line,
  .js .scrollstage[data-active] .stage-env .stage-label,
  .js .scrollstage[data-active] .stage-env .stage-glow,
  .js .scrollstage[data-active] .stage-mark { transition: none; }
  .stage-env .core-line   { stroke: var(--signal-aqua); }
  .stage-env .stage-label { fill: var(--on-dark); }
  .stage-env .stage-glow  { opacity: 1; }

  /* .vig-seq: --seq-t reverts to its initial 99 — every row settled. */
  .js .vig-seq, .js .vig-seq.in { animation: none !important; }

  /* .reveal-cine: no mask at all, fully opaque. */
  .js .reveal-cine, .js .reveal-cine.in {
    opacity: 1; transition: none;
    -webkit-mask-image: none; mask-image: none;
  }

  /* .depth-soft: no separation. */
  .js .depth-back, .js .depth-fore { animation: none !important; transform: none; }

  /* .strip: back to the wrapped grid, and no snap. */
  .js .strip-track {
    grid-auto-flow: row;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
    overflow: visible; scroll-snap-type: none;
    -webkit-mask-image: none; mask-image: none;
  }
  .js .strip-hint { display: none; }
  .strip-card { transition: none; }
}


/* ============================================================================
   V4.2 · G. MOTION QA — data-motion-freeze
   Put the attribute on <html> and every animated component in the page snaps
   to its deterministic final frame. It exists so that a visual-regression
   screenshot compares design, not timing.

     await page.addInitScript(() =>
       document.documentElement.setAttribute('data-motion-freeze', ''));
     await page.goto('/');
     await expect(page).toHaveScreenshot();

   The generic rule below finishes any animation that declares `both` fill by
   collapsing it to a 1ms run that has already completed. That covers .trace,
   .sig, .node, .gate-pulse, .sweep, .fade and .journey-spine correctly,
   because each of those ends on the frame it is meant to rest at.

   The two exceptions are stated explicitly underneath, and they are exactly
   the defect this project keeps finding: an animation whose FINAL keyframe is
   the empty state. `sig-loop` ends at opacity 0 and `seq-run` ends at
   --seq-t 0. Collapsing either would erase the asset — the same bug the
   reduced-motion block was written to prevent.
   ============================================================================ */

[data-motion-freeze],
[data-motion-freeze] *,
[data-motion-freeze] *::before,
[data-motion-freeze] *::after {
  animation-delay: -1ms !important;
  animation-duration: 1ms !important;
  animation-iteration-count: 1 !important;
  animation-play-state: paused !important;
  transition: none !important;
  scroll-behavior: auto !important;
}

/* Exception 1 — the idle pulse ends dark. Freeze it lit instead. */
[data-motion-freeze] .sig-idle {
  animation: none !important;
  stroke-dasharray: none; stroke-dashoffset: 0; opacity: 1;
}
/* Exception 2 — the sequence ends unwound. Freeze it settled: with the
   animation removed, --seq-t is its registered initial value, 99. */
[data-motion-freeze] .vig-seq, [data-motion-freeze] .vig-seq.in { animation: none !important; }

/* Components whose state is a class or an attribute rather than an animation.
   site.js pins .scrollstage to its last stage and refuses to start
   .chain-live's cycle, so these only have to remove the de-emphasis. */
[data-motion-freeze] .chain-live[data-chain-live] .chain-step { transform: none; }
[data-motion-freeze] .chain-live[data-chain-live] .chain-step::before,
[data-motion-freeze] .chain-live[data-chain-live] .chain-step::after { transform: none; opacity: 1; }
[data-motion-freeze] .scrollstage[data-active] .scrollstage-cue::before { transform: none; opacity: 1; }
[data-motion-freeze] .reveal-cine {
  opacity: 1; -webkit-mask-image: none; mask-image: none;
}
[data-motion-freeze] .depth-back, [data-motion-freeze] .depth-fore {
  animation: none !important; transform: none;
}


/* ============================================================================
   V4.2 · H. AI VISUAL LANGUAGE
   Intelligence read through the MOTION OF DATA, never through iconography.
   No brain, no robot, no circuit board, no double helix, no chat bubble, no
   sparkle. The argument is made by what the data does: scattered readings
   resolve into a pattern, a core stays quietly alive between requests, items
   route themselves into lanes. Everything here is drawn in the luminous-path
   vocabulary already in this file and uses its nodes, edges and glow stack.

   All three obey the same discipline as block E: transform and opacity only,
   `.js` gating, a rest phase in every loop, pause on hover and focus, off
   below 820px, and a reduced-motion AND freeze resolution that is always the
   ORGANISED final state.
   ============================================================================ */


/* --- H1. .emerge — pattern emergence ----------------------------------------
   The signature "intelligence arriving" moment. The author draws the FINISHED
   structure — a trend line and the points that sit on it, a matched pair, a
   filled schedule — and then gives each point the offset it starts scattered
   at, in user units:

     <g class="emerge" data-seq="180" data-step="70">
       <circle class="emerge-pt" cx="240" cy="180" r="4" style="--ex:-38;--ey:26"/>
       ...
       <use href="#trend" class="core-line trace" data-delay="900"/>
     </g>

   Because the organised position is the authored position, the reduced-motion
   and no-JS renderings are simply the drawing with no transform applied. There
   is no separate "final" markup to keep in sync, which is the failure mode of
   every emergence effect built the other way round.

   Runs ONCE on entry. It is a moment, not a loop; repeating it would turn the
   argument into a screensaver. */

.js .emerge .emerge-pt {
  transform-box: fill-box; transform-origin: center;
  transform: translate(calc(var(--ex, 0) * 1px), calc(var(--ey, 0) * 1px)) scale(0.72);
  opacity: 0.28;
}
.js .in .emerge .emerge-pt, .js .emerge.in .emerge-pt {
  animation: emerge-in var(--dur, 860ms) var(--ease-node) var(--delay, 0ms) both;
}
@keyframes emerge-in {
  from { transform: translate(calc(var(--ex, 0) * 1px), calc(var(--ey, 0) * 1px)) scale(0.72); opacity: 0.28; }
  to   { transform: translate(0, 0) scale(1); opacity: 1; }
}
/* A dash that is scattered rather than a point: same idea, no scale, so a
   tick or a segment does not thicken as it lands. */
.js .emerge .emerge-seg {
  transform-box: fill-box;
  transform: translate(calc(var(--ex, 0) * 1px), calc(var(--ey, 0) * 1px));
  opacity: 0.24;
}
.js .in .emerge .emerge-seg, .js .emerge.in .emerge-seg {
  animation: emerge-seg var(--dur, 860ms) var(--ease-node) var(--delay, 0ms) both;
}
@keyframes emerge-seg {
  from { transform: translate(calc(var(--ex, 0) * 1px), calc(var(--ey, 0) * 1px)); opacity: 0.24; }
  to   { transform: translate(0, 0); opacity: 1; }
}


/* --- H2. .think — the idle intelligence rhythm ------------------------------
   For a core or engine panel: a slow radial breath plus an occasional traverse
   across it. Deliberately quieter than `.sig-idle` — the halo never exceeds
   0.34 alpha and the scan dash is 0.10 of the path rather than 0.16 — because
   this one runs while a visitor is reading the paragraph beside it.

   One 9s cycle: the breath occupies 46% and the traverse 36%, offset from each
   other, so 5.4s of every 9 has nothing moving at all. That is the rest phase,
   and it is why the panel reads as alive rather than as busy. */

.think-pulse { transform-box: fill-box; transform-origin: center; opacity: 0.16; }
.js .in .think .think-pulse, .js .think.in .think-pulse {
  animation: think-pulse var(--think-cycle, 9s) var(--ease) var(--think-delay, 0ms) infinite both;
}
@keyframes think-pulse {
  0%   { transform: scale(1);    opacity: 0.16; }
  22%  { transform: scale(1.06); opacity: 0.34; }
  46%  { transform: scale(1);    opacity: 0.16; }
  100% { transform: scale(1);    opacity: 0.16; }
}

.js .think .think-scan { stroke-dasharray: 0.10 0.90; stroke-dashoffset: 1; opacity: 0; }
.js .in .think .think-scan, .js .think.in .think-scan {
  animation: think-scan var(--think-cycle, 9s) linear calc(var(--think-delay, 0ms) + 900ms) infinite;
}
@keyframes think-scan {
  0%   { stroke-dashoffset: 1; opacity: 0; }
  7%   { opacity: 0.7; }
  30%  { stroke-dashoffset: 0; opacity: 0.7; }
  36%  { stroke-dashoffset: 0; opacity: 0; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}

/* A reader who stops on the panel stops the panel. */
.js .think:hover .think-pulse, .js .think:focus-within .think-pulse,
.js .think:hover .think-scan,  .js .think:focus-within .think-scan { animation-play-state: paused; }


/* --- H3. .classify — routing into lanes -------------------------------------
   The triage and matching feel: items arrive unsorted and settle into the lane
   they belong to. Each item is authored AT its lane and carries the offset it
   starts unsorted at, exactly like .emerge — so, again, the settled rendering
   needs no second copy of the markup.

   12s cycle: settle 0-18%, HOLD 18-84% (7.9s), release 84-100%. The release is
   staggered in the same order as the settle, which reads as items being let go
   rather than as a reset. */

.js .classify .classify-item {
  transform-box: fill-box; transform-origin: center;
  transform: translate(0, 0); opacity: 1;
}
.js .in .classify .classify-item, .js .classify.in .classify-item {
  animation: classify-settle var(--classify-cycle, 12s) var(--ease-node) var(--delay, 0ms) infinite both;
}
@keyframes classify-settle {
  0%   { transform: translate(calc(var(--cx, 0) * 1px), calc(var(--cy, 0) * 1px)); opacity: 0.4; }
  18%  { transform: translate(0, 0); opacity: 1; }
  84%  { transform: translate(0, 0); opacity: 1; }
  100% { transform: translate(calc(var(--cx, 0) * 1px), calc(var(--cy, 0) * 1px)); opacity: 0.4; }
}
.js .classify:hover .classify-item, .js .classify:focus-within .classify-item { animation-play-state: paused; }

/* Below 820px every loop in this block stops and rests at the organised state.
   Same ruling as `.sig-idle`: battery, and honesty. */
@media (max-width: 820px) {
  .js .in .think .think-pulse, .js .think.in .think-pulse { animation: none; opacity: 0.16; transform: none; }
  .js .in .think .think-scan,  .js .think.in .think-scan  { animation: none; opacity: 0; }
  .js .in .classify .classify-item, .js .classify.in .classify-item { animation: none; transform: none; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  /* Organised, settled, still. Never blank: .emerge and .classify resolve to
     the authored positions, .think-pulse to its resting halo, and only the
     traverse — which is a transient, not structure — resolves to invisible. */
  .js .emerge .emerge-pt, .js .in .emerge .emerge-pt, .js .emerge.in .emerge-pt,
  .js .emerge .emerge-seg, .js .in .emerge .emerge-seg, .js .emerge.in .emerge-seg {
    animation: none !important; transform: none; opacity: 1;
  }
  .js .in .think .think-pulse, .js .think.in .think-pulse { animation: none !important; transform: none; opacity: 0.16; }
  .js .in .think .think-scan,  .js .think.in .think-scan  { animation: none !important; opacity: 0; }
  .js .in .classify .classify-item, .js .classify.in .classify-item {
    animation: none !important; transform: none; opacity: 1;
  }
}

/* Freeze. .emerge and .think end on the frame they should rest at, so the
   generic rule in block G already finishes them. `classify-settle` does NOT —
   its 100% keyframe is the UNSORTED state, the same trap as `sig-loop` and
   `seq-run`. Stated explicitly, for the third and last time in this file. */
[data-motion-freeze] .classify-item {
  animation: none !important; transform: none; opacity: 1;
}
[data-motion-freeze] .think-scan { animation: none !important; opacity: 0; }
[data-motion-freeze] .think-pulse { animation: none !important; transform: none; opacity: 0.16; }

/* ---------- Identity: the Caretech AI logo (teal edition v4) ----------
   Gradient C (the owner's reference: bright teal top-right flowing to deep
   blue-teal bottom-left) with the navy hand grown out of its lower arc +
   four bezier S-curve circuit traces out through the opening, two of them
   transitioning teal-to-red with red ring nodes. The C's gradients live in
   each inline mark's <defs>: header ids lgh / lgh-d, footer lgf / lgf-d
   (MARK(id) in build-chrome.js). Dark surfaces switch the C's stroke to the
   lifted -d gradient below; every -d stop measures over 3:1 on obsidian
   (#45D7DB 10.40:1, #3CCCD4 9.35:1, #2CAEBF 6.86:1, #1E7E93 3.87:1 —
   evidence/v5.1/logo5/verify-contrast.js).
   Flat parts measured against the surface each actually sits on:

     .mark-hand  #123A5C on white 11.8:1 · #3A6FA6 on obsidian 3.47:1
                 (the briefed #2E5F94 measures 2.76:1 on #061722 and fails
                 the 3:1 graphic bar — lifted one step, still navy, and
                 never white, which the owner rejected)
     .mark-t     #1B7A82 on white 4.35:1 · #37C8D6 on obsidian 8.31:1
     .mark-r     #E32B2B on white 4.32:1 · #FF5A52 on obsidian 5.93:1

   AI accent: #D13A1F light / #F4703F dark. The slogan line: Clinical Teal
   on light (5.9:1), Signal Aqua on dark (9.0:1). */
.brand { align-items: center; }
.brand .mark { width: 40px; height: 40px; flex: 0 0 40px; }
@media (max-width: 429px) { .brand .mark { width: 36px; height: 36px; flex: 0 0 36px; } }
.brand-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.brand-ai { color: #D13A1F; }
/* Stylish, not urgent: the tagline drops 600 -> 500, opens two notches of
   tracking and sets a hair smaller, so it signs the wordmark instead of
   competing with it. Same Manrope, same case. */
.brand-tag {
  font-family: var(--font-display); font-weight: 500; font-size: 0.695rem;
  letter-spacing: 0.045em; color: var(--clinical-teal);
  white-space: nowrap;
}
.ftr .brand-ai, .hdr-over-dark .brand-ai { color: #F4703F; }
.ftr .brand-tag, .hdr-over-dark .brand-tag { color: var(--signal-aqua); }
/* The C keeps its gradient on dark — it lifts to the -d stops. The ids are
   per-placement (header lgh, footer lgf) so each rule names its own. */
.ftr .mark-c { stroke: url(#lgf-d); }
.hdr-over-dark .mark-c { stroke: url(#lgh-d); }
.ftr .mark-r, .hdr-over-dark .mark-r { stroke: #FF5A52; }
.ftr .mark-t, .hdr-over-dark .mark-t { stroke: #37C8D6; }
@media (max-width: 519px) { .hdr .brand-tag { display: none; } }

/* The hand stays brand navy on every surface; on dark it lifts so the
   silhouette reads without ever turning white. */
.ftr .mark-hand, .hdr-over-dark .mark-hand { fill: #3A6FA6; }
.ftr-tagline { font-family: var(--font-display); font-weight: 700; font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--signal-aqua); margin: 10px 0 8px; }

/* Dark care-settings strip: the scene cards live on obsidian like the rest
   of the system — panels, not paper. Scene strokes brighten to survive. */
.band-dark .strip-card { background: var(--panel-dark); border-color: var(--line-dark); }
.band-dark .strip-card:hover { border-color: var(--line-dark-hi); }
.band-dark .strip-card h3 { color: var(--on-dark); }
.band-dark .strip-card p { color: var(--on-dark-2); }
/* The scenes draw with var()+fallback; on dark we redefine the vars so the
   two-tone survives instead of flattening every stroke to one colour. */
.band-dark .strip-scene { --line-strong: var(--line-dark); --slate: var(--on-dark-3); --clinical-teal: var(--signal-aqua); }
.band-dark .strip-hint { color: var(--on-dark-3); }

/* Light pages: white glass on scroll (opacity raised 0.86 -> 0.94:
   display type ghosted through). Dark-hero pages: dark glass on scroll,
   at EVERY depth — see the one-palette ruling at the header block. */
.hdr.is-scrolled { background: rgba(255, 255, 255, 0.94); }
.hdr-over-dark.is-scrolled {
  /* 0.97, not 0.94: ink body text scrolling beneath ghosted through the
     dark glass as gray scribble (blind review). Blur stays. */
  background: rgba(6, 23, 34, 0.97);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line-dark);
}
/* The jump bar rides directly under the header; on dark-hero pages a paper
   bar under dark glass reads as a seam, so it joins the glass. :has() is
   progressive — without it the bar stays paper, which is the old state. */
body:has(.hdr-over-dark) .jump { background: rgba(6, 23, 34, 0.97); border-bottom-color: var(--line-dark); }
body:has(.hdr-over-dark) .jump a { color: var(--on-dark-2); }
body:has(.hdr-over-dark) .jump a:hover { color: var(--on-dark); background: rgba(247, 250, 252, 0.07); }
body:has(.hdr-over-dark) .jump a[aria-current="true"] { background: var(--surface); color: var(--obsidian); }

/* ---------- Mobile fluency (v4.5 global) ---------- */
.jump { transition: transform 0.25s var(--ease); }
.jump.jump-away { transform: translateY(-110%); }
@media (prefers-reduced-motion: reduce) { .jump { transition: none; } }
/* Pills snap so none sits cut mid-word at the edge. `mandatory`, not
   `proximity` — proximity leaves free-scroll rests mid-pill, which is
   exactly the cut this rule claims to prevent (Android sweep D3). */
.jump-in { scroll-snap-type: x mandatory; scroll-padding-inline: 20px; }
.jump-in a { scroll-snap-align: start; }
/* Touch: the pill itself must be a >=44px target (iPad sweep S4 measured
   34.97px on 36 routes). Vertical padding, not height — the bar grows. */
@media (hover: none) { .jump a { padding-block: 11px; } }

/* ---------- v4.5 mobile fluency · homepage applier ---------- */
.foldlane { display: none; }
@media (max-width: 767px) { .foldlane { display: block; margin-top: 20px; max-width: 380px; } .foldlane svg { display: block; width: 100%; height: auto; } }
.hero h1 em { font-style: normal; color: var(--signal-aqua); }
@media (max-width: 430px) { [data-arch="A"] .hero h1 { font-size: clamp(2.45rem, 11.6vw, 3.05rem); } }
.counts { margin-bottom: clamp(28px, 4vw, 56px); }
@media (max-width: 640px) { .counts { grid-template-columns: 1fr 1fr; gap: 12px; } }
@media (max-width: 479px) { .counts { grid-template-columns: minmax(0, 1fr); } }
.count b, .count span { overflow-wrap: break-word; }
.count { background: rgba(247, 250, 252, 0.04); border: 1px solid var(--line-dark); border-radius: 14px; padding: 18px 16px; }
.count b { display: block; font-family: var(--font-display); font-weight: 800; font-size: clamp(1.3rem, 4.6vw, 1.7rem); line-height: 1.15; letter-spacing: -0.03em; color: var(--on-dark); }
.count span { display: block; margin-top: 8px; font-size: 0.82rem; line-height: 1.35; color: var(--on-dark-2); }
.js .journey-stop::after { content: ""; position: absolute; left: var(--spine-x, -16.5px); top: 7px; bottom: -1px; width: 1px; background: var(--signal-aqua); opacity: 0.5; transform: scaleY(0); transform-origin: top; transition: transform 620ms var(--ease); }
.js .journey-stop.in::after { transform: scaleY(1); }
.journey-core { --spine-x: -0.5px; } .journey-gate { --spine-x: -1.5px; }
.js .journey-stop::before { scale: 0.4; opacity: 0.35; transition: scale 420ms var(--ease-signal), opacity 420ms var(--ease); }
.js .journey-stop.in::before { scale: 1; opacity: 1; }
@media (prefers-reduced-motion: reduce) { .js .journey-stop::after { transform: scaleY(1); transition: none; } .js .journey-stop::before { scale: 1; opacity: 1; transition: none; } }
[data-motion-freeze] .journey-stop::after { transform: scaleY(1) !important; transition: none; }
[data-motion-freeze] .journey-stop::before { scale: 1 !important; opacity: 1 !important; transition: none; }
@media (max-width: 767px) { :root { --pad: 52px; } .head { margin-bottom: 28px; } .pad-xl { padding-block: clamp(48px, 12vw, 72px); } .pad-l { padding-block: clamp(38px, 9.5vw, 56px); } .pad-s { padding-block: clamp(26px, 6.5vw, 36px); } .cta { padding-block: 48px; } .hero { padding-block: calc(72px + 32px) 40px; } }

/* ---------- v5 · Agent E: capability-page component grammar ---------- */
/* Question-tile dashboards (healthcare-analytics #questions). */
.qtile .qmeta { font-family: var(--font-display); font-size: 0.78rem; font-weight: 700; letter-spacing: 0.02em; color: var(--ink); margin: 2px 0 0; }
.qtile .qchart { width: 100%; height: auto; display: block; margin: 14px 0 12px; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; }
.qtile .qstat { font-family: var(--font-display); font-size: 2.1rem; font-weight: 800; line-height: 1.1; color: var(--ink); margin: 14px 0 0; }
.qtile .qstat span { display: block; font-family: var(--font-body); font-size: 0.82rem; font-weight: 600; color: var(--slate); margin-top: 2px; }
.qtile .qchart text { font-family: var(--font-body); }
/* Source illustrations (iot-healthcare #sources). */
.src-ill { width: 100%; max-width: 300px; height: auto; display: block; margin: 0 0 6px; }
.src-merge { width: 100%; height: auto; display: block; margin: 36px 0 0; }
.rec-chip { font-family: var(--font-display); font-size: 0.85rem; font-weight: 700; color: var(--ink); text-align: center; margin: 10px 0 0; }
.rec-chip span { color: var(--slate); font-family: var(--font-body); font-weight: 500; }

/* ---------- v5 · Agent C: settings board + leadership cards ---------- */
/* --- Settings board (used on /industries/) --------------------------------
   The homepage .strip-scene card language, promoted to a wrapped board of
   room-scene tiles. Scenes are 320x150 inline SVG, rooms and objects only,
   one N6 person-mark per scene, no SVG text (so the 12px SVG-text law is
   satisfied vacuously). */
.board { display: grid; gap: var(--s2); grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); margin-top: var(--s4); }
.scene-tile { display: flex; flex-direction: column; gap: 10px; min-width: 0; padding: 20px 20px 22px; border: 1px solid var(--line-strong); border-radius: var(--r2); background: var(--surface); transition: transform 220ms var(--ease), border-color 180ms, box-shadow 220ms; }
.scene-tile:hover { transform: translateY(-3px); border-color: var(--ink); box-shadow: var(--lift-1); }
.scene-tile h3 { margin: 0; font-size: 1.05rem; }
.scene-tile p { margin: 0; font-size: 0.92rem; max-width: none; }
.tile-roles { font-family: var(--font-display); font-size: 0.75rem; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--clinical-teal); }
.scene-tile .pills { margin-top: auto; padding-top: 8px; }
.scene-tile .arrow-link { font-size: 0.85rem; }
@media (prefers-reduced-motion: reduce) { .scene-tile { transition: none; } }

/* --- Leadership cards (used on /about/team/) ------------------------------
   No portraits, no figurative illustration (ART-DIRECTION §8.1). Six distinct
   band-color panels, brand-type monogram, ghost initial, native
   details/summary for the focus-area keywords. Panels use palette darks; the
   two mixes have hex fallbacks for browsers without color-mix. White (--on-dark)
   monogram contrast on the six panels: 17.4 / 15.2 / 12.6 / 5.6 / 8.5 / 7.2. */
.lead-grid { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }
.lead-card { display: flex; flex-direction: column; min-width: 0; }
.lead-panel { position: relative; display: grid; place-items: center; aspect-ratio: 16 / 9; border-radius: var(--r2); overflow: hidden; margin-bottom: 18px; }
.lead-panel .mono { position: relative; z-index: 1; font-family: var(--font-display); font-weight: 800; font-size: clamp(2.5rem, 3.2vw, 3.3rem); letter-spacing: 0.02em; color: var(--on-dark); }
.lead-panel .mono-ghost { position: absolute; right: -10px; bottom: -42px; font-family: var(--font-display); font-weight: 800; font-size: 9.5rem; line-height: 1; color: rgba(247, 250, 252, 0.07); user-select: none; }
.lead-panel[data-band="1"] { background: var(--obsidian); }
.lead-panel[data-band="2"] { background: var(--atlantic); }
.lead-panel[data-band="3"] { background: var(--deep-ocean); }
.lead-panel[data-band="4"] { background: var(--clinical-teal); }
.lead-panel[data-band="5"] { background: #20478E; background: color-mix(in srgb, var(--cobalt) 55%, var(--obsidian)); }
.lead-panel[data-band="6"] { background: #0C5F5D; background: color-mix(in srgb, var(--intel-teal) 55%, var(--obsidian)); }
.lead-card h3 { margin-bottom: 4px; }
.lead-role { font-size: 0.88rem; font-weight: 600; color: var(--ink); margin: 0 0 10px; }
.lead-card > p:not(.lead-role) { font-size: 0.94rem; color: var(--slate); margin: 0 0 12px; }
.lead-card .arrow-link { font-size: 0.9rem; }
.lead-more { margin-top: auto; padding-top: 12px; }
.lead-more summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px; padding: 10px 0; border-top: 1px solid var(--line); font-family: var(--font-display); font-size: 0.82rem; font-weight: 600; color: var(--slate); }
.lead-more summary::-webkit-details-marker { display: none; }
.lead-more summary::after { content: "+"; margin-left: auto; font-size: 1.05rem; line-height: 1; color: var(--clinical-teal); }
.lead-more[open] summary::after { content: "\2212"; }
.lead-more p { font-size: 0.9rem; color: var(--slate); margin: 0; padding-bottom: 6px; }

/* ---------- v5 · Agent D: the Care Moves signature scrollstage ---------- */
/* v5 · Agent D · the care-moves signature scrollstage. */
#cm-scene { display: block; max-width: 700px; margin-inline: auto; }
/* site.css makes .scrollstage-scene itself sticky, but a stretched grid
   item is as tall as its track and has no room to travel — the sticky
   element must be a CHILD of the stretched item (site.css's own comment
   states the law; this wrapper applies it). NOTE FOR COORDINATOR: this is
   a latent defect in E3 for every future .scrollstage user — consider
   fixing .scrollstage-scene in site.css the same way. */
@media (min-width: 1200px) {
  .cm-sticky { position: sticky; top: calc(72px + 40px); }
  /* Room for the scene to stay pinned while the last cue is read. */
  #point-solutions .scrollstage { padding-bottom: 16vh; }
}
#cm-scene .cm-tool { transform-box: fill-box; }
.js .scrollstage[data-active] #cm-scene .cm-tool { transition: transform 950ms var(--ease-signal); }
.js .scrollstage[data-active="tools"] #cm-scene .cm-tool,
.js .scrollstage[data-active="web"]   #cm-scene .cm-tool {
  transform: translate(calc(var(--bx, 0) * 1px), calc(var(--by, 0) * 1px));
}
#cm-scene .cm-web { opacity: 0; }
.js .scrollstage[data-active] #cm-scene .cm-web,
.js .scrollstage[data-active] #cm-scene .cm-after,
.js .scrollstage[data-active] #cm-scene .cm-ext,
.js .scrollstage[data-active] #cm-scene .cm-cap { transition: opacity 700ms var(--ease); }
.js .scrollstage[data-active="tools"] #cm-scene .cm-web.cm-edge1 { opacity: 0.55; }
.js .scrollstage[data-active="web"]   #cm-scene .cm-web { opacity: 1; }
.js .scrollstage[data-active="tools"] #cm-scene .cm-after,
.js .scrollstage[data-active="web"]   #cm-scene .cm-after,
.js .scrollstage[data-active="tools"] #cm-scene .cm-ext,
.js .scrollstage[data-active="web"]   #cm-scene .cm-ext,
.js .scrollstage[data-active="platform"] #cm-scene .cm-ext { opacity: 0; }
#cm-scene .cm-cap { opacity: 0; }
#cm-scene .cm-cap-after { opacity: 1; }
.js .scrollstage[data-active="tools"] #cm-scene .cm-cap-tools { opacity: 1; }
.js .scrollstage[data-active="web"]   #cm-scene .cm-cap-web   { opacity: 1; }
.js .scrollstage[data-active="tools"] #cm-scene .cm-cap-after,
.js .scrollstage[data-active="web"]   #cm-scene .cm-cap-after { opacity: 0; }
/* Below the 821 gate the scene's 13-unit type cannot hold 12 CSS px;
   the cues carry the whole argument in DOM text, so the scene rests. */
@media (max-width: 820px) { #cm-scene { display: none; } }
/* Freeze and reduced motion: transitions are already killed globally;
   stated anyway so the resolution is explicit, not inherited. */
@media (prefers-reduced-motion: reduce) {
  .js .scrollstage[data-active] #cm-scene .cm-tool,
  .js .scrollstage[data-active] #cm-scene .cm-web,
  .js .scrollstage[data-active] #cm-scene .cm-after,
  .js .scrollstage[data-active] #cm-scene .cm-ext,
  .js .scrollstage[data-active] #cm-scene .cm-cap { transition: none; }
}
[data-motion-freeze] #cm-scene .cm-tool { transform: none !important; }

/* ---------- v5.1 · Agent P1: contact column, insight cards, leader glyphs ---------- */
/* --- contact: the right-hand column of the enquiry split ------------------- */
/* .cf-steps is the "what happens after you send" boxed mini-journey in the
   trust grammar: three boxed stages joined by a teal connector; the third box
   (a reply from a person) is the gate and takes the teal border. Light band:
   clinical-teal only, no gold. */
.cf-side { display: grid; gap: var(--s4); }
.cf-side > .block { padding-top: var(--s3); }
.cf-steps { list-style: none; margin: 10px 0 0; padding: 0; display: grid; }
.cf-step {
  border: 1px solid var(--line-strong); border-radius: var(--r2);
  background: var(--surface); padding: 16px 18px 15px;
}
.cf-step + .cf-step { margin-top: 26px; position: relative; }
.cf-step + .cf-step::before {
  content: ""; position: absolute; top: -27px; left: 27px;
  height: 26px; width: 1px; background: var(--clinical-teal); opacity: 0.6;
}
.cf-step + .cf-step::after {
  content: ""; position: absolute; top: -8px; left: 24px;
  border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-top: 5px solid var(--clinical-teal); opacity: 0.7;
}
.cf-step-gate { border-color: var(--clinical-teal); }
.cf-step-k {
  display: flex; align-items: baseline; gap: 10px;
  font-family: var(--font-display); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--clinical-teal); margin-bottom: 6px;
}
.cf-step-k b { font-weight: 600; color: var(--slate); font-variant-numeric: tabular-nums; }
.cf-step p { margin: 0; font-size: 0.92rem; color: var(--slate); }
/* per-market note: IN / US, two columns until phones */
.cf-market-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); margin-top: 14px; }
@media (max-width: 519px) { .cf-market-grid { grid-template-columns: 1fr; } }
.cf-market { border-top: 1px solid var(--line); padding-top: 12px; }
.cf-market h4 { margin: 8px 0 4px; }
.cf-market p { font-size: 0.9rem; margin: 0 0 8px; }
.cf-market .arrow-link { font-size: 0.85rem; }
.cf-chip {
  display: inline-block; font-family: var(--font-display); font-size: 0.66rem;
  font-weight: 800; letter-spacing: 0.08em; padding: 2px 6px;
  border: 1px solid var(--line-strong); border-radius: 4px; color: var(--slate);
}
/* compact .row variant inside the narrow column */
.cf-side .rows { margin-top: 6px; }
.cf-side .row { padding: var(--s3) 2px; grid-template-columns: 34px minmax(0, 1fr) auto; gap: 14px; }
.cf-side .row-b p { font-size: 0.9rem; }

/* --- insights index: essay identity cards ---------------------------------- */
/* Each card carries a textless miniature of that essay's key diagram on a
   dark tile. The tile carries class="band-dark" so the gold-only-on-dark
   gate (quality.spec) reads it as a dark surface — the gradient below then
   overrides .band-dark's band-scale background. Miniatures are textless, so
   the 12px SVG-text floor is not in play. */
.ins-grid { display: grid; gap: clamp(20px, 2.2vw, 36px); grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1080px) { .ins-grid { grid-template-columns: minmax(0, 1fr); max-width: 620px; } }
.ins-card {
  display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--line-strong); border-radius: var(--r3);
  overflow: hidden; background: var(--surface);
  transition: transform 220ms var(--ease), border-color 180ms, box-shadow 220ms;
}
.ins-card:hover { transform: translateY(-3px); border-color: var(--ink); box-shadow: var(--lift-2); }
.ins-vis { display: block; aspect-ratio: 16 / 9; background: linear-gradient(135deg, #0A2438 0%, #061722 100%); }
.ins-vis svg { width: 100%; height: 100%; display: block; }
.ins-body { display: flex; flex-direction: column; gap: 8px; padding: 20px 22px 22px; flex: 1; }
.ins-body h3 { margin: 0; }
.ins-body > p { margin: 0; font-size: 0.94rem; color: var(--slate); max-width: none; }
.ins-meta {
  margin-top: auto; padding-top: 14px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  font-family: var(--font-display); font-size: 0.8rem; font-weight: 600;
  color: var(--slate); letter-spacing: 0.02em;
}
.ins-meta .arrow-link { font-size: 0.85rem; }

/* --- about/team: per-card remit glyph row ---------------------------------- */
/* One micro-glyph row per leadership panel, bottom-left, tied to the remit
   (signal / credential card / network / people / message thread / calendar).
   Stroked #F7FAFC: >=3:1 on all six panel band colors, including the
   lightest (--clinical-teal, ~6.1:1 at full opacity). */
.lead-glyph { position: absolute; left: 18px; bottom: 14px; width: 96px; height: 24px; z-index: 1; opacity: 0.92; }

/* ---------- v5.1 · Agent P2: gate scrollstage + why-page ring rail ---------- */
/* ---- v5.1 · P2 · /solutions/healthcare-shift-management/ #gate ------------
   The confirmation-gate product sequence, on the E3 .scrollstage state
   machine. The scene SVG is AUTHORED at its finished frame (refusal
   recorded): with JS off there is no [data-active] and none of the rules
   below match, so the complete final frame renders. Under reduced motion and
   data-motion-freeze, site.js pins the LAST stage ("record"), which hides
   nothing. Stage state is scroll-driven; transitions are opacity only, and
   no step is gated behind a timer. */
#sg-scene { display: block; max-width: 640px; margin-inline: auto; }
@media (min-width: 1200px) {
  /* site.css makes .scrollstage-scene itself sticky, but a stretched grid
     item has no room to travel — the sticky must be a CHILD of the grid item
     (the care-moves .cm-sticky ruling, reapplied). Same latent E3 defect
     Agent D flagged; still worth fixing in .scrollstage-scene itself. */
  .sg-sticky { position: sticky; top: calc(72px + 40px); }
  #gate .scrollstage { padding-bottom: 16vh; }
}
.js .scrollstage[data-active] #sg-scene .sg-step,
.js .scrollstage[data-active] #sg-scene .sg-cap { transition: opacity 600ms var(--ease); }
/* Future steps are hidden until their stage; reached steps stay. */
.js .scrollstage[data-active="request"]  #sg-scene :is(.sg-s2, .sg-s3, .sg-s4, .sg-s5),
.js .scrollstage[data-active="conflict"] #sg-scene :is(.sg-s3, .sg-s4, .sg-s5),
.js .scrollstage[data-active="confirm"]  #sg-scene :is(.sg-s4, .sg-s5),
.js .scrollstage[data-active="refuse"]   #sg-scene .sg-s5 { opacity: 0; }
/* One caption at a time. The authored caption is the finished one. */
#sg-scene .sg-cap { opacity: 0; }
#sg-scene .sg-cap-record { opacity: 1; }
.js .scrollstage[data-active="request"]  #sg-scene .sg-cap-request,
.js .scrollstage[data-active="conflict"] #sg-scene .sg-cap-conflict,
.js .scrollstage[data-active="confirm"]  #sg-scene .sg-cap-confirm,
.js .scrollstage[data-active="refuse"]   #sg-scene .sg-cap-refuse,
.js .scrollstage[data-active="record"]   #sg-scene .sg-cap-record { opacity: 1; }
.js .scrollstage[data-active="request"]  #sg-scene .sg-cap-record,
.js .scrollstage[data-active="conflict"] #sg-scene .sg-cap-record,
.js .scrollstage[data-active="confirm"]  #sg-scene .sg-cap-record,
.js .scrollstage[data-active="refuse"]   #sg-scene .sg-cap-record { opacity: 0; }
/* Below the 821 gate the 13-unit type could fall under 12 CSS px; the cues
   carry the whole story in DOM text (the care-moves precedent). */
@media (max-width: 820px) { #sg-scene { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .js .scrollstage[data-active] #sg-scene .sg-step,
  .js .scrollstage[data-active] #sg-scene .sg-cap { transition: none; }
}

/* ---- v5.1 · P2 · /why-caretech-ai/ #human-command -------------------------
   At >=1440 the 720px-capped a04 ring stops floating in dead flanks: the band
   becomes [720px ring | rail of the six ring stages]. The ring keeps exactly
   720px because its 18-unit type renders 12.96px there — a narrower track
   breaks the rendered-type law. The rail is duplicate-summary content:
   below 1440 it hides; the ring labels (>=1080) or the journey (<1080) carry
   the stages instead. */
.ai-rail { display: none; }
@media (min-width: 1440px) {
  .ai-grid { display: grid; grid-template-columns: 720px minmax(0, 1fr); gap: clamp(48px, 6vw, 128px); align-items: center; }
  .ai-rail { display: block; max-width: 560px; }
  .ai-rail .rail-v { font-size: 0.92rem; }
}

/* ---------- v5.1 · Agent FM: refused-delivery journey stop ---------- */
/* --- .journey-no — the refused delivery. The desktop Messaging diagram
   crosses out "ALL WARD STAFF"; the mobile twin needs a stop that visibly
   did NOT happen. Marker mirrors .journey-return's dashed ring; the struck
   heading is the crossed-out box. All colors are var()s that the light-band
   remap re-points inside .journey, so no new literals and no gold leak. */
.journey-no::before {
  background: none;
  border: 1px dashed var(--line-recede);
  width: 9px; height: 9px; left: -21px;
}
.journey-no .journey-h {
  color: var(--on-dark-3);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.journey-no p { color: var(--on-dark-2); }
/* Light-band overrides: must outrank site.css's
   .band:not(.band-dark) .journey-stop p (0,3,1) and .journey-h rules. */
.band:not(.band-dark) .journey-no .journey-h { color: var(--slate); }
.band:not(.band-dark) .journey-no p,
.band-ice:not(.band-dark) .journey-no p { color: var(--slate); }

/* ---------- v5.1 · Agent CM: care-moves one-thread journey ---------- */
/* The scene SVG is AUTHORED at its finished frame: the thread fully
   accumulated, the last scene resolved, the person at the follow-up
   chair. With JS off there is no [data-active] and none of the
   stage rules below match, so the complete journey renders. Under
   reduced motion or data-motion-freeze, site.js pins the LAST stage
   ("followup"), which hides nothing. Scroll-driven via the shipped
   E3 .scrollstage machine — no new JS. */
#cj-scene { display: block; max-width: 600px; margin-inline: auto; }
@media (min-width: 1200px) {
  /* The sticky must be a CHILD of the stretched grid item — the
     .cm-sticky ruling, reapplied (E3's latent defect stands). */
  .cj-sticky { position: sticky; top: calc(72px + 40px); }
  #one-thread .scrollstage { padding-bottom: 16vh; }
}
/* Arrivals accumulate: each stage's entries stay once reached; only
   FUTURE entries are hidden. Authored = everything reached. */
.js .scrollstage[data-active] #cj-scene :is(.cj-a1, .cj-a2, .cj-a3, .cj-a4, .cj-a5) { transition: opacity 600ms var(--ease); }
.js .scrollstage[data-active="hospital"]   #cj-scene :is(.cj-a2, .cj-a3, .cj-a4, .cj-a5),
.js .scrollstage[data-active="home"]       #cj-scene :is(.cj-a3, .cj-a4, .cj-a5),
.js .scrollstage[data-active="rehab"]      #cj-scene :is(.cj-a4, .cj-a5),
.js .scrollstage[data-active="diagnostic"] #cj-scene .cj-a5 { opacity: 0; }
/* Rooms are all always drawn; only emphasis moves (E3 law). Dimming
   exists only while the state machine is running. */
.js .scrollstage[data-active] #cj-scene .cj-room { opacity: 0.45; transition: opacity 600ms var(--ease); }
.js .scrollstage[data-active] #cj-scene .cj-room.is-on { opacity: 1; }
/* --- The purpose-built vertical journey (below the 1200 grid gate):
   a scene stack with the thread as a living spine. Not a shrink. --- */
.cj-stack { position: relative; max-width: 600px; margin-inline: auto; display: grid; gap: var(--s5); padding-left: 28px; margin-top: var(--s5); }
.cj-stack::before { content: ""; position: absolute; left: 8px; top: 10px; bottom: 10px; width: 2px; border-radius: 1px; background: var(--signal-aqua); opacity: 0.55; }
.cj-card { position: relative; }
.cj-card::before { content: ""; position: absolute; left: -24px; top: 4px; width: 10px; height: 10px; border-radius: 50%; background: var(--signal-aqua); }
.cj-mini { width: 100%; height: auto; max-width: 420px; display: block; margin: 6px 0 14px; overflow: visible; }
.cj-k { font-family: var(--font-display); font-size: 0.8rem; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase; color: var(--on-dark-3); margin: 0 0 6px; }
.cj-card h3 { margin: 0 0 8px; }
.cj-card > p { margin: 0; color: var(--on-dark-2); }
.cj-tally { margin: 12px 0 0; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.cj-tally .cj-t-k { font-family: var(--font-display); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.4px; color: var(--on-dark-3); }
.cj-tally b { font-family: var(--font-display); font-weight: 600; font-size: 0.78rem; color: var(--on-dark-2); border: 1px solid var(--line-dark-hi); border-radius: 999px; padding: 3px 10px; }
.cj-tally b.cj-gate { color: var(--gold); border-color: var(--gold); }
/* One rendering at a time: the stage plays >=1200, the stack below. */
@media (max-width: 1199px) { #one-thread .scrollstage { display: none; } }
@media (min-width: 1200px) { .cj-stack { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .js .scrollstage[data-active] #cj-scene :is(.cj-a1, .cj-a2, .cj-a3, .cj-a4, .cj-a5),
  .js .scrollstage[data-active] #cj-scene .cj-room { transition: none; }
}
[data-motion-freeze] #cj-scene :is(.cj-a1, .cj-a2, .cj-a3, .cj-a4, .cj-a5),
[data-motion-freeze] #cj-scene .cj-room { transition: none !important; }

/* ---------- v5.1 · Agent LT: capability spectrum + trust posture grid ---------- */
/* --- §16 · /about/team/ — the leadership capability spectrum.
   A grid mapping leaders to strengths; every chip restates a role or
   focus area on the cards below it. Light band (band-ice): slate on ice
   measures 5.35:1, ink on white 15.9:1 (F0 §1 color table). */
.spectrum { border-top: 1px solid var(--line-strong); }
.spec-row { display: grid; grid-template-columns: clamp(150px, 22vw, 235px) 1fr; gap: 10px 20px; align-items: center; padding: 13px 0; border-bottom: 1px solid var(--line); }
.spec-k { font-family: var(--font-display); font-size: 0.72rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; color: var(--slate); }
.spec-who { display: flex; flex-wrap: wrap; gap: 8px; }
.spec-chip { font-size: 0.82rem; font-weight: 600; color: var(--ink); border: 1px solid var(--line-strong); border-radius: 999px; padding: 3px 12px; background: var(--surface); }
@media (max-width: 560px) { .spec-row { grid-template-columns: 1fr; gap: 8px; padding: 14px 0; } }

/* --- §17 · /trust/ — LAYER ONE executive trust grid.
   Nine domains as scannable .block tiles on the ice band; icons are
   non-text inline SVG (clinical teal on ice measures 5.28:1). */
.tcell { display: flex; flex-direction: column; }
.tcell .tico { width: 26px; height: 26px; margin-bottom: 12px; color: var(--clinical-teal); flex: none; }
.tcell h3 { font-family: var(--font-display); font-size: 0.76rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink); }

/* ---------- v5.1 · Agent DX: the specimen journey board ---------- */
.dx-board {
  background: var(--panel-dark);
  border: 1px solid var(--panel-stroke);
  border-radius: var(--r2);
  padding: clamp(20px, 3vw, 36px);
}
.dx-cols { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: clamp(24px, 3.4vw, 48px); }
@media (max-width: 1079px) { .dx-cols { grid-template-columns: minmax(0, 1fr); } }

.dx-head { display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: baseline; justify-content: space-between; margin-bottom: 14px; }
.dx-id { font-family: var(--font-display); font-weight: 700; font-size: 17px; color: var(--on-dark); letter-spacing: 0.2px; }
.dx-kicker { font-family: var(--font-display); font-size: 12px; font-weight: 700; letter-spacing: 1.6px; color: var(--on-dark-3); margin: 0; }

/* --- The rail ------------------------------------------------------------- */
.dx-rail { position: relative; padding-left: 28px; }
.dx-spine { position: absolute; left: 6px; top: 10px; bottom: 10px; width: 2px; background: rgba(50, 203, 190, 0.22); border-radius: 1px; }
.dx-spine::after { content: ""; position: absolute; inset: 0; background: var(--signal-aqua); opacity: 0.85; border-radius: 1px; transform-origin: top; }

.dx-stop { position: relative; display: flex; gap: 10px 14px; align-items: baseline; justify-content: space-between; padding: 9px 0; border-bottom: 1px solid var(--line-dark); }
.dx-stop:last-child { border-bottom: 0; }
.dx-stop::before { content: ""; position: absolute; inset: 2px -8px; border-radius: 8px; background: var(--glow-bloom); opacity: 0; pointer-events: none; }
.dx-b { min-width: 0; flex: 1 1 auto; }
.dx-b b { display: block; font-family: var(--font-body); font-weight: 600; font-size: 15px; line-height: 1.45; color: var(--on-dark); overflow-wrap: break-word; }
.dx-b small { display: block; font-size: 13px; line-height: 1.5; color: var(--on-dark-2); overflow-wrap: break-word; }
.dx-gate .dx-b b { color: var(--gold); }

.dx-dot { position: absolute; left: -26px; top: 14px; width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid var(--line-dark-hi); }
.dx-dot::after { content: ""; position: absolute; inset: 2px; border-radius: 50%; background: var(--signal-aqua); }
.dx-gate .dx-dot { border-color: var(--gold); }
.dx-gate .dx-dot::after { background: var(--gold); }
.dx-plain { padding-left: 0; }
.dx-plain::before { inset: 2px -6px; }

/* State cell: off / (hold) / on stacked in one grid cell — no reflow, ever. */
.dx-state { display: grid; grid-template-columns: minmax(min-content, 1fr); flex: 0 1 auto; min-width: min-content; text-align: right; }
.dx-state > span { grid-area: 1 / 1; min-width: 0; font-family: var(--font-body); font-weight: 600; font-size: 13px; line-height: 1.5; font-variant-numeric: tabular-nums; overflow-wrap: break-word; }
.dx-off { color: var(--on-dark-3); opacity: 0; }
.dx-hold { color: var(--gold); opacity: 0; }
.dx-on { color: var(--signal-aqua); }
.dx-gate .dx-on { color: var(--gold); }

/* --- The work surface (ai-diagnostic only) --------------------------------- */
.dx-work { min-width: 0; border-left: 1px solid var(--line-dark); padding-left: clamp(20px, 2.4vw, 40px); }
@media (max-width: 1079px) { .dx-work { border-left: 0; padding-left: 0; border-top: 1px solid var(--line-dark); padding-top: 20px; margin-top: 8px; } }
.dx-spark { display: block; width: 100%; max-width: 300px; height: auto; margin: 12px 0 4px; }
.dx-sparknote { font-size: 12px; line-height: 1.5; color: var(--on-dark-3); margin: 0 0 8px; }

.dx-bars { display: block; margin: 6px 0 0; padding: 0; max-width: 220px; }
.dx-bars i { display: block; height: 5px; border-radius: 2.5px; background: var(--on-dark-3); opacity: 0.55; transform-origin: left center; margin-top: 5px; }
.dx-bars i:nth-child(2) { width: 84%; }
.dx-bars i:nth-child(3) { width: 62%; }

.dx-queue { margin-top: 16px; border: 1px solid var(--line-dark-hi); border-radius: var(--r1); padding: 10px 12px; overflow: hidden; }
.dx-q { display: flex; justify-content: space-between; gap: 12px; font-family: var(--font-body); font-size: 13px; font-weight: 500; line-height: 1.5; color: var(--on-dark-2); padding: 4px 0; }
.dx-q1 { color: var(--on-dark); font-weight: 600; }
.dx-q1 .dx-qpos { color: var(--signal-aqua); font-weight: 600; }

.dx-foot { font-size: 14px; line-height: 1.6; color: var(--on-dark-2); margin: 18px 0 0; max-width: 68ch; }

/* --- MOTION — one run, gated three ways ------------------------------------
   html.js (no-JS renders finished) + .dx-run.in (site.js's observer; .dx-run
   is also .rise, so it is on the WATCH list) + no-preference (reduced motion
   renders finished). Freeze collapses everything to its final frame. */
@media (prefers-reduced-motion: no-preference) {
  /* Pre-reveal start states — only while JS is on and .in has not landed. */
  html.js .dx-run:not(.in) .dx-spine::after { transform: scaleY(0); }
  html.js .dx-run:not(.in) .dx-dot::after,
  html.js .dx-run:not(.in) .dx-on { opacity: 0; }
  html.js .dx-run:not(.in) .dx-off { opacity: 1; }
  html.js .dx-run:not(.in) .dx-bars i { transform: scaleX(0); }

  html.js .dx-run.in .dx-on { animation: dx-in 360ms var(--ease) calc(var(--t2, var(--t, 0)) * 1ms) both; }
  html.js .dx-run.in .dx-off { opacity: 1; animation: dx-out 360ms var(--ease) calc(var(--t, 0) * 1ms) both; }
  html.js .dx-run.in .dx-dot::after { animation: dx-in 320ms var(--ease-node) calc(var(--t, 0) * 1ms) both; }
  html.js .dx-run.in .dx-stop::before { animation: dx-flash 700ms var(--ease) calc(var(--t, 0) * 1ms) both; }
  html.js .dx-run.in .dx-hold { animation: dx-held 760ms var(--ease) calc(var(--t, 0) * 1ms) both; }
  html.js .dx-run.in .dx-bars i { animation: dx-grow 420ms var(--ease-signal) calc(var(--t, 0) * 1ms) both; }
  html.js .dx-run.in .dx-bars i:nth-child(2) { animation-delay: calc((var(--t, 0) + 170) * 1ms); }
  html.js .dx-run.in .dx-bars i:nth-child(3) { animation-delay: calc((var(--t, 0) + 340) * 1ms); }

  /* The queue reorders: 4471-B is AUTHORED first (the resting truth) and
     starts two rows down; the routine pair starts one row up. */
  html.js .dx-run.in .dx-q1 { animation: dx-q-up 460ms var(--ease-node) calc(var(--t, 0) * 1ms) both; }
  html.js .dx-run.in .dx-q2,
  html.js .dx-run.in .dx-q3 { animation: dx-q-down 460ms var(--ease-node) calc(var(--t, 0) * 1ms) both; }

  /* The spine travels, STOPS at the gold stop, then finishes the run. */
  html.js .dx-run.in.dx-r10 .dx-spine::after { animation: dx-spine10 var(--sd, 6900ms) linear var(--st, 240ms) both; }
  html.js .dx-run.in.dx-r9 .dx-spine::after { animation: dx-spine9 var(--sd, 6300ms) linear var(--st, 300ms) both; }
}

@keyframes dx-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes dx-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes dx-flash { 0% { opacity: 0; } 45% { opacity: 0.9; } 100% { opacity: 0; } }
@keyframes dx-held { 0% { opacity: 0; } 18% { opacity: 1; } 82% { opacity: 1; } 100% { opacity: 0; } }
@keyframes dx-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes dx-q-up { from { transform: translateY(200%); } to { transform: none; } }
@keyframes dx-q-down { from { transform: translateY(-100%); } to { transform: none; } }
/* Gate sits at stop 7 of 10 (~63%) / 7 of 9 (~70%): travel, hold ~700ms, go. */
@keyframes dx-spine10 { 0% { transform: scaleY(0); } 55% { transform: scaleY(0.63); } 65% { transform: scaleY(0.63); } 100% { transform: scaleY(1); } }
@keyframes dx-spine9 { 0% { transform: scaleY(0); } 58% { transform: scaleY(0.70); } 69% { transform: scaleY(0.70); } 100% { transform: scaleY(1); } }

/* ---------- v5.1 · Motion QA F1: offscreen rest for idle loops ---------- */
/* !important: the loops are declared via animation shorthands at higher
   specificity; the offstage rest must win unconditionally. Paused, not
   removed — scrolling back resumes the loop where it rested. */
.offstage .sig-idle, .offstage .think-scan, .offstage .think-pulse,
.offstage .gate-pulse, .offstage.think .think-scan, .offstage.think .think-pulse {
  animation-play-state: paused !important;
}

/* ---------- v5.1 · responsive sweep: chart type floor at 320 ---------- */
/* The 280-unit qtile charts render at ~0.91 scale on 320px screens,
   dragging 13-unit labels to 11.81px — under the 12px law (the sweep's
   one failure in 660 cells). 14 units renders 12.7px. */
@media (max-width: 359px) { .qtile .qchart text { font-size: 14px; } }

/* ---------- v5.3 · The mark is the owner's artwork ----------
   Served as an image so what ships is exactly the supplied file. The dark
   variant is the same artwork with a luminance floor (the navy hand would
   otherwise sit at ~1.6:1 on obsidian). Sizing rules above still apply. */
.brand .mark {
  background: url("/assets/img/logo-mark.2128409369.png") center / contain no-repeat;
}
.ftr .mark, .hdr-over-dark .mark {
  background-image: url("/assets/img/logo-mark-dark.4d954c8f9c.png");
}

/* ---------- v5.3 · homepage: hero pairing, choreography caps, engine legend ---------- */
/* --- Idle begins when the intro ends, and the intro now ends at ~2.3s. ----- */
[data-page="home"] { --idle-start: 2500ms; }

/* --- A. The hero pairing. ------------------------------------------------- */
[data-page="home"] .hero-grid { display: grid; }
@media (min-width: 1200px) {
  [data-page="home"] .hero-grid {
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    gap: clamp(40px, 3.4vw, 72px);
    align-items: end;
  }
  /* The h1's descender box, not its baseline, is what `end` aligns; pull the
     right column up by the difference so the lead's first line sits with the
     h1's last line rather than below it. */
  [data-page="home"] .hero-grid > .hero-say { padding-bottom: 0.06em; }
  [data-page="home"] .hero h1 { max-width: 14ch; margin-bottom: 0; }
  [data-page="home"] .hero-grid .lead { margin-top: 0; max-width: 44ch; }
  [data-page="home"] .hero-grid .hero-geo { margin-bottom: 0; }
  [data-page="home"] .hero-actions { margin-top: var(--s4); }
}
/* Below the pairing the columns stack in source order: eyebrow, h1, lead,
   markets, actions — which is the reading order the single-column hero had. */
@media (max-width: 1199px) {
  [data-page="home"] .hero-grid { gap: 0; }
}

/* --- B.i. Choreography caps. ---------------------------------------------- */
/* A garnish, never a gate: nothing on this page may start later than 1.5s,
   whatever an author writes in data-delay. Same selectors as site.css §V4-F,
   later in the cascade, delay longhand only — durations are untouched. */
.js [data-page="home"] .in .trace,      .js [data-page="home"] .dgm.in .trace,
.js [data-page="home"] .in .sig,        .js [data-page="home"] .dgm.in .sig,
.js [data-page="home"] .in .node,       .js [data-page="home"] .dgm.in .node,
.js [data-page="home"] .in .sweep,      .js [data-page="home"] .dgm.in .sweep,
.js [data-page="home"] .in .gate-pulse, .js [data-page="home"] .dgm.in .gate-pulse {
  animation-delay: min(var(--delay, 0ms), 1500ms);
}
/* The one cinematic wipe on the page. 1150ms of diagonal mask meant a reader
   who stopped scrolling could sit in front of three empty rows. */
.js [data-page="home"] .reveal-cine {
  transition: opacity 380ms var(--ease),
              mask-position 620ms var(--ease),
              -webkit-mask-position 620ms var(--ease);
}

/* --- B.ii. Seams and heads. ----------------------------------------------- */
/* Dark to dark is one continuous surface: paying full padding on both sides of
   an invisible boundary buys nothing but an empty viewport. */
[data-page="home"] .band-dark + .band-dark { padding-top: calc(var(--pad) * 0.72); }
[data-page="home"] .head { margin-bottom: clamp(32px, 3.4vw, 60px); }
/* A full-.wrap asset does not need a paragraph gap above it as well. */
[data-page="home"] .dgm { margin-top: clamp(8px, 1.4vw, 24px); }
/* The light-to-dark seam after the two product vignettes: a figure that ends
   in two caption lines does not also need 270px of nothing under it. */
[data-page="home"] #intelligence { padding-bottom: calc(var(--pad) * 0.78); }
[data-page="home"] #experience   { padding-bottom: calc(var(--pad) * 0.72); }
[data-page="home"] #settings     { padding-block: calc(var(--pad) * 0.8) calc(var(--pad) * 0.75); }

/* --- C. The engine and its legend. ---------------------------------------- */
/* The pairing starts at 1400, not at 1081, and the rendered-type law decides
   that: #a04's labels are 18 units on a 1000 canvas, so the svg must render at
   >=667px (18 x 667/1000 = 12.0px). At 1400 the svg column measures ~709px; at
   1280 it would be ~600px and the labels would fall to 10.8px. Below 1400 the
   svg keeps the centred 720px cap it already had, where the wrap is narrow
   enough that the flanks were never the problem. */
[data-page="home"] .engine { display: grid; align-items: center; }
[data-page="home"] .engine-legend { display: none; }
@media (min-width: 1400px) {
  [data-page="home"] .engine {
    grid-template-columns: minmax(0, 7.6fr) minmax(0, 4.4fr);
    gap: clamp(32px, 4vw, 80px);
  }
  [data-page="home"] .engine > .dgm[data-cap="720"] { margin-inline: 0; }
  [data-page="home"] .engine-legend { display: block; }
}
[data-page="home"] .engine-legend ol {
  list-style: none; margin: 0; padding: 0; display: grid; gap: 0;
}
[data-page="home"] .engine-legend li {
  display: grid; grid-template-columns: 2.4rem minmax(0, 1fr); gap: 14px;
  align-items: baseline;
  padding: 14px 0; border-bottom: 1px solid var(--line-dark);
}
[data-page="home"] .engine-legend li:last-child { border-bottom: 0; }
[data-page="home"] .engine-legend .n {
  font-family: var(--font-display); font-weight: 700; font-size: 0.74rem;
  letter-spacing: 0.14em; color: var(--signal-aqua);      /* 8.25:1 on obsidian */
  font-variant-numeric: tabular-nums;
}
[data-page="home"] .engine-legend b {
  display: block; font-family: var(--font-display); font-weight: 600;
  font-size: 1.02rem; line-height: 1.25; color: var(--on-dark); margin-bottom: 3px;
}
[data-page="home"] .engine-legend span {
  font-size: 0.95rem; line-height: 1.45; color: var(--on-dark-2);   /* 9.63:1 */
}
[data-page="home"] .engine-legend .is-gate .n,
[data-page="home"] .engine-legend .is-gate b { color: var(--gold); } /* 7.28:1 */

/* --- The product-experience aside. The 2fr column of a .split-offset carried
   one eyebrow and 700px of nothing beside a full-height figure. It now holds
   the claim the figure is evidence for, and stays with the figure. --------- */
[data-page="home"] .aside-note {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.02rem, 1.15vw, 1.2rem); line-height: 1.3;
  letter-spacing: -0.014em; color: var(--ink); max-width: 24ch; margin: 0;
}
/* The same voice one step under a .statement, in the column the statement
   leaves empty below it. */
[data-page="home"] .statement + .aside-note { margin-top: clamp(20px, 2vw, 32px); color: var(--slate); }
@media (min-width: 1001px) {
  [data-page="home"] .exp-aside { position: sticky; top: 132px; }
}

/* --- The closing line. It sits under the CTA h2, in the display face, at the
   scale of a thing a visitor repeats. -------------------------------------- */
[data-page="home"] .cta-kicker {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.2rem, 1.75vw, 1.65rem); line-height: 1.26;
  letter-spacing: -0.018em; color: var(--signal-aqua);    /* 8.25:1 on obsidian */
  max-width: 19ch; margin: 4px 0 0;
}

/* --- Reduced motion inherits the site's law: the finished state, never a
   blank one. Nothing above introduces an animation, so there is nothing to
   disable — only the wipe, which site.css already resolves to opaque. ------ */
