/* ==========================================================================
   Sentrova Solutions — design system
   Direction: "Instrument-grade". Cool ash ground, indigo signal, hairline rules.
   Tokens are the single source of truth. Do not hardcode colour or type below.
   ========================================================================== */

/* --- 1. Tokens ----------------------------------------------------------- */
:root {
  /* Colour. Warm paper ground, ink type, indigo signal. */
  --ink:        #0D1014;
  --ink-2:      #39424E;
  --ink-3:      #5F6979;
  --paper:      #F7F5F0;  /* warm ivory, less clinical than ash */
  --paper-deep: #EDE9E1;
  --panel:      #FFFFFF;
  --cream:      #FAF8F3;  /* extra-light warm section ground */
  --rule:       #D8D4CC;
  --rule-soft:  #E8E4DC;

  /* Indigo: the signal colour. Branded, not generic tech blue. */
  --indigo-50:  #F5F3FF;
  --indigo-100: #EDE9FE;
  --indigo-200: #DDD6FE;
  --indigo-300: #C4B5FD;
  --indigo-400: #A78BFA;
  --indigo-500: #8B5CF6;
  --indigo-600: #7C3AED;  /* hover lift */
  --indigo-700: #6D28D9;  /* THE brand value: vivid violet-indigo */
  --indigo-950: #1E1B2E;  /* warm near-black, not flat navy */

  /* The mark's own colours live in its SVG, because they are gradients.
     What is left here is the wordmark's full stop, which follows the
     letterforms and so belongs with the type. */
  --mark-lift:   var(--indigo-400);     /* #A78BFA — the dot, on deep ground */

  /* Aliases kept so existing rules and DESIGN.md stay honest. */
  --indigo:      var(--indigo-700);
  --indigo-lift: var(--indigo-600);
  --indigo-wash: var(--indigo-100);
  --indigo-edge: var(--indigo-200);

  /* Type. Sora carries every reading role — display, headings, body,
     navigation and buttons — at one weight axis. IBM Plex Mono stays for the
     instrument readouts (spec values, durations, hints) where a fixed advance
     width is doing real work. --display and --body are kept as separate
     tokens so the intent of each rule below still reads, even though they
     now resolve to the same face. */
  --display: "Sora Variable", Sora, "Helvetica Neue", Arial, sans-serif;
  --body:    "Sora Variable", Sora, "Segoe UI", system-ui, sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Weights, named after the role they serve rather than the number. */
  --w-display: 800;  /* H1 — ExtraBold */
  --w-heading: 700;  /* H2 — Bold */
  --w-sub:     600;  /* H3, eyebrows, buttons — SemiBold */
  --w-nav:     500;  /* navigation — Medium */

  /* Scale. H1 64–72px, H2 36–44px, body 16–18px, nav and buttons 14px. */
  --fs-display-xl: clamp(2.5rem, 5.6vw, 4.5rem);    /* 40 → 72 */
  --fs-display-l:  clamp(2rem, 3.9vw, 2.75rem);     /* 32 → 44 */
  --fs-h2:         clamp(1.75rem, 3vw, 2.25rem);    /* 28 → 36 */
  --fs-h3:         clamp(1.0625rem, 1.2vw, 1.125rem);
  --fs-lead:       clamp(1.0625rem, 1.3vw, 1.125rem);
  --fs-body:       1rem;
  --fs-small:      0.875rem;
  --fs-ui:         0.875rem;    /* navigation and buttons — 14px */
  --fs-label:      0.75rem;     /* tracked uppercase eyebrows */

  /* Corner radius. The boxes read as cards rather than cut sheet metal.
     Small for controls, large for the tiles and the closing CTA. */
  --radius-sm:   8px;
  --radius:     12px;
  --radius-lg:  18px;
  --radius-pill: 999px;

  /* Space */
  --gutter: 1.5rem;
  --rail:   5.75rem;
  --shell:  78rem;
  --band-y: clamp(4.5rem, 9vw, 8.5rem);

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* --- 2. Reset ------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Light-only design. Without this, an OS set to dark renders native selects,
     scrollbars and autofill dark against an ash page. */
  color-scheme: light;
}

/* Every fragment target clears the 4.25rem sticky header. Measured: without
   this, 42px of each section landed underneath it on desktop, 63px on mobile.
   :where() keeps specificity at 0 so any element can still override it. */
:where([id]) { scroll-margin-top: 5.5rem; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--fs-body);
  line-height: 1.6;
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Removes the 300ms double-tap zoom delay without disabling pinch zoom. */
  touch-action: manipulation;
  /* The default flash is a blue that fights the indigo. */
  -webkit-tap-highlight-color: rgba(36, 28, 201, 0.12);
}

/* Figures in the spec tables and readouts line up column to column. */
.spec__val, .hero__readout dd, .mono, .label, .panel__list li, .step__dur {
  font-variant-numeric: tabular-nums;
}

img, svg, canvas { display: block; max-width: 100%; }
h1, h2, h3, h4, p, ul, ol, figure, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--indigo);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection { background: var(--indigo); color: #fff; }

/* main carries tabindex="-1" so the skip link genuinely moves focus. That
   focus is programmatic, so it must not paint a ring. */
main[tabindex]:focus { outline: none; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip-link {
  position: absolute; top: 0; left: 0; z-index: 200;
  transform: translateY(-120%);
  padding: 0.75rem 1.25rem;
  background: var(--ink); color: #fff;
  font-family: var(--display); font-weight: var(--w-sub); font-size: var(--fs-ui);
  letter-spacing: 0.08em; text-transform: uppercase;
}
.skip-link:focus { transform: translateY(0); }

/* --- 3. Type roles ------------------------------------------------------- */
/* H1. ExtraBold, the only weight on the page that goes above 700. */
.display-xl {
  font-family: var(--display);
  font-weight: var(--w-display);
  font-size: var(--fs-display-xl);
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

/* Section display and H2 share the heading role: Bold, a touch of negative
   tracking, and enough leading that two lines do not lock together. */
.display-l,
.h2 {
  font-family: var(--display);
  font-weight: var(--w-heading);
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.display-l { font-size: var(--fs-display-l); }
.h2        { font-size: var(--fs-h2); }

.h3 {
  font-family: var(--display);
  font-weight: var(--w-sub);
  font-size: var(--fs-h3);
  line-height: 1.4;
  letter-spacing: -0.004em;
}

.lead {
  font-size: var(--fs-lead);
  line-height: 1.6;
  color: var(--ink-2);
  text-wrap: pretty;
}

.prose { color: var(--ink-2); text-wrap: pretty; }
.prose + .prose { margin-top: 1rem; }

/* Inline links stay in the surrounding type. Mono is reserved for standalone
   navigation, where the register shift is the point. */
.prose a, .panel__body a, .spec__val a {
  color: var(--indigo);
  border-bottom: 1px solid var(--indigo-edge);
  transition: border-color 0.18s var(--ease);
}
.prose a:hover, .panel__body a:hover, .spec__val a:hover { border-color: var(--indigo); }

/* The sub-heading role: Sora SemiBold, tracked open and set in caps. Mono is
   no longer the eyebrow face — it is reserved for figures below. */
.label {
  font-family: var(--display);
  font-size: var(--fs-label);
  font-weight: var(--w-sub);
  line-height: 1.4;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.label--ink { color: var(--ink); }
.page-head .label--ink { color: var(--indigo-700); }
.label--indigo { color: var(--indigo); }

.mono { font-family: var(--mono); font-size: var(--fs-small); }

.em-indigo { color: var(--indigo); }

/* Sora's period glyph is a square; this swaps in a round dot for display type. */
.hero__full-stop {
  display: inline-block;
  width: 0.19em;
  height: 0.19em;
  margin-left: 0.05em;
  border-radius: 50%;
  background: currentColor;
  vertical-align: 0.02em;
}

/* --- 4. Shell and grid --------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.band { padding-block: var(--band-y); }
.band--tight { padding-block: clamp(3rem, 5.5vw, 5rem); }
.band--panel { background: var(--panel); }
/* Third ground. The page now steps ash -> white -> indigo tint -> deep indigo,
   so colour builds toward the conversion band instead of appearing at random. */
.band--tint { background: var(--cream); }
.band--tint .panel { background: var(--panel); }
/* Warm near-black conversion band. Not flat navy — charcoal with violet undertone. */
.band--ink { background: var(--indigo-950); color: var(--paper); }

/* Extra warm white section */
.band--warm { background: var(--paper); }

/* Hairline that carries a label, rather than a decorative divider.
   It draws itself left to right as the section is reached — a scale being
   laid down. Nothing readable is ever hidden behind it. */
.band-head {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.5rem;
  padding-top: 1.1rem;
  margin-bottom: clamp(2.25rem, 4vw, 3.5rem);
}
.band-head::before {
  content: "";
  position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: var(--rule);
  transform-origin: left center;
}
.js .band-head::before { transform: scaleX(0); transition: transform 0.85s var(--ease); }
.js .band-head[data-shown="true"]::before { transform: scaleX(1); }
@media (min-width: 60rem) {
  .band-head {
    /* The heading column had no floor, so right at this breakpoint the fixed
       label and aside columns could crush it enough to force a wrap the
       aside column's height didn't match — the two blocks read staggered
       instead of level. 16rem keeps any of this component's headings on
       one line before the shell has room to spare. */
    grid-template-columns: 7rem minmax(16rem, 1fr) minmax(0, 21rem);
    gap: 2rem;
    align-items: start;
  }
}
.band-head__body { max-width: 46ch; }
/* Fills its column so its right edge lines up with the grid below it. */
.band-head__aside { max-width: none; }
/* The aside column is capped at minmax(0, 21rem) — already its full width, so
   the block sits flush against the shell's right edge. Widening the cap for
   this instance claims more of the row, which shifts the block's left edge
   inward, off the margin. */
@media (min-width: 60rem) {
  #enquiry .band-head { grid-template-columns: 7rem minmax(16rem, 1fr) minmax(0, 26rem); }
}

/* --- 5. Header ----------------------------------------------------------- */
.masthead {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--rule);
}

.masthead__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem;
  height: 4.25rem;
}

.brand { display: flex; align-items: center; gap: 0.7rem; }
.brand__mark { width: 44px; height: 44px; flex: none; overflow: visible; }
/* The mark carries its own paint. The board's artwork is built from
   gradients — the ribbon runs deep indigo through violet and back, each
   orbit arc fades along its own length, and the nodes are lit spheres —
   and a gradient cannot be expressed as a CSS colour token. So the fills
   live on the paths, and the footer carries a second cut of the same
   geometry with the light-ground stops swapped for deep-ground ones.
   Nothing here may set fill or stroke on the mark: CSS beats presentation
   attributes, and a flat colour would paint straight over the gradients. */
.brand__text { display: flex; flex-direction: column; line-height: 1; }
.brand__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.02rem; letter-spacing: 0.03em;
  text-transform: uppercase;
}
/* The full stop the board sets after the wordmark. Generated rather than
   typed into sixteen places, and hidden from the accessibility tree so the
   brand is announced as "Sentrova", not "Sentrova period". */
.brand__name::after {
  /* Drawn, not typed. Sora's full stop is a rounded square; the board's is a
     true circle, and at this size the difference is the whole point of it. */
  content: "";
  display: inline-block;
  width: 0.21em; height: 0.21em;
  border-radius: 50%;
  background: var(--indigo-600);
  /* Sits on the baseline rather than the middle of the x-height. */
  vertical-align: baseline;
  margin-left: 0.08em;
}
.footer .brand__name::after { background: var(--mark-lift); }
.brand__sub {
  font-family: var(--mono);
  font-size: 0.5625rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--ink-3);
  margin-top: 0.3rem;
}

.nav { display: none; }
@media (min-width: 62rem) {
  .nav { display: flex; align-items: center; gap: 1.75rem; }
}
.nav__link {
  font-family: var(--display);
  font-weight: var(--w-nav);
  font-size: var(--fs-ui);
  line-height: 1.4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding-block: 0.4rem;
  position: relative;
  transition: color 0.18s var(--ease);
}
.nav__link:hover { color: var(--ink); }
.nav__link[aria-current="page"] { color: var(--ink); }
.nav__link[aria-current="page"]::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--indigo);
}

