/* =============================================================
   Astrikos.ai homepage. Tokens and rules mirror DESIGN.md.
   Five acts: ink opening, paper industries, ink core, paper
   evidence, ink closing. One accent (the brand gradient).
   ============================================================= */

/* ---------- Fonts (self-hosted, SIL OFL) ---------- */
@font-face {
  font-family: "Space Grotesk";
  src: url("../fonts/space-grotesk-latin.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Plus Jakarta Sans";
  src: url("../fonts/plus-jakarta-sans-latin.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Space Mono";
  src: url("../fonts/space-mono-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ---------- Tokens ---------- */
:root {
  --ink-950: #05070D;
  --ink-900: #0A0D17;
  --ink-850: #0F1320;
  --ink-800: #161B2B;
  --ink-700: #232A3E;
  --ink-600: #343C55;
  --on-ink: #F2F4F8;
  --on-ink-muted: #A3A9BC;
  --on-ink-subtle: #858BA1;

  --paper-0: #FCFCFD;
  --paper-50: #F5F6F9;
  --paper-100: #ECEEF3;
  --paper-200: #DFE2EA;
  --paper-300: #C4C9D6;
  --on-paper: #0A0D17;
  --on-paper-muted: #4F566A;
  --on-paper-subtle: #666C80;

  --accent: #0E17FF;
  --accent-end: #FF00B7;
  --accent-on-ink: #8E94FF;
  --accent-tint: #ECEDFF;

  --danger: #C4251B;
  --danger-on-ink: #FF8A80;
  --success: #0F7B47;
  --success-on-ink: #5BD69B;

  --grad: linear-gradient(90deg, var(--accent) 0%, var(--accent-end) 100%);
  /* Shadows and veils are ink-950 (5,7,13) at alpha: no new hue enters the page. */
  --shadow-float: 0 24px 48px -24px rgba(5, 7, 13, .45);
  --shadow-panel: 0 32px 64px -32px rgba(5, 7, 13, .7);
  /* Cast upward by a section rising over the one before it (the curtain). */
  --shadow-curtain: 0 -24px 48px -24px rgba(5, 7, 13, .45);

  --font-display: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body: "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "Space Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  /* Spacing scale: 4, 8, 12, 16, 24, 32, 48, 64, 96, 128 and nothing between */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;
  --s-10: 128px;

  /* Shapes (DESIGN.md 6, revised 16 Oct 2026): anything you press is round (pills; a field of several lines
     takes --r-field); every container and every picture is square. --r-lg and --r-md stay as names so a
     component keeps saying which tier it is, but both are 0. --r-sm is only for focus frames. */
  --r-pill: 9999px;
  --r-field: 24px;
  --r-lg: 0px;
  --r-md: 0px;
  /* The one rounded container: the S!aP card, which grows out of the Request a demo pill (header) and is kept
     the same in the footer. 24 outside, 16 for its picture (24 less the 8 of padding). */
  --r-card: 24px;
  --r-card-in: 16px;
  --r-sm: 8px;

  --container: 1360px;
  --gutter: var(--s-4);
  --section-y: var(--s-8);
  --section-y-air: var(--s-7);
  /* --bar-h is the one-row bar. --header-h is what the header lays over the hero: the bar below
     1280, the labels row plus the expanded view from 1280 (site.js measures it; the default is its design height). */
  --bar-h: 64px;
  --header-h: var(--bar-h);

  /* Motion (references/entrance-motion.md). Declared once, read by site.js. */
  --ease-spring: linear(0, .006, .025 2.8%, .101 6.1%, .539 18.9%, .721 25.3%, .849 31.5%, .937 38.1%, .968 41.8%, .991 45.7%, 1.006 50.1%, 1.015 55%, 1.017 63.9%, 1.001);
  --ease-out-quint: cubic-bezier(.22, 1, .36, 1);
  --ease-out-expo: cubic-bezier(.16, 1, .3, 1);
  --dur-wipe: 1100ms;
  --dur-zoom: 1600ms;
  --cascade-step: 110ms;
  --char-spread: 650ms;
  --char-run: 450ms;
  --mark-lead: 260ms;
  --dur-hover: 400ms;
  /* Micro-interactions, from the Defiant reference (their easeOutQuart and easeInOutQuart). */
  --ease-out-quart: cubic-bezier(.165, .84, .44, 1);
  --ease-in-out-quart: cubic-bezier(.76, 0, .24, 1);
  --dur-roll: 500ms;
  --dur-line: 600ms;
  --dur-trace: 700ms;

  --z-header: 30;
  --z-panel: 40;
  --z-overlay: 50;
  --z-intro: 60;
}
@media (min-width: 640px) {
  :root { --gutter: var(--s-5); --section-y: var(--s-9); --section-y-air: var(--s-8); }
}
@media (min-width: 1024px) {
  :root { --gutter: var(--s-7); --section-y: var(--s-10); --section-y-air: var(--s-9); }
}
@media (min-width: 1280px) {
  :root { --header-h: 275px; }
}

/* Per-act semantic colours: components read these, never the raw scale. */
:root, .act-paper {
  --fg: var(--on-paper);
  --fg-muted: var(--on-paper-muted);
  --fg-subtle: var(--on-paper-subtle);
  --line: var(--paper-200);
  --line-strong: var(--paper-300);
  --card: var(--paper-0);
  --link: var(--accent);
  --focus: var(--accent);
  --chip-bg: var(--paper-0);
}
.act-ink {
  --fg: var(--on-ink);
  --fg-muted: var(--on-ink-muted);
  --fg-subtle: var(--on-ink-subtle);
  --line: var(--ink-700);
  --line-strong: var(--ink-600);
  --card: var(--ink-850);
  --link: var(--accent-on-ink);
  --focus: var(--accent-on-ink);
  --chip-bg: var(--ink-850);
  color: var(--fg);
}

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--bar-h) + var(--s-4));
  scrollbar-gutter: stable;
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--fg);
  background: var(--paper-50);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
img, video, svg { display: block; max-width: 100%; }
img { height: auto; }
p, h1, h2, h3, h4, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; }
button { font: inherit; color: inherit; }
address { font-style: normal; }
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 400;
  text-wrap: balance;
}
::selection { background: var(--accent); color: var(--on-ink); }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

.skip-link {
  position: absolute;
  left: var(--s-4);
  top: var(--s-4);
  z-index: calc(var(--z-overlay) + 1);
  padding: var(--s-3) var(--s-5);
  border-radius: var(--r-pill);
  background: var(--paper-0);
  color: var(--on-paper);
  font-weight: 600;
  transform: translateY(-200%);
}
.skip-link:focus { transform: none; }

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* The gutter never drops under a notch (viewport-fit=cover): env() is 0 everywhere else. */
.container {
  width: 100%;
  max-width: calc(var(--container) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: max(var(--gutter), env(safe-area-inset-left)) max(var(--gutter), env(safe-area-inset-right));
}

/* ---------- Type ---------- */
.display-xl { font-size: clamp(40px, 5.2vw, 72px); line-height: 1.04; letter-spacing: -.03em; }
.display-lg { font-size: clamp(32px, 3.8vw, 56px); line-height: 1.08; letter-spacing: -.025em; }
.headline-md { font-size: clamp(24px, 2.2vw, 32px); line-height: 1.15; letter-spacing: -.015em; font-weight: 500; }
.lead { font-size: 18px; line-height: 1.6; color: var(--fg-muted); max-width: 60ch; }
.section-head { display: grid; gap: var(--s-4); max-width: 880px; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--on-ink-muted);
}
.eyebrow::before {
  content: "";
  width: var(--s-6);
  height: 1px;
  background: var(--grad);
}

/* ---------- Buttons ---------- */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 48px;
  padding: var(--s-3) var(--s-5);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--dur-hover) var(--ease-out-expo);
}
.btn i { font-size: 18px; }
/* Optical centring (measured 16 Oct 2026). Plus Jakarta Sans at line-height 1 puts the cap middle 0.9px and the x-height
   middle 2px under the box middle, so the label reads low beside the arrow: it rides 1px up. A Remix glyph is 1.04em wide
   and its drawing sits centred in that, which leaves 3.4px of bearing at 18px on each side (the font's declared bearing was
   wrong until the same day, see tools/build_icons.py), so a trailing arrow read 3px wider on the right than the label does
   on the left: the arrow takes that bearing back. */
.btn > :is(.roll, .demo-form__submit-label) { translate: 0 -1px; }
.btn > :is(i, .ico):last-child { margin-right: -3px; }
.btn:active { transform: translateY(1px); }
/* The gradient is painted at 140% so the label never sits on its magenta end (DESIGN.md 2). */
.btn--primary {
  color: var(--on-ink);
  background: var(--grad);
  background-size: 140% 100%;
  background-position: 0 0;
}
.btn--ghost {
  color: var(--fg);
  border-color: var(--fg-subtle);
  background: transparent;
}
.btn--sm { min-height: 44px; padding: var(--s-2) var(--s-5); font-size: 14px; }
.btn--lg { min-height: 56px; padding: var(--s-4) var(--s-6); font-size: 16px; }
.btn[disabled], .btn[aria-disabled="true"] {
  cursor: not-allowed;
  transform: none;
  background: var(--ink-700);
  border-color: var(--ink-700);
  color: var(--on-ink-muted);
}
.act-paper .btn[disabled] { background: var(--paper-200); border-color: var(--paper-200); color: var(--on-paper-muted); }
.btn.is-loading { cursor: progress; }
.btn.is-loading::before {
  content: "";
  width: 16px;
  height: 16px;
  border-radius: var(--r-pill);
  border: 2px solid currentColor;
  border-right-color: transparent;
  animation: spin 800ms linear infinite;
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  color: var(--link);
  font-weight: 600;
  font-size: 15px;
  text-decoration: none;
}
.link-arrow i { font-size: 18px; transition: transform var(--dur-roll) var(--ease-out-quart); }
@media (hover: hover) and (pointer: fine) {
  .link-arrow:hover .u { background-size: 100% 1px; }
  .link-arrow:hover .ri-arrow-right-line { transform: translateX(4px); }
  .link-arrow:hover .ri-arrow-right-up-line { transform: translate(2px, -2px); }
}

/* ---------- Chips ---------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 36px;
  padding: var(--s-2) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--chip-bg);
  color: var(--fg);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.2;
}
.chip i { font-size: 16px; color: var(--link); }
.chip span { color: var(--fg-muted); font-weight: 400; }
.chip--link {
  position: relative;
  min-height: 44px;
  text-decoration: none;
}

/* =============================================================
   Micro-interactions (Defiant reference, DESIGN.md 9)
   site.js adds the wrappers below at runtime and marks every
   copy aria-hidden, so the authored markup stays as written and
   a page without JS keeps plain, working controls.
   ============================================================= */
/* Roll: the label slides up and an identical copy rises into its place. The box
   carries .2em above and below so descenders are never clipped. */
.roll {
  display: inline-grid;
  overflow: hidden;
  vertical-align: top;
  padding-block: .2em;
  margin-block: -.2em;
}
.roll__t {
  grid-area: 1 / 1;
  transition: transform var(--dur-roll) var(--ease-out-quart);
}
.roll__t + .roll__t { transform: translateY(calc(100% + .4em)); }
/* Icon swap: the arrow leaves along its own direction and a twin arrives behind it. */
.ico { display: inline-grid; overflow: hidden; vertical-align: middle; }
.ico > i { grid-area: 1 / 1; transition: transform var(--dur-roll) var(--ease-out-quart); }
.ico > i + i { transform: translateX(-110%); }
.ico--up > i + i { transform: translate(-110%, 110%); }
@media (hover: hover) and (pointer: fine) {
  :is(.btn, .mrow, .menu-open):hover .roll__t:first-child { transform: translateY(calc(-100% - .4em)); }
  :is(.btn, .mrow, .menu-open):hover .roll__t + .roll__t { transform: none; }
  .btn:hover .ico > i:first-child { transform: translateX(110%); }
  .btn:hover .ico--up > i:first-child { transform: translate(110%, -110%); }
  .btn:hover .ico > i + i { transform: none; }
}
/* A play triangle's weight sits left of its bounding box's centre (its point is at the right), so it moves right by 0.04em to read centred in its circle. */
.pg-video__play .ri-play-line { translate: .04em 0; }
/* Trace: a 1px outline draws clockwise from the top left once, as the pill arrives on screen, and stays
   (review 16 Oct 2026: the outline used to retract after drawing, and to redraw on hover; now it is
   the arrival only, and the drawn outline is the pill's border from then on). */
.trace {
  position: absolute;
  top: -1px;
  left: -1px;
  width: calc(100% + 2px);
  height: calc(100% + 2px);
  overflow: visible;
  pointer-events: none;
  fill: none;
  stroke: var(--link);
  stroke-width: 1;
  stroke-dasharray: var(--len, 0);
  stroke-dashoffset: var(--len, 0);
  transition: stroke-dashoffset var(--dur-trace) var(--ease-out-quart);
}
.is-traced > .trace { stroke-dashoffset: 0; }
/* No JS, or no motion: the outline is simply there. */
html:not(.js-motion) :is(.btn--ghost, .chip--link) { border-color: var(--link); }
:is(.btn--ghost, .chip--link):focus-visible > .trace { stroke-dashoffset: 0; }
/* Underline: a 1px rule grows from the left under the text. */
.u,
.insight__title a,
.validation__title,
.announce__more-label {
  background: linear-gradient(currentColor, currentColor) no-repeat 0 100% / 0 1px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  transition: background-size var(--dur-line) var(--ease-out-quart);
}
@media (hover: hover) and (pointer: fine) {
  :is(.footer__col a, .footer__legal a, .site-header__link):hover .u,
  .insight:hover .insight__title a,
  .validation:hover .validation__title,
  .announce__link:hover .announce__more-label { background-size: 100% 1px; }
}
:is(.link-arrow, .footer__col a, .footer__legal a, .site-header__link):focus-visible .u,
.insight:focus-within .insight__title a,
.validation:focus-visible .validation__title { background-size: 100% 1px; }

/* ---------- Logos (white alpha masks, act-ink only) ---------- */
.logo {
  height: var(--logo-h, 32px);
  width: auto;
  opacity: .72;
  transition: opacity var(--dur-hover) var(--ease-out-expo);
}
@media (hover: hover) and (pointer: fine) {
  .logo:hover { opacity: 1; }
}

/* =============================================================
   Announcement bar
   ============================================================= */
.announce {
  position: relative;
  background: var(--ink-950);
  color: var(--on-ink-muted);
  border-bottom: 1px solid var(--ink-700);
  font-size: 14px;
  line-height: 1.4;
}
.announce__in {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding-block: var(--s-1);
  padding-right: calc(var(--gutter) + var(--s-7));
  text-align: center;
}
/* The whole line is one link, so the tap target is the bar, not a short label. */
.announce__link {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  column-gap: var(--s-4);
  row-gap: var(--s-1);
  min-height: 44px;
  color: var(--on-ink-muted);
  text-decoration: none;
}
.announce__more { display: inline-flex; align-items: center; gap: var(--s-1); color: var(--on-ink); font-weight: 600; }
@media (max-width: 639px) {
  /* Phones: the ticker and the label drop, the arrow stays, and the bar holds to two lines. */
  .announce { font-size: 13px; }
  .announce__ticker, .announce__more-label { display: none; }
  .announce__link { display: block; padding-block: var(--s-1); }
  .announce__more { display: inline; margin-left: var(--s-1); }
}
.announce__close {
  position: absolute;
  right: var(--s-2);
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--on-ink-muted);
  font-size: 18px;
  cursor: pointer;
}
.announce__close:hover { color: var(--on-ink); }
.announce__close:focus-visible { outline-color: var(--accent-on-ink); }
.announce.is-dismissed { display: none; }

/* =============================================================
   Site header (DESIGN.md 8.2), after defiant.vc. One fixed header, one grid, two modes.
   Expanded (1280 and up, top of the page): the five category names over a hairline, then the
   A mark, the line and the sitemap's pages hang under them. Collapsed (once the page scrolls,
   and always below 1280): ONE row, the wordmark, the same names and Request a demo, sticky.
   Hovering the collapsed header (or keyboard focus inside it) grows the same header downward to
   the expanded view; there is no dialog. Menu exists only where hover cannot reach (touch,
   narrow screens). The surface is a plain glass: translucent ink with a blur. The header takes
   no space in the flow, so changing mode moves nothing. State classes (site.js): is-collapsed,
   is-open, is-solid, is-in; html.is-wide mirrors (min-width: 1280px). Motion language from the
   Defiant reference (DESIGN.md 9): quartic easing, long soft moves, hairlines that draw, labels
   that roll. Transform and opacity only.
   ============================================================= */
.site-header {
  --fg: var(--on-ink);
  --fg-muted: var(--on-ink-muted);
  --fg-subtle: var(--on-ink-subtle);
  --link: var(--accent-on-ink);
  --focus: var(--accent-on-ink);
  --line: rgba(242, 244, 248, .16);
  --glass: rgb(5 7 13 / .8);
  --glass-edge: rgb(242 244 248 / .08);
  /* The right-hand column holds Request a demo (and Menu on touch), so it never meets the last name. */
  --actions-w: clamp(216px, 17vw, 256px);
  /* One column template for the bar and the expanded view, so the names sit over their pages. */
  --cols: minmax(0, 1fr) repeat(2, minmax(0, 1fr)) minmax(0, 1.4fr) repeat(2, minmax(0, 1fr)) var(--actions-w);
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  height: 0;
  color: var(--on-ink);
}
@media (pointer: coarse) {
  .site-header { --actions-w: 256px; }
}
/* html reserves scroll-padding-top for the sticky bar so anchor jumps clear it. A focused control inside the
   bar itself sits in that reserved strip, so the browser would scroll the page up to "reveal" it, and a sticky
   element never moves: the page ran to the top on every Tab. A matching negative margin cancels it. */
.site-header :is(a, button, summary) { scroll-margin-top: calc(-1 * (var(--bar-h) + var(--s-4))); }
/* Leaving the viewport is what switches the header to its collapsed mode. */
.header-sentinel { position: absolute; top: 0; left: 0; width: 1px; height: var(--s-5); pointer-events: none; }
.brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  flex: none;
}
.brand img { height: 36px; width: auto; }

/* ---------- Row link: the expanded view and the footer menus share it ---------- */
.mrow {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  min-height: 32px;
  padding-block: var(--s-1);
  color: var(--fg);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.3;
  text-decoration: none;
}
/* ::before is the resting hairline, ::after draws over it on hover. */
.mrow::before,
.mrow::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  transform-origin: left;
}
.mrow::before { background: var(--line); }
.mrow::after {
  background: var(--fg);
  transform: scaleX(0);
  transition: transform var(--dur-line) var(--ease-out-quart);
}
.mrow__arrow {
  flex: none;
  font-size: 16px;
  color: var(--link);
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity var(--dur-roll) var(--ease-out-quart), transform var(--dur-roll) var(--ease-out-quart);
}
.mrow:focus-visible::after { transform: scaleX(1); }
.mrow:focus-visible .mrow__arrow { opacity: 1; transform: none; }
@media (hover: hover) and (pointer: fine) {
  .mrow:hover::after { transform: scaleX(1); }
  .mrow:hover .mrow__arrow { opacity: 1; transform: none; }
}
/* Touch: no hover, so the arrow is always shown and every row is a full target. */
@media (pointer: coarse) {
  .mrow { min-height: 44px; }
  .mrow__arrow { opacity: 1; transform: none; }
}

/* ---------- Bar: the wordmark, the names and Request a demo on one row ---------- */
.site-header__bar { position: relative; z-index: 1; height: var(--bar-h); }
/* The surface is a plain glass: translucent ink and a blur, faded in on opacity so nothing animates
   paint. Eighty percent ink keeps the names above 4.5:1 even over a pale section. */
.site-header__bar::before,
.site-header__panel::before {
  content: "";
  position: absolute;
  background: var(--glass);
  border-bottom: 1px solid var(--glass-edge);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  backdrop-filter: blur(20px) saturate(150%);
  pointer-events: none;
}
@media (prefers-reduced-transparency: reduce) {
  .site-header { --glass: var(--ink-950); }
  .site-header__bar::before,
  .site-header__panel::before { -webkit-backdrop-filter: none; backdrop-filter: none; }
}
.site-header__bar::before {
  inset: 0;
  opacity: 0;
  transition: opacity 800ms var(--ease-in-out-quart);
}
.site-header.is-solid .site-header__bar::before { opacity: 1; }
/* Open, the panel's glass covers the bar too, so the bar's own glass steps aside. */
.site-header.is-open .site-header__bar::before { opacity: 0; }
.site-header__bar-in {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-5);
  height: 100%;
}
.site-header__brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  flex: none;
}
.site-header__brand img { height: 24px; width: auto; }
.site-header__links { display: none; align-items: center; gap: var(--s-4); margin-left: auto; }
.site-header__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--fg-muted);
  /* 14 (was 12, review 16 Oct 2026: easier to read in the collapsed bar). One size in every mode, so the
     glide between the centred row and the columns (--dx) never changes a name's width mid-move. */
  font-size: 14px;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  transition: color var(--dur-hover) var(--ease-out-expo), transform 900ms var(--ease-in-out-quart);
}
/* Instance exception on index.html (DESIGN.md, Exceptions): the top-row links in the brand display face. */
.site-header__links--display .site-header__link { font-family: var(--font-display); }
@media (hover: hover) and (pointer: fine) {
  .site-header__link:hover { color: var(--fg); }
}
.site-header__link:focus-visible { color: var(--fg); }
.site-header__actions { display: flex; align-items: center; gap: var(--s-4); flex: none; margin-left: auto; }
.site-header__demo { flex: none; }
/* Menu is a plain text button with a plus. It shows only where hover cannot open the view: touch,
   and screens too narrow for the names. The plus turns a quarter on hover and into a cross open. */
.site-header__toggle {
  display: none;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s-2);
  min-width: 56px;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--fg);
  font: 500 13px/1.2 var(--font-body);
  cursor: pointer;
}
@media (max-width: 1023px), (hover: none), (pointer: coarse) {
  .site-header__toggle { display: inline-flex; }
}
.site-header__toggle i { font-size: 16px; }
.menu-open .ri-add-line { transition: transform 700ms var(--ease-out-quart); }
@media (hover: hover) and (pointer: fine) {
  .menu-open:hover .ri-add-line { transform: rotate(90deg); }
}
.site-header.is-open .menu-open .ri-add-line,
.site-header.is-open .menu-open:hover .ri-add-line { transform: rotate(45deg); }
/* The one hairline: under the names, in the expanded view. */
.site-header__rule {
  display: none;
  height: 1px;
  background: var(--line);
  transform-origin: left;
  transition: opacity 600ms var(--ease-out-quart), transform 1000ms var(--ease-out-quart) 100ms;
}
@media (min-width: 1024px) {
  .site-header__links { display: flex; }
  .site-header__actions { margin-left: 0; }
}
/* 1024 to 1279: the wordmark and the button take equal columns either side, so the names sit in the middle
   of the bar. From 1280 the grid below does it. */
@media (min-width: 1024px) and (max-width: 1279px) {
  .site-header__bar-in { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); }
  .site-header__brand { justify-self: start; }
  .site-header__links { margin-inline: 0; }
  .site-header__actions { justify-self: end; }
}
/* The wordmark fades and rises in and leaves a little faster. Visibility follows the fade so a
   hidden control can never take focus. */
.site-header__brand {
  transition: opacity 700ms var(--ease-out-quart) 150ms, transform 900ms var(--ease-out-quart) 150ms, visibility 0s linear 0s;
}
.site-header__actions { transition: transform 900ms var(--ease-in-out-quart), opacity 700ms var(--ease-out-quart) 300ms; }
.site-header__links { transition: opacity 700ms var(--ease-out-quart) 150ms; }

/* ---------- Expanded view: the panel ---------- */
.site-header__panel {
  position: absolute;
  top: var(--bar-h);
  left: 0;
  right: 0;
  max-height: calc(100dvh - var(--bar-h) - var(--s-7));
  visibility: hidden;
  transition: visibility 0s linear 700ms;
}
/* It grows from the top of the header: a glass that scales down, with the content rising over it. */
.site-header__panel::before {
  top: calc(-1 * var(--bar-h));
  right: 0;
  bottom: 0;
  left: 0;
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 700ms var(--ease-in-out-quart);
}
.site-header.is-open .site-header__panel::before { transform: none; transition-duration: 900ms; }
.site-header__scroll {
  position: relative;
  max-height: inherit;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-bottom: env(safe-area-inset-bottom);
}
.site-header__panel-in {
  display: grid;
  gap: var(--s-5);
  padding-block: var(--s-5) var(--s-6);
}
.site-header__intro {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--s-4);
}
.site-header__mark { display: block; width: 64px; height: 64px; flex: none; }
.site-header__mark img { width: 100%; height: 100%; }
.site-header__tagline { max-width: 40ch; font-size: 12px; line-height: 1.5; color: var(--fg-muted); }
.site-header__groups { display: grid; grid-template-columns: minmax(0, 1fr); }
.site-header__group { border-bottom: 1px solid var(--line); }
.site-header__label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 48px;
  font-family: var(--font-display);
  font-size: 18px;
  color: var(--fg);
  list-style: none;
  cursor: pointer;
}
.site-header__label::-webkit-details-marker { display: none; }
.site-header__label i { font-size: 20px; color: var(--fg-muted); transition: transform var(--dur-hover) var(--ease-out-expo); }
.site-header__group[open] .site-header__label i { transform: rotate(180deg); }
.site-header__col { padding-bottom: var(--s-3); }
.site-header__col .mrow { min-height: 44px; padding-right: var(--s-5); font-size: 14px; }
.site-header__col .roll { display: grid; min-width: 0; }
.site-header__col .mrow__arrow { position: absolute; right: 0; top: 50%; margin-top: -8px; }
.site-header__base { display: none; }
.site-header__promo { display: none; }