.nav-toggle {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  min-height: 44px; min-width: 44px; /* touch target floor */
  background: none; border: 1px solid var(--rule);
  padding: 0.5rem 0.9rem; cursor: pointer;
  font-family: var(--display); font-weight: var(--w-nav); font-size: var(--fs-ui);
  letter-spacing: 0.08em; text-transform: uppercase;
  border-radius: var(--radius);
}

/* Header CTA: one clear conversion action beside the plain nav links. */
.nav__cta { margin-left: 0.25rem; padding: 0.55rem 1.1rem; }
.drawer__cta {
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
  width: 100%; margin-top: 1.25rem;
}
@media (min-width: 62rem) { .nav-toggle { display: none; } }

.drawer {
  display: none;
  border-top: 1px solid var(--rule);
  background: var(--panel);
  max-height: calc(100dvh - 4.25rem);
  overflow-y: auto;
  /* Stops a scroll gesture inside the drawer from scrolling the page behind it. */
  overscroll-behavior: contain;
}
.drawer[data-open="true"] { display: block; }
@media (min-width: 62rem) { .drawer[data-open="true"] { display: none; } }

/* Without scripting the toggle cannot work, so the links are simply exposed. */
@media (max-width: 61.99rem) {
  html:not(.js) .drawer { display: block; }
  html:not(.js) .nav-toggle { display: none; }
}
.drawer__list { padding-block: 0.5rem 1.25rem; }
.drawer__link {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--rule-soft);
  font-family: var(--display); font-weight: 700;
  font-size: 1.4rem; letter-spacing: -0.015em;
}
.drawer__link span { font-family: var(--mono); font-size: var(--fs-label); color: var(--ink-3); font-weight: 400; letter-spacing: 0.14em; }

/* --- 6. Instrument rail -------------------------------------------------- */
/* Encodes reading position. Hidden where there is no room for it to be true. */
/* Appears only once the viewport is wide enough to clear the 78rem shell —
   below that there is no margin to sit in and it would cross the type. */
.rail { display: none; }
@media (min-width: 96rem) {
  .rail {
    display: block;
    position: fixed; top: 50%; left: 1.75rem; z-index: 90;
    transform: translateY(-50%);
    padding-left: 0.85rem;
    border-left: 1px solid var(--rule);
  }
}
.rail__item {
  display: flex; align-items: center; gap: 0.6rem;
  padding-block: 0.42rem;
  font-family: var(--mono); font-size: 0.625rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-3);
  transition: color 0.2s var(--ease);
}
.rail__tick {
  width: 12px; height: 1px; background: var(--rule);
  transition: width 0.25s var(--ease), background-color 0.25s var(--ease);
}
.rail__item:hover { color: var(--ink-2); }
.rail__item[data-active="true"] { color: var(--ink); }
.rail__item[data-active="true"] .rail__tick { width: 26px; background: var(--indigo); }

/* --- 7. Hero + signal field --------------------------------------------- */
.hero { position: relative; overflow: hidden; border-bottom: 1px solid var(--rule); }

.hero__field {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  /* Keep the field off the headline so type stays the loudest thing. */
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,0.12) 0%, rgba(0,0,0,0.35) 42%, #000 100%);
  mask-image: linear-gradient(180deg, rgba(0,0,0,0.12) 0%, rgba(0,0,0,0.35) 42%, #000 100%);
}
@media (min-width: 62rem) {
  .hero__field {
    -webkit-mask-image: linear-gradient(96deg, rgba(0,0,0,0.10) 0%, rgba(0,0,0,0.16) 36%, #000 74%);
    mask-image: linear-gradient(96deg, rgba(0,0,0,0.10) 0%, rgba(0,0,0,0.16) 36%, #000 74%);
  }
}

.hero__inner {
  position: relative; z-index: 1;
  padding-block: clamp(4.5rem, 11vw, 9.5rem) clamp(3.5rem, 7vw, 6rem);
  pointer-events: none;
}
.hero__inner a, .hero__inner button { pointer-events: auto; }

.hero__eyebrow { display: block; margin-bottom: 1.6rem; }


.hero__title { max-width: 18ch; }
.hero__lead { max-width: 48ch; margin-top: 1.6rem; }

.hero__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2.4rem; }
/* 49rem is where both labels measurably stop fitting on one line together;
   below it they wrap. Two buttons at their own natural widths, stacked and
   ragged — one of them two lines tall on a phone — read as a mistake, so
   below that point they stack deliberately and match. */
@media (max-width: 49rem) {
  .hero__actions { flex-direction: column; align-items: stretch; }
  .hero__actions .btn { justify-content: center; text-align: center; }
}
/* Where the row sits inside a narrow column instead of across the page, the
   viewport cannot tell us whether the buttons fit — on the 404 page they are
   in a 535px column at 1440px wide and wrapped ragged, 280px above 370px.
   This stacks them at every width, to one width and one left edge. */
.hero__actions--stack { flex-direction: column; align-items: stretch; }
.hero__actions--stack .btn { justify-content: center; text-align: center; }

.hero__readout {
  margin-top: clamp(3rem, 6vw, 5rem);
  display: grid; gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  grid-template-columns: 1fr;
  max-width: 46rem;
}
@media (min-width: 48rem) { .hero__readout { grid-template-columns: repeat(3, 1fr); } }
.hero__readout div { background: var(--indigo-50); padding: 1.15rem 1.25rem; }
.hero__readout dt { margin-bottom: 0.45rem; }
.hero__readout dd { font-family: var(--body); font-weight: 500; font-size: var(--fs-small); color: var(--ink-2); }

/* --- 7b. Inner page head -------------------------------------------------
   The canvas field is the home page's one loud moment. Inner pages stay quiet
   and carry their weight in type and rules instead. */
.page-head {
  border-bottom: 1px solid var(--rule);
  padding-block: clamp(3.25rem, 7vw, 6rem) clamp(2.5rem, 5vw, 4rem);
}
.page-head__grid { display: grid; gap: 1.75rem; grid-template-columns: 1fr; }
@media (min-width: 62rem) {
  .page-head__grid { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 3.5rem; align-items: end; }
}
.page-head__title { max-width: 16ch; margin-top: 1.25rem; }
.page-head__lead { max-width: 46ch; }
.page-head__meta { display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; margin-top: 1.75rem; }
.page-head__meta li { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-3); }

/* --- 8. Buttons ---------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.7rem;
  padding: 0.85rem 1.35rem;
  border: 1px solid var(--indigo-700);
  background: var(--indigo-700); color: #fff;
  font-family: var(--display); font-weight: var(--w-sub); font-size: var(--fs-ui);
  line-height: 1.4; letter-spacing: 0.08em; text-transform: uppercase;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease);
}
.btn:hover { background: var(--indigo-950); border-color: var(--indigo-950); }
.btn__arrow { transition: transform 0.22s var(--ease); }
.btn:hover .btn__arrow { transform: translateX(3px); }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn--ghost:hover { background: transparent; border-color: var(--ink); color: var(--ink); }

.btn--onink { background: #fff; border-color: #fff; color: var(--indigo-950); }
.btn--onink:hover { background: var(--indigo-50); border-color: var(--indigo-50); color: var(--indigo-700); }

.link-mono {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--display); font-weight: var(--w-sub); font-size: var(--fs-ui);
  line-height: 1.4; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--indigo);
  border-bottom: 1px solid var(--indigo-edge);
  padding-bottom: 2px;
  transition: border-color 0.18s var(--ease);
}
.link-mono:hover { border-color: var(--indigo); }
/* Standalone links are targets in their own right. WCAG 2.5.8 asks for 24px;
   a bare 14px line box is 22. Inline links inside a sentence are exempt and
   are left as they are. */
.link-mono, .footer__list a, .panel--link .h3 { min-height: 24px; }
.footer__list a { display: inline-flex; align-items: center; }

/* --- 9. Panels ----------------------------------------------------------- */
.grid {
  display: grid;
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  grid-template-columns: 1fr;
}
@media (min-width: 46rem) { .grid[data-cols="2"] { grid-template-columns: repeat(2, 1fr); } }
/* A 3-item grid must never pass through 2 columns — that leaves the third
   card alone on its own row with a phantom empty cell beside it. It stays
   single-column until there's room for all three at once. */