/* Shown: opened by hover, focus or Menu, or the page is wide and still at the top. */
:is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__panel {
  visibility: visible;
  transition-delay: 0s;
}
.site-header__mark,
.site-header__tagline,
.site-header__group,
.site-header__promo {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 300ms var(--ease-out-quart), transform 300ms var(--ease-out-quart);
}
.site-header__group .mrow::before { transform: scaleX(0); transition: transform 300ms var(--ease-out-quart); }
.site-header__base { transform: scaleX(0); transform-origin: left; transition: transform 300ms var(--ease-out-quart); }
:is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) :is(.site-header__mark, .site-header__tagline, .site-header__group, .site-header__promo) {
  opacity: 1;
  transform: none;
  transition-duration: 800ms, 1000ms;
  transition-delay: calc(220ms + var(--g, 0) * 80ms);
}
:is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__group .mrow::before {
  transform: none;
  transition-duration: 1000ms;
  transition-delay: calc(360ms + var(--g, 0) * 80ms + var(--r, 0) * 45ms);
}
:is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__base {
  transform: none;
  transition-duration: 1100ms;
  transition-delay: 400ms;
}
/* While the view is open the wordmark gives way to the A mark in the panel. */
.site-header.is-open .site-header__brand { opacity: 0; visibility: hidden; transform: translateY(8px); transition-duration: 300ms, 300ms, 0s; transition-delay: 0s, 0s, 300ms; }

/* From 1024 the groups are open columns whose summaries are plain labels; below, disclosures. */
@media (min-width: 1024px) {
  .site-header__panel-in { grid-template-columns: repeat(5, minmax(0, 1fr)); column-gap: var(--s-5); }
  .site-header__intro { grid-column: 1 / -1; }
  .site-header__groups { grid-column: 1 / -1; grid-template-columns: repeat(5, minmax(0, 1fr)); column-gap: var(--s-5); }
  .site-header__group { border-bottom: 0; }
  .site-header__label { min-height: 0; padding-bottom: var(--s-3); font-family: var(--font-body); font-size: 12px; font-weight: 500; color: var(--fg-muted); cursor: default; pointer-events: none; }
  .site-header__label i { display: none; }
  .site-header__col { padding-bottom: 0; }
  .site-header__col .mrow { min-height: 32px; font-size: 13px; }
}

/* ---------- 1280 and up: the grid, the expanded view at the top of the page ---------- */
@media (min-width: 1280px) {
  .site-header { --bar-row1: 48px; }
  .site-header__bar-in,
  .site-header__panel-in { display: grid; grid-template-columns: var(--cols); column-gap: var(--s-5); }
  .site-header__bar-in { grid-template-rows: 1fr; align-items: center; }
  /* One row: the wordmark in the first column, each name in its own column, Request a demo in the last.
     In the expanded view the names and the button sit in the labels row (48 tall, over the hairline);
     collapsed they move down to centre in the bar. That is a transform, so the move is smooth. */
  .site-header__brand { grid-column: 1; grid-row: 1; justify-self: start; }
  .site-header__links {
    display: grid;
    grid-column: 2 / 7;
    grid-row: 1;
    grid-template-columns: subgrid;
    gap: 0 var(--s-5);
    align-self: start;
    height: var(--bar-row1);
    margin-left: 0;
    pointer-events: none; /* the grid spans five columns; only the names take the pointer */
    transition: transform 900ms var(--ease-in-out-quart), opacity 700ms var(--ease-out-quart) 150ms;
  }
  .site-header__link { min-height: var(--bar-row1); justify-self: start; pointer-events: auto; }
  /* The button fills the column, so the card under it shares its edges. */
  .site-header__actions { grid-column: 7; grid-row: 1; justify-self: stretch; transform: translateY(calc((var(--bar-row1) - var(--bar-h)) / 2)); }
  .site-header__demo { flex: 1; transition: transform var(--dur-hover) var(--ease-out-expo), opacity 400ms var(--ease-out-quart) 700ms, visibility 0s linear 700ms; }
  /* Expanded (at the top, or opened from the collapsed bar) the S!aP card takes the column and the button steps
     aside; it keeps its place so nothing moves, and it leaves the tab order while hidden. */
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__demo {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition-delay: 0s, 0s, 400ms;
  }
  /* Collapsed, the names gather into a row at the centre of the bar; open, or expanded at the top, each
     returns to the column over its pages. site.js measures the distance (--dx) for each name. A transform,
     so the glide costs no layout. */
  .site-header.is-collapsed:not(.is-open) .site-header__link { transform: translateX(var(--dx, 0px)); }
  .site-header.is-collapsed .site-header__links { transform: translateY(calc((var(--bar-h) - var(--bar-row1)) / 2)); }
  .site-header.is-collapsed .site-header__actions { transform: none; }
  .site-header__rule--top {
    display: block;
    grid-column: 2 / 7;
    grid-row: 1;
    align-self: start;
    margin-top: calc(var(--bar-row1) - 1px);
  }
  .site-header.is-collapsed .site-header__rule--top { opacity: 0; transition-delay: 0s; }

  /* The expanded view: the intro in the first column, the sitemap's pages under their names, a base hairline,
     and at the right the S!aP card, the one container, running the full height of the view from just under the
     header's top edge to the base hairline. The panel starts at that top edge so the card can reach it; --lead
     gives the intro and the pages back the space the names row takes, so they sit where they always have. */
  .site-header { --lead: calc(var(--bar-row1) - var(--s-2) + var(--s-3)); }
  .site-header.is-collapsed { --lead: calc(var(--bar-h) - var(--s-2) + var(--s-3)); }
  .site-header__panel { top: var(--s-2); max-height: calc(100dvh - var(--s-2) - var(--s-7)); }
  .site-header__panel-in { align-items: start; gap: 0 var(--s-5); padding-block: 0 var(--s-5); }
  .site-header__intro { grid-column: 1; grid-row: 1; grid-template-columns: minmax(0, 1fr); align-items: start; gap: var(--s-4); padding-top: var(--lead); }
  .site-header__mark { width: 128px; height: 128px; }
  .site-header__tagline { max-width: 26ch; }
  .site-header__groups { grid-column: 2 / 7; grid-row: 1; grid-template-columns: subgrid; column-gap: var(--s-5); padding-top: var(--lead); }
  .site-header__label { display: none; }
  .site-header__col .mrow { min-height: 28px; padding-block: var(--s-1); padding-right: var(--s-4); line-height: 1.25; }
  .site-header__overview { display: none; }
  .site-header__base { display: block; grid-column: 1 / 7; grid-row: 2; height: 1px; margin-top: var(--s-5); background: var(--line); }

  /* The S!aP card: a container with a media slot, in the button's column, stretched to the full height of the
     view (contain: size keeps its own content from setting that height).
     It morphs out of the Request a demo pill. Closed, its gradient IS the pill: a ring so thick (half the pill's
     height) that it fills a 44px shape sitting exactly under the button. Opening, the shape grows to the card's
     rectangle while the ring thins to a 1px gradient border, the 4% tint comes up inside it and the contents
     arrive last. Closing runs the same moves back and hands over to the button once it is a pill again. The shape
     is the card's own ::before (ring) and ::after (tint): one element, one link. They stay painted while the
     closed panel is hidden; the link and its contents do not, so they are out of the tab order. */
  .site-header__promo {
    --pill: 44px;                                                         /* .btn--sm */
    --pill-top: calc((var(--bar-h) - var(--pill)) / 2 - var(--s-2));      /* centred in the bar; the card starts 8 down */
    position: relative;
    isolation: isolate;
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    gap: var(--s-3);
    grid-column: 7;
    grid-row: 1 / 3;
    align-self: stretch;
    contain: size;
    min-width: 0;
    padding: var(--s-2);
    border-radius: var(--r-card);
    color: var(--fg);
    text-decoration: none;
    opacity: 1;
    transform: none;
    pointer-events: none;
  }
  .site-header__promo::before,
  .site-header__promo::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: var(--pill-top);
    height: var(--pill);
    box-sizing: border-box;
    border-radius: calc(var(--pill) / 2);
    visibility: visible;
    pointer-events: none;
    opacity: 0;
  }
  .site-header__promo::before {
    z-index: -1;
    padding: calc(var(--pill) / 2);
    background: var(--grad);
    background-size: 140% 100%;            /* as the button paints it, so the pill is the same pixels */
    -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;
    /* Closing: contract, thicken the ring back to a fill, and keep the shape until the button is opaque over it. */
    transition:
      top 700ms var(--ease-in-out-quart) 100ms,
      height 700ms var(--ease-in-out-quart) 100ms,
      border-radius 700ms var(--ease-in-out-quart) 100ms,
      padding 500ms var(--ease-in-out-quart) 250ms,
      filter var(--dur-hover) var(--ease-out-quart),
      opacity 0s linear 1100ms;
  }
  .site-header__promo::after {
    z-index: -2;
    background: rgb(242 244 248 / .04);
    transition:
      top 700ms var(--ease-in-out-quart) 100ms,
      height 700ms var(--ease-in-out-quart) 100ms,
      border-radius 700ms var(--ease-in-out-quart) 100ms,
      opacity 250ms var(--ease-out-quart);
  }
  .site-header__promo-media,
  .site-header__promo-body {
    opacity: 0;
    transform: translateY(var(--s-2));
    transition: opacity 200ms var(--ease-out-quart), transform 200ms var(--ease-out-quart);
  }
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__promo { pointer-events: auto; }
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__promo::before,
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__promo::after {
    top: 0;
    height: 100%;
    border-radius: var(--r-card);
    opacity: 1;
  }
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__promo::before {
    padding: 1px;                           /* the fill thins to the gradient border */
    transition:
      top 800ms var(--ease-in-out-quart) 60ms,
      height 800ms var(--ease-in-out-quart) 60ms,
      border-radius 800ms var(--ease-in-out-quart) 60ms,
      padding 700ms var(--ease-in-out-quart) 160ms,
      filter var(--dur-hover) var(--ease-out-quart),
      opacity 0s linear 0s;
  }
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__promo::after {
    transition:
      top 800ms var(--ease-in-out-quart) 60ms,
      height 800ms var(--ease-in-out-quart) 60ms,
      border-radius 800ms var(--ease-in-out-quart) 60ms,
      opacity 600ms var(--ease-out-quart) 380ms;
  }
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__promo-media,
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__promo-body {
    opacity: 1;
    transform: none;
    transition: opacity 500ms var(--ease-out-quart) 560ms, transform 700ms var(--ease-out-quart) 560ms;
  }
  :is(.site-header.is-open, .is-wide .site-header:not(.is-collapsed)) .site-header__promo-body { transition-delay: 660ms; }
  .site-header__promo-media { position: relative; min-height: 0; overflow: hidden; border-radius: var(--r-card-in); background: var(--ink-800); }
  .site-header__promo-media img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transform: scale(1.04);
    transition: transform 700ms var(--ease-out-quart);
  }
  .site-header__promo-body {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    padding: 0 var(--s-2) var(--s-1);
    font-family: var(--font-display);
    font-size: 16px;
    font-weight: 500;
    line-height: 1.2;
  }
  .site-header__promo-body i { font-size: 18px; color: var(--link); transition: transform var(--dur-roll) var(--ease-out-quart); }
  .site-header__promo:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
  .site-header__promo:focus-visible img { transform: none; }
  @media (hover: hover) and (pointer: fine) {
    .site-header__promo:hover::before { filter: brightness(1.25); }
    .site-header__promo:hover img { transform: none; }
    .site-header__promo:hover .site-header__promo-body i { transform: translate(2px, -2px); }
  }
  @media (prefers-reduced-motion: reduce) {
    .site-header__promo::before, .site-header__promo::after,
    .site-header__promo-media, .site-header__promo-body { transition-duration: 0s !important; transition-delay: 0s !important; }
  }

  /* Expanded: the wordmark gives way; the view starts under the names, and the empty bar must not
     catch the pointer over it. */
  .is-wide .site-header:not(.is-collapsed) .site-header__brand {
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    transition-duration: 300ms, 300ms, 0s;
    transition-delay: 0s, 0s, 300ms;
  }
  .is-wide .site-header:not(.is-collapsed) .site-header__bar { pointer-events: none; }
  .is-wide .site-header:not(.is-collapsed) :is(.site-header__link, .site-header__actions) { pointer-events: auto; }
}
@media (min-width: 1280px) and (pointer: coarse) {
  .site-header__actions { gap: var(--s-4); }
}