@media (min-width: 68rem) { .grid[data-cols="3"] { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 46rem) { .grid[data-cols="4"] { grid-template-columns: repeat(2, 1fr); } }
/* A lone card following a 2-up row: centred and narrower once that row is
   actually 2-up, so it reads as a deliberate capstone, not a stray leftover. */
.grid--single { margin-top: 1.5rem; }
@media (min-width: 46rem) { .grid--single { max-width: 34rem; margin-inline: auto; } }
@media (min-width: 68rem) { .grid[data-cols="4"] { grid-template-columns: repeat(4, 1fr); } }

.panel {
  background: var(--panel);
  padding: clamp(1.5rem, 2.6vw, 2.1rem);
  display: flex; flex-direction: column; gap: 0.85rem;
  position: relative;
  /* A grid item defaults to min-width:auto, so it refuses to shrink below its
     longest unbreakable string. The contact email is wider than a 320px track,
     which pushed the panel past the viewport and scrolled the page sideways. */
  min-width: 0;
}
/* Paired with the above: the address has to be allowed to break, or it simply
   overflows the panel instead of the page. */
.panel h3, .panel a { overflow-wrap: anywhere; }
/* Start, not centre. The eyebrow shares a row with the corner arrow, and
   centring dropped a one-line eyebrow 4px below a two-line one beside it. */
.panel__key { display: flex; align-items: start; justify-content: space-between; gap: 1rem; }
.panel__body { color: var(--ink-2); font-size: var(--fs-small); line-height: 1.6; }
.panel__list { display: flex; flex-direction: column; gap: 0.4rem; margin-top: auto; padding-top: 1rem; }

/* --- Cards read off one set of lines -------------------------------------
   A card was only as aligned as its own content. Where one title wrapped to
   two lines and its neighbour's did not, the body copy beneath it started
   24px lower — on the home page that is true at every desktop width, so the
   defect was permanent rather than a corner case. A two-line eyebrow did the
   same again to the title below it.

   Subgrid hands the four slots — eyebrow, title, body, list — to the parent
   grid's rows, so each slot is as tall as the tallest in its row and every
   card in that row starts its copy on the same line. It costs nothing where
   nothing wraps, which min-height on the title could not have said.

   The 1px gaps the parent uses to draw its hairlines fall inside the panel's
   own background, so they stay invisible between slots and keep showing
   between cards. Browsers without subgrid keep the flex column above. */
@supports (grid-template-rows: subgrid) {
  .grid > .panel {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 4;
    row-gap: 0.85rem;
  }
  .grid > .panel > .label,
  .grid > .panel > .panel__key { grid-row: 1; }
  .grid > .panel > .h3,
  .grid > .panel > h3          { grid-row: 2; }
  .grid > .panel > .panel__body { grid-row: 3; }
  /* Its row is the last one and already shared across the card row, so the
     flex auto-margin that used to push it down has nothing left to do. */
  .grid > .panel > .panel__list { grid-row: 4; margin-top: 0; }
}
.panel__list li {
  font-family: var(--mono); font-size: 0.75rem;
  color: var(--ink-2);
  padding-top: 0.4rem; border-top: 1px solid var(--rule-soft);
}

/* An odd-numbered grid leaves a hole in its last row. The fifth product takes
   that whole row instead, and lays its spec line out across it. */
@media (min-width: 46rem) {
  .panel--wide { grid-column: 1 / -1; }
  .panel--wide .panel__body { max-width: 54ch; }
  .panel__list--row { flex-direction: row; flex-wrap: wrap; gap: 0 2.5rem; }
  .panel__list--row li { flex: 1 1 12rem; }
}

/* The "when to call someone else" column is a different kind of statement from
   the two beside it: a negative, and the most trust-building thing on the page.
   Four identical three-column rows down one page read as one texture, so the
   counter-column carries the wash and the eye has somewhere to land. */
.panel--counter { background: var(--indigo-wash); }
.panel--counter .panel__list li { border-top-color: var(--indigo-edge); }

/* --- The decision ladder -------------------------------------------------
   Six ways a step of work can be classified, in order of how much of it
   leaves the person. They were six identical neutral panels, which made the
   order invisible and left the reader to hold six labels in their head.
   Each rung now carries its own colour — a stripe, a tile and a label —
   so the one you are looking for can be found without reading all six.

   Scoped rather than built on .panel for the same reason the fit table is:
   .panel is the neutral card on six pages, and a coloured edge and a tile
   are not neutral. */
.ladder {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.1rem;
}
@media (min-width: 46rem)  { .ladder { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 72rem)  { .ladder { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Each rung hands its three slots to the parent's rows, so the titles and
   the bodies across a row start on one line however a label wraps. */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 46rem) {
    .ladder { grid-template-rows: repeat(3, auto auto auto); }
    .rung { display: grid; grid-template-rows: subgrid; grid-row: span 3; }
  }
  @media (min-width: 72rem) {
    .ladder { grid-template-rows: repeat(2, auto auto auto); }
  }
}

.rung {
  position: relative;
  min-width: 0;
  padding: clamp(1.25rem, 2vw, 1.6rem) clamp(1.2rem, 2vw, 1.6rem);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: 0 1px 2px rgba(13, 16, 20, 0.04), 0 10px 26px rgba(13, 16, 20, 0.05);
  overflow: hidden;
}
/* The stripe is an inset box rather than a border so it follows the radius
   instead of squaring the corners off. */
.rung::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 4px;
  background: linear-gradient(180deg, var(--rung-a), var(--rung-b));
}
/* The tint is the accent at a few per cent, washed out to the right so the
   copy sits on paper rather than on colour. */
.rung::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, var(--rung-wash), transparent 62%);
}
.rung > * { position: relative; z-index: 1; }

.rung[data-kind="keep"]      { --rung-a: #A78BFA; --rung-b: #7C3AED; --rung-ink: #6D28D9; --rung-tile: #EDE9FE; --rung-wash: rgba(124, 58, 237, 0.07); }
.rung[data-kind="augment"]   { --rung-a: #7DD3FC; --rung-b: #2563EB; --rung-ink: #1D4ED8; --rung-tile: #DBEAFE; --rung-wash: rgba(37, 99, 235, 0.07); }
.rung[data-kind="delegate"]  { --rung-a: #6EE7B7; --rung-b: #059669; --rung-ink: #047857; --rung-tile: #D1FAE5; --rung-wash: rgba(5, 150, 105, 0.07); }
.rung[data-kind="automate"]  { --rung-a: #FCD34D; --rung-b: #F97316; --rung-ink: #C2410C; --rung-tile: #FFEDD5; --rung-wash: rgba(249, 115, 22, 0.08); }
.rung[data-kind="agentify"]  { --rung-a: #67E8F9; --rung-b: #0891B2; --rung-ink: #0E7490; --rung-tile: #CFFAFE; --rung-wash: rgba(8, 145, 178, 0.07); }
.rung[data-kind="eliminate"] { --rung-a: #F9A8D4; --rung-b: #DB2777; --rung-ink: #BE185D; --rung-tile: #FCE7F3; --rung-wash: rgba(219, 39, 119, 0.07); }

.rung__key { display: flex; align-items: center; gap: 0.75rem; }
.rung__tile {
  flex: none; width: 2.6rem; height: 2.6rem; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--rung-tile); color: var(--rung-ink);
}
.rung__tile svg { width: 1.3rem; height: 1.3rem; display: block; }
.rung__label { color: var(--rung-ink); white-space: nowrap; }
/* Runs out to the card's padding, which is what stops the label reading as
   a floating word beside an icon. */
.rung__rule {
  flex: 1 1 auto; min-width: 0.75rem; height: 1px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--rung-ink) 30%, transparent), transparent);
}

.rung__title { margin-top: 1rem; }
.rung__body {
  margin-top: 0.55rem;
  color: var(--ink-2); font-size: var(--fs-small); line-height: 1.6;
}

/* The eyebrow takes its rule above rather than beside it, so the two words
   can stack in the narrow first column. Compound selector on purpose:
   .label--accent sets display:flex, and which of the two wins on a plain
   class would depend on the order they happen to sit in this file. */
.label--accent.label--stacked { display: block; }
.label__rule--over {
  display: block; width: 2rem; height: 3px; margin-bottom: 0.7rem;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--indigo-700), #4F46E5);
}

/* --- The fit table -------------------------------------------------------
   Two columns that answer opposite questions, so they are built to be read
   against each other: the same rows, the same rhythm, and one difference —
   the colour of the mark at the head of every line. Green for the ones we
   want, violet for the ones that belong somewhere else.

   It carries its own card rather than reusing .panel, because .panel is a
   neutral container used on six pages and this one needs an accent edge, a
   badge, and a row that starts with an icon. Bending the shared component
   into that shape would have cost every other card that uses it. */
.band--fit { position: relative; overflow: hidden; }
/* Two soft washes and a dot field, well under the copy. Decoration: they
   carry nothing, so they are painted rather than marked up. */
.fit__wash {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(22rem 22rem at 96% -4%, rgba(124, 58, 237, 0.10), transparent 70%),
    radial-gradient(18rem 18rem at -4% 104%, rgba(16, 185, 129, 0.09), transparent 70%);
}
.fit__wash::before,
.fit__wash::after {
  content: ""; position: absolute; width: 7.5rem; height: 5rem;
  background-image: radial-gradient(currentColor 1.2px, transparent 1.2px);
  background-size: 0.75rem 0.75rem;
  color: var(--indigo-300); opacity: 0.5;
}
.fit__wash::before { top: 12%; right: 2.5%; }
.fit__wash::after  { bottom: 8%; left: 2%; color: var(--indigo-200); }
@media (max-width: 60rem) { .fit__wash::before, .fit__wash::after { display: none; } }

/* The eyebrow keeps its size and tracking and takes the brand colour, with
   the rule the board runs off the end of it. */
.label--accent { color: var(--indigo-700); display: flex; align-items: center; gap: 0.7rem; }
.label__rule {
  flex: none; width: 3.25rem; height: 3px; border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--indigo-700), #4F46E5 55%, transparent);
}
/* Only the last word takes the gradient, so the sentence still reads as one
   line of type rather than as a two-tone label. */