/* "View all": the last row of a group that lists only three. It carries the accent and its arrow is
   always showing, so it reads as the way on. */
.site-header__all .mrow { color: var(--link); }
.site-header__all .mrow::before { background: transparent; }
.site-header__all .mrow__arrow { opacity: 1; transform: none; }
/* Phones under 400: the wordmark, Menu and Request a demo tighten so the bar fits 375, and under 360 Menu keeps
   only its plus so it fits 320. */
@media (max-width: 399px) {
  .site-header__bar-in { gap: var(--s-3); }
  .site-header__brand img { height: 20px; }
  .site-header__actions { gap: var(--s-2); }
  .site-header__demo { padding-inline: var(--s-4); font-size: 13px; }
}
@media (max-width: 359px) {
  .site-header__bar-in { gap: var(--s-2); }
  .site-header__actions { gap: var(--s-1); }
  .site-header__toggle { min-width: 44px; justify-content: center; font-size: 0; gap: 0; }
  .site-header__toggle i { font-size: 20px; }
  .site-header__demo { padding-inline: var(--s-3); }
}

/* Entrance after the preloader: the names left to right, the hairline drawing, the mark and line
   fading up, then the groups one after another, each hairline drawing as its row arrives. */
.js-motion .site-header:not(.is-in) :is(.site-header__brand, .site-header__links, .site-header__actions),
.js-motion .site-header:not(.is-in) :is(.site-header__mark, .site-header__tagline, .site-header__group, .site-header__promo) {
  opacity: 0;
}
.js-motion .site-header:not(.is-in) .site-header__rule,
.js-motion .site-header:not(.is-in) .site-header__group .mrow::before,
.js-motion .site-header:not(.is-in) .site-header__base { transform: scaleX(0); }
/* The preloader's line hands over to this one (site.js, finish): shown where it is, no draw, for the first 400ms. */
.js-motion .site-header.is-handoff .site-header__rule--top { transform: none; transition: none; }

/* =============================================================
   Preloader (every load; DESIGN.md 8.0)
   Shown from first paint by the head script; site.js drives it.
   Without JS, under reduced motion, or with a #section in the URL
   the class is never added and none of this renders.
   ============================================================= */
.intro { display: none; }
.is-intro .intro {
  display: block;
  position: fixed;
  inset: 0;
  z-index: var(--z-intro);
  color: var(--on-ink);
}
.is-intro-lock,
.is-intro-lock body { overflow: hidden; }
/* The ground carries the debossed word and mark; the slit opens through it. */
.intro__ground {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--ink-900);
}
.intro__stage {
  position: relative;
  display: grid;
  place-items: center;
  width: min(86vw, 1040px);
}
/* Debossed: the glyphs share the ground's colour, lit from the top left. */
.intro__word,
.intro__mark {
  grid-area: 1 / 1;
  height: auto;
  overflow: visible;
  fill: var(--ink-900);
  filter: drop-shadow(-1px -1px 0 rgba(242, 244, 248, .3)) drop-shadow(-2px -2px 6px rgba(242, 244, 248, .05)) drop-shadow(3px 5px 10px rgba(5, 7, 13, .95));
}
.intro__word { width: 100%; }
/* Chrome (review 16 Oct 2026): assets/js/chrome-gl.js paints the same letters and mark as polished chrome on a
   canvas over the stage, and the debossed SVG steps aside once the first chrome frame has drawn. */
.intro__chrome { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.intro__stage.has-chrome :is(.intro__word, .intro__mark) { visibility: hidden; }
.intro__mark {
  width: clamp(96px, 14vw, 176px);
  opacity: 0;
  transform: scale(.92);
  transition: opacity var(--dur-roll) var(--ease-out-quart) 200ms, transform 700ms var(--ease-out-quart) 200ms;
}
.intro__ch {
  opacity: 0;
  transition: opacity var(--dur-roll) var(--ease-out-quart);
}
.intro__ch.is-on { opacity: 1; }
/* At 100% the letters fold back right to left and the mark takes their place. */
.intro.is-collapsing .intro__ch {
  opacity: 0;
  transition-duration: 300ms;
  transition-delay: calc((7 - var(--k)) * 30ms);
}
.intro.is-collapsing .intro__mark { opacity: 1; transform: none; }
.intro__row {
  position: absolute;
  top: 72%;
  left: 0;
  right: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: var(--s-2);
}
.intro__meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 13px;
  font-weight: 500;
  color: var(--on-ink-muted);
  transition: opacity 300ms var(--ease-out-quart);
}
.intro__count {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--on-ink);
  font-variant-numeric: tabular-nums;
}
.intro__rule {
  position: relative;
  height: 1px;
  background: rgba(242, 244, 248, .16);
}
.intro__fill {
  position: absolute;
  inset: 0;
  background: var(--on-ink);
  transform: scaleX(0);
  transform-origin: left;
}
.intro__brand { height: 20px; width: auto; transform-origin: 0 0; }
.intro.is-collapsing .intro__meta { opacity: 0; }

/* =============================================================
   Hero
   ============================================================= */
.hero {
  position: relative;
  isolation: isolate;
  display: grid;
  align-content: center;
  /* One step short of the screen leaves room for the announcement bar, so the hero's own
     bottom edge and the video control sit inside the first screen. */
  min-height: clamp(640px, calc(100svh - var(--s-7)), 1000px);
  padding-top: var(--header-h);
  background: var(--ink-950);
  color: var(--on-ink);
  overflow: hidden;
}
.hero__sentinel { position: absolute; top: var(--s-10); left: 0; width: 1px; height: 1px; }
.hero__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  container-type: size;
  overflow: hidden;
}
/* Video and HUD share one cover-scaled 16:9 stage, so the targets stay on their objects. */
.hero__stage {
  position: absolute;
  left: 50%;
  top: 50%;
  width: max(100cqw, calc(100cqh * 16 / 9));
  aspect-ratio: 16 / 9;
  transform: translate(-50%, -50%);
}
.hero__stage picture, .hero__poster, .hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero__video { opacity: 0; transition: opacity 800ms var(--ease-out-expo); }
.hero__video.is-playing { opacity: 1; }
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    /* Held dark under the whole header so the expanded view reads over any scene. */
    linear-gradient(180deg, rgba(5, 7, 13, .82) 0, rgba(5, 7, 13, .62) var(--header-h), rgba(5, 7, 13, 0) calc(var(--header-h) + 160px)),
    linear-gradient(0deg, var(--ink-950) 0%, rgba(5, 7, 13, 0) 30%),
    linear-gradient(90deg, rgba(5, 7, 13, .9) 0%, rgba(5, 7, 13, .66) 38%, rgba(5, 7, 13, .12) 72%, rgba(5, 7, 13, 0) 100%);
}
/* Tracked data-point boxes, drawn on a canvas that shares the video's cover-scaled stage (assets/js/site.js §6).
   Below 1024 the copy covers the cropped stage, so the boxes are not drawn. */
.hero__tracks {
  position: absolute;
  inset: 0;
  display: none;
  width: 100%;
  height: 100%;
  font-family: var(--font-mono);
  pointer-events: none;
}
@media (min-width: 1024px) { .hero__tracks { display: block; } }

.hero__content {
  position: relative;
  padding-block: var(--s-9) var(--s-9);
}
.hero__content > * { max-width: 800px; }
.hero__content > .hero__title { max-width: 940px; margin-top: var(--s-5); }
.hero__lead {
  margin-top: var(--s-5);
  max-width: 46ch;
  font-size: 18px;
  line-height: 1.6;
  color: var(--on-ink-muted);
}
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s-4); margin-top: var(--s-7); }
@media (max-width: 767px) {
  /* One spacing step short of the screen: the CTA clears the fold under the announcement
     bar, and the top of the logo rail shows that the page continues. */
  .hero { align-content: end; min-height: clamp(560px, calc(100svh - var(--s-8)), 1000px); }
  .hero__scrim {
    background:
      linear-gradient(180deg, rgba(5, 7, 13, .6) 0%, rgba(5, 7, 13, .35) 40%, var(--ink-950) 100%);
  }
  .hero__content { padding-block: var(--s-6) var(--s-7); }
  .hero__lead { font-size: 16px; }
}

/* =============================================================
   Trusted-by rail (the page's one marquee)
   ============================================================= */
.trusted { background: var(--ink-950); padding-block: 0 var(--s-8); }
/* A rail is one line at every width (review 16 Oct 2026): the label on the left, the marquee beside it.
   The trusted-by strip is one; the systems integrated on a case study are another. */
.trusted__in,
.rail {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--s-7);
}
.trusted__in { padding-top: var(--s-6); border-top: 1px solid var(--ink-700); }
.trusted__label,
.rail__label { font-size: 14px; font-weight: 500; color: var(--fg-muted, var(--on-ink-muted)); white-space: nowrap; }
.marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, var(--ink-950) 8%, var(--ink-950) 92%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, var(--ink-950) 8%, var(--ink-950) 92%, transparent 100%);
}
.marquee__track {
  display: flex;
  align-items: center;
  gap: var(--s-8);
  width: max-content;
  padding-right: var(--s-8);
}
.marquee__track li { flex: none; white-space: nowrap; }
.marquee.is-running .marquee__track { animation: marquee 44s linear infinite; }
.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track,
.marquee.is-paused .marquee__track { animation-play-state: paused; }
@media (max-width: 767px) {
  .trusted__in, .rail { gap: var(--s-4); }
  .trusted__label, .rail__label { font-size: 13px; }
  .marquee__track { gap: var(--s-7); padding-right: var(--s-7); }
}

/* =============================================================
   Industries (act 2, paper)
   ============================================================= */
.industries { background: var(--paper-100); padding-block: var(--section-y); }
/* The rule lives on the wrap so the edge fade on a scrolling row never fades it. */
.tabs-wrap { margin-top: var(--s-7); border-bottom: 1px solid var(--paper-300); }
.tabs {
  display: flex;
  gap: var(--s-1);
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}
.tabs::-webkit-scrollbar { display: none; }
/* Edge fade only on the side that hides tabs (set by an IntersectionObserver in site.js).
   A mask reads alpha only; ink-950 is used as the opaque stop, as on the marquee. */
.tabs-wrap.is-clip-end .tabs {
  -webkit-mask-image: linear-gradient(90deg, var(--ink-950) calc(100% - var(--s-9)), transparent);
  mask-image: linear-gradient(90deg, var(--ink-950) calc(100% - var(--s-9)), transparent);
}
.tabs-wrap.is-clip-start .tabs {
  -webkit-mask-image: linear-gradient(90deg, transparent, var(--ink-950) var(--s-9));
  mask-image: linear-gradient(90deg, transparent, var(--ink-950) var(--s-9));
}
.tabs-wrap.is-clip-start.is-clip-end .tabs {
  -webkit-mask-image: linear-gradient(90deg, transparent, var(--ink-950) var(--s-9), var(--ink-950) calc(100% - var(--s-9)), transparent);
  mask-image: linear-gradient(90deg, transparent, var(--ink-950) var(--s-9), var(--ink-950) calc(100% - var(--s-9)), transparent);
}
.tab {
  position: relative;
  flex: none;
  min-height: 52px;
  padding: var(--s-3) var(--s-4);
  border: 0;
  /* Only the keyboard focus ring shows this shape; small corners keep it a clean frame. */
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: transparent;
  color: var(--on-paper-muted);
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  cursor: pointer;
  scroll-snap-align: start;
  transition: color var(--dur-hover) var(--ease-out-expo);
}
.tab:hover, .tab[aria-selected="true"] { color: var(--on-paper); }
.tab::after {
  content: "";
  position: absolute;
  left: var(--s-4);
  right: var(--s-4);
  bottom: -1px;
  height: 2px;
  background: var(--grad);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 500ms var(--ease-out-expo);
}
.tab[aria-selected="true"]::after { transform: scaleX(1); }
.tab:focus-visible { outline-offset: -3px; }

.ind-panels { margin-top: var(--s-6); }
.ind-panel {
  --fg: var(--on-ink);
  --fg-muted: var(--on-ink-muted);
  --link: var(--accent-on-ink);
  --focus: var(--accent-on-ink);
  --line: var(--ink-600);
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--ink-950);
  color: var(--on-ink);
}
.ind-panel[hidden] { display: none; }
.ind-panel:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.ind-panel__media { position: absolute; inset: 0; z-index: -1; }
.ind-panel__media img { width: 100%; height: 100%; object-fit: cover; }
.ind-panel__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(0deg, rgba(5, 7, 13, .86) 0%, rgba(5, 7, 13, .6) 34%, rgba(5, 7, 13, .14) 66%, rgba(5, 7, 13, 0) 100%),
    linear-gradient(90deg, rgba(5, 7, 13, .4) 0%, rgba(5, 7, 13, 0) 55%);
}
.ind-panel__body {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--s-5);
  align-items: end;
  min-height: 560px;
  padding: var(--s-9) var(--s-7) var(--s-7);
}
/* No box: the words sit straight on the picture, which is darkened from the foot up. */
.ind-card {
  grid-column: 1 / span 7;
  display: grid;
  justify-items: start;
  gap: var(--s-4);
}
.ind-card__title { max-width: 18ch; font-size: clamp(32px, 3.6vw, 52px); line-height: 1.08; letter-spacing: -.025em; font-weight: 400; }
/* Panel switch: the words settle in, the picture and the loop ease out of a slight zoom. */
.ind-panel.is-entering .ind-card { opacity: 0; transform: translateY(16px); }
.ind-panel.is-entering .ind-panel__media :is(img, video) { transform: scale(1.04); }
.ind-panel .ind-card { transition: opacity 600ms var(--ease-out-expo), transform 800ms var(--ease-out-expo); }
.ind-panel .ind-panel__media :is(img, video) { transition: transform var(--dur-zoom) var(--ease-out-expo); }
/* The slow loop fades in over its own poster frame once it is playing. */
.ind-panel__media video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; }
.ind-panel__media video.is-playing { opacity: 1; transition: opacity 800ms var(--ease-out-expo), transform var(--dur-zoom) var(--ease-out-expo); }
.ind-panels { position: relative; }
@media (max-width: 1023px) {
  /* Image in flow on top; the words rise over its lower edge by one spacing step. */
  .ind-panel__media { position: relative; inset: auto; z-index: 0; aspect-ratio: 16 / 10; }
  .ind-panel__media::after { background: linear-gradient(0deg, var(--ink-950) 0%, rgba(5, 7, 13, .2) 55%, rgba(5, 7, 13, .1) 100%); }
  .ind-panel__body {
    position: relative;
    z-index: 1;
    grid-template-columns: 1fr;
    gap: var(--s-6);
    min-height: 0;
    margin-top: calc(-1 * var(--s-8));
    padding: 0 var(--s-6) var(--s-6);
  }
  .ind-card { grid-column: 1 / -1; }
}
@media (max-width: 639px) {
  .ind-panel__body { padding: 0 var(--s-5) var(--s-5); }
}

/* =============================================================
   Platform band (act 3, ink): a sticky screen beside three stages
   ============================================================= */
.platform { background: var(--ink-900); padding-block: var(--section-y); }
.v-story { display: grid; gap: var(--s-8); margin-top: var(--s-8); }
.v-steps { position: relative; display: grid; gap: var(--s-8); }
.v-step { position: relative; display: grid; gap: var(--s-4); align-content: start; }
/* Media is a plain picture: no frame, no caption (review 16 Oct 2026). */
.v-step__fig { overflow: hidden; background: var(--ink-800); }
.v-step__fig img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; object-position: left top; }
.v-step__name { font-size: clamp(28px, 3vw, 44px); line-height: 1.1; letter-spacing: -.02em; }
.v-step__text { max-width: 48ch; font-size: 18px; color: var(--fg-muted); }
.v-step__sys { max-width: 56ch; font-size: 15px; color: var(--fg-muted); }
.v-step__sys b { display: block; margin-bottom: var(--s-1); color: var(--fg); font-weight: 600; }
.v-step__line { font-family: var(--font-display); font-size: 20px; font-weight: 500; letter-spacing: -.01em; color: var(--fg); }
.v-step .link-arrow { justify-self: start; }
.v-stats { display: flex; flex-wrap: wrap; gap: var(--s-5) var(--s-7); }
.v-stat { display: grid; gap: var(--s-1); }
.v-stat__num { font-family: var(--font-display); font-weight: 300; font-size: clamp(44px, 5vw, 64px); line-height: 1; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.v-stat__label { font-size: 15px; color: var(--fg-muted); }
.v-stage { display: none; }

@media (min-width: 1024px) {
  /* The heading is pinned with the screen. It is opaque so the stages slide under it, and it fades out below. */
  /* The pinned heading takes 32 above it (was 16, review 16 Oct 2026), so it no longer sits against the bar. */
  .platform .section-head { position: sticky; top: var(--bar-h); z-index: 3; max-width: none; padding-block: var(--s-6) var(--s-4); background: var(--ink-900); box-shadow: 0 calc(-1 * var(--bar-h)) 0 0 var(--ink-900); }
  /* The shadow above carries the heading's ink up behind the bar, so the screen sliding away at the end of the band
     is hidden there too, not blurred through the bar's glass (review 22 Oct 2026). */
  .platform .section-head::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: var(--s-5); pointer-events: none; background: linear-gradient(180deg, var(--ink-900), rgba(10, 13, 23, 0)); }
  .v-story { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--s-9); align-items: start; margin-top: var(--s-6); }
  .v-steps { gap: 0; padding-left: var(--s-6); }
  /* The reading line: a hairline with an accent segment that follows the active stage. */
  .v-steps::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 1px; background: var(--line); }
  .v-step { min-height: 72vh; padding-block: var(--s-7); opacity: .38; transition: opacity 500ms var(--ease-out-quart); }
  .v-step:first-child { padding-top: 0; }
  .v-step:last-child { min-height: 0; padding-bottom: 0; }
  .v-step.is-active { opacity: 1; }
  .v-step::before { content: ""; position: absolute; left: calc(-1 * var(--s-6)); top: var(--s-7); width: 1px; height: 64px; background: var(--grad); transform: scaleY(0); transform-origin: top; transition: transform 600ms var(--ease-out-quart); }
  .v-step:first-child::before { top: 0; }
  .v-step.is-active::before { transform: scaleY(1); }
  .v-step__fig { display: none; }
  .v-stage { display: block; position: sticky; top: calc(var(--bar-h) + var(--head-h) + var(--s-4)); }
  /* The right column is the media alone (review 16 Oct 2026): no frame, no caption, nothing else. */
  .v-stage__shot { position: relative; aspect-ratio: 16 / 10; overflow: hidden; background: var(--ink-850); }
  .v-stage__shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: left top; opacity: 0; transform: scale(1.04); transition: opacity 600ms var(--ease-out-quart), transform var(--dur-zoom) var(--ease-out-expo); }
  .v-stage[data-active="0"] .v-stage__shot img:nth-child(1),
  .v-stage[data-active="1"] .v-stage__shot img:nth-child(2),
  .v-stage[data-active="2"] .v-stage__shot img:nth-child(3) { opacity: 1; transform: none; }

  /* Every instance of the band, the homepage's and the S!aP page's (review 22 Oct 2026). The heading takes 64 above it inside the pinned bar, so --head-h grows by
     the same 64 (112 to 176) and the screen stays clear of it. The screen fills the height left under the heading, with
     24 above it and 48 below, and a 16:10 floor so a short window never squashes it. The last stage keeps the same
     tall stage as the others (it had none), so the screen stays put while "Act" is read. */
  .platform { --head-h: 176px; }
  .platform .section-head h2 { padding-top: var(--s-8); }
  .platform .v-stage { top: calc(var(--bar-h) + var(--head-h) + var(--s-5)); }
  .platform .v-stage__shot { aspect-ratio: auto; height: calc(100svh - var(--bar-h) - var(--head-h) - var(--s-5) - var(--s-7)); min-height: 360px; }
  .platform .v-step:last-child { min-height: 72vh; padding-bottom: var(--s-7); }
  /* The three pictures are stock photographs now (22 Oct 2026), not screens read from the top left, so they centre. */
  .platform :is(.v-stage__shot, .v-step__fig) img { object-position: center; }
}

/* =============================================================
   Proof and numbers (act 3, ink)
   ============================================================= */
.proof { background: var(--ink-950); padding-block: var(--section-y-air) var(--section-y); }
.proof__grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--s-6);
  align-items: center;
}
/* The twin (assets/js/twin.js) draws on a canvas that fills this box. */
.proof__figure-wrap { grid-column: 1 / span 6; width: 100%; max-width: 640px; margin-inline: auto; }
.proof__figure { position: relative; width: 100%; aspect-ratio: 3 / 2; touch-action: pan-y; }
.proof__figure canvas { position: absolute; inset: 0; display: block; }
.proof__copy { grid-column: 7 / span 6; display: grid; gap: var(--s-4); }
.stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-6) var(--s-5);
  margin-top: var(--s-5);
}
.stat {
  display: flex;
  flex-direction: column-reverse;
  justify-content: flex-end;
  gap: var(--s-2);
  padding-top: var(--s-4);
  border-top: 1px solid var(--ink-700);
}
.stat__num {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(44px, 5vw, 72px);
  line-height: 1;
  letter-spacing: -.04em;
  font-variant-numeric: tabular-nums;
  color: var(--on-ink);
}
.proof__lead { max-width: 48ch; font-size: 16px; color: var(--on-ink-muted); }
.proof__cta { justify-self: start; margin-top: var(--s-3); }
.stat__label { font-size: 14px; line-height: 1.5; color: var(--on-ink-muted); max-width: 30ch; }

/* The light version of this act (22 Oct 2026, kept by the client's pick): on the page's white, in the paper semantics.
   The twin reads data-theme="light" for its own palette. The dark rules above stay as the fallback: remove class
   "proof--light" and attribute data-theme="light" from #proof in index.html to return to them. */
.proof.proof--light { background: var(--paper-0); --fg: var(--on-paper); --fg-muted: var(--on-paper-muted); --fg-subtle: var(--on-paper-subtle); --line: var(--paper-200); --link: var(--accent); --focus: var(--accent); --chip-bg: var(--paper-0); color: var(--on-paper); }
.proof--light .stat { border-top-color: var(--paper-300); }
.proof--light .stat__num { color: var(--on-paper); }
.proof--light .proof__lead, .proof--light .stat__label { color: var(--on-paper-muted); }

/* Footer: room under the legal row (review 17 Oct 2026): 0 to 64 on this page's footer only. */
body > .site-footer .footer__legal { padding-bottom: var(--s-8); }

@media (max-width: 1023px) {
  .proof__figure-wrap, .proof__copy { grid-column: 1 / -1; }
  .proof__figure-wrap { max-width: 480px; }
}
@media (max-width: 639px) {
  .stats { gap: var(--s-5) var(--s-4); }
  .stat__num { font-size: 44px; }
}

/* =============================================================
   Customer stories (act 4, paper): a mosaic of photographs. Redesigned 16 Oct 2026 (the WebGL layer and its pointer
   light are gone): each card keeps its photograph, veil, figure, line and link, and gains a rectangle in the colour of
   the brand the case study is about, down its right edge, holding the brand's logo. On hover (and keyboard focus) that
   colour fills the whole card as a tint over the photograph. Brand colours are the one place the page leaves its accent:
   they are the client's subjects' own (or, where none is known, chosen), set per card as --brand.
   ============================================================= */