.h2__accent {
  font-style: normal;
  background: linear-gradient(96deg, var(--indigo-600) 10%, #6D28D9 55%, #4F46E5 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.band-head__aside--barred { border-left: 3px solid transparent; padding-left: 1.1rem;
  border-image: linear-gradient(180deg, var(--indigo-600), #4F46E5) 1; }

.fit {
  position: relative; z-index: 1;
  display: grid; gap: 1.25rem;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 60rem) {
  /* row-gap stays 0: a subgrid child inherits its parent's row gaps, so a
     gap set here for the columns would also open up between the badge, the
     title and every row inside the card. The spacing between those is the
     slots' own margins and padding. */
  .fit { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 1.5rem; row-gap: 0; }
}

.fit__card {
  position: relative; min-width: 0;
  padding: clamp(1.4rem, 2.4vw, 2rem) clamp(1.3rem, 2.4vw, 2rem);
  border-radius: var(--radius-lg);
  /* The accent edge is an inset shadow rather than a border so it follows
     the radius instead of squaring off the corners. */
  box-shadow: 0 1px 2px rgba(13, 16, 20, 0.04), 0 10px 30px rgba(13, 16, 20, 0.06);
  overflow: hidden;
}
.fit__card::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 4px;
}
.fit__card--yes { background: var(--panel); }
.fit__card--yes::before { background: linear-gradient(180deg, #10B981, #22C55E 45%, #0EA5A4); }
.fit__card--no  { background: var(--indigo-50); }
.fit__card--no::before  { background: linear-gradient(180deg, var(--indigo-600), #4F46E5); }

.fit__key { display: flex; align-items: center; gap: 0.7rem; }
.fit__badge {
  flex: none; width: 2.1rem; height: 2.1rem; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
}
.fit__badge svg { width: 1.05rem; height: 1.05rem; display: block; }
.fit__card--yes .fit__badge { background: linear-gradient(140deg, #10B981, #059669); }
.fit__card--no  .fit__badge { background: linear-gradient(140deg, var(--indigo-600), #5B21B6); }
.fit__label {
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.16em; text-transform: uppercase;
}
.fit__card--yes .fit__label { color: #047857; }
.fit__card--no  .fit__label { color: var(--indigo-700); }

.fit__title { margin-top: 0.9rem; margin-bottom: 0.55rem; }
.fit__card--no .fit__title { color: var(--indigo-950); }

.fit__list { display: flex; flex-direction: column; }
.fit__item {
  display: flex; align-items: center; gap: 0.85rem;
  padding: 0.72rem 0;
  border-bottom: 1px solid var(--rule-soft);
}
.fit__card--no .fit__item { border-bottom-color: var(--indigo-200); }
.fit__item:last-child { border-bottom: 0; padding-bottom: 0; }
.fit__tick {
  flex: none; width: 1.7rem; height: 1.7rem; border-radius: 50%;
  display: grid; place-items: center;
}
.fit__tick svg { width: 0.85rem; height: 0.85rem; display: block; }
.fit__card--yes .fit__tick { background: #DCFCE7; color: #059669; }
.fit__card--no  .fit__tick { background: var(--indigo-100); color: var(--indigo-700); }
/* Body sans, not the instrument mono: these are sentences to read, not
   readouts to scan. */
.fit__text { font-size: var(--fs-small); line-height: 1.55; color: var(--ink-2); }
.fit__card--no .fit__text { color: #3F3A63; }

/* Seven shared rows: the badge, the title, and one per line of the table.
   Handing the list items themselves to the parent's rows is the point —
   card-level subgrid alone keeps the two titles level but lets row four
   drift out of step the moment one side's sentence wraps and the other's
   does not, which it does at every width from 960 to 1200.

   This block sits after the slot rules on purpose: `display: contents` and
   `display: flex` on .fit__list have the same specificity, so the one that
   comes last wins whatever media query it is wrapped in. */
@media (min-width: 60rem) {
  @supports (grid-template-rows: subgrid) {
    .fit { grid-template-rows: auto auto repeat(5, auto); }
    .fit__card { display: grid; grid-template-rows: subgrid; grid-row: span 7; }
    /* The <ul> box goes so its children become rows of the card's subgrid.
       It keeps role="list", because display:contents drops the implicit list
       semantics in some screen readers. */
    .fit__list { display: contents; }
  }
}

/* Link panels lift on hover; the whole tile is the target. */
.panel--link { transition: background-color 0.2s var(--ease); }
.panel--link:hover { background: var(--indigo-wash); }
.panel--link:hover .panel__arrow { transform: translate(3px, -3px); }
.panel__arrow { transition: transform 0.22s var(--ease); color: var(--indigo); }
.panel__stretch::after { content: ""; position: absolute; inset: 0; }

/* --- 10. Sequence (real ordered process only) ---------------------------- */
.sequence {
  display: grid; gap: 1px;
  background: var(--rule); border: 1px solid var(--rule);
  border-radius: var(--radius-lg); overflow: hidden;
}
.step {
  background: var(--panel);
  display: grid; gap: 0.9rem;
  padding: clamp(1.5rem, 2.6vw, 2.1rem);
  grid-template-columns: 1fr;
}
@media (min-width: 54rem) {
  .step { grid-template-columns: 7rem minmax(0, 20rem) minmax(0, 1fr); gap: 2rem; align-items: start; }
}
.step__stage { display: flex; align-items: baseline; gap: 0.5rem; }
.step__dur { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-3); }

/* --- 11. Spec table ------------------------------------------------------ */
.spec { border-top: 1px solid var(--rule); }
.spec__row {
  display: grid; gap: 0.4rem 2rem;
  padding-block: 1.15rem;
  border-bottom: 1px solid var(--rule-soft);
  grid-template-columns: 1fr;
}
@media (min-width: 46rem) { .spec__row { grid-template-columns: 13rem minmax(0, 1fr); align-items: baseline; } }
.spec__row dt.label--ink { color: var(--indigo-700); }
.spec__val { color: var(--ink-2); }
/* A spec row that carries a named phase, not just a value: the name leads,
   the sentence follows underneath it. */
.spec__name {
  display: block;
  margin-bottom: 0.35rem;
  font-family: var(--display); font-weight: 700;
  font-size: 1.05rem; line-height: 1.2; letter-spacing: -0.005em;
  color: var(--ink);
}

/* --- 12. Callout band ---------------------------------------------------- */
.callout { display: grid; gap: 2rem; align-items: end; grid-template-columns: 1fr; }
@media (min-width: 58rem) { .callout { grid-template-columns: minmax(0, 1fr) auto; } }
.callout__title { max-width: 20ch; }
.callout__note { max-width: 46ch; }
/* Copy on the deep indigo conversion band. */
.callout__lead { max-width: 44ch; margin-top: 1.25rem; color: var(--indigo-300); }
/* indigo-400, not 500: the label size sits at 3.96:1 with 500 and clears AA
   only from 400 upward on the deep ground. */
.label--ondeep { color: var(--indigo-400); }

/* --- 13. Form ------------------------------------------------------------ */
.form {
  display: grid; gap: 1px;
  background: var(--rule); border: 1px solid var(--rule);
  border-radius: var(--radius-lg); overflow: hidden;
}
.field { background: var(--panel); padding: 1.1rem 1.25rem 1.25rem; display: block; }
@media (min-width: 46rem) { .form { grid-template-columns: repeat(2, 1fr); } .field--full { grid-column: 1 / -1; } }
.field__label { display: block; margin-bottom: 0.55rem; }
.field__input {
  width: 100%; border: 0; background: transparent;
  padding: 0.15rem 0; font-size: var(--fs-body);
  border-bottom: 1px solid var(--rule);
  transition: border-color 0.18s var(--ease);
}
.field__input:hover { border-color: var(--ink-3); }
.field__input:focus { outline: none; border-color: var(--indigo); }
.field__input:focus-visible { outline: 2px solid var(--indigo); outline-offset: 4px; }
textarea.field__input { resize: vertical; min-height: 7rem; }
select.field__input {
  appearance: none;
  cursor: pointer;
  /* Explicit pair: Windows in dark mode otherwise paints the option list
     dark-on-dark and the chosen value becomes unreadable. */
  background-color: var(--panel);
  color: var(--ink);
  padding-right: 1.5rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
                    linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
  background-position: right 8px top 50%, right 3px top 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
select.field__input option { background-color: var(--panel); color: var(--ink); }
.field__hint { display: block; margin-top: 0.5rem; font-family: var(--mono); font-size: 0.6875rem; color: var(--ink-3); }
.field__error { display: none; margin-top: 0.5rem; font-family: var(--mono); font-size: 0.6875rem; color: #B3231C; }
.field[data-invalid="true"] .field__error { display: block; }
.field[data-invalid="true"] .field__hint { display: none; }
.field[data-invalid="true"] .field__input { border-color: #B3231C; }

/* Spam trap. Off-screen rather than display:none, which many bots skip.
   Paired with tabindex="-1" and aria-hidden, so no person ever meets it. */
.field--trap {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}

.form-actions {
  grid-column: 1 / -1; background: var(--panel);
  padding: 1.25rem; display: flex; flex-wrap: wrap;
  align-items: center; gap: 1rem; justify-content: space-between;
}
.form-status {
  font-family: var(--display); font-weight: var(--w-sub); font-size: var(--fs-label);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-2); max-width: 32ch;
}
.form-status:empty { display: none; }
.form-status[data-state="error"] { color: #B3231C; }
.form-status[data-state="sent"] { color: var(--indigo); }

/* --- 14. Footer ---------------------------------------------------------- */
.footer {
  background: var(--indigo-950); color: var(--paper);
  padding-block: clamp(3.5rem, 6vw, 5.5rem) 2rem;
  border-top: 1px solid rgba(255,255,255,0.08);
}
.footer .label { color: var(--indigo-400); }
.footer__grid { display: grid; gap: 2.5rem; grid-template-columns: 1fr; }
@media (min-width: 52rem) { .footer__grid { grid-template-columns: 1.4fr repeat(2, 1fr); gap: 3rem; } }
.footer__note {
  color: var(--indigo-400); margin-top: 1.25rem;
  max-width: 34ch; font-size: var(--fs-small); text-wrap: pretty;
}
.footer__addr { color: var(--indigo-400); font-size: var(--fs-small); }
.footer__list { display: flex; flex-direction: column; gap: 0.65rem; margin-top: 1.1rem; }
.footer__list a { color: var(--indigo-200); font-size: var(--fs-small); transition: color 0.18s var(--ease); }
.footer__list a:hover { color: #fff; }
.footer__base {
  margin-top: 3rem; padding-top: 1.5rem;
  border-top: 1px solid rgba(255,255,255,0.08);
  display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: space-between;
}
.footer__base .label { color: var(--indigo-400); }
.footer__legal { display: flex; flex-wrap: wrap; gap: 0.35rem 1.5rem; }
.footer__legal a {
  color: var(--indigo-200); font-size: var(--fs-small);
  /* A standalone link is a target, not running text: WCAG 2.5.8 wants 24px,
     and the bare line box is 22. */
  display: inline-flex; align-items: center; min-height: 24px;
  transition: color 0.18s var(--ease);
}
.footer__legal a:hover { color: #fff; }
.footer .brand__name { color: #fff; }
.footer .brand__sub { color: var(--indigo-400); }

/* --- 15. Motion ----------------------------------------------------------
   Motion is gated on the .js class, set synchronously in the document head,
   and only ever moves decoration. No text is withheld behind an animation. */

/* Entrance animation keyframes */
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes slideInLeft {
  from { opacity: 0; transform: translateX(-30px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes slideInRight {
  from { opacity: 0; transform: translateX(30px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes drawLine {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
}
@keyframes float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}
@keyframes gradientShift {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}
@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

/* Video background layer for sections */
.video-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  pointer-events: none;
}
.video-bg__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(13,16,20,0.55) 0%, rgba(13,16,20,0.75) 100%);
  pointer-events: none;
}
.video-bg__overlay--light {
  background: linear-gradient(180deg, rgba(236,238,241,0.85) 0%, rgba(236,238,241,0.95) 100%);
}

/* Animated gradient background (video-like motion without video file) */
.animated-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.animated-bg__gradient {
  position: absolute;
  inset: -50%;
  background: radial-gradient(ellipse at 30% 20%, rgba(36,28,201,0.08) 0%, transparent 50%),
              radial-gradient(ellipse at 70% 80%, rgba(36,28,201,0.06) 0%, transparent 50%),
              radial-gradient(ellipse at 50% 50%, rgba(142,145,178,0.04) 0%, transparent 60%);
  animation: gradientShift 20s ease-in-out infinite;
  background-size: 200% 200%;
}

/* Canvas video simulation container */
.canvas-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

/* Scroll-triggered animation classes — only when JS is active */
.js [data-animate] {
  opacity: 0;
}
.js [data-animated="true"][data-animate="fade-up"] {
  animation: fadeUp 0.6s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="fade-in"] {
  animation: fadeIn 0.5s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="scale-in"] {
  animation: scaleIn 0.5s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="slide-left"] {
  animation: slideInLeft 0.6s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="slide-right"] {
  animation: slideInRight 0.6s var(--ease) forwards;
}

/* Stagger delays for grid children — children animate individually when the
   parent is revealed.

   The animation is set on every child by the rule below, and the nth-child
   rules that follow only adjust *when* each one starts. That split matters:
   these delays used to carry the animation itself and stopped at the sixth
   child, so a seventh child was left at opacity 0 for good. Three places on
   this site were losing content to it — the last two stages of the 45-day
   plan, and the tail of the privacy and terms sequences. A container can now
   hold any number of children without any of them going missing. */
.js [data-stagger] > * {
  opacity: 0;
}
.js [data-animated="true"][data-stagger] > * {
  animation: fadeUp 0.5s var(--ease) forwards;
  animation-delay: 480ms;
}
.js [data-animated="true"][data-stagger] > *:nth-child(1) { animation-delay: 0ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(2) { animation-delay: 80ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(3) { animation-delay: 160ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(4) { animation-delay: 240ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(5) { animation-delay: 320ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(6) { animation-delay: 400ms; }

/* Enhanced hover effects */
.panel {
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.panel:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(13,16,20,0.08);
}
.panel--link:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(36,28,201,0.08);
}

/* Button press feedback */
.btn:active {
  transform: scale(0.98);
  transition: transform 0.1s ease-out;
}
.btn:not(.btn--ghost):not(.btn--onink) {
  position: relative;
  overflow: hidden;
}
.btn:not(.btn--ghost):not(.btn--onink)::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.18), transparent);
  transform: translateX(-100%);
  transition: transform 0s;
}
.btn:not(.btn--ghost):not(.btn--onink):hover::before {
  transform: translateX(100%);
  transition: transform 0.6s ease-in-out;
}

/* Link underline animation */
.footer__list a {
  position: relative;
  display: inline-block;
}
.footer__list a::after {
  content: "";
  position: absolute;
  bottom: -2px;
  left: 0;
  width: 0;
  height: 1px;
  background: currentColor;
  transition: width 0.25s var(--ease);
}
.footer__list a:hover::after {
  width: 100%;
}

/* Hero title word reveal */
.hero__title .word {
  display: inline-block;
  opacity: 0;
  transform: translateY(12px);
  animation: fadeUp 0.5s var(--ease) forwards;
}
.js .hero__title .word:nth-child(1)  { animation-delay: 0.1s; }
.js .hero__title .word:nth-child(2)  { animation-delay: 0.18s; }
.js .hero__title .word:nth-child(3)  { animation-delay: 0.26s; }
.js .hero__title .word:nth-child(4)  { animation-delay: 0.34s; }
.js .hero__title .word:nth-child(5)  { animation-delay: 0.42s; }
.js .hero__title .word:nth-child(6)  { animation-delay: 0.50s; }
.js .hero__title .word:nth-child(7)  { animation-delay: 0.58s; }
.js .hero__title .word:nth-child(8)  { animation-delay: 0.66s; }
.js .hero__title .word:nth-child(9)  { animation-delay: 0.74s; }
.js .hero__title .word:nth-child(10) { animation-delay: 0.82s; }

/* Floating animation for decorative elements */
.float {
  animation: float 4s ease-in-out infinite;
}

/* Shimmer loading effect */
.shimmer {
  background: linear-gradient(90deg, var(--panel) 25%, var(--paper-deep) 50%, var(--panel) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  /* The welcome sequence never plays; the page arrives already assembled. */
  .js .intro { display: none; }
  .js .band-head::before { transform: none; }
  /* These must carry the .js prefix. The rule they override is
     `.js [data-animate] { opacity: 0 }` — a class plus an attribute — and a
     bare attribute selector loses to it on specificity no matter which media
     query it sits in. Without the prefix every element below the fold stays
     invisible until the scroll observer reaches it, which is the one thing
     reduced motion is supposed to prevent. */
  .js [data-animate] { opacity: 1 !important; animation: none !important; transform: none !important; }
  .js [data-stagger] > * { opacity: 1 !important; animation: none !important; transform: none !important; }
  /* !important because this block sits earlier in the file than the rule it
     overrides; without it the later declaration wins on source order. */
  .js .billboard__words span { opacity: 1 !important; transform: none !important; animation: none !important; }
  /* The hero words are staggered by an inline animation-delay, which the
     blanket duration override above cannot reach — it zeroes the duration but
     leaves each word waiting up to 0.74s. Without this the headline still
     animates in over ~1.2s for exactly the people who asked it not to. */
  .hero__title .word {
    animation: none !important;
    animation-delay: 0s !important;
    opacity: 1;
    transform: none;
  }
  .billboard__device { animation: none; }
  .type-cursor::after { animation: none; opacity: 0; }
  .animated-bg__gradient { animation: none; }
  .float { animation: none; }
}

/* --- 15b. Billboard (reference-image style) ----------------------------- */
.billboard {
  background: var(--cream);
  padding-block: clamp(5rem, 12vw, 10rem);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.billboard__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 3rem;
  align-items: center;
}
@media (min-width: 62rem) {
  .billboard__grid {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: 5rem;
  }
}
.billboard__words {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.6rem, 7vw, 5.5rem);
  line-height: 0.98;
  letter-spacing: -0.03em;
  color: var(--ink);
}
.billboard__words span {
  display: block;
}
/* The hidden start state has to be gated on .js like every other entrance in
   this file. Setting opacity:0 unconditionally while the animation that clears
   it is .js-only left these three words permanently invisible with scripting
   off — not delayed, gone. */
.js .billboard__words span {
  opacity: 0;
  transform: translateY(24px);
  animation: fadeUp 0.7s var(--ease) forwards;
}
.js .billboard__words span:nth-child(1) { animation-delay: 0ms; }
.js .billboard__words span:nth-child(2) { animation-delay: 120ms; }
.js .billboard__words span:nth-child(3) { animation-delay: 240ms; }
.js .billboard__words span:nth-child(4) { animation-delay: 360ms; }
.billboard__device {
  position: relative;
  border-radius: var(--radius-lg);
  background: var(--ink);
  padding: 3% 3% 4%;
  box-shadow: 0 32px 80px rgba(13,16,20,0.18), 0 8px 24px rgba(13,16,20,0.08);
  transform: perspective(1200px) rotateY(-6deg) rotateX(4deg);
  transition: transform 0.6s var(--ease);
  animation: deviceFloat 6s ease-in-out infinite;
}
.billboard__device:hover {
  animation: none;
  transform: perspective(1200px) rotateY(-2deg) rotateX(2deg);
}
@keyframes deviceFloat {
  0%, 100% { transform: perspective(1200px) rotateY(-6deg) rotateX(4deg) translateY(0); }
  50% { transform: perspective(1200px) rotateY(-5deg) rotateX(3deg) translateY(-8px); }
}
.billboard__screen {
  aspect-ratio: 16 / 10;
  background: var(--paper);
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 8%;
}
.billboard__screen__line {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.4rem, 3.5vw, 2.8rem);
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ink);
}
.billboard__caption {
  margin-top: 1.5rem;
  color: var(--ink-2);
  max-width: 42ch;
}

/* --- 15c. AI video / how-it-works section ------------------------------- */
.ai-video {
  position: relative;
  overflow: hidden;
  background: var(--panel);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.ai-video__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.45;
}
.ai-video__inner {
  position: relative;
  z-index: 2;
  padding-block: clamp(5rem, 10vw, 8rem);
}
.ai-video__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 3rem;
  align-items: center;
}
@media (min-width: 62rem) {
  .ai-video__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    gap: 5rem;
  }
}
.ai-video__title {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2rem, 4.5vw, 3.6rem);
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ink);
}
.ai-video__body {
  color: #384259;
  font-size: var(--fs-lead);
  line-height: 1.58;
  margin-top: 1.25rem;
  max-width: 52ch;
}
/* Only the last word takes the gradient, the way the fit heading does. */
.ai-video__accent {
  font-style: normal;
  background: linear-gradient(96deg, var(--indigo-600) 6%, #6D28D9 48%, #4F46E5 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* --- Handover artifacts --------------------------------------------------
   Four documents, each its own card rather than a quadrant of one framed
   box. The frame said they were one thing; they are four, produced at four
   different points in the loop, and the tint and the tile on each are what
   tie a card back to the step above it. Nothing here is client data. */
.artifacts { position: relative; }
.artifacts__head {
  display: flex; align-items: center; gap: 0.8rem;
  margin-bottom: 1.1rem;
}
.artifacts__note { display: block; color: var(--ink-2); }
.artifacts__tile {
  flex: none; width: 2.5rem; height: 2.5rem; border-radius: var(--radius);
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(140deg, #6366F1, var(--indigo-600) 55%, #5B21B6);
  box-shadow: 0 4px 12px rgba(99, 102, 241, 0.28);
}
.artifacts__tile svg { width: 1.25rem; height: 1.25rem; display: block; }
/* Runs out to the edge of the column, so the head reads as a rule with a
   label on it rather than a label with a stub beside it. */
.artifacts__rule {
  flex: 1 1 auto; min-width: 1.5rem; height: 1px;
  background: linear-gradient(90deg, var(--indigo-400), rgba(79, 70, 229, 0.35) 70%, transparent);
}

.artifacts__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 34rem) {
  .artifacts__grid { grid-template-columns: 1fr 1fr; }
  /* The four cards hand their slots to the same rows, so a title or a note
     that wraps on one card cannot push its neighbour's rows out of step. */
  @supports (grid-template-rows: subgrid) {
    .artifacts__grid { grid-template-rows: repeat(2, auto auto auto auto); }
    .artifact { display: grid; grid-template-rows: subgrid; grid-row: span 4; }
  }
}

.artifact {
  background: var(--panel);
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius-lg);
  padding: 1.15rem 1.15rem 1.05rem;
  min-width: 0;
  box-shadow: 0 1px 2px rgba(13, 16, 20, 0.03), 0 8px 24px rgba(13, 16, 20, 0.05);
}
/* Each card carries the colour of the step that produces it. */
.artifact[data-kind="sense"]   { background: #F2F8FF; border-color: #DCE9FB; }
.artifact[data-kind="decide"]  { background: #F6F4FE; border-color: #E4DEFA; }
.artifact[data-kind="measure"] { background: #F1FDFB; border-color: #D5F0E7; }
.artifact[data-kind="improve"] { background: #EFF7FE; border-color: #D9E8F9; }

.artifact__key { display: flex; align-items: center; gap: 0.7rem; }
.artifact__tile {
  flex: none; width: 2.2rem; height: 2.2rem; border-radius: 0.65rem;
  display: grid; place-items: center; color: #fff;
}
.artifact__tile svg { width: 1.1rem; height: 1.1rem; display: block; }
.artifact[data-kind="sense"]   .artifact__tile { background: linear-gradient(140deg, #60A5FA, #4F46E5); }
.artifact[data-kind="decide"]  .artifact__tile { background: linear-gradient(140deg, #A78BFA, #6D28D9); }
.artifact[data-kind="measure"] .artifact__tile { background: linear-gradient(140deg, #34D399, #0D9488); }
.artifact[data-kind="improve"] .artifact__tile { background: linear-gradient(140deg, #7DD3FC, #4F46E5); }

.artifact__label { color: var(--indigo-700); }
.artifact[data-kind="sense"]   .artifact__label { color: #4F46E5; }
.artifact[data-kind="measure"] .artifact__label { color: #0F766E; }
.artifact[data-kind="improve"] .artifact__label { color: #2563EB; }

.artifact__title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.12rem; line-height: 1.2;
  color: var(--ink);
  margin-top: 0.75rem;
}
.artifact__rows { list-style: none; margin: 0.7rem 0 0; padding: 0; }
.artifact__rows li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 0.8rem;
  padding: 0.42rem 0;
  border-bottom: 1px solid rgba(13, 16, 20, 0.07);
  font-family: var(--mono); font-size: 0.68rem; line-height: 1.4;
  color: var(--ink-2);
}
.artifact__rows li:last-child { border-bottom: 0; }
.artifact__rows li span:last-child { color: var(--ink-3); text-align: right; }
.artifact__rows [data-ok] { color: #0F766E; letter-spacing: 0.08em; }
.artifact__rows [data-held] { color: var(--ink); font-weight: 500; letter-spacing: 0.08em; }

/* The note is the point of the card: what the document is for. It gets a
   mark of its own so it does not read as a caption. */
.artifact__note {
  display: flex; align-items: start; gap: 0.7rem;
  margin-top: 0.85rem; padding-top: 0.85rem;
  border-top: 1px solid rgba(13, 16, 20, 0.07);
  font-size: 0.8rem; line-height: 1.45; color: var(--ink-2);
}
.artifact__noteicon { flex: none; width: 1.35rem; height: 1.35rem; margin-top: 0.05rem; }
.artifact__noteicon svg { width: 100%; height: 100%; display: block; }
.artifact[data-kind="sense"]   .artifact__noteicon { color: #4F46E5; }
.artifact[data-kind="decide"]  .artifact__noteicon { color: var(--indigo-700); }
.artifact[data-kind="measure"] .artifact__noteicon { color: #0D9488; }
.artifact[data-kind="improve"] .artifact__noteicon { color: #2563EB; }

/* Motion is JS-gated: without scripting the grid is simply resolved. */
.js .artifact {
  opacity: 0;
  transform: translate(var(--dx, 0), var(--dy, 0)) rotate(var(--rot, 0deg));
  transition: transform 0.85s var(--ease), opacity 0.55s var(--ease);
  transition-delay: calc(var(--i, 0) * 110ms);
}
.js .artifacts[data-state="in"] .artifact { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .artifact { opacity: 1; transform: none; transition: none; }
}

.ai-video__steps {
  display: grid;
  gap: 1rem;
  margin-top: 2rem;
}
.ai-video__step {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: 1rem;
  align-items: start;
}
.ai-video__step__num {
  position: relative;
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--indigo-300);
  border-radius: 50%;
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--indigo-700);
  box-shadow: 0 2px 10px rgba(109, 40, 217, 0.12);
}
/* A gradient ring, drawn by masking the middle out of a gradient disc, so
   it does not depend on knowing the colour behind it — this block sits on
   the page ground, not on a panel. The 1px border above stays as the
   fallback wherever the mask is not supported, and the disc is only painted
   where it is, or it would cover the number. */
@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .ai-video__step__num { border-color: transparent; }
  .ai-video__step__num::before {
    content: "";
    position: absolute; inset: -1px; border-radius: 50%;
    padding: 2px;
    background: linear-gradient(145deg, var(--indigo-400), #4F46E5 60%, #4338CA);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    pointer-events: none;
  }
}
.ai-video__step__text h3 {
  font-family: var(--body);
  font-weight: 600;
  font-size: var(--fs-h3);
  color: var(--ink);
}
.ai-video__step__text p {
  color: var(--ink-2);
  font-size: var(--fs-small);
  margin-top: 0.25rem;
}

/* Typing cursor */
.type-cursor::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 1em;
  background: var(--indigo);
  margin-left: 0.15em;
  vertical-align: text-bottom;
  animation: blink 1s step-end infinite;
}
@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

/* --- 16. Showcase sections (video-like animated backgrounds) ----------- */
.showcase {
  position: relative;
  overflow: hidden;
  min-height: 420px;
  display: flex;
  align-items: center;
}
.showcase__canvas,
.showcase__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  object-fit: cover;
}
.showcase__video {
  opacity: 0.35;
}
.showcase__video--dim {
  opacity: 0.22;
}
.showcase__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(90deg, rgba(13,16,20,0.82) 0%, rgba(13,16,20,0.55) 50%, rgba(13,16,20,0.75) 100%);
  pointer-events: none;
}
.showcase__overlay--light {
  background: linear-gradient(90deg, rgba(236,238,241,0.92) 0%, rgba(236,238,241,0.75) 50%, rgba(236,238,241,0.88) 100%);
}
.showcase__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  padding-block: clamp(4rem, 8vw, 7rem);
}
.showcase__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
  align-items: center;
}
@media (min-width: 62rem) {
  .showcase__grid {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: 4rem;
  }
  .showcase__grid--reverse {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  }
  .showcase__grid--reverse .showcase__text {
    order: 2;
  }
}
.showcase__text {
  max-width: 48ch;
}
.showcase__title {
  color: #fff;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.8rem, 3.2vw, 2.6rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
.showcase__body {
  color: var(--indigo-50);
  font-size: var(--fs-lead);
  line-height: 1.58;
  margin-top: 1.25rem;
  text-wrap: pretty;
}
.showcase__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.75rem;
}
.showcase__meta li {
  font-family: var(--display);
  font-weight: var(--w-sub);
  font-size: var(--fs-label);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--indigo-50);
  padding: 0.35rem 0.8rem;
  /* The chip carries its own ground. Without it the text is only as legible
     as whatever frame the canvas happens to be painting, and the lightest
     one measured left it at 4.39 — short of the 4.5 floor. A fill of its own
     puts legibility under this rule's control instead of the animation's,
     and the animation itself is untouched. */
  background: rgba(13, 16, 20, 0.45);
  /* 3:1 is the floor for a component boundary; the old blue managed 1.9. */
  border: 1px solid var(--indigo-200);
  border-radius: var(--radius-pill);
  min-height: 24px; display: inline-flex; align-items: center;
}
.showcase--light .showcase__title { color: var(--ink); }
.showcase--light .showcase__body { color: var(--ink-2); }
.showcase--light .showcase__meta li { color: var(--indigo); border-color: var(--indigo-edge); background: rgba(255, 255, 255, 0.55); }

/* --- 16b. Welcome sequence ------------------------------------------------
   A single quiet moment before the page: the drifting field, the name, and a
   rule drawing itself. It is decoration only — removed at once without
   scripting or under prefers-reduced-motion, and it withholds no content. */
.intro { display: none; }
.js .intro {
  display: block;
  position: fixed; inset: 0; z-index: 400;
  /* The glow belongs to the centrepiece, not to the whole frame. Measured
     against the board: its corners sit at a luminance of about 2.6, so the
     wash that used to cover the page is gone and what is left is tight. */
  background:
    radial-gradient(26% 21% at 50% 65%, rgba(58, 92, 240, 0.40), transparent 70%),
    radial-gradient(60% 42% at 50% 62%, rgba(24, 36, 126, 0.30), transparent 76%),
    #030410;
  color: #fff; overflow: hidden; cursor: pointer;
  opacity: 1; visibility: visible;
  transition: opacity 0.7s var(--ease), visibility 0s linear 0.7s;
}
.js .intro[data-state="done"] { opacity: 0; visibility: hidden; pointer-events: none; }

/* Two canvases: the drifting network across the whole ground, and the orbit
   centrepiece. Both are decoration and neither carries meaning. */
.intro__field,
.intro__orbit { position: absolute; inset: 0; width: 100%; height: 100%; }
.intro__field { opacity: 0.55; }
.intro__orbit {
  opacity: 0; transform: scale(0.94);
  animation: introOrbit 1.5s var(--ease) 0.5s forwards;
}
@keyframes introOrbit { to { opacity: 1; transform: scale(1); } }

/* The lockup, top left, exactly the mark the masthead carries. */
.intro__lockup {
  position: absolute; z-index: 3;
  top: clamp(1.5rem, 4.2vh, 3.1rem); left: clamp(1.5rem, 3.8vw, 3.6rem);
  display: flex; align-items: center; gap: 0.95rem;
  opacity: 0; animation: introFade 0.7s var(--ease) 0.1s forwards;
}
.intro__lockup .brand__mark { width: 62px; height: 62px; flex: none; overflow: visible; }
.intro__lockup-text { display: flex; flex-direction: column; line-height: 1; }
.intro__lockup-name {
  font-family: var(--display); font-weight: 700;
  font-size: 1.62rem; letter-spacing: 0.07em;
  text-transform: uppercase; color: #fff;
}
.intro__lockup-sub {
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.4em;
  text-transform: uppercase; color: rgba(226, 231, 250, 0.86); margin-top: 0.52rem;
}

/* Top right, the three words the board runs as a strapline. */
.intro__meta {
  position: absolute; z-index: 3;
  top: clamp(1.9rem, 4.2vh, 3.1rem); right: clamp(1.4rem, 3.6vw, 3.4rem);
  display: flex; align-items: center; gap: 0.85rem;
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.3em;
  text-transform: uppercase; color: rgba(214, 220, 245, 0.78);
  opacity: 0; animation: introFade 0.7s var(--ease) 0.3s forwards;
}
.intro__meta i { font-style: normal; color: var(--indigo-400); opacity: 0.8; }
/* The board closes the strapline with a short rule running off the edge. */
.intro__meta::after {
  content: ""; width: clamp(2rem, 5vw, 4.5rem); height: 1px; margin-left: 0.4rem;
  background: linear-gradient(90deg, rgba(196, 181, 253, 0.75), transparent);
}

.intro__content {
  position: absolute; z-index: 3; left: 50%; top: 37.6%;
  transform: translate(-50%, -50%);
  /* Wide enough that "Sentrova Solutions" holds one line at full size, the
     way the board sets it. At 60rem it broke to three lines. */
  width: min(100%, 76rem); text-align: center; padding-inline: var(--gutter);
}

/* The eyebrow carries a rule on each side, drawn out from the word. */
.intro__eyebrow {
  display: flex; align-items: center; justify-content: center; gap: 1.1rem;
  font-family: var(--mono); font-size: clamp(0.6rem, 2.1vw, 0.98rem);
  letter-spacing: 0.34em; text-transform: uppercase; color: #F4F7FF;
  opacity: 0; animation: introFade 0.6s var(--ease) 0.2s forwards;
}
.intro__eyebrow::before,
.intro__eyebrow::after {
  content: ""; width: clamp(2rem, 7vw, 5.5rem); height: 1px;
  background: linear-gradient(90deg, transparent, rgba(167, 139, 250, 0.85));
  transform: scaleX(0); transform-origin: right center;
  animation: introRule 0.9s var(--ease) 0.45s forwards;
}
.intro__eyebrow::after {
  background: linear-gradient(90deg, rgba(167, 139, 250, 0.85), transparent);
  transform-origin: left center;
}

.intro__title {
  margin-top: 2.9rem;
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2.5rem, 7.6vw, 6.2rem);
  /* Measured off the board: its two lines advance 90px at a 99px size. */
  line-height: 0.93; letter-spacing: -0.028em;
}
.intro__line { display: block; }
.intro__word {
  display: inline-block; margin-inline: 0.02em;
  /* The board's type is lit, not flat: a soft top-down sheen on the white
     words, and the brand gradient on the last one. */
  background: linear-gradient(176deg, #FFFFFF 26%, #B9C2E4 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  opacity: 0; transform: translateY(0.5em); filter: blur(9px);
  animation: introWord 0.75s var(--ease) forwards;
}
.intro__word--brand {
  background: linear-gradient(176deg, #C4B5FD 18%, #7C3AED 100%);
  -webkit-background-clip: text; background-clip: text;
}
.intro__line:nth-child(1) .intro__word:nth-child(1) { animation-delay: 0.45s; }
.intro__line:nth-child(1) .intro__word:nth-child(2) { animation-delay: 0.57s; }
.intro__line:nth-child(2) .intro__word:nth-child(1) { animation-delay: 0.69s; }
.intro__line:nth-child(2) .intro__word:nth-child(2) { animation-delay: 0.81s; }

.intro__sub {
  margin-top: 1.3rem;
  font-family: var(--mono); font-size: clamp(0.68rem, 1.3vw, 0.95rem);
  letter-spacing: 0.3em; text-transform: uppercase;
  color: rgba(238, 242, 255, 0.95);
  opacity: 0; animation: introFade 0.7s var(--ease) 1.15s forwards;
}

@keyframes introWord { to { opacity: 1; transform: translateY(0); filter: blur(0); } }
@keyframes introFade { to { opacity: 1; } }
@keyframes introRule { to { transform: scaleX(1); } }

@media (max-width: 40rem) {
  .intro__meta { display: none; }
  .intro__eyebrow { gap: 0.6rem; letter-spacing: 0.22em; }
  .intro__eyebrow::before,
  .intro__eyebrow::after { width: clamp(1rem, 5vw, 2.5rem); }
  .intro__lockup .brand__mark { width: 36px; height: 36px; }
  .intro__lockup-name { font-size: 0.92rem; }
  .intro__content { top: 27%; }
}

/* While the welcome sequence plays, the page behind it holds its breath:
   every entrance animation resumes from the top once it lifts. */
html[data-intro-active] :where(main, header, footer) * {
  animation-play-state: paused !important;
}

/* --- 16c. Reading progress ------------------------------------------------
   Two pixels of position feedback under the masthead. Transform only; the
   bar never animates on its own, it follows the scroll. */
.page-progress {
  position: fixed; top: 4.25rem; left: 0; right: 0; z-index: 99;
  height: 2px; background: var(--indigo);
  transform: scaleX(0); transform-origin: left center;
  pointer-events: none; opacity: 0;
}
.js .page-progress { opacity: 1; }

/* --- 16e. Figures ---------------------------------------------------------
   Technical plates: hairline frame, mono plate line, no decoration beyond
   the drawing itself. */
.figure {
  margin-top: clamp(2.25rem, 4vw, 3.25rem);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--panel);
}
.figure__img { width: 100%; height: auto; display: block; }
/* The live field sits inside the frame artwork's own drawing area (the
   plate's border and axis chrome stay static SVG; only the data lives). */
.figure__stage { position: relative; }
.figure__field-host {
  position: absolute;
  left: 4%; right: 4%; top: 18.571%; bottom: 17.143%;
  overflow: hidden;
}
.figure__field { width: 100%; height: 100%; display: block; }
.figure__cap {
  padding: 0.85rem 1.25rem;
  border-top: 1px solid var(--rule);
  color: var(--ink-3);
}

/* --- 16e2. Build-week cadence --------------------------------------------
   The week as five checkpoints on one rail, replacing a flat exported SVG
   that could not reflow, could not be read by a screen reader beyond its alt
   text, and went stale the moment the copy changed. Everything here is live
   markup on the house tokens.

   The rail is drawn by each step rather than by one absolutely positioned
   line: the segment is a flex sibling of the node, so it inherits the node's
   vertical centre for free and needs no magic offset that a type-size change
   would break. A negative margin carries it across the grid gap, and the
   last step has no segment, so the line stops at the final node instead of
   running on to the container edge. */
.cadence {
  --cadence-gap: 1.75rem;
  --cadence-node: 2.75rem;
  margin-top: clamp(2.25rem, 4vw, 3.25rem);
  padding: clamp(1.5rem, 3vw, 2.5rem);
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
}

.cadence__head {
  display: flex; flex-wrap: wrap; align-items: start;
  justify-content: space-between; gap: 1rem 1.5rem;
  padding-bottom: clamp(1.25rem, 2.4vw, 1.75rem);
  border-bottom: 1px solid var(--rule-soft);
}
.cadence__eyebrow {
  display: flex; align-items: center; gap: 0.75rem;
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--indigo-700);
}
/* The short stroke after the eyebrow, reused at the far end of the footer. */
.cadence__rule { display: block; width: 1.75rem; height: 1px; background: var(--indigo-200); flex: none; }
.cadence__heading {
  margin-top: 0.7rem;
  font-family: var(--display); font-weight: var(--w-heading);
  font-size: clamp(1.4rem, 2.4vw, 1.9rem);
  line-height: 1.2; letter-spacing: -0.01em;
  color: var(--ink); text-wrap: balance;
}
.cadence__heading-accent { color: var(--indigo-700); }
.cadence__count {
  flex: none;
  padding: 0.4rem 0.95rem;
  border: 1px solid var(--indigo-200);
  border-radius: var(--radius-pill);
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--indigo-700);
  min-height: 24px; display: inline-flex; align-items: center;
}

.cadence__steps {
  display: grid; gap: var(--cadence-gap);
  padding-block: clamp(1.75rem, 3vw, 2.5rem);
  grid-template-columns: 1fr;
  counter-reset: none;
}
.cadence__step { position: relative; min-width: 0; }

.cadence__meta { display: flex; align-items: baseline; gap: 0.6rem; }
.cadence__n {
  font-family: var(--mono); font-size: 0.6875rem;
  letter-spacing: 0.2em; color: var(--indigo-600);
  font-variant-numeric: tabular-nums;
}
.cadence__day {
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--ink);
}

.cadence__rail { display: flex; align-items: center; margin-top: 0.9rem; }
.cadence__node {
  flex: none;
  width: var(--cadence-node); height: var(--cadence-node);
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--indigo-700); color: #fff;
  /* A flat ring, not elevation: no blur, no offset. */
  box-shadow: 0 0 0 5px var(--indigo-100);
}
.cadence__node svg { width: 1.15rem; height: 1.15rem; }
.cadence__line { flex: 1; height: 1px; background: var(--indigo-300); }
.cadence__step:last-child .cadence__line { display: none; }

.cadence__text { margin-top: 1.4rem; }
.cadence__title {
  font-family: var(--display); font-weight: var(--w-heading);
  font-size: var(--fs-h3); line-height: 1.3; letter-spacing: -0.004em;
  color: var(--ink);
}
.cadence__body {
  margin-top: 0.55rem;
  color: var(--ink-2); font-size: var(--fs-small); line-height: 1.6;
  text-wrap: pretty;
}
.cadence__tag {
  margin-top: 1rem;
  /* flex-wrap, not white-space: the label and its arrow are separate flex
     items, and flex items never wrap on white-space alone. Without this the
     pill held one line at any cost and ran 8px past the card at 320px. */
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 0.4rem;
  max-width: 100%;
  /* The longest pill sets the minimum column width for the whole five-up.
     At 0.12em and 0.8rem it measured 202px against a 201px column — over by
     a pixel at every width, because the shell caps at 78rem. Trimmed to
     190px, which leaves the row real slack. */
  padding: 0.35rem 0.7rem;
  background: var(--indigo-50);
  border-radius: var(--radius-pill);
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--indigo-700);
  min-height: 24px;
}

.cadence__foot {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 0.75rem 1.5rem;
  padding-top: clamp(1.1rem, 2.2vw, 1.5rem);
  border-top: 1px solid var(--rule-soft);
}
.cadence__foot-item {
  display: flex; align-items: center; gap: 0.7rem;
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-3);
}
.cadence__dot {
  flex: none; width: 0.85rem; height: 0.85rem;
  border-radius: 50%;
  border: 1px solid var(--indigo-300);
  background:
    radial-gradient(circle, var(--indigo-700) 0 38%, transparent 39%);
}

/* Below the five-up: the same five checkpoints as a vertical run, with the
   rail turned on its side so the line still carries the eye from one node to
   the next. The rail spans the whole step, so the segment reaches the node
   below it however tall the copy above runs. */
@media (max-width: 77.99rem) {
  .cadence__step {
    display: grid;
    grid-template-columns: var(--cadence-node) minmax(0, 1fr);
    column-gap: 1.1rem;
  }
  .cadence__rail {
    grid-column: 1; grid-row: 1 / -1;
    flex-direction: column; align-self: stretch;
    margin-top: 0;
  }
  .cadence__line {
    width: 1px; height: auto; flex: 1;
    margin-bottom: calc(var(--cadence-gap) * -1);
  }
  .cadence__meta { grid-column: 2; grid-row: 1; min-height: var(--cadence-node); align-items: center; }
  .cadence__text { grid-column: 2; grid-row: 2; margin-top: 0.6rem; }
}

/* 78rem is measured, not chosen, and it is where the shell stops growing.
   The widest pill is 193px. Below this the columns are narrower than that,
   so the pills quietly fold to two lines — at 1200px by a single pixel. From
   78rem the shell is capped, columns settle at 202px, and the row holds. */
@media (min-width: 78rem) {
  .cadence__steps {
    grid-template-columns: repeat(5, minmax(0, 1fr));
    column-gap: var(--cadence-gap);
    row-gap: 0;
  }
  /* Carries the segment across the gap to the next node. */
  .cadence__line { margin-right: calc(var(--cadence-gap) * -1); }

  /* Five checkpoints only read as one row if their five slots do. A step
     whose body runs to four lines would otherwise push its tag below the
     tags beside it, which is the defect the panel grids already fix this
     way. Subgrid hands the slots to the parent's rows, so each is as tall as
     the tallest in the row; the wrapper is dissolved with display:contents
     so its children can reach those rows. Spacing stays on the items rather
     than the gap, because the five steps are not evenly spaced. */
  @supports (grid-template-rows: subgrid) {
    .cadence__step {
      display: grid;
      grid-template-rows: subgrid;
      grid-row: span 5;
    }
    .cadence__text { display: contents; }
    .cadence__meta  { grid-row: 1; }
    .cadence__rail  { grid-row: 2; }
    .cadence__title { grid-row: 3; margin-top: 1.4rem; }
    .cadence__body  { grid-row: 4; }
    .cadence__tag   { grid-row: 5; align-self: start; justify-self: start; }
  }
  /* Only here does the pill need to hold one line: it is what sets the
     column width. Stacked, it has a whole row and may wrap, which is what
     keeps it inside a 320px card. */
  .cadence__tag { white-space: nowrap;
  }
}

/* --- 16e3. Structure pillars ----------------------------------------------
   The three claims on the about page's structure band. Their own component
   rather than `.panel` with modifiers: `.panel` carries a hairline border and
   a flat ground on six pages, and these want the opposite — no border, a
   lifted shadow, and an icon column standing beside the text rather than
   above it. Colours measured off the design: ground #FAFCFE, card #FFFFFF,
   the separation carried by the shadow rather than by a rule. */
#structure.band--panel { background: #FAFCFE; }

/* The design sets this band's eyebrow above the heading rather than in the
   left column, so the rule, the eyebrow and the heading share one left edge
   with the cards under them. Below 60rem `.band-head` is already a single
   column and stacks on its own. */
@media (min-width: 60rem) {
  .band-head--stacked {
    grid-template-columns: minmax(16rem, 1fr) minmax(0, 21rem);
    row-gap: 0.9rem;
  }
  .band-head--stacked > .label          { grid-column: 1; grid-row: 1; }
  .band-head--stacked .band-head__body  { grid-column: 1; grid-row: 2; }
  .band-head--stacked .band-head__aside { grid-column: 2; grid-row: 1 / span 2; }
}

.pillars {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.6rem;
}
.pillar {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.95rem;
  align-items: start;
  padding: 1.15rem 1.4rem;
  background: var(--panel);
  border-radius: 20px;
  /* Two shadows, not one: a tight contact edge so the card reads as sitting
     on the ground, and a wide soft fall so it reads as lifted off it. The
     design's ramp runs about 25px past the card, which is the second blur. */
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.05),
    0 14px 30px -12px rgba(20, 24, 48, 0.16);
}
.pillar__tile {
  display: grid;
  place-items: center;
  width: 4.2rem;
  height: 4.2rem;
  border-radius: 50%;
  /* #F1EDFE off the design, which is --indigo-100 to within a hair. */
  background: var(--indigo-100);
  color: var(--indigo-700);
}
.pillar__tile svg { width: 1.85rem; height: 1.85rem; display: block; }
.pillar__label { margin: 0 0 0.3rem; }
.pillar__title {
  font-family: var(--display);
  font-size: 1.0625rem;
  font-weight: var(--w-sub);
  line-height: 1.35;
  color: var(--ink);
  margin: 0 0 0.55rem;
}
.pillar__body { color: var(--ink-2); line-height: 1.65; margin: 0; }
.pillar__title, .pillar__body { overflow-wrap: anywhere; }

@media (min-width: 46rem) {
  .pillars { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The third card sits centred under the pair and a little wider than one
     column — 54% of the row, which is what the design measures. */
  .pillar--wide { grid-column: 1 / -1; justify-self: center; width: min(100%, 54%); }
  .pillar { padding: 1.2rem 1.45rem; }
}

/* --- 16e4. The 45-day stages ---------------------------------------------
   The eight stages of the foundation build. They were rows of a `.spec`
   list divided by hairlines — the same component the measure band, the
   contact page and the services page use — so they could not be given cards
   without dragging six other places along. Ground, card, radius and the two
   shadows are the ones measured off the structure design; this is the same
   design system, so they are the same values. */
#implementation.band--panel { background: #FAFCFE; }

.stages { display: grid; gap: 0.7rem; margin: 0; }
.stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.55rem;
  padding: 1.05rem 1.2rem;
  background: var(--panel);
  border-radius: 16px;
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.05),
    0 10px 24px -14px rgba(20, 24, 48, 0.16);
}
.stage__days {
  justify-self: start;
  align-self: start;
  padding: 0.32rem 0.7rem;
  border-radius: var(--radius-pill);
  background: var(--indigo-100);
  color: var(--indigo-700);
  white-space: nowrap;
}
.stage__val { margin: 0; color: var(--ink-2); line-height: 1.6; }
.stage__title {
  display: block;
  font-family: var(--display);
  font-size: 1.0625rem;
  font-weight: var(--w-sub);
  line-height: 1.35;
  color: var(--ink);
  margin-bottom: 0.3rem;
}
@media (min-width: 48rem) {
  .stage {
    /* The pill column is fixed so all eight titles start on one left edge,
       and wide enough for the longest range before it wraps. */
    grid-template-columns: 8.5rem minmax(0, 1fr);
    gap: 0 1.5rem;
    padding: 1.1rem 1.45rem;
  }
  .stage__days { grid-column: 1; grid-row: 1; }
  .stage__val  { grid-column: 2; grid-row: 1; }
}

/* --- 16e5. Assurances -----------------------------------------------------
   Why the delivery model holds: four claims, each carrying its own accent
   down a spine and around its mark, set together inside one tinted panel so
   they read as a set rather than four loose boxes.

   Spines and tiles take the colours measured off the design. The eyebrow
   text does not: the design's green (#05BD9A) and orange (#FE9246) come out
   at 2.4:1 and 2.2:1 on white, well under the AA floor this repo holds
   itself to. Decoration can carry them; words cannot. So each kind keeps a
   darker ink for its label, the same split the decision ladder uses. */
.assurances {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.9rem;
  padding: 0.9rem;
  background: #F5F8FE;
  border: 1px solid rgba(80, 110, 190, 0.10);
  border-radius: 22px;
}
@media (min-width: 52rem) {
  .assurances { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.05rem; padding: 1.05rem; }
}
.assurance {
  position: relative;
  overflow: hidden;
  padding: 1.1rem 1.3rem 1.2rem 1.45rem;
  background: var(--panel);
  border-radius: 14px;
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.04),
    0 8px 20px -12px rgba(20, 24, 48, 0.16);
}
/* The spine is clipped to the card's radius by the overflow above, so it
   curves with the corner instead of squaring it off. */
.assurance::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 6px;
  background: linear-gradient(180deg, var(--as-a), var(--as-b));
}
.assurance[data-kind="infrastructure"] { --as-a: #9B7BFF; --as-b: #7542FA; --as-ink: #6D28D9; --as-tile: #E9E5FD; }
.assurance[data-kind="auditability"]   { --as-a: #6E95FF; --as-b: #3965FA; --as-ink: #1D4ED8; --as-tile: #E1E9FE; }
.assurance[data-kind="safety"]         { --as-a: #4BD9BC; --as-b: #05BD9A; --as-ink: #047857; --as-tile: #DDF5F1; }
.assurance[data-kind="ownership"]      { --as-a: #FFB37A; --as-b: #FE9246; --as-ink: #C2410C; --as-tile: #FEEBE2; }

.assurance__key { display: flex; align-items: center; gap: 0.75rem; margin: 0 0 0.7rem; }
.assurance__tile {
  flex: none;
  display: grid; place-items: center;
  width: 2.55rem; height: 2.55rem;
  border-radius: 50%;
  background: var(--as-tile);
  color: var(--as-ink);
}
.assurance__tile svg { width: 1.2rem; height: 1.2rem; display: block; }
.assurance__label { color: var(--as-ink); }
.assurance__title {
  font-family: var(--display);
  font-size: 1.0625rem;
  font-weight: var(--w-sub);
  line-height: 1.35;
  color: var(--ink);
  margin: 0 0 0.45rem;
}
.assurance__body { color: var(--ink-2); line-height: 1.6; margin: 0; }
.assurance__title, .assurance__body { overflow-wrap: anywhere; }

/* A hairline beside the aside, not the solid bar the 45-day band uses. Only
   once the aside has a column of its own — stacked, a left rule reads as a
   quote. */
@media (min-width: 60rem) {
  .band-head__aside--ruled { border-left: 1px solid var(--rule-soft); padding-left: 1.4rem; }
}

/* Cooler and a shade deeper than the near-white band that follows it, so the
   two do not run together. */
#ownership { background: #F4F6FB; }

/* --- 16e6. Governance controls -------------------------------------------
   The three controls every workflow is built on. Ground, spines, tiles and
   the pill fills are sampled off the design; the inks are chosen against
   them, because a colour that reads on a spine does not always read as a
   word. The design sets the step numbers at #999AAA, which measures 2.64:1
   on the card — quiet is the point, but not at that price, so they take
   --ink-3 at 5.27:1 and stay quiet anyway. */
#governance.band--panel { background: #F7F7F5; }

.controls { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.1rem; }
@media (min-width: 56rem) {
  .controls { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.35rem; }
}
.control {
  position: relative;
  overflow: hidden;
  /* Four rows with the body on 1fr, so the three feet line up across the
     row however long each body runs. */
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  padding: 1.15rem 1.3rem 1.2rem 1.5rem;
  background: linear-gradient(160deg, #F5F8FB, #FAFAFC 55%, #F9F9FB);
  border: 1px solid rgba(80, 110, 190, 0.10);
  border-radius: 16px;
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.04),
    0 10px 26px -14px rgba(20, 24, 48, 0.18);
}
.control::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 6px;
  background: linear-gradient(180deg, var(--ctl-a), var(--ctl-b));
}
.control[data-kind="review"]  { --ctl-a: #7C5CF5; --ctl-b: #4426AE; --ctl-ink: #6D28D9; --ctl-tile: #E1DDF9; }
.control[data-kind="state"]   { --ctl-a: #4FA3E8; --ctl-b: #2271CE; --ctl-ink: #1D4ED8; --ctl-tile: #DAE8FA; }
.control[data-kind="logging"] { --ctl-a: #A78BFA; --ctl-b: #6D28D9; --ctl-ink: #6D28D9; --ctl-tile: #E2DDF9; }

.control__key { display: flex; align-items: center; gap: 0.8rem; margin: 0 0 0.85rem; padding-right: 2.4rem; }
.control__tile {
  flex: none;
  display: grid; place-items: center;
  width: 2.6rem; height: 2.6rem;
  border-radius: 12px;
  background: var(--ctl-tile);
  color: var(--ctl-ink);
}
.control__tile svg { width: 1.25rem; height: 1.25rem; display: block; }
.control__label { color: var(--ctl-ink); }
.control__n {
  position: absolute; top: 1.15rem; right: 1.3rem;
  font-family: var(--mono); font-size: var(--fs-small);
  color: var(--ink-3); letter-spacing: 0.04em;
}
.control__title {
  font-family: var(--display); font-size: 1.0625rem; font-weight: var(--w-sub);
  line-height: 1.35; color: var(--ink); margin: 0 0 0.5rem;
}
.control__body { color: var(--ink-2); line-height: 1.6; margin: 0 0 1.15rem; }
.control__foot { display: flex; align-items: center; gap: 0.75rem; margin: 0; }
.control__disc {
  flex: none;
  display: grid; place-items: center;
  width: 2.1rem; height: 2.1rem;
  border-radius: 50%;
  background: #EAE9FA;
  color: var(--ctl-ink);
}
.control__disc svg { width: 0.95rem; height: 0.95rem; display: block; }
.control__out { color: var(--ctl-ink); }
.control__title, .control__body { overflow-wrap: anywhere; }

/* The three foundations, restated under the aside as the design sets them. */
.foundations {
  display: flex; flex-wrap: wrap; gap: 0.5rem;
  margin: 0.9rem 0 0; padding: 0; list-style: none;
}
.foundations li {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.33rem 0.7rem;
  border-radius: var(--radius-pill);
  background: #EAE8FA;
  color: var(--indigo-700);
}
.foundations svg { width: 0.85rem; height: 0.85rem; display: block; flex: none; }

/* --- 16f. Small motion courtesies ---------------------------------------- */
/* Nav links draw their own underline on the way past. */
.nav__link::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: var(--indigo);
  transform: scaleX(0); transform-origin: left center;
  transition: transform 0.25s var(--ease);
}
.nav__link:hover::after { transform: scaleX(1); }
.nav__link[aria-current="page"]::after {
  height: 2px; background: var(--indigo);
  transform: scaleX(1); transition: none;
}
/* The mark answers the pointer with a half-unit lift, and the three nodes
   take a turn around the ring — the board's orbit effect.

   It runs on hover and focus rather than always. The masthead is sticky, so
   an orbit that started on its own would be in the corner of the eye for the
   whole session, and WCAG 2.2.2 asks for a way to stop any motion that runs
   past five seconds without being asked for. One turn on approach reads as
   alive; a permanent one reads as a distraction. */
.brand__mark { transition: transform 0.25s var(--ease); }
.brand:hover .brand__mark,
.brand:focus-visible .brand__mark { transform: translateY(-1px); }

/* view-box, not fill-box: the three nodes sit at 184deg, 15deg and 90deg, so
   the box that bounds them is off-centre and rotating about it would swing
   them off the orbit instead of around it. The viewBox centre is the centre
   of the circle they ride. */
.brand__mark [data-orbit] {
  transform-box: view-box;
  transform-origin: 50% 50%;
}
.brand:hover .brand__mark [data-orbit],
.brand:focus-visible .brand__mark [data-orbit] {
  animation: markOrbit 3.2s cubic-bezier(0.62, 0, 0.38, 1) 1;
}
@keyframes markOrbit { to { transform: rotate(360deg); } }

/* The nodes also warm very slightly, which is the board's "subtle glow" at
   a size where an actual blur would only muddy a 4px dot. */
.brand__mark [data-node-a] path,
.brand__mark [data-node-b] path,
.brand__mark [data-node-c] path { transition: opacity 0.25s var(--ease); }
.brand:hover .brand__mark [data-node-b] path { opacity: 0.85; }

@media (prefers-reduced-motion: reduce) {
  .brand:hover .brand__mark [data-orbit],
  .brand:focus-visible .brand__mark [data-orbit] { animation: none; }
}

/* --- 17. Print ----------------------------------------------------------- */
@media print {
  .masthead, .rail, .hero__field, .footer, .showcase__canvas, .showcase__overlay,
  .intro, .page-progress, .canvas-video { display: none; }
  .showcase { min-height: auto; background: #fff !important; }
  body { background: #fff; }
}