.stories { background: var(--paper-50); padding-block: var(--section-y) var(--section-y-air); }
.cases { display: grid; gap: var(--s-4); margin-top: var(--s-7); }
.case {
  --fg: var(--on-ink);
  --fg-muted: var(--on-ink-muted);
  --link: var(--accent-on-ink);
  --focus: var(--accent-on-ink);
  --brand: var(--ink-700);
  --band: clamp(104px, 22%, 168px);
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--s-7);
  min-height: 440px;
  padding: var(--s-6) calc(var(--band) + var(--s-5)) var(--s-6) var(--s-6);
  overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--ink-900);
  color: var(--on-ink);
}
.case__photo { position: absolute; inset: 0; z-index: -3; width: 100%; height: 100%; object-fit: cover; }
.case::before { content: ""; position: absolute; inset: 0; z-index: -2; background: linear-gradient(0deg, rgba(5, 7, 13, .86) 0%, rgba(5, 7, 13, .5) 100%); }
/* The brand rectangle, and the same element filling the card on hover: it is clipped to the band at rest. */
.case__fill {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--brand);
  clip-path: inset(0 0 0 calc(100% - var(--band)));
  transition: clip-path 700ms var(--ease-out-quint), opacity 500ms var(--ease-out-quart);
}
/* The logo sits in the band, centred in it at the top. */
.case__top { position: absolute; top: var(--s-6); right: 0; width: var(--band); display: flex; justify-content: center; padding-inline: var(--s-3); }
.case__top .logo { height: var(--logo-h, 40px); width: auto; max-width: 100%; object-fit: contain; opacity: 1; }
.case__mark { font-family: var(--font-display); font-size: 20px; font-weight: 500; line-height: 1.1; letter-spacing: -.02em; text-align: center; text-wrap: balance; }
.case__bottom { display: grid; gap: var(--s-3); margin-top: auto; }
.case__fig { font-family: var(--font-display); font-weight: 300; font-size: clamp(64px, 7.2vw, 112px); line-height: .96; letter-spacing: -.045em; font-variant-numeric: tabular-nums; }
.case__line { max-width: 24ch; font-size: clamp(18px, 1.6vw, 22px); font-weight: 400; line-height: 1.3; letter-spacing: -.01em; }
.case__link { position: static; justify-self: start; display: inline-flex; align-items: center; gap: var(--s-2); min-height: 44px; color: var(--link); font-size: 15px; font-weight: 600; text-decoration: none; transition: color 500ms var(--ease-out-quart); }
/* The whole card is the target. */
.case__link::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.case__link i { font-size: 18px; transition: transform var(--dur-roll) var(--ease-out-quart); }
.case:focus-within { outline: 2px solid var(--accent-on-ink); outline-offset: 4px; }
.case:focus-within .case__fill { clip-path: inset(0); opacity: .84; }
.case:focus-within .case__link { color: var(--on-ink); }
@media (hover: hover) and (pointer: fine) {
  .case:hover .case__fill { clip-path: inset(0); opacity: .84; }
  .case:hover .case__link { color: var(--on-ink); }
  .case:hover .case__link i { transform: translateX(4px); }
}
@media (min-width: 768px) {
  .cases { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .case:nth-child(1), .case:nth-child(4) { grid-column: span 7; }
  .case:nth-child(2), .case:nth-child(3) { grid-column: span 5; }
  .case:nth-child(1) .case__line, .case:nth-child(4) .case__line { max-width: 30ch; }
}
@media (max-width: 639px) {
  /* Phones: a narrower band and figure, so the figure never runs under it. */
  .case { --band: 88px; padding-right: calc(var(--band) + var(--s-4)); }
  .case__fig { font-size: 56px; }
  .case__top { padding-inline: var(--s-2); }
}
@media (prefers-reduced-motion: reduce) { .case__fill, .case__link, .case__link i { transition: none; } }

/* =============================================================
   Trust marks (review 16 Oct 2026: "in place of numbers, logos for certifications or other trust
   elements, as graphics"; revised the same day: a set of marks runs as a marquee, one line, and the
   seal has no accent arc). A mark (a certification seal drawn from two circles and the standard's
   code, or a programme's own logo as an image), its name and one short line. Seals are type and
   circles only, so they read on ink and on paper alike. Programme logos keep their brand colour and
   are plain <img>, never a CSS mask (a mask image is blocked when the page is opened from disk).
   ============================================================= */
.trust-marquee { width: 100%; }
/* Gap between marks 64 to 32 (review 17 Oct 2026: "space between each logo can be reduced a little"). */
.trust { display: flex; align-items: center; gap: var(--s-6); width: max-content; padding-right: var(--s-6); }
.trust > li { flex: none; }
.trust > .trust__item { white-space: normal; display: grid; grid-template-columns: 64px minmax(0, 1fr); align-items: center; gap: var(--s-1) var(--s-4); width: 288px; }
.trust__mark { grid-row: span 2; display: grid; place-items: center; width: 64px; height: 64px; color: var(--fg); }
.trust__name { align-self: end; font-family: var(--font-display); font-size: 16px; font-weight: 500; line-height: 1.25; }
.trust__what { align-self: start; font-size: 13px; line-height: 1.4; color: var(--fg-muted); }
.seal { width: 64px; height: 64px; overflow: visible; }
.seal__ring { fill: none; stroke: var(--line-strong); stroke-width: 1; }
.seal__dash { fill: none; stroke: var(--fg-subtle); stroke-width: .75; stroke-dasharray: 1 2.6; }
.seal__top { font: 500 7px/1 var(--font-body); letter-spacing: .12em; fill: var(--fg-muted); text-anchor: middle; }
.seal__main { font: 500 14px/1 var(--font-display); letter-spacing: -.02em; fill: var(--fg); text-anchor: middle; }
.trust__logo { display: block; width: auto; max-width: 64px; max-height: 40px; height: auto; }
.trust__logo--sq { max-width: 40px; max-height: 40px; }
/* Still, in full, when it cannot move: no script, or reduced motion. */
.trust-marquee:not(.is-running) { -webkit-mask-image: none; mask-image: none; }
.trust-marquee:not(.is-running) .trust { width: auto; flex-wrap: wrap; gap: var(--s-5) var(--s-7); padding-right: 0; }
@media (max-width: 479px) {
  .trust { gap: var(--s-5); padding-right: var(--s-5); }
  .trust > .trust__item { width: 240px; grid-template-columns: 48px minmax(0, 1fr); column-gap: var(--s-3); }
  .trust__mark, .seal { width: 48px; height: 48px; }
  .trust__logo { max-width: 48px; max-height: 32px; }
}

/* =============================================================
   Company (act 4, paper raised): content beside a media container (review 16 Oct 2026). The
   statement, the trust marks and one link at the left; one photograph, square, at the right.
   ============================================================= */
.company { background: var(--paper-100); padding-block: var(--section-y-air); }
.co-grid { display: grid; gap: var(--s-8); margin-top: var(--s-8); }
.co-lead { display: grid; gap: var(--s-7); align-content: start; justify-items: start; }
#company .co-lead { gap: var(--s-8); }   /* homepage only (review 22 Oct 2026): 64 between the line, the marks and the link */
.co-say { max-width: 22ch; font-family: var(--font-display); font-size: clamp(28px, 3vw, 44px); line-height: 1.16; letter-spacing: -.02em; text-wrap: balance; }
.co-media { position: relative; overflow: hidden; aspect-ratio: 4 / 5; background: var(--paper-200); }
.co-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.co-grid--media { align-items: stretch; }
@media (min-width: 1024px) {
  .co-grid--media { grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); gap: var(--s-9); align-items: center; }
}
@media (max-width: 1023px) { .co-media { aspect-ratio: 3 / 2; } }
.co-facts { display: flex; flex-wrap: wrap; gap: var(--s-6) var(--s-7); }
.co-fact { display: flex; flex-direction: column-reverse; gap: var(--s-1); max-width: 22ch; }
.co-fact__num { font-family: var(--font-display); font-weight: 300; font-size: clamp(44px, 5vw, 64px); line-height: 1; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.co-fact__label { font-size: 15px; line-height: 1.45; color: var(--on-paper-muted); }
.co-years { border-top: 1px solid var(--paper-300); }
.co-year { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: var(--s-5); align-items: baseline; padding-block: var(--s-5); border-bottom: 1px solid var(--paper-300); }
.co-year__y { font-family: var(--font-display); font-weight: 300; font-size: clamp(28px, 2.6vw, 40px); line-height: 1; letter-spacing: -.03em; font-variant-numeric: tabular-nums; color: var(--on-paper-muted); }
.co-year__t { max-width: 44ch; font-size: 18px; line-height: 1.45; }
.co-year:last-child .co-year__y { color: var(--on-paper); }
@media (min-width: 1024px) {
  .co-grid:not(.co-grid--media) { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: var(--s-9); align-items: start; }
  .co-grid:not(.co-grid--media) .co-lead { position: sticky; top: calc(var(--bar-h) + var(--s-5)); }
}
@media (max-width: 479px) {
  .co-year { grid-template-columns: 72px minmax(0, 1fr); gap: var(--s-4); }
}

/* =============================================================
   Insights (act 4, paper)
   ============================================================= */
.insights { background: var(--paper-50); padding-block: var(--section-y-air) var(--section-y); }
.insights__head { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: var(--s-4); }
.insight-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-5);
  margin-top: var(--s-7);
}
/* An article card is a square picture and words on the section's own ground: no box, no fill, no
   rounding, and the picture does not move (review 16 Oct 2026). The title's underline is the hover. */
.insight {
  position: relative;
  display: flex;
  flex-direction: column;
}
.insight__media { aspect-ratio: 16 / 9; overflow: hidden; background: var(--paper-200); }
.insight__media img { width: 100%; height: 100%; object-fit: cover; }
.insight__body { display: flex; flex-direction: column; gap: var(--s-3); flex: 1; padding: var(--s-4) 0 0; }
.insight__meta { font-size: 13px; font-weight: 500; color: var(--on-paper-subtle); }
.insight__title { font-size: 20px; line-height: 1.3; font-weight: 500; }
.insight__title a { text-decoration: none; }
.insight__title a::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.insight__title a:focus-visible { outline: none; }
.insight:focus-within { outline: 2px solid var(--focus); outline-offset: 4px; }
.insight .link-arrow { margin-top: auto; }
@media (hover: hover) and (pointer: fine) {
  .insight:hover .link-arrow i { transform: translateX(4px); }
}
@media (min-width: 1024px) { .insight-grid { gap: var(--s-7) var(--s-5); } }
/* Two columns: the newest article leads full width with its picture beside the words. */
@media (max-width: 1023px) {
  .insight-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--s-7); }
  .insight:first-child { grid-column: 1 / -1; flex-direction: row; gap: var(--s-6); }
  .insight:first-child .insight__media { flex: 0 0 50%; aspect-ratio: auto; min-height: 240px; }
  .insight:first-child .insight__body { padding: 0; justify-content: center; }
}
@media (max-width: 639px) {
  .insight-grid { grid-template-columns: 1fr; }
  .insight:first-child { flex-direction: column; gap: 0; }
  .insight:first-child .insight__media { flex: none; aspect-ratio: 16 / 9; min-height: 0; }
  .insight:first-child .insight__body { padding: var(--s-4) 0 0; }
}

/* =============================================================
   Partner ecosystem (act 5, ink)
   ============================================================= */
.partners { background: var(--ink-900); padding-block: var(--section-y) var(--section-y-air); }
.eco-rows { margin-top: var(--s-7); border-top: 1px solid var(--ink-700); }
.eco-row {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--s-6);
  align-items: center;
  padding-block: var(--s-7);
  border-bottom: 1px solid var(--ink-700);
}
.eco-row__head { grid-column: span 4; display: grid; gap: var(--s-2); }
.eco-row__title { font-size: 22px; line-height: 1.25; font-weight: 500; }
.eco-row__text { font-size: 15px; color: var(--on-ink-muted); max-width: 34ch; }
/* One line, always (review 16 Oct 2026): each row's logos run as a marquee beside its heading. */
.eco-row__marquee { grid-column: span 8; min-width: 0; }
.eco-row__logos { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-6) var(--s-8); }
.eco-row__logos li { display: flex; align-items: center; min-height: 48px; }
/* The partners section's own marquee (review 16 Oct 2026): every partner's mark in one line under the heading. */
.pg-partner-marquee { margin-top: var(--s-8); }
.pg-partner-marquee + .eco-proof { margin-top: var(--s-8); }
.eco-proof {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--s-5);
  margin-top: var(--s-9);
  align-items: stretch;
}
/* The quote and the validation list each sit in a container of their own (review 16 Oct 2026): a solid raised ink
   panel with a hairline edge, square, with room inside. */
.quote {
  grid-column: span 7;
  display: grid;
  align-content: space-between;
  gap: var(--s-8);
  padding: var(--s-8) var(--s-7);
  background: var(--ink-850);
  border: 1px solid var(--ink-700);
}
.quote blockquote p {
  font-family: var(--font-display);
  font-size: clamp(24px, 2.4vw, 34px);
  line-height: 1.3;
  letter-spacing: -.015em;
  max-width: 34ch;
}
.quote__by { display: flex; align-items: center; gap: var(--s-4); font-size: 15px; color: var(--on-ink-muted); }
.quote__by strong { color: var(--on-ink); font-weight: 600; }
.quote__by .logo { opacity: 1; }
.validations { grid-column: span 5; display: grid; align-content: center; padding: var(--s-4) var(--s-7); background: var(--ink-850); border: 1px solid var(--ink-700); }
.validations li:last-child .validation { border-bottom: 0; }
.validation {
  position: relative;
  display: grid;
  gap: var(--s-1);
  padding: var(--s-6) var(--s-7) var(--s-6) 0;
  border-bottom: 1px solid var(--ink-700);
  color: var(--on-ink);
  text-decoration: none;
}
.validation__who { font-size: 13px; color: var(--on-ink-muted); }
.validation__title { justify-self: start; font-family: var(--font-display); font-size: 20px; line-height: 1.3; }
.validation__meta { font-size: 13px; color: var(--on-ink-subtle); }
.validation__arrow {
  position: absolute;
  right: var(--s-2);
  top: 50%;
  font-size: 22px;
  color: var(--accent-on-ink);
  transform: translateY(-50%);
  transition: transform var(--dur-hover) var(--ease-out-expo);
}
@media (hover: hover) and (pointer: fine) {
  .validation:hover .validation__arrow { transform: translate(4px, -50%); }
}
.partners__foot { display: flex; justify-content: flex-start; margin-top: var(--s-8); }
/* Become a partner sits inside the validation panel, after its last row (review 17 Oct 2026). */
.validations__cta { padding-top: var(--s-5); padding-bottom: var(--s-3); }
.validations li:last-child .validation { border-bottom: 1px solid var(--ink-700); }
@media (max-width: 1023px) {
  .quote { padding: var(--s-7) var(--s-5); gap: var(--s-6); }
  .validations { padding: var(--s-3) var(--s-5); }
  .eco-proof { margin-top: var(--s-8); }
  .eco-row__head, .eco-row__marquee, .quote, .validations { grid-column: 1 / -1; }
  .eco-row { gap: var(--s-5); }
}

/* =============================================================
   Closing CTA band (act 5)
   ============================================================= */
.cta-band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding-block: var(--s-9);
  background: var(--ink-950);
  border-top: 1px solid var(--ink-700);
}
/* Two soft pools of the brand gradient's own hues under the band, at low alpha. */
.cta-band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(48% 120% at 18% 120%, rgba(14, 23, 255, .32) 0%, rgba(14, 23, 255, 0) 70%),
    radial-gradient(40% 110% at 82% 120%, rgba(255, 0, 183, .18) 0%, rgba(255, 0, 183, 0) 70%);
}
.cta-band__in { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-6); }
.cta-band__title { font-size: clamp(28px, 3.4vw, 48px); line-height: 1.1; letter-spacing: -.025em; }
@media (max-width: 639px) { .cta-band { padding-block: var(--s-8); } }

/* =============================================================
   Footer (act 5), DESIGN.md 8.14. Client direction 13 Oct 2026, after mercury.com: a warm cinematic picture behind
   the links, the footer filling the screen, and the header's links showing again as its top row. REVISED 16 Oct
   2026: the scroll takeover is gone (nothing eases or scrolls the page on its own); the screen-filling card stays.
   The footer is in the flow at the end of the page, at least a screen tall, so it rises over the page with the
   reader's own scroll; from 1280 wide and 700 tall site.js docks it when its top is near the top of the screen
   (the header's footer mode, the lists rise in).
   ============================================================= */
.site-footer {
  --fg: var(--on-ink);
  --fg-muted: var(--on-ink-muted);
  --fg-subtle: var(--on-ink-subtle);
  --link: var(--accent-on-ink);
  --focus: var(--accent-on-ink);
  --line: rgb(242 244 248 / .16);
  /* The header's own column template (site-header, DESIGN.md 8.2), so the lists sit under its names. */
  --actions-w: clamp(216px, 17vw, 256px);
  --gcols: repeat(2, minmax(0, 1fr)) minmax(0, 1.4fr) repeat(2, minmax(0, 1fr));
  --cols: minmax(0, 1fr) var(--gcols) var(--actions-w);
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
  overflow: clip;
  background: var(--ink-950);
  color: var(--on-ink);
}
/* The page's own footer is a card over the page: above the dimming layer, with the curtain's shadow along its top edge. */
body > .site-footer { z-index: 26; box-shadow: var(--shadow-curtain); }
.footer-shade { display: none; }
@media (min-width: 1280px) {
  .footer-shade { display: block; position: fixed; inset: 0; z-index: 25; background: var(--ink-950); opacity: 0; pointer-events: none; will-change: opacity; }
}
@media (pointer: coarse) { .site-footer { --actions-w: 256px; } }

/* ---------- Media: the loop (edge blur baked into the file) under a veil ---------- */
.footer__media { position: absolute; inset: 0; z-index: -1; overflow: hidden; border-radius: inherit; background: var(--ink-950); }
.footer__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Even, so on-ink text keeps its contrast wherever it sits over the loop: .72 or more at the lists, a little lighter
   where the summit and the tower show between the lists and the close. */
.footer__veil {
  position: absolute;
  inset: 0;
  /* Even where text can sit anywhere (under 1280 the footer is taller than the screen). */
  background: linear-gradient(180deg, rgb(5 7 13 / .86) 0%, rgb(5 7 13 / .76) 45%, rgb(5 7 13 / .7) 62%, rgb(5 7 13 / .86) 85%, var(--ink-950) 100%);
}
/* From 1280 the lists sit in the top third and the close in the lower third, so the veil opens between them where
   the summit and the tower show; .66 or more wherever text can sit, so on-ink text keeps its contrast. */
@media (min-width: 1280px) and (min-height: 700px) {
  .footer__veil {
    background: linear-gradient(180deg, rgb(5 7 13 / .84) 0%, rgb(5 7 13 / .76) 36%, rgb(5 7 13 / .34) 46%, rgb(5 7 13 / .34) 56%, rgb(5 7 13 / .66) 64%, rgb(5 7 13 / .74) 74%, rgb(5 7 13 / .86) 84%, rgb(5 7 13 / .94) 92%, var(--ink-950) 100%);
  }
}

/* ---------- Body ---------- */
.footer__body { flex: 1; display: flex; flex-direction: column; gap: var(--s-8); padding-block: var(--s-9) var(--s-5); }
.footer__top { display: grid; gap: var(--s-5); }

/* The Astrikos intro: no container (client, 14 Oct 2026); it sits on the picture like the lists. */
.footer__intro {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-5);
}
.footer__mark { display: block; width: 64px; height: 64px; flex: none; }
.footer__mark img { width: 100%; height: 100%; }
.footer__facts { display: grid; gap: var(--s-3); }
.footer__tagline { max-width: 34ch; font-family: var(--font-display); font-size: 16px; line-height: 1.4; }
/* Social marks: icons only, no ring; the 44 target comes from padding, and the row is pulled back so the
   first glyph lines up with the text above it. */
.footer__social { display: flex; flex-wrap: wrap; margin-inline-start: calc(-1 * var(--s-3)); }
.footer__social a {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  color: var(--fg);
  font-size: 20px;
  text-decoration: none;
  transition: color var(--dur-hover) var(--ease-out-expo), transform var(--dur-hover) var(--ease-out-expo);
}
@media (hover: hover) and (pointer: fine) { .footer__social a:hover { color: var(--accent-on-ink); transform: translateY(-2px); } }
.footer__social a:focus-visible { color: var(--fg); outline: 2px solid var(--focus); outline-offset: -4px; border-radius: var(--r-sm); }

/* The groups: no container. Disclosures on phones (closed by site.js below 640), label columns from 640. */
.footer__col { border-bottom: 1px solid var(--line); }
.footer__col summary { display: flex; align-items: center; justify-content: space-between; min-height: 52px; list-style: none; cursor: pointer; }
.footer__col summary::-webkit-details-marker { display: none; }
.footer__heading { font-family: var(--font-display); font-size: 18px; font-weight: 500; line-height: 1.2; color: var(--fg); }
.footer__col summary i { font-size: 20px; color: var(--fg-muted); transition: transform var(--dur-hover) var(--ease-out-expo); }
.footer__col[open] summary i { transform: rotate(180deg); }
.footer__col ul { padding-bottom: var(--s-4); }
.footer__col .mrow { min-height: 44px; padding-right: var(--s-5); font-size: 15px; }
.footer__col .roll { display: grid; min-width: 0; }
.footer__col .mrow__arrow { position: absolute; right: 0; top: 50%; margin-top: -8px; }

/* The close: Mercury's statement and field. */
.footer__close { display: grid; gap: var(--s-5); margin-top: auto; }
.footer__line { max-width: 16ch; font-family: var(--font-display); font-size: clamp(32px, 3.8vw, 56px); font-weight: 400; line-height: 1.08; letter-spacing: -.025em; }
.demo-form { display: grid; gap: var(--s-2); }
.demo-form__label { font-size: 14px; font-weight: 600; }
.demo-form__row { display: flex; gap: var(--s-2); }
.demo-form__input {
  flex: 1;
  min-width: 0;
  min-height: 56px;
  padding: var(--s-3) var(--s-5);
  border: 1px solid var(--on-ink-subtle);
  border-radius: var(--r-pill);
  background: rgb(5 7 13 / .7);
  color: var(--on-ink);
  font: 400 16px/1.2 var(--font-body);
}
.demo-form__input::placeholder { color: var(--on-ink-subtle); }
.demo-form__input:focus-visible { outline: 2px solid var(--accent-on-ink); outline-offset: 2px; border-color: var(--accent-on-ink); }
.demo-form__input[aria-invalid="true"] { border-color: var(--danger-on-ink); }
.demo-form__input:disabled { background: var(--ink-800); color: var(--on-ink-muted); cursor: not-allowed; }
.demo-form__submit { min-height: 56px; padding-inline: var(--s-6); }
.demo-form__hint { font-size: 13px; color: var(--fg-muted); }
.demo-form__error { display: flex; align-items: center; gap: var(--s-2); font-size: 14px; color: var(--danger-on-ink); }
.demo-form__error[hidden], .demo-form__done[hidden] { display: none; }
.demo-form__done { display: flex; align-items: center; gap: var(--s-2); min-height: 56px; color: var(--success-on-ink); font-weight: 600; }
.demo-form__done i { font-size: 20px; }

.footer__legal {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s-2) var(--s-5);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
  font-size: 13px;
  color: var(--fg-muted);
}
.footer__copy, .footer__legal-links { grid-column: 1 / -1; }
.footer__legal-links { display: flex; flex-wrap: wrap; gap: 0 var(--s-5); }
.footer__legal a { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; color: var(--fg-muted); text-decoration: none; }
@media (hover: hover) and (pointer: fine) { .footer__legal a:hover { color: var(--fg); } }

@media (max-width: 639px) {
  .demo-form__row { flex-direction: column; }
}
@media (min-width: 640px) {
  .footer__map { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-5); }
  .footer__col { border-bottom: 0; }
  .footer__col summary { min-height: 0; padding-bottom: var(--s-3); cursor: default; pointer-events: none; }
  .footer__heading { font-size: 12px; color: var(--fg-muted); }
  .footer__col summary i { display: none; }
  .footer__col ul { padding-bottom: 0; }
  .footer__col .mrow { min-height: 36px; }
}
@media (min-width: 640px) and (pointer: coarse) { .footer__col .mrow { min-height: 44px; } }
@media (min-width: 768px) {
  .footer__legal { grid-template-columns: auto minmax(0, 1fr); }
  .footer__copy, .footer__legal-links { grid-column: auto; }
  .footer__legal-links { justify-content: flex-end; }
}

/* 1024 to 1279: the Astrikos intro is a strip across the top, the five groups below it. */
@media (min-width: 1024px) {
  .footer__intro { flex-direction: row; align-items: center; gap: var(--s-6); }
  .footer__facts { flex: 1; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--s-6); }
  .footer__map { grid-template-columns: var(--gcols); }
  .footer__col .mrow { min-height: 40px; }
  .footer__close { grid-template-columns: var(--gcols); column-gap: var(--s-5); align-items: end; }
  .footer__line { grid-column: 1 / 3; }
  .demo-form, .demo-form__done { grid-column: 3 / -1; }
}

/* 1280 and up: laid out as the header's expanded view (review 16 Oct 2026, "align the footer with the navbar"):
   the header's column grid; the five headings in a 48 row with a hairline under it; the Astrikos intro in the
   first column and the pages under their headings, both starting 12 below that hairline; a base hairline 24 under
   the lists; and the S!aP card in the last column from the headings row (8 down, as the header's) to the base
   hairline. The hairlines are two empty spans so the card can span the rows they sit in. */
/* The docked footer (site.js sets html.has-footer-header at 1280 wide and 700 tall): the header's names row is the top
   of the footer, so the block starts at the very top of the screen and the footer's own headings and first hairline
   give way to the header's. */
@media (min-width: 1280px) and (min-height: 700px) {
  html.has-footer-header .footer__body { padding-top: 0; gap: var(--s-6); }
  html.has-footer-header .footer__col summary {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  html.has-footer-header .footer__rule { visibility: hidden; }
  /* The hidden heading no longer holds its 48 row open, so the list keeps the space itself (the same lead as the intro). */
  html.has-footer-header .footer__col ul { padding-top: var(--lead); }
}
@media (min-width: 1280px) {
  .footer__top { --row-names: 48px; --lead: calc(var(--row-names) + var(--s-3)); grid-template-columns: var(--cols); column-gap: var(--s-5); row-gap: 0; align-items: start; }
  .footer__intro { grid-column: 1; grid-row: 1; flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: var(--s-4); padding-top: var(--lead); }
  .footer__facts { flex: none; grid-template-columns: minmax(0, 1fr); gap: var(--s-4); }
  .footer__mark { width: 128px; height: 128px; }
  .footer__tagline { max-width: 26ch; font-size: 12px; line-height: 1.5; color: var(--fg-muted); }
  .footer__rule, .footer__base { display: block; grid-column: 1 / 7; height: 1px; background: var(--line); }
  .footer__rule { grid-column: 2 / 7; grid-row: 1; align-self: start; margin-top: calc(var(--row-names) - 1px); }
  .footer__base { grid-row: 2; margin-top: var(--s-5); }
  /* The headings are the header's names: 14, medium, muted, in a 48 row. */
  .footer__col summary { display: flex; align-items: center; min-height: var(--row-names); padding-bottom: 0; }
  .footer__heading { font-size: 14px; font-weight: 500; line-height: 1.2; color: var(--fg-muted); }
  .footer__col ul { padding-top: var(--s-3); }
  /* Rows are 40 tall (the header's are 28): the footer has the room, and the links read cramped at 28 (client, 16 Oct 2026). 32 on screens 820 tall or less. */
  .footer__col .mrow { min-height: 40px; padding-block: var(--s-1); padding-right: var(--s-4); font-size: 14px; line-height: 1.25; }
  /* The intro's column is narrow here; with a mouse the five marks take 32 targets (the footer-link rule) and the
     row reaches 8 into the padding on each side, so they stay on one line. Touch keeps 44. */
  .footer__social { flex-wrap: nowrap; margin-inline: calc(-1 * var(--s-2)); }
  .footer__social a { width: 32px; height: 32px; }
  .footer__map {
    grid-column: 2 / 7;
    grid-row: 1;
    grid-template-columns: subgrid;
    column-gap: var(--s-5);
    padding: 0;
  }
  .footer__close { grid-template-columns: var(--cols); }
  .footer__line { grid-column: 1 / 4; }
  .demo-form, .demo-form__done { grid-column: 4 / -1; }
  /* The S!aP card, as the header's expanded view shows it (review 16 Oct 2026): the right-hand column, the
     height of the lists, a 1px brand ring, a 4% tint, the picture and "Try S!aP". Square, as every container. */
  .footer__promo {
    position: relative;
    isolation: isolate;
    display: grid;
    grid-column: 7;
    grid-row: 1 / 3;
    grid-template-rows: minmax(0, 1fr) auto;
    gap: var(--s-3);
    align-self: stretch;
    contain: size;
    min-height: 0;
    margin: var(--s-2) 0 0;
    padding: var(--s-2);
    color: var(--fg);
    text-decoration: none;
    border-radius: var(--r-card);
  }
  .footer__promo::before,
  .footer__promo::after { content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: var(--r-card); }
  .footer__promo::before {
    z-index: -1;
    padding: 1px;
    background: var(--grad);
    background-size: 140% 100%;
    -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;
    transition: filter var(--dur-hover) var(--ease-out-quart);
  }
  .footer__promo::after { z-index: -2; background: rgb(242 244 248 / .04); }
  .footer__promo-media { position: relative; min-height: 0; overflow: hidden; background: var(--ink-800); border-radius: var(--r-card-in); }
  .footer__promo-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transform: scale(1.04); transition: transform 700ms var(--ease-out-quart); }
  .footer__promo-body { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); padding: 0 var(--s-2) var(--s-1); font-family: var(--font-display); font-size: 16px; font-weight: 500; line-height: 1.2; }
  .footer__promo-body i { font-size: 18px; color: var(--link); transition: transform var(--dur-roll) var(--ease-out-quart); }
  .footer__promo:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
  @media (hover: hover) and (pointer: fine) {
    .footer__promo:hover::before { filter: brightness(1.25); }
    .footer__promo:hover img { transform: none; }
    .footer__promo:hover .footer__promo-body i { transform: translate(2px, -2px); }
  }
}
/* The S!aP lockup on the card's picture (22 Oct 2026, review: the wordmark was too big): the picture is the plain
   backdrop, and the small word "Try" sits over a wordmark at 38% of the card's width (it was 59% baked in). One
   centred column, the same in the header and the footer. */
.promo-lock { position: absolute; inset: 0; display: grid; grid-template-columns: 38%; place-content: center; justify-items: center; gap: var(--s-2); pointer-events: none; }
.promo-lock__eyebrow { font-size: 12px; font-weight: 500; line-height: 1.4; color: var(--on-ink-muted); }
.promo-lock .promo-lock__mark { position: static; display: block; width: 100%; height: auto; object-fit: contain; transform: none; transition: none; }
.footer__promo { display: none; }
.footer__rule, .footer__base { display: none; }
@media (min-width: 1280px) { .footer__promo { display: grid; } .footer__rule, .footer__base { display: block; } }
@media (min-width: 1280px) and (pointer: coarse) {
  .footer__social { flex-wrap: wrap; }
  .footer__social a { width: 44px; height: 44px; }
}
/* The card widened the last column; below 1400 the lists give back 8 of the arrow's room so "Energy and Utilities" stays on one line. */
@media (min-width: 1280px) and (max-width: 1399px) { .footer__col .mrow { padding-right: var(--s-1); } }
@media (min-width: 1280px) and (max-width: 1339px) and (pointer: fine) { .site-header, .site-footer { --actions-w: 184px; } }
@media (min-width: 1280px) and (max-height: 820px) {
  .footer__col .mrow { min-height: 32px; }
  .footer__body { gap: var(--s-6); }
}

/* ---------- The lists rise in once the footer has docked (motion, 1280 wide and 700 tall) ---------- */
@media (min-width: 1280px) and (min-height: 700px) {
  .js-motion.has-footer-header :is(.footer__intro, .footer__col, .footer__promo) {
    transition: opacity 700ms var(--ease-out-quart), transform 1200ms var(--ease-spring);
    transition-delay: calc(120ms + var(--g, 0) * 70ms);
  }
  .js-motion.has-footer-header .site-footer:not(.is-open) :is(.footer__intro, .footer__col, .footer__promo) {
    opacity: 0;
    transform: translateY(24px);
    transition-duration: 250ms;
    transition-delay: 0s;
  }
  .js-motion.has-footer-header .footer__col .mrow::before {
    transition: transform 900ms var(--ease-out-quart);
    transition-delay: calc(260ms + var(--g, 0) * 70ms + var(--r, 0) * 40ms);
  }
  .js-motion.has-footer-header .site-footer:not(.is-open) .footer__col .mrow::before { transform: scaleX(0); transition-duration: 200ms; transition-delay: 0s; }
  .js-motion.has-footer-header .footer__base { transform-origin: left; transition: transform 1000ms var(--ease-out-quart) 300ms; }
  .js-motion.has-footer-header .site-footer:not(.is-open) .footer__base { transform: scaleX(0); transition-duration: 200ms; transition-delay: 0s; }
}

/* ---------- The header in footer mode (1280 wide and 700 tall): its names spread to the footer's columns ---------- */
@media (min-width: 1280px) {
  .site-header.is-collapsed .site-header__rule--top { transform: scaleX(0); }
  .site-header.is-footer .site-header__bar::before { opacity: 0; transition-duration: 500ms; }
  /* A spring on the spread (DESIGN.md 9, --ease-spring): the names overshoot their columns by a hair and settle. */
  .site-header.is-collapsed.is-footer .site-header__link {
    transform: none;
    color: var(--fg);
    transition: color var(--dur-hover) var(--ease-out-expo), transform 1100ms var(--ease-spring);
    transition-delay: 0s, calc(var(--i, 0) * 40ms);
  }
  .site-header.is-collapsed.is-footer .site-header__links { transform: none; transition: transform 800ms var(--ease-out-quart), opacity 700ms var(--ease-out-quart); }
  .site-header.is-collapsed.is-footer .site-header__rule--top { opacity: 1; transform: none; transition: opacity 400ms var(--ease-out-quart) 200ms, transform 1000ms var(--ease-out-quart) 200ms; }
  /* The header's S!aP shape (the pill under its button) steps aside with the button, so it never shows beside the footer's own card. */
  .site-header.is-footer .site-header__promo { visibility: hidden; transition: visibility 0s linear 300ms; }
  .site-header.is-footer :is(.site-header__brand, .site-header__actions) {
    opacity: 0;
    visibility: hidden;
    transition: opacity 300ms var(--ease-out-quart), visibility 0s linear 300ms;
  }
}

/* Lenis owns smooth scrolling; the native smooth behaviour would fight it. */
html.lenis { scroll-behavior: auto; }
html.lenis,
html.lenis body { height: auto; }
.lenis:not(.lenis-autoToggle).lenis-stopped { overflow: clip; }
.lenis.lenis-smooth iframe { pointer-events: none; }

/* Below 1024 every 12-track grid stacks to one track. Its children already span 1 / -1 there,
   and one track stops eleven gaps from outgrowing a 320 screen. */
@media (max-width: 1023px) {
  .proof__grid, .eco-row, .eco-proof { grid-template-columns: minmax(0, 1fr); }
}

/* Act assignments: semantic colours per section */
.hero, .trusted, .platform, .proof, .partners, .cta-band { --fg: var(--on-ink); --fg-muted: var(--on-ink-muted); --fg-subtle: var(--on-ink-subtle); --line: var(--ink-700); --link: var(--accent-on-ink); --focus: var(--accent-on-ink); --chip-bg: var(--ink-850); color: var(--on-ink); }
.industries, .stories, .company, .insights { color: var(--on-paper); }

/* =============================================================
   Entrance motion (references/entrance-motion.md)
   Hidden states exist only under html.js-motion, which site.js
   adds when motion is allowed. Without JS or with reduced motion
   everything renders in its final state.
   ============================================================= */
/* Scattered letters (Defiant reference): each letter fades in at its own random delay.
   Opacity only, letters never travel, so kerning and line breaks are untouched. */
.ch {
  transition: opacity var(--char-run) var(--ease-out-quart);
  transition-delay: calc(var(--d, 0) * var(--char-spread));
}
.js-motion [data-split]:not(.is-in) .ch { opacity: 0; }
.js-motion .rise:not(.is-in) { opacity: 0; transform: translateY(16px); }
.js-motion .rise { transition: opacity 700ms var(--ease-out-expo), transform 900ms var(--ease-out-expo); transition-delay: 160ms; }
.js-motion [data-cascade]:not(.is-in) > * { opacity: 0; transform: translateY(24px); }
.js-motion [data-cascade] > * {
  transition: opacity 700ms var(--ease-out-expo), transform 900ms var(--ease-spring);
  transition-delay: calc(var(--rank, 0) * var(--cascade-step));
}
.js-motion [data-cascade].is-in > * { transition-property: opacity, transform; }
/* Clip the row on x so the pre-entrance offset can never widen the page on a phone. */
.js-motion [data-momentum] { overflow-x: clip; }
.js-motion [data-momentum]:not(.is-in) > * { opacity: 0; transform: translateX(48px); }
.js-motion [data-momentum] > * {
  transition: opacity 700ms var(--ease-out-quint), transform 1000ms var(--ease-out-quint);
  transition-delay: calc(var(--rank, 0) * 70ms);
}
.js-motion .wipe:not(.is-in) { clip-path: inset(0 0 100% 0); }
.js-motion .wipe { transition: clip-path var(--dur-wipe) var(--ease-out-quint); }
.js-motion .wipe:not(.is-in) img { transform: scale(1.12); }
.js-motion .wipe img { transition: transform var(--dur-zoom) var(--ease-out-expo); }
/* Once a card has arrived, hover owns its transform again. */
.js-motion [data-cascade].is-settled > * { transition: transform var(--dur-hover) var(--ease-out-expo), border-color var(--dur-hover) var(--ease-out-expo); transition-delay: 0s; }

/* ---------- Keyframes ---------- */
@keyframes marquee { to { transform: translateX(-50%); } }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- Reduced motion: everything rests in its final state ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 1ms !important; transition-delay: 0s !important; }
  .marquee .marquee__track { animation: none !important; }
  .hero__video { display: none; }
  .btn.is-loading::before { animation: none; }
}
