/* ==========================================================================
   Sentrova Solutions — design system
   Direction: "Instrument-grade". Cool ash ground, indigo signal, hairline rules.
   Tokens are the single source of truth. Do not hardcode colour or type below.
   ========================================================================== */

/* --- 1. Tokens ----------------------------------------------------------- */
:root {
  /* Colour. Warm paper ground, ink type, indigo signal. */
  --ink:        #0D1014;
  --ink-2:      #39424E;
  --ink-3:      #5F6979;
  --paper:      #F7F5F0;  /* warm ivory, less clinical than ash */
  --paper-deep: #EDE9E1;
  --panel:      #FFFFFF;
  --cream:      #FAF8F3;  /* extra-light warm section ground */
  --rule:       #D8D4CC;
  --rule-soft:  #E8E4DC;

  /* Indigo: the signal colour. Branded, not generic tech blue. */
  --indigo-50:  #F5F3FF;
  --indigo-100: #EDE9FE;
  --indigo-200: #DDD6FE;
  --indigo-300: #C4B5FD;
  --indigo-400: #A78BFA;
  --indigo-500: #8B5CF6;
  --indigo-600: #7C3AED;  /* hover lift */
  --indigo-700: #6D28D9;  /* THE brand value: vivid violet-indigo */
  --indigo-950: #1E1B2E;  /* warm near-black, not flat navy */

  /* The mark's own colours live in its SVG, because they are gradients.
     What is left here is the wordmark's full stop, which follows the
     letterforms and so belongs with the type. */
  --mark-lift:   var(--indigo-400);     /* #A78BFA — the dot, on deep ground */

  /* Aliases kept so existing rules and DESIGN.md stay honest. */
  --indigo:      var(--indigo-700);
  --indigo-lift: var(--indigo-600);
  --indigo-wash: var(--indigo-100);
  --indigo-edge: var(--indigo-200);

  /* Type. Sora carries every reading role — display, headings, body,
     navigation and buttons — at one weight axis. IBM Plex Mono stays for the
     instrument readouts (spec values, durations, hints) where a fixed advance
     width is doing real work. --display and --body are kept as separate
     tokens so the intent of each rule below still reads, even though they
     now resolve to the same face. */
  --display: "Sora Variable", Sora, "Helvetica Neue", Arial, sans-serif;
  --body:    "Sora Variable", Sora, "Segoe UI", system-ui, sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Weights, named after the role they serve rather than the number. */
  --w-display: 800;  /* H1 — ExtraBold */
  --w-heading: 700;  /* H2 — Bold */
  --w-sub:     600;  /* H3, eyebrows, buttons — SemiBold */
  --w-nav:     500;  /* navigation — Medium */

  /* Scale. H1 64–72px, H2 36–44px, body 16–18px, nav and buttons 14px. */
  --fs-display-xl: clamp(2.5rem, 5.6vw, 4.5rem);    /* 40 → 72 */
  --fs-display-l:  clamp(2rem, 3.9vw, 2.75rem);     /* 32 → 44 */
  --fs-h2:         clamp(1.75rem, 3vw, 2.25rem);    /* 28 → 36 */
  --fs-h3:         clamp(1.0625rem, 1.2vw, 1.125rem);
  --fs-lead:       clamp(1.0625rem, 1.3vw, 1.125rem);
  --fs-body:       1rem;
  --fs-small:      0.875rem;
  --fs-ui:         0.875rem;    /* navigation and buttons — 14px */
  --fs-label:      0.75rem;     /* tracked uppercase eyebrows */

  /* Corner radius. The boxes read as cards rather than cut sheet metal.
     Small for controls, large for the tiles and the closing CTA. */
  --radius-sm:   8px;
  --radius:     12px;
  --radius-lg:  18px;
  --radius-pill: 999px;

  /* Space */
  --gutter: 1.5rem;
  --rail:   5.75rem;
  --shell:  78rem;
  --band-y: clamp(4.5rem, 9vw, 8.5rem);

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* --- 2. Reset ------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Light-only design. Without this, an OS set to dark renders native selects,
     scrollbars and autofill dark against an ash page. */
  color-scheme: light;
}

/* Every fragment target clears the 4.25rem sticky header. Measured: without
   this, 42px of each section landed underneath it on desktop, 63px on mobile.
   :where() keeps specificity at 0 so any element can still override it. */
:where([id]) { scroll-margin-top: 5.5rem; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--fs-body);
  line-height: 1.6;
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Removes the 300ms double-tap zoom delay without disabling pinch zoom. */
  touch-action: manipulation;
  /* The default flash is a blue that fights the indigo. */
  -webkit-tap-highlight-color: rgba(36, 28, 201, 0.12);
}

/* Figures in the spec tables and readouts line up column to column. */
.spec__val, .hero__readout dd, .mono, .label, .panel__list li, .step__dur {
  font-variant-numeric: tabular-nums;
}

img, svg, canvas { display: block; max-width: 100%; }
h1, h2, h3, h4, p, ul, ol, figure, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--indigo);
  outline-offset: 3px;
  border-radius: 4px;
}

::selection { background: var(--indigo); color: #fff; }

/* main carries tabindex="-1" so the skip link genuinely moves focus. That
   focus is programmatic, so it must not paint a ring. */
main[tabindex]:focus { outline: none; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip-link {
  position: absolute; top: 0; left: 0; z-index: 200;
  transform: translateY(-120%);
  padding: 0.75rem 1.25rem;
  background: var(--ink); color: #fff;
  font-family: var(--display); font-weight: var(--w-sub); font-size: var(--fs-ui);
  letter-spacing: 0.08em; text-transform: uppercase;
}
.skip-link:focus { transform: translateY(0); }

/* --- 3. Type roles ------------------------------------------------------- */
/* H1. ExtraBold, the only weight on the page that goes above 700. */
.display-xl {
  font-family: var(--display);
  font-weight: var(--w-display);
  font-size: var(--fs-display-xl);
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

/* Section display and H2 share the heading role: Bold, a touch of negative
   tracking, and enough leading that two lines do not lock together. */
.display-l,
.h2 {
  font-family: var(--display);
  font-weight: var(--w-heading);
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.display-l { font-size: var(--fs-display-l); }
.h2        { font-size: var(--fs-h2); }

.h3 {
  font-family: var(--display);
  font-weight: var(--w-sub);
  font-size: var(--fs-h3);
  line-height: 1.4;
  letter-spacing: -0.004em;
}

.lead {
  font-size: var(--fs-lead);
  line-height: 1.6;
  color: var(--ink-2);
  text-wrap: pretty;
}

.prose { color: var(--ink-2); text-wrap: pretty; }
.prose + .prose { margin-top: 1rem; }

/* Inline links stay in the surrounding type. Mono is reserved for standalone
   navigation, where the register shift is the point. */
.prose a, .panel__body a, .spec__val a {
  color: var(--indigo);
  border-bottom: 1px solid var(--indigo-edge);
  transition: border-color 0.18s var(--ease);
}
.prose a:hover, .panel__body a:hover, .spec__val a:hover { border-color: var(--indigo); }

/* The sub-heading role: Sora SemiBold, tracked open and set in caps. Mono is
   no longer the eyebrow face — it is reserved for figures below. */
.label {
  font-family: var(--display);
  font-size: var(--fs-label);
  font-weight: var(--w-sub);
  line-height: 1.4;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.label--ink { color: var(--ink); }
.page-head .label--ink { color: var(--indigo-700); }
.label--indigo { color: var(--indigo); }

.mono { font-family: var(--mono); font-size: var(--fs-small); }

/* Gradient definitions the marks reference by id. Taken out of the flow
   rather than hidden: a paint server inside a `display: none` subtree is not
   resolved in every engine, and then every glyph that used it comes out
   unpainted. */
.svg-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.em-indigo { color: var(--indigo); }

/* Sora's period glyph is a square; this swaps in a round dot for display type. */
.hero__full-stop {
  display: inline-block;
  width: 0.19em;
  height: 0.19em;
  margin-left: 0.05em;
  border-radius: 50%;
  background: currentColor;
  vertical-align: 0.02em;
}

/* --- 4. Shell and grid --------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.band { padding-block: var(--band-y); }
.band--tight { padding-block: clamp(3rem, 5.5vw, 5rem); }
.band--panel { background: var(--panel); }
/* Third ground. The page now steps ash -> white -> indigo tint -> deep indigo,
   so colour builds toward the conversion band instead of appearing at random. */
.band--tint { background: var(--cream); }
.band--tint .panel { background: var(--panel); }
/* Warm near-black conversion band. Not flat navy — charcoal with violet undertone. */
.band--ink { background: var(--indigo-950); color: var(--paper); }

/* Extra warm white section */
.band--warm { background: var(--paper); }

/* Hairline that carries a label, rather than a decorative divider.
   It draws itself left to right as the section is reached — a scale being
   laid down. Nothing readable is ever hidden behind it. */
.band-head {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.5rem;
  padding-top: 1.1rem;
  margin-bottom: clamp(2.25rem, 4vw, 3.5rem);
}
.band-head::before {
  content: "";
  position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: var(--rule);
  transform-origin: left center;
}
.js .band-head::before { transform: scaleX(0); transition: transform 0.85s var(--ease); }
.js .band-head[data-shown="true"]::before { transform: scaleX(1); }
@media (min-width: 60rem) {
  .band-head {
    /* The heading column had no floor, so right at this breakpoint the fixed
       label and aside columns could crush it enough to force a wrap the
       aside column's height didn't match — the two blocks read staggered
       instead of level. 16rem keeps any of this component's headings on
       one line before the shell has room to spare. */
    grid-template-columns: 7rem minmax(16rem, 1fr) minmax(0, 21rem);
    gap: 2rem;
    align-items: start;
  }
}
.band-head__body { max-width: 46ch; }
/* Fills its column so its right edge lines up with the grid below it. */
.band-head__aside { max-width: none; }
/* The aside column is capped at minmax(0, 21rem) — already its full width, so
   the block sits flush against the shell's right edge. Widening the cap for
   this instance claims more of the row, which shifts the block's left edge
   inward, off the margin. */
@media (min-width: 60rem) {
  #enquiry .band-head { grid-template-columns: 7rem minmax(16rem, 1fr) minmax(0, 26rem); }
}

/* --- 5. Header ----------------------------------------------------------- */
.masthead {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--rule);
}

.masthead__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem;
  height: 4.25rem;
  /* Header runs edge to edge: logo flush to the left gutter, nav and CTA flush to the right. */
  max-width: none;
  padding-inline: clamp(1rem, 3vw, 2.5rem);
}

.brand { display: flex; align-items: center; gap: 0.7rem; }
.brand__mark { width: 44px; height: 44px; flex: none; overflow: visible; }
/* The mark carries its own paint. The board's artwork is built from
   gradients — the ribbon runs deep indigo through violet and back, each
   orbit arc fades along its own length, and the nodes are lit spheres —
   and a gradient cannot be expressed as a CSS colour token. So the fills
   live on the paths, and the footer carries a second cut of the same
   geometry with the light-ground stops swapped for deep-ground ones.
   Nothing here may set fill or stroke on the mark: CSS beats presentation
   attributes, and a flat colour would paint straight over the gradients. */
.brand__text { display: flex; flex-direction: column; line-height: 1; }
.brand__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.02rem; letter-spacing: 0.03em;
  text-transform: uppercase;
}
/* The full stop the board sets after the wordmark. Generated rather than
   typed into sixteen places, and hidden from the accessibility tree so the
   brand is announced as "Sentrova", not "Sentrova period". */
.brand__name::after {
  /* Drawn, not typed. Sora's full stop is a rounded square; the board's is a
     true circle, and at this size the difference is the whole point of it. */
  content: "";
  display: inline-block;
  width: 0.21em; height: 0.21em;
  border-radius: 50%;
  background: var(--indigo-600);
  /* Sits on the baseline rather than the middle of the x-height. */
  vertical-align: baseline;
  margin-left: 0.08em;
}
.footer .brand__name::after { background: var(--mark-lift); }
.brand__sub {
  font-family: var(--mono);
  font-size: 0.5625rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--ink-3);
  margin-top: 0.3rem;
}

.nav { display: none; }
@media (min-width: 62rem) {
  .nav { display: flex; align-items: center; gap: 1.75rem; }
}
.nav__link {
  font-family: var(--display);
  font-weight: var(--w-nav);
  font-size: var(--fs-ui);
  line-height: 1.4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding-block: 0.4rem;
  position: relative;
  transition: color 0.18s var(--ease);
}
.nav__link:hover { color: var(--ink); }
.nav__link[aria-current="page"] { color: var(--ink); }
.nav__link[aria-current="page"]::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 2px; background: var(--indigo);
}

.nav-toggle {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  min-height: 44px; min-width: 44px; /* touch target floor */
  background: none; border: 1px solid var(--rule);
  padding: 0.5rem 0.9rem; cursor: pointer;
  font-family: var(--display); font-weight: var(--w-nav); font-size: var(--fs-ui);
  letter-spacing: 0.08em; text-transform: uppercase;
  border-radius: var(--radius);
}

/* Header CTA: one clear conversion action beside the plain nav links. */
.nav__cta { margin-left: 0.25rem; padding: 0.5rem 0.95rem; }
.drawer__cta {
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
  width: 100%; margin-top: 1.25rem;
}
@media (min-width: 62rem) { .nav-toggle { display: none; } }

.drawer {
  display: none;
  border-top: 1px solid var(--rule);
  background: var(--panel);
  max-height: calc(100dvh - 4.25rem);
  overflow-y: auto;
  /* Stops a scroll gesture inside the drawer from scrolling the page behind it. */
  overscroll-behavior: contain;
}
.drawer[data-open="true"] { display: block; }
@media (min-width: 62rem) { .drawer[data-open="true"] { display: none; } }

/* Without scripting the toggle cannot work, so the links are simply exposed. */
@media (max-width: 61.99rem) {
  html:not(.js) .drawer { display: block; }
  html:not(.js) .nav-toggle { display: none; }
}
.drawer__list { padding-block: 0.5rem 1.25rem; }
.drawer__link {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--rule-soft);
  font-family: var(--display); font-weight: 700;
  font-size: 1.4rem; letter-spacing: -0.015em;
}
.drawer__link span { font-family: var(--mono); font-size: var(--fs-label); color: var(--ink-3); font-weight: 400; letter-spacing: 0.14em; }

/* --- 6. Instrument rail -------------------------------------------------- */
/* Encodes reading position. Hidden where there is no room for it to be true. */
/* Appears only once the viewport is wide enough to clear the 78rem shell —
   below that there is no margin to sit in and it would cross the type. */
.rail { display: none; }
@media (min-width: 96rem) {
  .rail {
    display: block;
    position: fixed; top: 50%; left: 1.75rem; z-index: 90;
    transform: translateY(-50%);
    padding-left: 0.85rem;
    border-left: 1px solid var(--rule);
  }
}
.rail__item {
  display: flex; align-items: center; gap: 0.6rem;
  padding-block: 0.42rem;
  font-family: var(--mono); font-size: 0.625rem;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-3);
  transition: color 0.2s var(--ease);
}
.rail__tick {
  width: 12px; height: 1px; background: var(--rule);
  transition: width 0.25s var(--ease), background-color 0.25s var(--ease);
}
.rail__item:hover { color: var(--ink-2); }
.rail__item[data-active="true"] { color: var(--ink); }
.rail__item[data-active="true"] .rail__tick { width: 26px; background: var(--indigo); }

/* --- 7. Hero + signal field --------------------------------------------- */
.hero { position: relative; overflow: hidden; border-bottom: 1px solid var(--rule); }

.hero__field {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  /* Keep the field off the headline so type stays the loudest thing. */
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,0.12) 0%, rgba(0,0,0,0.35) 42%, #000 100%);
  mask-image: linear-gradient(180deg, rgba(0,0,0,0.12) 0%, rgba(0,0,0,0.35) 42%, #000 100%);
}
@media (min-width: 62rem) {
  .hero__field {
    -webkit-mask-image: linear-gradient(96deg, rgba(0,0,0,0.10) 0%, rgba(0,0,0,0.16) 36%, #000 74%);
    mask-image: linear-gradient(96deg, rgba(0,0,0,0.10) 0%, rgba(0,0,0,0.16) 36%, #000 74%);
  }
}

.hero__inner {
  position: relative; z-index: 1;
  padding-block: clamp(4.5rem, 11vw, 9.5rem) clamp(3.5rem, 7vw, 6rem);
  pointer-events: none;
}
.hero__inner a, .hero__inner button { pointer-events: auto; }

.hero__eyebrow { display: block; margin-bottom: 1.6rem; }


.hero__title { max-width: 18ch; }
.hero__lead { max-width: 48ch; margin-top: 1.6rem; }

.hero__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 2.4rem; }
/* 49rem is where both labels measurably stop fitting on one line together;
   below it they wrap. Two buttons at their own natural widths, stacked and
   ragged — one of them two lines tall on a phone — read as a mistake, so
   below that point they stack deliberately and match. */
@media (max-width: 49rem) {
  .hero__actions { flex-direction: column; align-items: stretch; gap: 0.6rem; }
  .hero__actions .btn { justify-content: center; text-align: center; }
  .nav-toggle { min-height: 44px; padding: 0.4rem 0.8rem; }
  .drawer__cta { margin-top: 1rem; }
}
/* Where the row sits inside a narrow column instead of across the page, the
   viewport cannot tell us whether the buttons fit — on the 404 page they are
   in a 535px column at 1440px wide and wrapped ragged, 280px above 370px.
   This stacks them at every width, to one width and one left edge. */
.hero__actions--stack { flex-direction: column; align-items: stretch; }
.hero__actions--stack .btn { justify-content: center; text-align: center; }

.hero__readout {
  margin-top: clamp(3rem, 6vw, 5rem);
  display: grid; gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  grid-template-columns: 1fr;
  max-width: 46rem;
}
@media (min-width: 48rem) { .hero__readout { grid-template-columns: repeat(3, 1fr); } }
.hero__readout div { background: var(--indigo-50); padding: 1.15rem 1.25rem; }
.hero__readout dt { margin-bottom: 0.45rem; }
.hero__readout dd { font-family: var(--body); font-weight: 500; font-size: var(--fs-small); color: var(--ink-2); }
/* The three promises as one glass card rather than three lavender cells:
   white over the signal field, hairlines between, and each term carrying
   its mark on the same lit disc the rest of the site uses. */
.hero__readout {
  gap: 0;
  background: rgba(255, 255, 255, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.95);
  border-radius: 20px;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.05),
    0 18px 40px -22px rgba(76, 29, 149, 0.4);
}
.hero__readout div { background: transparent; padding: 1.3rem 1.35rem 1.4rem; }
.hero__readout div + div { border-top: 1px solid var(--rule-soft); }
@media (min-width: 48rem) {
  .hero__readout div + div { border-top: 0; border-left: 1px solid var(--rule-soft); }
}
.hero__readout dt { color: var(--indigo); margin-bottom: 0.4rem; }
.hero__readout dd { color: var(--ink); font-weight: 500; line-height: 1.55; }
.readout__mark {
  display: grid; place-items: center;
  width: 2.7rem; height: 2.7rem; margin-bottom: 0.85rem;
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 28%, #FFFFFF 0%, rgba(255, 255, 255, 0) 48%),
    linear-gradient(145deg, #F7F4FF 0%, #E6DEFD 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.9),
    0 8px 16px -10px rgba(76, 29, 149, 0.5);
}
.readout__mark svg { width: 1.6rem; height: 1.6rem; display: block; }

/* --- 7a. The operating layer, in depth ------------------------------------
   The home page's one three-dimensional object: four glass plates stacked in
   real perspective — the systems you already run, the context and
   permissions over them, the redesigned workflow, and agents where they are
   justified — with a beam through all four carrying a pulse upwards. Pure
   CSS 3D: no library, no image, nothing to download, and sharp at any size.

   It sits in the half of the hero the headline never reaches, and only from
   the width where it measurably clears every line of the copy; below that the
   signal field carries the hero on its own, as it always has. Under reduced
   motion it is assembled and still. */
.stack3d { display: none; }
@media (min-width: 75rem) {
  .stack3d {
    /* One plate's side. Everything else — the gap between plates, the box,
       where each label lands — is derived from it, so the drawing scales as
       one object. */
    --size: clamp(10rem, 12.6vw, 13rem);
    --gap: calc(var(--size) * 0.24);
    display: block;
    position: absolute;
    z-index: 1;
    top: calc(clamp(4.5rem, 11vw, 9.5rem) - 1rem);
    /* Allowed four rem into the page margin, never past the gutter: the
       space right of the headline is a near-constant 416px inside the shell
       at every width from here up, which is not quite enough on its own. */
    right: max(var(--gutter), calc((100% - var(--shell)) / 2 + var(--gutter) - 4rem));
    width: calc(var(--size) * 1.412 + 9.6rem);
    height: calc(var(--size) * 1.6);
    perspective: 1600px;
    perspective-origin: 40% 30%;
    pointer-events: none;
    animation: stack3d-fade 0.9s ease-out 0.2s both;
  }
}
/* A soft ground under the stack, flat on the page, so it reads as resting on
   something rather than floating over the dots. */
.stack3d::after {
  content: "";
  position: absolute;
  left: 0; width: calc(var(--size) * 1.45);
  bottom: 0; height: calc(var(--size) * 0.34);
  background: radial-gradient(closest-side, rgba(76, 29, 149, 0.18), rgba(76, 29, 149, 0));
}
/* Pointer parallax lives on its own wrapper and turns the scene and its
   labels together, by a few degrees at most. */
.stack3d__tilt {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--px, 0deg)) rotateY(var(--py, 0deg));
}
/* The scene's centre, shared by the plates and by the labels. */
.stack3d__scene,
.stack3d__notes {
  position: absolute;
  left: calc(var(--size) * 0.706 + 0.25rem);
  top: calc(var(--size) * 1.106);
  transform-style: preserve-3d;
}
.stack3d__scene {
  width: var(--size); height: var(--size);
  margin: calc(var(--size) / -2) 0 0 calc(var(--size) / -2);
  /* Fixed: 58° back, 42° round. The labels' positions below are worked out
     from these two numbers, so they are not variables anyone should tune. */
  transform: rotateX(58deg) rotateZ(-42deg);
}
.stack3d__plate {
  position: absolute; inset: 0;
  border-radius: 22px;
  transform-style: preserve-3d;
  transform: translateZ(calc(var(--i) * var(--gap)));
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(245, 243, 255, 0.78));
  border: 1px solid rgba(124, 58, 237, 0.2);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.7),
    0 24px 48px -26px rgba(46, 16, 101, 0.42);
  animation:
    stack3d-in 1s cubic-bezier(0.22, 1, 0.36, 1) calc(var(--i) * 140ms + 200ms) both,
    stack3d-float 7s ease-in-out calc(var(--i) * 0.6s + 1.4s) infinite;
}
.stack3d__plate--1 { --i: 0; background: linear-gradient(135deg, rgba(239, 244, 255, 0.96), rgba(219, 231, 254, 0.82)); }
.stack3d__plate--2 { --i: 1; background: linear-gradient(135deg, rgba(240, 249, 250, 0.95), rgba(220, 240, 243, 0.8)); }
.stack3d__plate--3 { --i: 2; }
/* The top plate is the most transparent, so the workflow it sits on shows
   through it — glass, not a lid. */
.stack3d__plate--4 {
  --i: 3;
  background: linear-gradient(135deg, rgba(245, 243, 255, 0.78), rgba(221, 214, 254, 0.6));
  border-color: rgba(124, 58, 237, 0.36);
}
/* The entrance moves the plates and nothing else. An opacity animation on a
   plate — even one that has finished at 1 — makes it a flattening group,
   and every label drawn inside one was squashed to 8px against its plane.
   The fade happens once, on the root of the scene, where flattening is what
   is meant to happen anyway. */
@keyframes stack3d-in {
  from { translate: 0 0 9rem; }
  to   { translate: 0 0 0; }
}
@keyframes stack3d-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes stack3d-float {
  0%, 100% { translate: 0 0 0; }
  50%      { translate: 0 0 0.45rem; }
}

/* What is drawn on each plate, flat in its plane, so the perspective does
   the work: tiles for systems, rows and a lock for context, a path for the
   workflow, and one lit core for the agent layer. */
.stack3d__tiles {
  position: absolute; inset: 16%;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 9%;
}
.stack3d__tiles i {
  border-radius: 9px;
  background: linear-gradient(135deg, #6E95F0, #2F52D8);
  box-shadow: 0 6px 14px -8px rgba(47, 82, 216, 0.8);
}
.stack3d__tiles i:nth-child(2n) { background: linear-gradient(135deg, #A9C1FA, #6E8FEA); }
.stack3d__rows {
  position: absolute; left: 16%; right: 34%; top: 20%; bottom: 20%;
  display: grid; align-content: space-between;
}
.stack3d__rows i { height: 0.5rem; border-radius: 99px; background: linear-gradient(90deg, #2BA7B0, #8FD7D8); }
.stack3d__rows i:nth-child(2) { width: 72%; }
.stack3d__rows i:nth-child(3) { width: 86%; }
.stack3d__rows i:nth-child(4) { width: 54%; }
.stack3d__lock {
  position: absolute; right: 13%; top: 50%;
  width: 20%; aspect-ratio: 1; margin-top: -10%;
  border-radius: 10px;
  background: linear-gradient(135deg, #34AFB6, #116C84);
  box-shadow: 0 8px 18px -8px rgba(17, 108, 132, 0.8);
}
.stack3d__lock::before {
  content: "";
  position: absolute; left: 26%; right: 26%; bottom: 82%; height: 40%;
  border: 3px solid #116C84; border-bottom: 0;
  border-radius: 99px 99px 0 0;
}
.stack3d__path { position: absolute; inset: 10%; width: 80%; height: 80%; overflow: visible; }
.stack3d__path path { fill: none; stroke: #8158F2; stroke-width: 3.2; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 4 5; }
.stack3d__path circle { fill: #FFFFFF; stroke: #6D28D9; stroke-width: 3.2; }
.stack3d__path circle:last-of-type { fill: #6D28D9; }
.stack3d__core {
  position: absolute; left: 50%; top: 50%;
  width: 34%; aspect-ratio: 1; margin: -17% 0 0 -17%;
  border-radius: 50%;
  background: radial-gradient(circle at 38% 34%, #C4B5FD 0%, #7C3AED 46%, #4C1D95 100%);
  box-shadow: 0 0 0 0.6rem rgba(124, 58, 237, 0.14), 0 0 2.6rem 0.4rem rgba(124, 58, 237, 0.45);
}

/* The beam stands up through the centre of all four plates and carries a
   pulse from the systems to the agents: data in at the bottom, judgment at
   the top. */
.stack3d__beam {
  position: absolute; left: 50%; top: 50%;
  width: 3px; height: calc(3 * var(--gap));
  margin-left: -1.5px;
  transform-origin: 50% 0;
  transform: rotateX(90deg);
  background: linear-gradient(0deg, rgba(124, 58, 237, 0.08), rgba(124, 58, 237, 0.45));
  border-radius: 3px;
  overflow: hidden;
}
.stack3d__beam::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 0;
  height: 32%;
  background: linear-gradient(180deg, rgba(196, 181, 253, 0), #8B5CF6 60%, rgba(255, 255, 255, 0.95));
  border-radius: 3px;
  animation: stack3d-pulse 3.2s cubic-bezier(0.45, 0, 0.2, 1) 1.6s infinite;
  transform: translateY(-100%);
}
@keyframes stack3d-pulse {
  from { transform: translateY(-100%); }
  70%, to { transform: translateY(320%); }
}

/* The labels hang off each plate's right-hand corner, at that corner's real
   depth, so the one perspective places both and they cannot drift apart.
   Inside a plate they were hidden by the plate above; out here they sit to
   the right of every plate's footprint and nothing covers them.

   Where the corner lands, from the scene's angles (58° back, 42° round) for
   the local corner at (+s/2, +s/2) on plate i:
     x =  s/2 (cos 42 + sin 42)              = 0.706 s
     y =  s/2 (cos 42 - sin 42) cos 58  - i·gap·sin 58  = 0.0196 s - 0.848 i·gap
     z =  s/2 (cos 42 - sin 42) sin 58  + i·gap·cos 58  = 0.0314 s + 0.530 i·gap */
.stack3d__notes { list-style: none; margin: 0; padding: 0; }
.stack3d__note {
  position: absolute; left: 0; top: 0;
  display: inline-flex; align-items: center; gap: 0.45rem;
  white-space: nowrap;
  transform:
    translate3d(
      calc(var(--size) * 0.706 + 0.7rem),
      calc(var(--size) * 0.0196 - 0.848 * var(--i) * var(--gap)),
      calc(var(--size) * 0.0314 + 0.53 * var(--i) * var(--gap)))
    translateY(-50%);
  font-family: var(--display);
  font-size: 0.75rem;
  font-weight: var(--w-sub);
  color: var(--ink);
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--indigo-edge);
  border-radius: var(--radius-pill);
  padding: 0.26rem 0.62rem 0.26rem 0.3rem;
  box-shadow: 0 6px 16px -10px rgba(20, 24, 48, 0.35);
  animation: stack3d-in 1s cubic-bezier(0.22, 1, 0.36, 1) calc(var(--i) * 140ms + 200ms) both;
}
/* A short leader back to the corner it names. */
.stack3d__note::before {
  content: "";
  position: absolute; right: 100%; top: 50%;
  width: 0.7rem; height: 1px;
  background: var(--indigo-300);
}
.stack3d__note--1 { --i: 0; }
.stack3d__note--2 { --i: 1; }
.stack3d__note--3 { --i: 2; }
.stack3d__note--4 { --i: 3; }
.stack3d__note b {
  font-family: var(--mono);
  font-weight: 500;
  font-size: 0.6875rem;
  color: var(--indigo-700);
  background: var(--indigo-100);
  border-radius: var(--radius-pill);
  padding: 0.1rem 0.4rem;
}

/* --- 7b. Inner page head -------------------------------------------------
   The canvas field is the home page's one loud moment. Inner pages stay quiet
   and carry their weight in type and rules instead. */
.page-head {
  border-bottom: 1px solid var(--rule);
  padding-block: clamp(3.25rem, 7vw, 6rem) clamp(2.5rem, 5vw, 4rem);
}
.page-head__grid { display: grid; gap: 1.75rem; grid-template-columns: 1fr; }
@media (min-width: 62rem) {
  .page-head__grid { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 3.5rem; align-items: end; }
}
.page-head__title { max-width: 16ch; margin-top: 1.25rem; }
.page-head__lead { max-width: 46ch; }
.page-head__meta { display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; margin-top: 1.75rem; }
.page-head__meta li { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-3); }


/* --- 7c. Inner page heads, in depth ---------------------------------------
   Each inner page opens with its own small object in the space above the
   lead, drawn with the same angles, plates and light as the home page's
   stack, so the four read as one family:
     services — the six workflow areas standing on one platform
     approach — a workflow running from trigger to outcome, and back
     about    — one senior team over scope, build and run
     contact  — your note going in, our reply coming back
   Pure CSS 3D, hidden from the reading order (the copy beside each says it
   in words), still under reduced motion, and only drawn where the head has
   two columns to give it. Below that the head is type alone, as it was.

   A box is three faces — top, front and left, the three this angle shows —
   positioned from four numbers on the element: where it stands on the plate
   and how big it is. The labels are billboards: each undoes the scene's two
   angles, in reverse, so it faces the reader whatever the box is doing. */
.iso-wrap { display: none; }
@media (min-width: 62rem) {
  .iso-wrap {
    --S: clamp(10.5rem, 13vw, 13rem);
    display: block;
    position: relative;
    height: calc(var(--S) * 1.18);
    margin-bottom: 1.25rem;
    perspective: 1600px;
    perspective-origin: 50% 25%;
    pointer-events: none;
  }
  /* The fade is an entrance, and entrances on this site only run with
     scripting on: without it the scene is simply there, like the text. */
  .js .iso-wrap { animation: stack3d-fade 0.9s ease-out 0.15s both; }
}
.iso-tilt {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transform: rotateX(var(--px, 0deg)) rotateY(var(--py, 0deg));
}
.iso {
  position: absolute;
  left: 50%; top: 62%;
  width: var(--S); height: var(--S);
  margin: calc(var(--S) / -2) 0 0 calc(var(--S) / -2);
  transform-style: preserve-3d;
  transform: rotateX(58deg) rotateZ(-42deg);
}
.iso::after {
  /* the ground shadow, in the plate's own plane */
  content: "";
  position: absolute; inset: -14%;
  transform: translateZ(-2px);
  background: radial-gradient(closest-side, rgba(76, 29, 149, 0.16), rgba(76, 29, 149, 0));
}
.iso__base {
  position: absolute; inset: 0;
  border-radius: 22px;
  transform-style: preserve-3d;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.96), rgba(237, 233, 254, 0.8));
  border: 1px solid rgba(124, 58, 237, 0.2);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.7), 0 24px 48px -26px rgba(46, 16, 101, 0.38);
}
.iso__links { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.iso__links path { fill: none; stroke: #A78BFA; stroke-width: 0.9; stroke-dasharray: 2 2.2; stroke-linecap: round; }
.iso__links .iso__loop { stroke: #34AFB6; }

/* The light that runs the floor path. */
.iso__dot {
  position: absolute;
  width: 0.55rem; height: 0.55rem;
  margin: -0.275rem 0 0 -0.275rem;
  border-radius: 50%;
  background: radial-gradient(circle, #FFFFFF 0%, #A78BFA 45%, rgba(124, 58, 237, 0) 75%);
  box-shadow: 0 0 0.7rem 0.25rem rgba(139, 92, 246, 0.55);
  transform: translateZ(1px);
  left: 50%; top: 50%;
}
.iso-wrap--services .iso__dot { animation: iso-run-services 7s linear 1.4s infinite; }
.iso-wrap--approach .iso__dot { animation: iso-run-approach 7s linear 1.4s infinite; }
.iso-wrap--about .iso__dot    { animation: iso-run-about 4.2s ease-in-out 1.4s infinite alternate; }
.iso-wrap--work .iso__dot     { animation: iso-run-work 9s linear 1.4s infinite; }
/* The about page's principles band: the work its copy describes, drawn as one
   path across the band rather than tucked into the corner of a head. */
@media (min-width: 62rem) {
  .iso-wrap--work {
    /* Fixed, not fluid: the six labels need a plate this size to keep two
       steps apart in the same row, and the band has the room from 992px. */
    --S: 21rem;
    height: calc(var(--S) * 0.98);
    max-width: 46rem;
    margin: -0.5rem auto 2rem;
  }
  .iso-wrap--work .iso { top: 58%; }
}

.iso__box {
  position: absolute;
  left: var(--x); top: var(--y);
  width: var(--w); height: var(--d);
  transform-style: preserve-3d;
  /* Moves only. An opacity animation here would make the box a flattening
     group and squash every label inside it — measured on the home stack. */
  animation:
    iso-drop 0.9s cubic-bezier(0.22, 1, 0.36, 1) calc(var(--n) * 90ms + 250ms) both,
    iso-bob 6s ease-in-out calc(var(--n) * 0.45s + 1.4s) infinite;
}
.iso__box > i { position: absolute; display: block; }
.iso__top   { inset: 0; transform: translateZ(var(--h)); border-radius: 4px; background: var(--top); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.55); }
.iso__front { left: 0; top: 100%; width: 100%; height: var(--h); transform-origin: 50% 0; transform: rotateX(90deg); background: var(--front); }
.iso__left  { left: 0; top: 0; width: var(--h); height: 100%; transform-origin: 0 50%; transform: rotateY(-90deg); background: var(--side); }
.iso__box--b { --top: linear-gradient(135deg, #F3F7FF, #CFDCFD); --front: linear-gradient(180deg, #A9C1FA, #6E8FEA); --side: linear-gradient(180deg, #7E9CF0, #3F60D8); }
.iso__box--v { --top: linear-gradient(135deg, #F8F5FF, #DDD3FE); --front: linear-gradient(180deg, #BBA4F9, #8B5CF6); --side: linear-gradient(180deg, #9B7BF4, #6326D6); }
.iso__box--t { --top: linear-gradient(135deg, #F0FAFA, #C8ECEE); --front: linear-gradient(180deg, #86D3D7, #34AFB6); --side: linear-gradient(180deg, #4DB9BF, #12798A); }
@keyframes iso-drop { from { translate: 0 0 4.5rem; } to { translate: 0 0 0; } }
@keyframes iso-bob  { 0%, 100% { translate: 0 0 0; } 50% { translate: 0 0 0.3rem; } }

.iso__tag {
  position: absolute;
  left: 50%; top: 50%;
  white-space: nowrap;
  transform: translateZ(calc(var(--h, 0rem) + 0.4rem)) rotateZ(42deg) rotateX(-58deg) translate(-50%, -100%);
  font-family: var(--display);
  font-size: 0.6875rem;
  font-weight: var(--w-sub);
  letter-spacing: 0.01em;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid var(--indigo-edge);
  border-radius: var(--radius-pill);
  padding: 0.18rem 0.55rem;
  box-shadow: 0 6px 14px -9px rgba(20, 24, 48, 0.4);
}
.iso__tag--floor { --h: 0rem; }
/* On the services platform the blocks carry the numbers of the six cards
   right below them, which is what ties the drawing to the page. */
.iso__tag--num {
  font-family: var(--mono);
  font-weight: 500;
  color: var(--indigo-700);
  background: var(--indigo-100);
  border-color: var(--indigo-edge);
  padding: 0.12rem 0.42rem;
}
.iso__tag--loop { left: 18%; top: 92%; color: #12798A; border-color: rgba(52, 175, 182, 0.45); }

/* about: one team over all three — a mast up from each box to one bar. */
.iso-wrap--about { --team: 7rem; }
/* The bar runs well clear of the three labels under it; the scene is taller
   and sits lower to give it the room. */
@media (min-width: 62rem) {
  .iso-wrap--about { height: calc(var(--S) * 1.42); }
  .iso-wrap--about .iso { top: 70%; }
}
.iso__mast {
  position: absolute;
  left: var(--cx); top: var(--cy);
  width: 2px; height: calc(var(--team) - var(--h));
  margin-left: -1px;
  transform-origin: 50% 0;
  transform: translateZ(var(--h)) rotateX(90deg);
  background: linear-gradient(0deg, rgba(124, 58, 237, 0.15), rgba(124, 58, 237, 0.6));
}
.iso__bar {
  position: absolute;
  left: 21%; top: 21%;
  width: calc(var(--S) * 0.58 * 1.4142); height: 4px;
  margin-top: -2px;
  transform-origin: 0 50%;
  transform: translateZ(var(--team)) rotateZ(45deg);
  border-radius: 4px;
  background: linear-gradient(90deg, #8B5CF6, #6D28D9);
  box-shadow: 0 0 0.9rem rgba(139, 92, 246, 0.6);
}
.iso__tag--team {
  --h: var(--team);
  color: #fff;
  background: var(--indigo-700);
  border-color: var(--indigo-700);
}

/* contact: your note on the plate, our reply floating above it. */
.iso__base--note { inset: 6% 6% 6% 6%; }
.iso__rows { position: absolute; left: 14%; right: 30%; top: 18%; bottom: 40%; display: grid; align-content: space-between; }
.iso__rows i { display: block; height: 0.42rem; border-radius: 99px; background: linear-gradient(90deg, #6E8FEA, #A9C1FA); }
.iso__rows i:nth-child(2) { width: 82%; }
.iso__rows i:nth-child(3) { width: 64%; }
.iso__rows i:nth-child(4) { width: 46%; }
.iso__base--note .iso__tag--floor { left: 26%; top: 84%; }
.iso__float {
  position: absolute;
  left: 38%; top: 22%; width: 54%; height: 54%;
  border-radius: 16px;
  transform-style: preserve-3d;
  transform: translateZ(4.2rem);
  background: linear-gradient(135deg, rgba(245, 243, 255, 0.92), rgba(196, 181, 253, 0.85));
  border: 1px solid rgba(124, 58, 237, 0.4);
  animation: iso-drop 0.9s cubic-bezier(0.22, 1, 0.36, 1) 0.5s both, iso-bob 6s ease-in-out 1.6s infinite;
}
/* The glow is a gradient on its own layer just under the card, not a
   box-shadow: a wide blurred shadow on a plane in 3D is rasterised wrong at
   2x density, and a pale slab cut across the card on every retina screen. */
.iso__float::before {
  content: "";
  position: absolute; inset: -22%;
  transform: translateZ(-1px);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(139, 92, 246, 0.28), rgba(139, 92, 246, 0));
}
.iso__check { position: absolute; inset: 0; width: 100%; height: 100%; }
.iso__check circle { fill: #6D28D9; }
.iso__check path { fill: none; stroke: #FFFFFF; stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }
.iso__check .iso__line { stroke: #8B5CF6; stroke-width: 6; }
.iso__tag--float { --h: 0.3rem; }
.iso__beam--note {
  position: absolute; left: 65%; top: 49%;
  width: 3px; height: 4.2rem; margin-left: -1.5px;
  transform-origin: 50% 0;
  transform: rotateX(90deg);
  overflow: hidden;
  border-radius: 3px;
  background: linear-gradient(0deg, rgba(124, 58, 237, 0.1), rgba(124, 58, 237, 0.45));
}
.iso__beam--note::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 0; height: 35%;
  background: linear-gradient(180deg, rgba(196, 181, 253, 0), #8B5CF6 60%, #FFFFFF);
  transform: translateY(-100%);
  animation: stack3d-pulse 2.8s cubic-bezier(0.45, 0, 0.2, 1) 1.4s infinite;
}
@keyframes iso-run-services {
  0% { left: 17%; top: 21%; }
  13.38% { left: 49%; top: 17%; }
  26.76% { left: 81%; top: 21%; }
  50% { left: 81%; top: 77%; }
  63.38% { left: 49%; top: 81%; }
  76.76% { left: 17%; top: 77%; }
  100% { left: 17%; top: 21%; }
}
@keyframes iso-run-approach {
  0% { left: 32%; top: 8%; }
  14.02% { left: 23%; top: 47%; }
  28.05% { left: 62%; top: 38%; }
  42.07% { left: 53%; top: 77%; }
  56.1% { left: 92%; top: 68%; }
  63.29% { left: 72.69%; top: 74.94%; }
  68.96% { left: 56.75%; top: 77.75%; }
  73.38% { left: 44.19%; top: 76.44%; }
  77.12% { left: 35%; top: 71%; }
  81.05% { left: 29.19%; top: 61.44%; }
  85.92% { left: 26.75%; top: 47.75%; }
  92.17% { left: 27.69%; top: 29.94%; }
  100% { left: 32%; top: 8%; }
}
@keyframes iso-run-about {
  0% { left: 21%; top: 21%; }
  50% { left: 50%; top: 50%; }
  100% { left: 79%; top: 79%; }
}

/* --- 8. Buttons ---------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.7rem;
  padding: 0.65rem 1.1rem;
  border: 1px solid var(--indigo-700);
  background: var(--indigo-700); color: #fff;
  font-family: var(--display); font-weight: var(--w-sub); font-size: var(--fs-ui);
  line-height: 1.4; letter-spacing: 0.08em; text-transform: uppercase;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color 0.18s var(--ease), border-color 0.18s var(--ease), color 0.18s var(--ease);
}
.btn:hover { background: var(--indigo-950); border-color: var(--indigo-950); }
.btn__arrow { transition: transform 0.22s var(--ease); }
.btn:hover .btn__arrow { transform: translateX(3px); }
@media (max-width: 49rem) {
  .btn { padding: 0.55rem 0.95rem; font-size: 0.75rem; letter-spacing: 0.05em; gap: 0.5rem; }
}

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn--ghost:hover { background: transparent; border-color: var(--ink); color: var(--ink); }

.btn--onink { background: #fff; border-color: #fff; color: var(--indigo-950); }
.btn--onink:hover { background: var(--indigo-50); border-color: var(--indigo-50); color: var(--indigo-700); }

.link-mono {
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-family: var(--display); font-weight: var(--w-sub); font-size: var(--fs-ui);
  line-height: 1.4; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--indigo);
  border-bottom: 1px solid var(--indigo-edge);
  padding-bottom: 2px;
  transition: border-color 0.18s var(--ease);
}
.link-mono:hover { border-color: var(--indigo); }
/* Standalone links are targets in their own right. WCAG 2.5.8 asks for 24px;
   a bare 14px line box is 22. Inline links inside a sentence are exempt and
   are left as they are. */
.link-mono, .footer__list a, .panel--link .h3 { min-height: 24px; }
.footer__list a { display: inline-flex; align-items: center; }

/* --- 9. Panels ----------------------------------------------------------- */
.grid {
  display: grid;
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  grid-template-columns: 1fr;
}
@media (min-width: 46rem) { .grid[data-cols="2"] { grid-template-columns: repeat(2, 1fr); } }
/* A 3-item grid must never pass through 2 columns — that leaves the third
   card alone on its own row with a phantom empty cell beside it. It stays
   single-column until there's room for all three at once. */
@media (min-width: 68rem) { .grid[data-cols="3"] { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 46rem) { .grid[data-cols="4"] { grid-template-columns: repeat(2, 1fr); } }
/* A lone card following a 2-up row: centred and narrower once that row is
   actually 2-up, so it reads as a deliberate capstone, not a stray leftover. */
.grid--single { margin-top: 1.5rem; }
@media (min-width: 46rem) { .grid--single { max-width: 34rem; margin-inline: auto; } }
@media (min-width: 68rem) { .grid[data-cols="4"] { grid-template-columns: repeat(4, 1fr); } }

.panel {
  background: var(--panel);
  padding: clamp(1.5rem, 2.6vw, 2.1rem);
  display: flex; flex-direction: column; gap: 0.85rem;
  position: relative;
  /* A grid item defaults to min-width:auto, so it refuses to shrink below its
     longest unbreakable string. The contact email is wider than a 320px track,
     which pushed the panel past the viewport and scrolled the page sideways. */
  min-width: 0;
}
/* Paired with the above: the address has to be allowed to break, or it simply
   overflows the panel instead of the page. */
.panel h3, .panel a { overflow-wrap: anywhere; }
/* Start, not centre. The eyebrow shares a row with the corner arrow, and
   centring dropped a one-line eyebrow 4px below a two-line one beside it. */
.panel__key { display: flex; align-items: start; justify-content: space-between; gap: 1rem; }
/* The design numbers the six workflow areas, so the eyebrow shares its line
   with the mark. The gap is the design's: 21px between the two, measured. */
/* The eyebrow stretches to the head's height and centres its own text, so
   every card's eyebrow box starts on the row's line whether its words wrap or
   not; centring the eyebrow itself moved a one-line one 8px off its
   neighbours. */
.panel__head { display: flex; align-items: stretch; gap: 1.1rem; margin: 0; min-width: 0; }
.panel__head .label { display: flex; align-items: center; min-width: 0; }
/* A rounded square, not a pill — 36 by 21 at a 6px radius in the design. */
.panel__n {
  flex: none;
  align-self: center;
  display: grid; place-items: center;
  min-width: 2.25rem; height: 1.35rem;
  padding-inline: 0.45rem;
  border-radius: 0.4rem;
  background: var(--indigo-100);
  color: var(--indigo-700);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
}
/* Measured at 80 characters a line on the home page, set at 14px: the
   longest line on the site in its smallest type, which is the pairing that
   reads worst. Every component built since — the pillars, assurances,
   controls and stages — sets body copy at 16px, so this was also the odd one
   out. The cap keeps the line inside the range a reader scans without
   losing their place, at whatever width the card ends up. */
.panel__body {
  color: var(--ink-2);
  font-size: var(--fs-body);
  line-height: 1.6;
  max-width: 52ch;
}
.panel__list { display: flex; flex-direction: column; gap: 0.4rem; margin-top: auto; padding-top: 1rem; }
/* Inside the title rather than beside it. The card is a subgrid with four
   named slots; a fifth direct child is placed into the title's row as a
   second column, and the title came out four characters to a line. */
.panel__mark { display: block; width: 2.2rem; height: 2.2rem; margin-bottom: 0.6rem; }
.panel__mark svg { width: 100%; height: 100%; display: block; }

/* The two failure modes are warnings, so they carry the rose the site keeps
   for "stop": a wash from the top corner, a rose eyebrow, and the mark on a
   lit disc with a struck badge — read as a problem before a word is read. */
.panel--failure {
  background:
    radial-gradient(110% 80% at 0% 0%, rgba(219, 39, 119, 0.06) 0%, rgba(219, 39, 119, 0) 55%),
    var(--panel);
}
.panel--failure .label { color: #BE185D; }
.panel--failure .panel__mark {
  display: grid; place-items: center;
  width: 3.2rem; height: 3.2rem; margin-bottom: 1rem;
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 28%, #FFFFFF 0%, rgba(255, 255, 255, 0) 48%),
    linear-gradient(145deg, #FBF7FF 0%, #EDE6FD 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.9),
    0 10px 20px -12px rgba(190, 24, 93, 0.45);
}
.panel--failure .panel__mark svg { width: 1.95rem; height: 1.95rem; }

/* --- Cards read off one set of lines -------------------------------------
   A card was only as aligned as its own content. Where one title wrapped to
   two lines and its neighbour's did not, the body copy beneath it started
   24px lower — on the home page that is true at every desktop width, so the
   defect was permanent rather than a corner case. A two-line eyebrow did the
   same again to the title below it.

   Subgrid hands the four slots — eyebrow, title, body, list — to the parent
   grid's rows, so each slot is as tall as the tallest in its row and every
   card in that row starts its copy on the same line. It costs nothing where
   nothing wraps, which min-height on the title could not have said.

   The 1px gaps the parent uses to draw its hairlines fall inside the panel's
   own background, so they stay invisible between slots and keep showing
   between cards. Browsers without subgrid keep the flex column above. */
@supports (grid-template-rows: subgrid) {
  .grid > .panel {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 4;
    row-gap: 0.85rem;
  }
  .grid > .panel > .label,
  .grid > .panel > .panel__head,
  .grid > .panel > .panel__key { grid-row: 1; }
  .grid > .panel > .h3,
  .grid > .panel > h3          { grid-row: 2; }
  .grid > .panel > .panel__body { grid-row: 3; }
  /* Its row is the last one and already shared across the card row, so the
     flex auto-margin that used to push it down has nothing left to do. */
  .grid > .panel > .panel__list { grid-row: 4; margin-top: 0; }
}
.panel__list li {
  font-family: var(--mono); font-size: 0.75rem;
  color: var(--ink-2);
  padding-top: 0.4rem; border-top: 1px solid var(--rule-soft);
}

/* An odd-numbered grid leaves a hole in its last row. The fifth product takes
   that whole row instead, and lays its spec line out across it. */
@media (min-width: 46rem) {
  .panel--wide { grid-column: 1 / -1; }
  .panel--wide .panel__body { max-width: 54ch; }
  .panel__list--row { flex-direction: row; flex-wrap: wrap; gap: 0 2.5rem; }
  .panel__list--row li { flex: 1 1 12rem; }
}

/* The "when to call someone else" column is a different kind of statement from
   the two beside it: a negative, and the most trust-building thing on the page.
   Four identical three-column rows down one page read as one texture, so the
   counter-column carries the wash and the eye has somewhere to land. */
.panel--counter { background: var(--indigo-wash); }
.panel--counter .panel__list li { border-top-color: var(--indigo-edge); }

/* --- The decision ladder -------------------------------------------------
   Six ways a step of work can be classified, in order of how much of it
   leaves the person. They were six identical neutral panels, which made the
   order invisible and left the reader to hold six labels in their head.
   Each rung now carries its own colour — a stripe, a tile and a label —
   so the one you are looking for can be found without reading all six.

   Scoped rather than built on .panel for the same reason the fit table is:
   .panel is the neutral card on six pages, and a coloured edge and a tile
   are not neutral. */
.ladder {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.1rem;
}
@media (min-width: 46rem)  { .ladder { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 72rem)  { .ladder { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Each rung hands its three slots to the parent's rows, so the titles and
   the bodies across a row start on one line however a label wraps. */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 46rem) {
    .ladder { grid-template-rows: repeat(3, auto auto auto); }
    .rung { display: grid; grid-template-rows: subgrid; grid-row: span 3; }
  }
  @media (min-width: 72rem) {
    .ladder { grid-template-rows: repeat(2, auto auto auto); }
  }
}

.rung {
  position: relative;
  min-width: 0;
  padding: clamp(1.25rem, 2vw, 1.6rem) clamp(1.2rem, 2vw, 1.6rem);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: 0 1px 2px rgba(13, 16, 20, 0.04), 0 10px 26px rgba(13, 16, 20, 0.05);
  overflow: hidden;
}
/* The stripe is an inset box rather than a border so it follows the radius
   instead of squaring the corners off. */
.rung::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 4px;
  background: linear-gradient(180deg, var(--rung-a), var(--rung-b));
}
/* The tint is the accent at a few per cent, washed out to the right so the
   copy sits on paper rather than on colour. */
.rung::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, var(--rung-wash), transparent 62%);
}
.rung > * { position: relative; z-index: 1; }

.rung[data-kind="keep"]      { --rung-a: #A78BFA; --rung-b: #7C3AED; --rung-ink: #6D28D9; --rung-tile: #EDE9FE; --rung-wash: rgba(124, 58, 237, 0.07); }
.rung[data-kind="augment"]   { --rung-a: #7DD3FC; --rung-b: #2563EB; --rung-ink: #1D4ED8; --rung-tile: #DBEAFE; --rung-wash: rgba(37, 99, 235, 0.07); }
.rung[data-kind="delegate"]  { --rung-a: #6EE7B7; --rung-b: #059669; --rung-ink: #047857; --rung-tile: #D1FAE5; --rung-wash: rgba(5, 150, 105, 0.07); }
.rung[data-kind="automate"]  { --rung-a: #FCD34D; --rung-b: #F97316; --rung-ink: #C2410C; --rung-tile: #FFEDD5; --rung-wash: rgba(249, 115, 22, 0.08); }
.rung[data-kind="agentify"]  { --rung-a: #67E8F9; --rung-b: #0891B2; --rung-ink: #0E7490; --rung-tile: #CFFAFE; --rung-wash: rgba(8, 145, 178, 0.07); }
.rung[data-kind="eliminate"] { --rung-a: #F9A8D4; --rung-b: #DB2777; --rung-ink: #BE185D; --rung-tile: #FCE7F3; --rung-wash: rgba(219, 39, 119, 0.07); }

.rung__key { display: flex; align-items: center; gap: 0.75rem; }
/* The same raised disc the About pillars sit on — lit from the top left,
   a bright rim, a shadow in the rung's own colour — carrying a multi-tone
   mark drawn on the site's 32-unit grid rather than a thin line glyph. */
.rung__tile {
  flex: none; width: 3.15rem; height: 3.15rem; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--rung-ink);
  background:
    radial-gradient(circle at 32% 28%, #FFFFFF 0%, rgba(255, 255, 255, 0) 48%),
    linear-gradient(145deg, color-mix(in srgb, var(--rung-tile) 45%, #FFFFFF) 0%, var(--rung-tile) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.9),
    inset 0 -5px 10px -6px color-mix(in srgb, var(--rung-b) 30%, transparent),
    0 9px 18px -11px color-mix(in srgb, var(--rung-b) 70%, transparent);
}
.rung__tile svg { width: 1.9rem; height: 1.9rem; display: block; }
.rung__label { color: var(--rung-ink); white-space: nowrap; }
/* Runs out to the card's padding, which is what stops the label reading as
   a floating word beside an icon. */
.rung__rule {
  flex: 1 1 auto; min-width: 0.75rem; height: 1px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--rung-ink) 30%, transparent), transparent);
}

.rung__title { margin-top: 1rem; }
.rung__body {
  /* The last body copy on the site still set at 14px. One reading size
     across every card, or the ladder reads as a footnote to the sections
     around it. */
  margin-top: 0.55rem;
  color: var(--ink-2); line-height: 1.6;
}

/* The eyebrow takes its rule above rather than beside it, so the two words
   can stack in the narrow first column. Compound selector on purpose:
   .label--accent sets display:flex, and which of the two wins on a plain
   class would depend on the order they happen to sit in this file. */
.label--accent.label--stacked { display: block; }
.label__rule--over {
  display: block; width: 2rem; height: 3px; margin-bottom: 0.7rem;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--indigo-700), #4F46E5);
}

/* --- The fit table -------------------------------------------------------
   Two columns that answer opposite questions, so they are built to be read
   against each other: the same rows, the same rhythm, and one difference —
   the colour of the mark at the head of every line. Green for the ones we
   want, violet for the ones that belong somewhere else.

   It carries its own card rather than reusing .panel, because .panel is a
   neutral container used on six pages and this one needs an accent edge, a
   badge, and a row that starts with an icon. Bending the shared component
   into that shape would have cost every other card that uses it. */
.band--fit { position: relative; overflow: hidden; }
/* Two soft washes and a dot field, well under the copy. Decoration: they
   carry nothing, so they are painted rather than marked up. */
.fit__wash {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(22rem 22rem at 96% -4%, rgba(124, 58, 237, 0.10), transparent 70%),
    radial-gradient(18rem 18rem at -4% 104%, rgba(16, 185, 129, 0.09), transparent 70%);
}
.fit__wash::before,
.fit__wash::after {
  content: ""; position: absolute; width: 7.5rem; height: 5rem;
  background-image: radial-gradient(currentColor 1.2px, transparent 1.2px);
  background-size: 0.75rem 0.75rem;
  color: var(--indigo-300); opacity: 0.5;
}
.fit__wash::before { top: 12%; right: 2.5%; }
.fit__wash::after  { bottom: 8%; left: 2%; color: var(--indigo-200); }
@media (max-width: 60rem) { .fit__wash::before, .fit__wash::after { display: none; } }

/* The eyebrow keeps its size and tracking and takes the brand colour, with
   the rule the board runs off the end of it. */
.label--accent { color: var(--indigo-700); display: flex; align-items: center; gap: 0.7rem; }
.label__rule {
  flex: none; width: 3.25rem; height: 3px; border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--indigo-700), #4F46E5 55%, transparent);
}
/* Only the last word takes the gradient, so the sentence still reads as one
   line of type rather than as a two-tone label. */
.h2__accent {
  font-style: normal;
  background: linear-gradient(96deg, var(--indigo-600) 10%, #6D28D9 55%, #4F46E5 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.band-head__aside--barred { border-left: 3px solid transparent; padding-left: 1.1rem;
  border-image: linear-gradient(180deg, var(--indigo-600), #4F46E5) 1; }

.fit {
  position: relative; z-index: 1;
  display: grid; gap: 1.25rem;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
@media (min-width: 60rem) {
  /* row-gap stays 0: a subgrid child inherits its parent's row gaps, so a
     gap set here for the columns would also open up between the badge, the
     title and every row inside the card. The spacing between those is the
     slots' own margins and padding. */
  .fit { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 1.5rem; row-gap: 0; }
}

.fit__card {
  position: relative; min-width: 0;
  padding: clamp(1.4rem, 2.4vw, 2rem) clamp(1.3rem, 2.4vw, 2rem);
  border-radius: var(--radius-lg);
  /* The accent edge is an inset shadow rather than a border so it follows
     the radius instead of squaring off the corners. */
  box-shadow: 0 1px 2px rgba(13, 16, 20, 0.04), 0 10px 30px rgba(13, 16, 20, 0.06);
  overflow: hidden;
}
.fit__card::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 4px;
}
.fit__card--yes { background: var(--panel); }
.fit__card--yes::before { background: linear-gradient(180deg, #10B981, #22C55E 45%, #0EA5A4); }
.fit__card--no  { background: var(--indigo-50); }
.fit__card--no::before  { background: linear-gradient(180deg, var(--indigo-600), #4F46E5); }

.fit__key { display: flex; align-items: center; gap: 0.7rem; }
.fit__badge {
  flex: none; width: 2.1rem; height: 2.1rem; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
}
.fit__badge svg { width: 1.05rem; height: 1.05rem; display: block; }
.fit__card--yes .fit__badge { background: linear-gradient(140deg, #10B981, #059669); }
.fit__card--no  .fit__badge { background: linear-gradient(140deg, var(--indigo-600), #5B21B6); }
.fit__label {
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.16em; text-transform: uppercase;
}
.fit__card--yes .fit__label { color: #047857; }
.fit__card--no  .fit__label { color: var(--indigo-700); }

.fit__title { margin-top: 0.9rem; margin-bottom: 0.55rem; }
.fit__card--no .fit__title { color: var(--indigo-950); }

.fit__list { display: flex; flex-direction: column; }
.fit__item {
  display: flex; align-items: center; gap: 0.85rem;
  padding: 0.72rem 0;
  border-bottom: 1px solid var(--rule-soft);
}
.fit__card--no .fit__item { border-bottom-color: var(--indigo-200); }
.fit__item:last-child { border-bottom: 0; padding-bottom: 0; }
.fit__tick {
  flex: none; width: 1.7rem; height: 1.7rem; border-radius: 50%;
  display: grid; place-items: center;
}
.fit__tick svg { width: 0.85rem; height: 0.85rem; display: block; }
.fit__card--yes .fit__tick { background: #DCFCE7; color: #059669; }
.fit__card--no  .fit__tick { background: var(--indigo-100); color: var(--indigo-700); }
/* Body sans, not the instrument mono: these are sentences to read, not
   readouts to scan. */
.fit__text { font-size: var(--fs-small); line-height: 1.55; color: var(--ink-2); }
.fit__card--no .fit__text { color: #3F3A63; }

/* Seven shared rows: the badge, the title, and one per line of the table.
   Handing the list items themselves to the parent's rows is the point —
   card-level subgrid alone keeps the two titles level but lets row four
   drift out of step the moment one side's sentence wraps and the other's
   does not, which it does at every width from 960 to 1200.

   This block sits after the slot rules on purpose: `display: contents` and
   `display: flex` on .fit__list have the same specificity, so the one that
   comes last wins whatever media query it is wrapped in. */
@media (min-width: 60rem) {
  @supports (grid-template-rows: subgrid) {
    .fit { grid-template-rows: auto auto repeat(5, auto); }
    .fit__card { display: grid; grid-template-rows: subgrid; grid-row: span 7; }
    /* The <ul> box goes so its children become rows of the card's subgrid.
       It keeps role="list", because display:contents drops the implicit list
       semantics in some screen readers. */
    .fit__list { display: contents; }
  }
}

/* Link panels lift on hover; the whole tile is the target. */
.panel--link { transition: background-color 0.2s var(--ease); }
.panel--link:hover { background: var(--indigo-wash); }
.panel--link:hover .panel__arrow { transform: translate(3px, -3px); }
.panel__arrow { transition: transform 0.22s var(--ease); color: var(--indigo); }
.panel__stretch::after { content: ""; position: absolute; inset: 0; }

/* --- 10. Sequence (real ordered process only) ---------------------------- */
.sequence {
  display: grid; gap: 1px;
  background: var(--rule); border: 1px solid var(--rule);
  border-radius: var(--radius-lg); overflow: hidden;
}
.step {
  background: var(--panel);
  display: grid; gap: 0.9rem;
  padding: clamp(1.5rem, 2.6vw, 2.1rem);
  grid-template-columns: auto minmax(0, 1fr);
  /* Deliberately not positioned. The rail below each disc has to hang past
     the bottom of its own card and across the rule under it; make the card a
     positioned element and the next card — later in the DOM, so painted
     above — covers that overhang with its own white ground. Unpositioned,
     the card's background stays in the layer beneath every positioned box,
     and the rail draws over it. */
}
/* The three columns live in one box so the rail is one grid item beside one
   other and stretches to the card on its own. `grid-row: 1 / -1` on the rail
   did not: with no rows declared, -1 is the end of the one-row explicit grid,
   so the rail spanned row 1 while the rest auto-placed into implicit rows
   below it, and the rail came out 38px tall in a 464px card. */
.step__cols { display: grid; gap: 0.9rem; min-width: 0; }
.step__rail {
  position: relative;
  display: flex; justify-content: center;
  width: 2.4rem;
  margin-right: 0.35rem;
}
.step__n {
  position: relative; z-index: 1;
  flex: none;
  display: grid; place-items: center;
  width: 2.4rem; height: 2.4rem;
  border-radius: 50%;
  background: var(--indigo-100);
  color: var(--indigo-700);
  font-family: var(--mono);
  font-size: 0.8125rem;
}
/* What makes the four read as one run rather than four cards: a line from
   each disc to the next, crossing the rule the sequence draws between them.
   It runs from this disc's centre to the next one's — this card's remaining
   height, its bottom padding, the rule, the next card's top padding, and half
   a disc — so the length cannot be a fixed one. The last step has nowhere
   to go. */
.step__rail::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  top: 1.2rem;
  bottom: calc(-2 * clamp(1.5rem, 2.6vw, 2.1rem) - 1.2rem - 1px);
  background: linear-gradient(180deg, var(--indigo-200), var(--indigo-100));
}
.step:last-child .step__rail::after { display: none; }
@media (min-width: 54rem) {
  .step { gap: 2rem; }
  /* `align-items: start` belongs here and not on the step: on the step it
     would stop the rail stretching, which is the whole mechanism. */
  .step__cols {
    grid-template-columns: 7rem minmax(0, 17rem) minmax(0, 1fr);
    gap: 2rem; align-items: start;
  }
  .step__rail { margin-right: 0; }
}
.step__stage { display: flex; align-items: baseline; gap: 0.5rem; }
.step__dur { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-3); }
/* The band promises each stage ends in something you can hold; this is that
   thing, named under the stage rather than left inside the paragraph. */
.step__head { display: grid; gap: 0.7rem; align-content: start; }
.step__gives {
  display: flex; align-items: center; gap: 0.55rem;
  margin: 0;
  font-size: 0.8125rem;
  color: var(--ink-2);
}
/* Where the step's title is itself the thing handed over — the contact
   page's four — the mark sits beside the title rather than under it, and
   there is no second line to write. Repeating one sentence down all four
   rows would have been four chances to say nothing. */
.step__head--inline {
  grid-auto-flow: column;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0.6rem;
}
.step__mark { flex: none; display: grid; place-items: center; width: 2rem; height: 2rem; }
.step__mark svg { width: 100%; height: 100%; display: block; }

/* --- 11. Spec table ------------------------------------------------------ */
.spec { border-top: 1px solid var(--rule); }
.spec__row {
  display: grid; gap: 0.4rem 2rem;
  padding-block: 1.15rem;
  border-bottom: 1px solid var(--rule-soft);
  grid-template-columns: 1fr;
}
@media (min-width: 46rem) { .spec__row { grid-template-columns: 13rem minmax(0, 1fr); align-items: baseline; } }
.spec__row dt.label--ink { color: var(--indigo-700); }
/* A modifier, not a change to the component: `.spec` is the key-value list
   on four pages, and only the two bands that name a set of things worth
   telling apart at a glance get marks. */
.spec--marked .spec__row dt { display: flex; align-items: center; gap: 0.6rem; }
.spec__mark { flex: none; display: block; width: 1.75rem; height: 1.75rem; }
.spec__mark svg { width: 100%; height: 100%; display: block; }
/* --- 11b. The last of the inline styles ----------------------------------
   Twenty-four style attributes were spread across the seven pages: spacing,
   two colours, and the position/overflow pair under each closing band's
   artwork. None of them was per-instance data — they were stylesheet rules
   written in the markup, where no other rule could see them and where every
   one of them is a reason `style-src` has to keep allowing 'unsafe-inline'.
   The four that remain are the artifact cards' own offsets, which are
   genuinely per-instance and are custom properties the stylesheet reads. */
.callout--spaced { margin-top: 2.5rem; }
.callout__title--spaced { margin-top: 1rem; }
.band--over-art { position: relative; overflow: hidden; }
.shell--over-art { position: relative; z-index: 2; }
.page-head__meta--spaced { margin-top: 2rem; }

/* The hero's five capabilities as glass pills, each with its mark on the
   lit disc the readout below uses. They were a line of 12px mono text that
   read as a footnote to the buttons above it. Not links: no hover, nothing
   that invites a click that goes nowhere. */
.caps { gap: 0.6rem 0.65rem; }
.caps .caps__item {
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.4rem 0.95rem 0.4rem 0.4rem;
  font-family: var(--display); font-weight: 500; font-size: 0.8125rem;
  letter-spacing: 0; color: var(--ink);
  background: rgba(255, 255, 255, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.95);
  border-radius: var(--radius-pill);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.05),
    0 8px 18px -12px rgba(76, 29, 149, 0.35);
}
.caps__mark {
  flex: none; display: grid; place-items: center;
  width: 1.95rem; height: 1.95rem; border-radius: 50%;
  background:
    radial-gradient(circle at 32% 28%, #FFFFFF 0%, rgba(255, 255, 255, 0) 48%),
    linear-gradient(145deg, #F7F4FF 0%, #E6DEFD 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.9);
}
.caps__mark svg { width: 1.2rem; height: 1.2rem; display: block; }
/* On a phone the pills stack into a short list of their own; a size down
   keeps that list about a fifth shorter. 12px is the floor. */
@media (max-width: 30rem) {
  .caps { gap: 0.45rem; }
  .caps .caps__item { gap: 0.4rem; padding: 0.28rem 0.7rem 0.28rem 0.28rem; font-size: 0.75rem; }
  .caps__mark { width: 1.6rem; height: 1.6rem; }
  .caps__mark svg { width: 1rem; height: 1rem; }
}
.label--spaced { margin-bottom: 1.25rem; }
.label--ondark { color: var(--indigo-50); }
.mono-note {
  margin-top: 1.5rem;
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--ink-3);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.spec__val { color: var(--ink-2); }
/* A spec row that carries a named phase, not just a value: the name leads,
   the sentence follows underneath it. */
.spec__name {
  display: block;
  margin-bottom: 0.35rem;
  font-family: var(--display); font-weight: 700;
  font-size: 1.05rem; line-height: 1.2; letter-spacing: -0.005em;
  color: var(--ink);
}

/* --- 12. Callout band ---------------------------------------------------- */
.callout { display: grid; gap: 2rem; align-items: end; grid-template-columns: 1fr; }
@media (min-width: 58rem) { .callout { grid-template-columns: minmax(0, 1fr) auto; } }
.callout__title { max-width: 20ch; }
.callout__note { max-width: 46ch; }
/* Copy on the deep indigo conversion band. */
.callout__lead { max-width: 44ch; margin-top: 1.25rem; color: var(--indigo-300); }
/* indigo-400, not 500: the label size sits at 3.96:1 with 500 and clears AA
   only from 400 upward on the deep ground. */
.label--ondeep { color: var(--indigo-400); }

/* --- 13. Form ------------------------------------------------------------
   One card: the fields on paper-white, and beside them a deep panel that says
   what happens after Send. It was a ruled spreadsheet of hairline cells with
   underlined inputs, which read as a back-office screen. Fields are now soft
   filled boxes with a focus ring in the brand violet — the convention people
   expect of a form, so nobody has to work out where to type. */
.enquiry {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  background: var(--panel);
  border-radius: 24px;
  overflow: hidden;
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.05),
    0 24px 48px -24px rgba(20, 24, 48, 0.22);
}
@media (min-width: 62rem) {
  .enquiry { grid-template-columns: minmax(0, 1fr) minmax(18rem, 22rem); }
}

.form {
  display: grid; gap: 1.35rem 1.25rem;
  align-content: start;
  padding: clamp(1.4rem, 3.2vw, 2.6rem);
}
@media (min-width: 46rem) { .form { grid-template-columns: repeat(2, minmax(0, 1fr)); } .field--full { grid-column: 1 / -1; } }

.field { display: block; min-width: 0; }
/* Sentence case at reading size: the tracked uppercase eyebrow is for
   section labels, and nine of them stacked made the form shout. */
.field__label {
  display: block;
  margin-bottom: 0.5rem;
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: 0.9375rem; letter-spacing: 0; text-transform: none;
  color: var(--ink);
}
/* Inline, so on a wrapped label the mark stays on the last word rather than
   dropping to a line of its own. */
.field__req { color: var(--indigo); margin-left: 0.15rem; }
.field__optional {
  display: inline-block; margin-left: 0.45rem; vertical-align: 0.1em;
  font-family: var(--body); font-weight: 500; font-size: 0.75rem;
  color: var(--ink-3);
  padding: 0.05rem 0.5rem; border-radius: var(--radius-pill);
  background: var(--paper);
}
.field__input {
  width: 100%;
  min-height: 3rem;
  padding: 0.75rem 0.95rem;
  font-size: var(--fs-body); line-height: 1.5; color: var(--ink);
  background: #FAF9FC;
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius);
  transition: border-color 0.18s var(--ease), outline-color 0.18s var(--ease), background-color 0.18s var(--ease);
}
.field__input:hover { border-color: var(--rule); background: #FFFFFF; }
/* The focus indicator is a 4px violet halo plus the border going violet, on
   every focus, not only keyboard focus — a click into a field should show
   where the caret is too. The halo is an outline rather than a box-shadow:
   Windows high-contrast mode drops shadows but keeps outlines, recoloured. */
.field__input:focus,
.field__input:focus-visible {
  background: #FFFFFF;
  border-color: var(--indigo-600);
  outline: 4px solid rgba(124, 58, 237, 0.16);
  outline-offset: 0;
}
textarea.field__input { resize: vertical; min-height: 9rem; }
select.field__input {
  appearance: none;
  cursor: pointer;
  /* Explicit pair: Windows in dark mode otherwise paints the option list
     dark-on-dark and the chosen value becomes unreadable. */
  background-color: var(--panel);
  color: var(--ink);
  padding-right: 2rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
                    linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
  background-position: right 14px top 50%, right 9px top 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
select.field__input option { background-color: var(--panel); color: var(--ink); }
.field__hint { display: block; margin-top: 0.5rem; font-size: 0.8125rem; line-height: 1.5; color: var(--ink-3); }
.field__error { display: none; margin-top: 0.5rem; font-size: 0.8125rem; font-weight: 500; line-height: 1.5; color: #B3231C; }
.field[data-invalid="true"] .field__error { display: block; }
.field[data-invalid="true"] .field__hint { display: none; }
.field[data-invalid="true"] .field__input { border-color: #B3231C; background: #FFFFFF; }
.field[data-invalid="true"] .field__input:focus { outline-color: rgba(179, 35, 28, 0.16); }

/* Spam trap. Off-screen rather than display:none, which many bots skip.
   Paired with tabindex="-1" and aria-hidden, so no person ever meets it.
   The input itself collapses to nothing: it is not a target for anyone, and
   the filled-field padding would otherwise give it a size. */
.field--trap {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}
.field--trap .field__input { width: 0; min-height: 0; padding: 0; border: 0; }

.form-actions {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.25rem;
  padding-top: 1.35rem; margin-top: 0.2rem;
  border-top: 1px solid var(--rule-soft);
}
.form-actions .btn {
  border-radius: var(--radius);
  padding: 0.8rem 1.35rem;
  box-shadow: 0 10px 22px -12px rgba(109, 40, 217, 0.65);
}
.form-actions__note { font-size: 0.8125rem; color: var(--ink-3); }
.form-status {
  flex-basis: 100%;
  font-family: var(--display); font-weight: var(--w-sub); font-size: var(--fs-small);
  color: var(--ink-2);
}
.form-status:empty { display: none; }
.form-status[data-state="error"] { color: #B3231C; }
.form-status[data-state="sent"] { color: var(--indigo); }

/* The panel beside the form. The site's deep ground, lit from one corner
   in the brand violet, carrying only promises the page already makes. */
.enquiry__aside {
  position: relative;
  display: flex; flex-direction: column; gap: 1.1rem;
  padding: clamp(1.6rem, 3vw, 2.4rem);
  color: var(--paper);
  background:
    radial-gradient(120% 70% at 100% 0%, rgba(124, 58, 237, 0.55) 0%, rgba(124, 58, 237, 0) 60%),
    radial-gradient(90% 60% at 0% 100%, rgba(76, 123, 234, 0.28) 0%, rgba(76, 123, 234, 0) 65%),
    var(--indigo-950);
}
/* indigo-300, not the 400 the deep bands use: the violet glow lifts the
   ground under this label to about #46287F, where 400 measures 4.1:1. */
.enquiry__aside .label { color: var(--indigo-300); }
.enquiry__title {
  font-family: var(--display); font-weight: var(--w-heading);
  font-size: clamp(1.25rem, 1.8vw, 1.5rem); line-height: 1.3;
  color: #FFFFFF; max-width: 18ch;
}
.enquiry__list { display: grid; gap: 1rem; margin: 0.3rem 0 0; padding: 0; list-style: none; }
.enquiry__list li { display: flex; gap: 0.8rem; align-items: flex-start; color: var(--indigo-200); font-size: var(--fs-small); line-height: 1.55; }
.enquiry__list b { display: block; color: #FFFFFF; font-family: var(--display); font-weight: var(--w-sub); font-size: 0.9375rem; margin-bottom: 0.1rem; }
.enquiry__icon {
  flex: none; display: grid; place-items: center;
  width: 2.25rem; height: 2.25rem; border-radius: 11px;
  color: var(--indigo-300);
  background: rgba(255, 255, 255, 0.07);
  box-shadow: inset 0 0 0 1px rgba(196, 181, 253, 0.22);
}
.enquiry__icon svg { width: 1.2rem; height: 1.2rem; display: block; }
.enquiry__alt {
  margin-top: auto;
  padding-top: 1.2rem;
  border-top: 1px solid rgba(196, 181, 253, 0.2);
  display: grid; gap: 0.7rem; justify-items: start;
}
.enquiry__alt p { color: var(--indigo-200); font-size: var(--fs-small); }
.enquiry__alt .btn { border-radius: var(--radius); }

/* The three ways to reach us, each with its own mark on the shared tile. */
.reach[data-kind="email"] { --rc-tile: #EDE9FE; --rc-ink: #6D28D9; }
.reach[data-kind="call"]  { --rc-tile: #DBEAFE; --rc-ink: #1D4ED8; }
.reach[data-kind="place"] { --rc-tile: #D5F2F1; --rc-ink: #0F766E; }
.reach__key { display: flex; align-items: center; gap: 0.8rem; margin: 0 0 1rem; }
.reach__tile {
  flex: none; display: grid; place-items: center;
  width: 3rem; height: 3rem; border-radius: 50%;
  background:
    radial-gradient(circle at 32% 28%, #FFFFFF 0%, rgba(255, 255, 255, 0) 48%),
    linear-gradient(145deg, color-mix(in srgb, var(--rc-tile) 45%, #FFFFFF) 0%, var(--rc-tile) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.9),
    0 9px 18px -11px color-mix(in srgb, var(--rc-ink) 70%, transparent);
}
.reach__tile svg { width: 1.8rem; height: 1.8rem; display: block; }
.reach__label { color: var(--rc-ink); }

/* --- 13b. Legal pages ------------------------------------------------------
   Privacy and terms read as documents: a contents list that stays beside
   you, and each clause as its own numbered card at a reading width. They
   were a two-column table whose titles sat stranded at the far right. */
.legal-head__mark {
  display: grid; place-items: center;
  width: 3.4rem; height: 3.4rem; margin-bottom: 1.1rem;
  border-radius: 16px;
  background:
    radial-gradient(circle at 30% 26%, #FFFFFF 0%, rgba(255, 255, 255, 0) 52%),
    linear-gradient(145deg, #F7F4FF 0%, #E6DEFD 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.9),
    0 12px 22px -12px rgba(76, 29, 149, 0.5);
}
.legal-head__mark svg { width: 2rem; height: 2rem; display: block; }

/* The privacy summary takes the contact cards' tile, in its own colours. */
.brief[data-kind="what"] { --rc-tile: #EDE9FE; --rc-ink: #6D28D9; }
.brief[data-kind="why"]  { --rc-tile: #DBEAFE; --rc-ink: #1D4ED8; }
.brief[data-kind="who"]  { --rc-tile: #D5F2F1; --rc-ink: #0F766E; }
.brief__key { display: flex; align-items: center; gap: 0.8rem; margin: 0 0 1rem; }
.brief__tile {
  flex: none; display: grid; place-items: center;
  width: 3rem; height: 3rem; border-radius: 50%;
  background:
    radial-gradient(circle at 32% 28%, #FFFFFF 0%, rgba(255, 255, 255, 0) 48%),
    linear-gradient(145deg, color-mix(in srgb, var(--rc-tile) 45%, #FFFFFF) 0%, var(--rc-tile) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.9),
    0 9px 18px -11px color-mix(in srgb, var(--rc-ink) 70%, transparent);
}
.brief__tile svg { width: 1.8rem; height: 1.8rem; display: block; }
.brief__label { color: var(--rc-ink); }

.legal {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}
@media (min-width: 62rem) {
  .legal { grid-template-columns: 15.5rem minmax(0, 1fr); gap: clamp(2rem, 4vw, 3.5rem); }
  /* Below the masthead, and with it as you read. */
  .legal__toc { position: sticky; top: 6.5rem; }
}
.legal__toc-title {
  margin: 0 0 0.75rem;
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-3);
}
.legal__toc-list { list-style: none; margin: 0; padding: 0; }
.legal__toc-link {
  display: flex; align-items: center; gap: 0.7rem;
  min-height: 44px; padding: 0.4rem 0.85rem;
  font-size: var(--fs-small); color: var(--ink-2);
  transition: color 0.18s var(--ease), background-color 0.18s var(--ease), border-color 0.18s var(--ease);
}
.legal__toc-n { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-3); }
.legal__toc-link:hover { color: var(--ink); }
.legal__toc-link[data-active="true"] { color: var(--indigo); font-weight: var(--w-sub); }
.legal__toc-link[data-active="true"] .legal__toc-n { color: var(--indigo); }

/* Wide: a ruled list down the side, the current clause marked in violet. */
@media (min-width: 62rem) {
  .legal__toc-list { border-left: 1px solid var(--rule); }
  .legal__toc-link { margin-left: -1px; border-left: 2px solid transparent; border-radius: 0 10px 10px 0; }
  .legal__toc-link:hover { background: rgba(124, 58, 237, 0.05); }
  .legal__toc-link[data-active="true"] { border-left-color: var(--indigo); background: var(--indigo-50); }
}
/* Narrow: the same list as a row of chips you can swipe through. */
@media (max-width: 61.99rem) {
  .legal__toc-list {
    display: flex; gap: 0.5rem;
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity; scrollbar-width: none;
    margin-inline: calc(var(--gutter) * -1); padding: 0.1rem var(--gutter) 0.4rem;
    /* Without this, snapping lines the first chip up with the screen edge
       rather than with the text above it. */
    scroll-padding-inline: var(--gutter);
  }
  .legal__toc-list::-webkit-scrollbar { display: none; }
  .legal__toc-list li { flex: none; scroll-snap-align: start; }
  .legal__toc-link {
    white-space: nowrap;
    background: var(--panel);
    border: 1px solid var(--rule-soft);
    border-radius: var(--radius-pill);
  }
  .legal__toc-link[data-active="true"] { border-color: var(--indigo-200); background: var(--indigo-50); }
}

.legal__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1rem; }
.legal__item {
  min-width: 0;
  padding: clamp(1.3rem, 2.6vw, 2rem);
  background: var(--panel);
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius-lg);
  box-shadow: 0 1px 2px rgba(20, 24, 48, 0.04), 0 12px 28px -20px rgba(20, 24, 48, 0.22);
}
.legal__head { display: flex; align-items: flex-start; gap: 1rem; }
.legal__n {
  flex: none; display: grid; place-items: center;
  width: 2.6rem; height: 2.6rem; border-radius: 12px;
  font-family: var(--mono); font-size: 0.875rem; font-weight: 500;
  color: var(--indigo);
  background: linear-gradient(145deg, var(--indigo-50) 0%, var(--indigo-100) 100%);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.9);
}
.legal__title { margin-top: 0.3rem; }
/* 40rem of Sora is about 75 characters a line. 70ch looked right and came
   out near 96: Sora's zero, which ch measures, is narrower than its prose. */
.legal__body { margin-top: 1rem; }
.legal__body p { max-width: 40rem; }
@media (min-width: 46rem) { .legal__body { padding-left: 3.6rem; } }
.legal__body p { color: var(--ink-2); line-height: 1.75; }
.legal__body p + p { margin-top: 0.9rem; }
.legal__body a { color: var(--indigo); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--indigo-edge); }
.legal__body a:hover { text-decoration-color: var(--indigo); }

/* --- 14. Footer ---------------------------------------------------------- */
.footer {
  background: var(--indigo-950); color: var(--paper);
  padding-block: clamp(3.5rem, 6vw, 5.5rem) 2rem;
  border-top: 1px solid rgba(255,255,255,0.08);
}
.footer .label { color: var(--indigo-400); }
.footer__grid { display: grid; gap: 2.5rem; grid-template-columns: 1fr; }
@media (min-width: 52rem) { .footer__grid { grid-template-columns: 1.4fr repeat(2, 1fr); gap: 3rem; } }
.footer__note {
  color: var(--indigo-400); margin-top: 1.25rem;
  max-width: 34ch; font-size: var(--fs-small); text-wrap: pretty;
}
.footer__addr { color: var(--indigo-400); font-size: var(--fs-small); }
.footer__list { display: flex; flex-direction: column; gap: 0.65rem; margin-top: 1.1rem; }
.footer__list a { color: var(--indigo-200); font-size: var(--fs-small); transition: color 0.18s var(--ease); }
.footer__list a:hover { color: #fff; }
.footer__base {
  margin-top: 3rem; padding-top: 1.5rem;
  border-top: 1px solid rgba(255,255,255,0.08);
  display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: space-between;
}
.footer__base .label { color: var(--indigo-400); }
.footer__legal { display: flex; flex-wrap: wrap; gap: 0.35rem 1.5rem; }
.footer__legal a {
  color: var(--indigo-200); font-size: var(--fs-small);
  /* A standalone link is a target, not running text: WCAG 2.5.8 wants 24px,
     and the bare line box is 22. */
  display: inline-flex; align-items: center; min-height: 24px;
  transition: color 0.18s var(--ease);
}
.footer__legal a:hover { color: #fff; }
.footer .brand__name { color: #fff; }
.footer .brand__sub { color: var(--indigo-400); }

/* --- 15. Motion ----------------------------------------------------------
   Motion is gated on the .js class, set synchronously in the document head,
   and only ever moves decoration. No text is withheld behind an animation. */

/* Entrance animation keyframes */
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes slideInLeft {
  from { opacity: 0; transform: translateX(-30px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes slideInRight {
  from { opacity: 0; transform: translateX(30px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes drawLine {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.6; }
}
@keyframes float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}
@keyframes gradientShift {
  0% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}
@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

/* Video background layer for sections */
.video-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  pointer-events: none;
}
.video-bg__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(13,16,20,0.55) 0%, rgba(13,16,20,0.75) 100%);
  pointer-events: none;
}
.video-bg__overlay--light {
  background: linear-gradient(180deg, rgba(236,238,241,0.85) 0%, rgba(236,238,241,0.95) 100%);
}

/* Animated gradient background (video-like motion without video file) */
.animated-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.animated-bg__gradient {
  position: absolute;
  inset: -50%;
  background: radial-gradient(ellipse at 30% 20%, rgba(36,28,201,0.08) 0%, transparent 50%),
              radial-gradient(ellipse at 70% 80%, rgba(36,28,201,0.06) 0%, transparent 50%),
              radial-gradient(ellipse at 50% 50%, rgba(142,145,178,0.04) 0%, transparent 60%);
  animation: gradientShift 20s ease-in-out infinite;
  background-size: 200% 200%;
}

/* Canvas video simulation container */
.canvas-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

/* Scroll-triggered animation classes — only when JS is active */
.js [data-animate] {
  opacity: 0;
}
.js .page-head [data-animate],
.js .hero [data-animate] {
  opacity: 1;
  animation: fadeUp 0.35s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="fade-up"] {
  animation: fadeUp 0.6s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="fade-in"] {
  animation: fadeIn 0.5s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="scale-in"] {
  animation: scaleIn 0.5s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="slide-left"] {
  animation: slideInLeft 0.6s var(--ease) forwards;
}
.js [data-animated="true"][data-animate="slide-right"] {
  animation: slideInRight 0.6s var(--ease) forwards;
}

/* Stagger delays for grid children — children animate individually when the
   parent is revealed.

   The animation is set on every child by the rule below, and the nth-child
   rules that follow only adjust *when* each one starts. That split matters:
   these delays used to carry the animation itself and stopped at the sixth
   child, so a seventh child was left at opacity 0 for good. Three places on
   this site were losing content to it — the last two stages of the 45-day
   plan, and the tail of the privacy and terms sequences. A container can now
   hold any number of children without any of them going missing. */
.js [data-stagger] > * {
  opacity: 0;
}
.js [data-animated="true"][data-stagger] > * {
  animation: fadeUp 0.5s var(--ease) forwards;
  animation-delay: 480ms;
}
.js [data-animated="true"][data-stagger] > *:nth-child(1) { animation-delay: 0ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(2) { animation-delay: 80ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(3) { animation-delay: 160ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(4) { animation-delay: 240ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(5) { animation-delay: 320ms; }
.js [data-animated="true"][data-stagger] > *:nth-child(6) { animation-delay: 400ms; }

/* Enhanced hover effects */
.panel {
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.panel:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(13,16,20,0.08);
}
.panel--link:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(36,28,201,0.08);
}

/* Button press feedback */
.btn:active {
  transform: scale(0.98);
  transition: transform 0.1s ease-out;
}
.btn:not(.btn--ghost):not(.btn--onink) {
  position: relative;
  overflow: hidden;
}
.btn:not(.btn--ghost):not(.btn--onink)::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.18), transparent);
  transform: translateX(-100%);
  transition: transform 0s;
}
.btn:not(.btn--ghost):not(.btn--onink):hover::before {
  transform: translateX(100%);
  transition: transform 0.6s ease-in-out;
}

/* Link underline animation */
.footer__list a {
  position: relative;
  display: inline-block;
}
.footer__list a::after {
  content: "";
  position: absolute;
  bottom: -2px;
  left: 0;
  width: 0;
  height: 1px;
  background: currentColor;
  transition: width 0.25s var(--ease);
}
.footer__list a:hover::after {
  width: 100%;
}

/* Hero title word reveal */
.hero__title .word {
  display: inline-block;
  opacity: 0;
  transform: translateY(12px);
  animation: fadeUp 0.5s var(--ease) forwards;
}
.js .hero__title .word:nth-child(1)  { animation-delay: 0.1s; }
.js .hero__title .word:nth-child(2)  { animation-delay: 0.18s; }
.js .hero__title .word:nth-child(3)  { animation-delay: 0.26s; }
.js .hero__title .word:nth-child(4)  { animation-delay: 0.34s; }
.js .hero__title .word:nth-child(5)  { animation-delay: 0.42s; }
.js .hero__title .word:nth-child(6)  { animation-delay: 0.50s; }
.js .hero__title .word:nth-child(7)  { animation-delay: 0.58s; }
.js .hero__title .word:nth-child(8)  { animation-delay: 0.66s; }
.js .hero__title .word:nth-child(9)  { animation-delay: 0.74s; }
.js .hero__title .word:nth-child(10) { animation-delay: 0.82s; }

/* Floating animation for decorative elements */
.float {
  animation: float 4s ease-in-out infinite;
}

/* Shimmer loading effect */
.shimmer {
  background: linear-gradient(90deg, var(--panel) 25%, var(--paper-deep) 50%, var(--panel) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  /* The welcome sequence never plays; the page arrives already assembled. */
  .js .intro { display: none; }
  .js .band-head::before { transform: none; }
  /* These must carry the .js prefix. The rule they override is
     `.js [data-animate] { opacity: 0 }` — a class plus an attribute — and a
     bare attribute selector loses to it on specificity no matter which media
     query it sits in. Without the prefix every element below the fold stays
     invisible until the scroll observer reaches it, which is the one thing
     reduced motion is supposed to prevent. */
  .js [data-animate] { opacity: 1 !important; animation: none !important; transform: none !important; }
  .js [data-stagger] > * { opacity: 1 !important; animation: none !important; transform: none !important; }
  /* !important because this block sits earlier in the file than the rule it
     overrides; without it the later declaration wins on source order. */
  .js .billboard__words span { opacity: 1 !important; transform: none !important; animation: none !important; }
  /* The hero words are staggered by an inline animation-delay, which the
     blanket duration override above cannot reach — it zeroes the duration but
     leaves each word waiting up to 0.74s. Without this the headline still
     animates in over ~1.2s for exactly the people who asked it not to. */
  .hero__title .word {
    animation: none !important;
    animation-delay: 0s !important;
    opacity: 1;
    transform: none;
  }
  .billboard__device { animation: none; }
  .type-cursor::after { animation: none; opacity: 0; }
  .animated-bg__gradient { animation: none; }
  .float { animation: none; }
}

/* --- 15b. Billboard (reference-image style) ----------------------------- */
.billboard {
  background: var(--cream);
  padding-block: clamp(5rem, 12vw, 10rem);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.billboard__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 3rem;
  align-items: center;
}
@media (min-width: 62rem) {
  .billboard__grid {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: 5rem;
  }
}
.billboard__words {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2.6rem, 7vw, 5.5rem);
  line-height: 0.98;
  letter-spacing: -0.03em;
  color: var(--ink);
}
.billboard__words span {
  display: block;
}
/* The hidden start state has to be gated on .js like every other entrance in
   this file. Setting opacity:0 unconditionally while the animation that clears
   it is .js-only left these three words permanently invisible with scripting
   off — not delayed, gone. */
.js .billboard__words span {
  opacity: 0;
  transform: translateY(24px);
  animation: fadeUp 0.7s var(--ease) forwards;
}
.js .billboard__words span:nth-child(1) { animation-delay: 0ms; }
.js .billboard__words span:nth-child(2) { animation-delay: 120ms; }
.js .billboard__words span:nth-child(3) { animation-delay: 240ms; }
.js .billboard__words span:nth-child(4) { animation-delay: 360ms; }
.billboard__device {
  position: relative;
  border-radius: var(--radius-lg);
  background: var(--ink);
  padding: 3% 3% 4%;
  box-shadow: 0 32px 80px rgba(13,16,20,0.18), 0 8px 24px rgba(13,16,20,0.08);
  transform: perspective(1200px) rotateY(-6deg) rotateX(4deg);
  transition: transform 0.6s var(--ease);
  animation: deviceFloat 6s ease-in-out infinite;
}
.billboard__device:hover {
  animation: none;
  transform: perspective(1200px) rotateY(-2deg) rotateX(2deg);
}
@keyframes deviceFloat {
  0%, 100% { transform: perspective(1200px) rotateY(-6deg) rotateX(4deg) translateY(0); }
  50% { transform: perspective(1200px) rotateY(-5deg) rotateX(3deg) translateY(-8px); }
}
.billboard__screen {
  aspect-ratio: 16 / 10;
  background: var(--paper);
  border-radius: var(--radius-sm);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 8%;
}
.billboard__screen__line {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.4rem, 3.5vw, 2.8rem);
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ink);
}
.billboard__caption {
  margin-top: 1.5rem;
  color: var(--ink-2);
  max-width: 42ch;
}

/* --- 15c. AI video / how-it-works section ------------------------------- */
.ai-video {
  position: relative;
  overflow: hidden;
  background: var(--panel);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.ai-video__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.45;
}
.ai-video__inner {
  position: relative;
  z-index: 2;
  padding-block: clamp(5rem, 10vw, 8rem);
}
.ai-video__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 3rem;
  align-items: center;
}
@media (min-width: 62rem) {
  .ai-video__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    gap: 5rem;
  }
}
.ai-video__title {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(2rem, 4.5vw, 3.6rem);
  line-height: 1.05;
  letter-spacing: -0.025em;
  color: var(--ink);
}
.ai-video__body {
  color: #384259;
  font-size: var(--fs-lead);
  line-height: 1.58;
  margin-top: 1.25rem;
  max-width: 52ch;
}
/* Only the last word takes the gradient, the way the fit heading does. */
.ai-video__accent {
  font-style: normal;
  background: linear-gradient(96deg, var(--indigo-600) 6%, #6D28D9 48%, #4F46E5 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* --- Handover artifacts --------------------------------------------------
   Four documents, each its own card rather than a quadrant of one framed
   box. The frame said they were one thing; they are four, produced at four
   different points in the loop, and the tint and the tile on each are what
   tie a card back to the step above it. Nothing here is client data. */
.artifacts { position: relative; }
.artifacts__head {
  display: flex; align-items: center; gap: 0.8rem;
  margin-bottom: 1.1rem;
}
.artifacts__note { display: block; color: var(--ink-2); }
.artifacts__tile {
  flex: none; width: 2.5rem; height: 2.5rem; border-radius: var(--radius);
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(140deg, #6366F1, var(--indigo-600) 55%, #5B21B6);
  box-shadow: 0 4px 12px rgba(99, 102, 241, 0.28);
}
.artifacts__tile svg { width: 1.25rem; height: 1.25rem; display: block; }
/* Runs out to the edge of the column, so the head reads as a rule with a
   label on it rather than a label with a stub beside it. */
.artifacts__rule {
  flex: 1 1 auto; min-width: 1.5rem; height: 1px;
  background: linear-gradient(90deg, var(--indigo-400), rgba(79, 70, 229, 0.35) 70%, transparent);
}

.artifacts__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 34rem) {
  .artifacts__grid { grid-template-columns: 1fr 1fr; }
  /* The four cards hand their slots to the same rows, so a title or a note
     that wraps on one card cannot push its neighbour's rows out of step. */
  @supports (grid-template-rows: subgrid) {
    .artifacts__grid { grid-template-rows: repeat(2, auto auto auto auto); }
    .artifact { display: grid; grid-template-rows: subgrid; grid-row: span 4; }
  }
}

.artifact {
  background: var(--panel);
  border: 1px solid var(--rule-soft);
  border-radius: var(--radius-lg);
  padding: 1.15rem 1.15rem 1.05rem;
  min-width: 0;
  box-shadow: 0 1px 2px rgba(13, 16, 20, 0.03), 0 8px 24px rgba(13, 16, 20, 0.05);
}
/* Each card carries the colour of the step that produces it. */
.artifact[data-kind="sense"]   { background: #F2F8FF; border-color: #DCE9FB; }
.artifact[data-kind="decide"]  { background: #F6F4FE; border-color: #E4DEFA; }
.artifact[data-kind="measure"] { background: #F1FDFB; border-color: #D5F0E7; }
.artifact[data-kind="improve"] { background: #EFF7FE; border-color: #D9E8F9; }

.artifact__key { display: flex; align-items: center; gap: 0.7rem; }
.artifact__tile {
  flex: none; width: 2.2rem; height: 2.2rem; border-radius: 0.65rem;
  display: grid; place-items: center; color: #fff;
}
.artifact__tile svg { width: 1.1rem; height: 1.1rem; display: block; }
.artifact[data-kind="sense"]   .artifact__tile { background: linear-gradient(140deg, #60A5FA, #4F46E5); }
.artifact[data-kind="decide"]  .artifact__tile { background: linear-gradient(140deg, #A78BFA, #6D28D9); }
.artifact[data-kind="measure"] .artifact__tile { background: linear-gradient(140deg, #34D399, #0D9488); }
.artifact[data-kind="improve"] .artifact__tile { background: linear-gradient(140deg, #7DD3FC, #4F46E5); }

.artifact__label { color: var(--indigo-700); }
.artifact[data-kind="sense"]   .artifact__label { color: #4F46E5; }
.artifact[data-kind="measure"] .artifact__label { color: #0F766E; }
.artifact[data-kind="improve"] .artifact__label { color: #2563EB; }

.artifact__title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.12rem; line-height: 1.2;
  color: var(--ink);
  margin-top: 0.75rem;
}
.artifact__rows { list-style: none; margin: 0.7rem 0 0; padding: 0; }
.artifact__rows li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 0.8rem;
  padding: 0.42rem 0;
  border-bottom: 1px solid rgba(13, 16, 20, 0.07);
  font-family: var(--mono); font-size: 0.68rem; line-height: 1.4;
  color: var(--ink-2);
}
.artifact__rows li:last-child { border-bottom: 0; }
.artifact__rows li span:last-child { color: var(--ink-3); text-align: right; }
.artifact__rows [data-ok] { color: #0F766E; letter-spacing: 0.08em; }
.artifact__rows [data-held] { color: var(--ink); font-weight: 500; letter-spacing: 0.08em; }

/* The note is the point of the card: what the document is for. It gets a
   mark of its own so it does not read as a caption. */
.artifact__note {
  display: flex; align-items: start; gap: 0.7rem;
  margin-top: 0.85rem; padding-top: 0.85rem;
  border-top: 1px solid rgba(13, 16, 20, 0.07);
  font-size: var(--fs-small); line-height: 1.5; color: var(--ink-2);
}
.artifact__noteicon { flex: none; width: 1.35rem; height: 1.35rem; margin-top: 0.05rem; }
.artifact__noteicon svg { width: 100%; height: 100%; display: block; }
.artifact[data-kind="sense"]   .artifact__noteicon { color: #4F46E5; }
.artifact[data-kind="decide"]  .artifact__noteicon { color: var(--indigo-700); }
.artifact[data-kind="measure"] .artifact__noteicon { color: #0D9488; }
.artifact[data-kind="improve"] .artifact__noteicon { color: #2563EB; }

/* Motion is JS-gated: without scripting the grid is simply resolved. */
.js .artifact {
  opacity: 0;
  transform: translate(var(--dx, 0), var(--dy, 0)) rotate(var(--rot, 0deg));
  transition: transform 0.85s var(--ease), opacity 0.55s var(--ease);
  transition-delay: calc(var(--i, 0) * 110ms);
}
.js .artifacts[data-state="in"] .artifact { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .artifact { opacity: 1; transform: none; transition: none; }
}

.ai-video__steps {
  display: grid;
  gap: 1rem;
  margin-top: 2rem;
}
.ai-video__step {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: 1rem;
  align-items: start;
}
.ai-video__step__num {
  position: relative;
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  border: 1px solid var(--indigo-300);
  border-radius: 50%;
  font-family: var(--mono);
  font-size: var(--fs-label);
  color: var(--indigo-700);
  box-shadow: 0 2px 10px rgba(109, 40, 217, 0.12);
}
/* A gradient ring, drawn by masking the middle out of a gradient disc, so
   it does not depend on knowing the colour behind it — this block sits on
   the page ground, not on a panel. The 1px border above stays as the
   fallback wherever the mask is not supported, and the disc is only painted
   where it is, or it would cover the number. */
@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .ai-video__step__num { border-color: transparent; }
  .ai-video__step__num::before {
    content: "";
    position: absolute; inset: -1px; border-radius: 50%;
    padding: 2px;
    background: linear-gradient(145deg, var(--indigo-400), #4F46E5 60%, #4338CA);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    pointer-events: none;
  }
}
.ai-video__step__text h3 {
  font-family: var(--body);
  font-weight: 600;
  font-size: var(--fs-h3);
  color: var(--ink);
}
.ai-video__step__text p {
  color: var(--ink-2);
  font-size: var(--fs-small);
  margin-top: 0.25rem;
}

/* Typing cursor */
.type-cursor::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 1em;
  background: var(--indigo);
  margin-left: 0.15em;
  vertical-align: text-bottom;
  animation: blink 1s step-end infinite;
}
@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

/* --- 16. Showcase sections (video-like animated backgrounds) ----------- */
.showcase {
  position: relative;
  overflow: hidden;
  min-height: 420px;
  display: flex;
  align-items: center;
}
.showcase__canvas,
.showcase__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  object-fit: cover;
}
.showcase__video {
  opacity: 0.35;
}
.showcase__video--dim {
  opacity: 0.22;
}
.showcase__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(90deg, rgba(13,16,20,0.82) 0%, rgba(13,16,20,0.55) 50%, rgba(13,16,20,0.75) 100%);
  pointer-events: none;
}
.showcase__overlay--light {
  background: linear-gradient(90deg, rgba(236,238,241,0.92) 0%, rgba(236,238,241,0.75) 50%, rgba(236,238,241,0.88) 100%);
}
.showcase__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  padding-block: clamp(4rem, 8vw, 7rem);
}
.showcase__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2.5rem;
  align-items: center;
}
@media (min-width: 62rem) {
  .showcase__grid {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: 4rem;
  }
  .showcase__grid--reverse {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  }
  .showcase__grid--reverse .showcase__text {
    order: 2;
  }
}
.showcase__text {
  max-width: 48ch;
}
.showcase__title {
  color: #fff;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.8rem, 3.2vw, 2.6rem);
  line-height: 1.06;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
.showcase__body {
  color: var(--indigo-50);
  font-size: var(--fs-lead);
  line-height: 1.58;
  margin-top: 1.25rem;
  text-wrap: pretty;
}
.showcase__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1.75rem;
}
.showcase__meta li {
  font-family: var(--display);
  font-weight: var(--w-sub);
  font-size: var(--fs-label);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--indigo-50);
  padding: 0.35rem 0.8rem;
  /* The chip carries its own ground. Without it the text is only as legible
     as whatever frame the canvas happens to be painting, and the lightest
     one measured left it at 4.39 — short of the 4.5 floor. A fill of its own
     puts legibility under this rule's control instead of the animation's,
     and the animation itself is untouched. */
  background: rgba(13, 16, 20, 0.45);
  /* 3:1 is the floor for a component boundary; the old blue managed 1.9. */
  border: 1px solid var(--indigo-200);
  border-radius: var(--radius-pill);
  min-height: 24px; display: inline-flex; align-items: center;
}
/* --- 16a1. Showcase sketches ---------------------------------------------
   Three claims on the services page ran as a column of type against an empty
   half-screen of animated wallpaper. Each now has the thing it claims drawn
   beside it: the state machine, the perimeter, the record. They are real
   markup rather than one flat picture, so the labels are selectable text that
   scales with the reader's type size and reaches a screen reader in order.

   The card carries its own ground for the same reason the meta chips do: the
   canvas underneath is animating, so legibility cannot be left to whichever
   frame it happens to be painting. */
.sketch {
  border-radius: 20px;
  padding: 1.35rem 1.4rem 1.5rem;
  display: grid;
  gap: 0.75rem;
  align-content: start;
}
.sketch--dark {
  background: rgba(10, 13, 19, 0.62);
  border: 1px solid rgba(196, 181, 253, 0.28);
  box-shadow: 0 20px 44px -22px rgba(0, 0, 0, 0.75);
}
.sketch--light {
  background: rgba(255, 255, 255, 0.78);
  border: 1px solid var(--indigo-edge);
  box-shadow: 0 18px 40px -22px rgba(20, 24, 48, 0.3);
}
.sketch__cap {
  margin: 0;
  font-family: var(--display);
  font-size: var(--fs-label);
  font-weight: var(--w-sub);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--indigo-200);
}

/* The chain: one step per row, joined by a short rule. */
.chain { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.chain__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.75rem;
  align-items: start;
  padding: 0.62rem 0.7rem;
  border-radius: 13px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.09);
}
.chain__mark {
  flex: none;
  display: grid; place-items: center;
  width: 1.9rem; height: 1.9rem;
  border-radius: 9px;
  background: rgba(167, 139, 250, 0.2);
  color: var(--indigo-300);
}
.chain__mark svg { width: 1.05rem; height: 1.05rem; display: block; }
.chain__row--gate .chain__mark,
.chain__row--exec .chain__mark { background: rgba(125, 211, 212, 0.18); color: #8FD7D8; }
.chain__text {
  min-width: 0;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--indigo-100);
}
.chain__text b {
  display: block;
  font-family: var(--display);
  font-weight: var(--w-sub);
  font-size: 0.875rem;
  color: #fff;
}
/* The one outcome that does not continue down the chain. */
.chain__verdict {
  grid-column: 2;
  margin-top: 0.4rem;
  justify-self: start;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.02em;
  color: #F6C9C0;
  background: rgba(239, 134, 110, 0.14);
  border: 1px solid rgba(239, 134, 110, 0.4);
  border-radius: var(--radius-pill);
  padding: 0.16rem 0.5rem;
}
.chain__link {
  width: 2px;
  height: 0.85rem;
  margin-left: calc(0.7rem + 0.95rem - 1px);
  background: linear-gradient(180deg, rgba(196, 181, 253, 0.55), rgba(196, 181, 253, 0.2));
}

/* The perimeter: what is inside the box, the one thing that crosses it, and
   what sits outside. The dashed edge is the boundary itself. */
.vpc {
  border: 1.5px dashed var(--indigo-300);
  border-radius: 16px;
  background: rgba(245, 243, 255, 0.72);
  padding: 0.9rem 1rem 1rem;
}
.vpc__cap {
  display: flex; align-items: center; gap: 0.45rem;
  margin: 0 0 0.7rem;
  font-family: var(--display);
  font-size: var(--fs-label);
  font-weight: var(--w-sub);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--indigo-700);
}
.vpc__dot {
  width: 0.45rem; height: 0.45rem; border-radius: 50%;
  background: var(--indigo-700);
}
.vpc__parts { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
@media (min-width: 26rem) { .vpc__parts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.vpc__parts li {
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--ink-2);
  background: var(--panel);
  border: 1px solid var(--indigo-edge);
  border-radius: 10px;
  padding: 0.42rem 0.55rem;
}
/* The only thing that crosses the boundary, labelled on the way out. */
.vpc__leaves {
  display: flex; align-items: center; gap: 0.5rem;
  margin: 0;
  padding-left: 0.3rem;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--ink-2);
}
/* The arrow is a box wide enough to hold its own head. Drawn as a 2px
   element with the head hanging off it, the head sat outside its own bounds
   — harmless to look at, and a thing every overflow sweep has to be told to
   ignore. */
.vpc__arrow {
  flex: none;
  position: relative;
  width: 0.9rem; height: 1.6rem;
  margin-left: 0.5rem;
}
.vpc__arrow::before {
  content: "";
  position: absolute; left: 50%; top: 0; bottom: 0.3rem;
  width: 2px; margin-left: -1px;
  background: linear-gradient(180deg, var(--indigo-300), var(--indigo-500));
}
.vpc__arrow::after {
  content: "";
  position: absolute; left: 50%; bottom: 0.2rem;
  width: 0.42rem; height: 0.42rem;
  margin-left: -0.21rem;
  border-right: 2px solid var(--indigo-500);
  border-bottom: 2px solid var(--indigo-500);
  transform: rotate(45deg);
}
.vpc__out {
  display: flex; align-items: center; gap: 0.55rem;
  margin: 0;
  padding: 0.55rem 0.7rem;
  border-radius: 12px;
  background: var(--panel);
  border: 1px solid var(--rule);
  font-size: 0.8125rem;
  color: var(--ink-2);
}
.vpc__mark {
  flex: none;
  display: grid; place-items: center;
  width: 1.8rem; height: 1.8rem;
  border-radius: 9px;
  background: var(--indigo-100);
  color: var(--indigo-700);
}
.vpc__mark svg { width: 1rem; height: 1rem; display: block; }

/* The record: four things kept with every decision, one line each. */
.record { margin: 0; display: grid; gap: 1px; background: rgba(255, 255, 255, 0.1); border-radius: 13px; overflow: hidden; }
.record__row {
  display: grid;
  gap: 0.15rem;
  padding: 0.6rem 0.75rem;
  background: rgba(255, 255, 255, 0.05);
}
@media (min-width: 26rem) {
  .record__row { grid-template-columns: minmax(0, 9.5rem) minmax(0, 1fr); gap: 0.75rem; align-items: baseline; }
}
.record__row dt {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--indigo-200);
}
.record__row dd { margin: 0; font-size: 0.8125rem; line-height: 1.4; color: var(--indigo-100); }
.record__foot {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--indigo-200);
}

.showcase--light .showcase__title { color: var(--ink); }
.showcase--light .showcase__body { color: var(--ink-2); }
.showcase--light .showcase__meta li { color: var(--indigo); border-color: var(--indigo-edge); background: rgba(255, 255, 255, 0.55); }

/* --- 16b. Welcome sequence ------------------------------------------------
   A single quiet moment before the page: the drifting field, the name, and a
   rule drawing itself. It is decoration only — removed at once without
   scripting or under prefers-reduced-motion, and it withholds no content. */
.intro { display: none; }
.js .intro {
  display: block;
  position: fixed; inset: 0; z-index: 400;
  /* The glow belongs to the centrepiece, not to the whole frame. Measured
     against the board: its corners sit at a luminance of about 2.6, so the
     wash that used to cover the page is gone and what is left is tight. */
  background:
    radial-gradient(26% 21% at 50% 65%, rgba(58, 92, 240, 0.40), transparent 70%),
    radial-gradient(60% 42% at 50% 62%, rgba(24, 36, 126, 0.30), transparent 76%),
    #030410;
  color: #fff; overflow: hidden; cursor: pointer;
  opacity: 1; visibility: visible;
  transition: opacity 0.7s var(--ease), visibility 0s linear 0.7s;
}
.js .intro[data-state="done"] { opacity: 0; visibility: hidden; pointer-events: none; }

/* Two canvases: the drifting network across the whole ground, and the orbit
   centrepiece. Both are decoration and neither carries meaning. */
.intro__field,
.intro__orbit { position: absolute; inset: 0; width: 100%; height: 100%; }
.intro__field { opacity: 0.55; }
.intro__orbit {
  opacity: 0; transform: scale(0.94);
  animation: introOrbit 1.5s var(--ease) 0.5s forwards;
}
@keyframes introOrbit { to { opacity: 1; transform: scale(1); } }

/* The lockup, top left, exactly the mark the masthead carries. */
.intro__lockup {
  position: absolute; z-index: 3;
  top: clamp(1.5rem, 4.2vh, 3.1rem); left: clamp(1.5rem, 3.8vw, 3.6rem);
  display: flex; align-items: center; gap: 0.95rem;
  opacity: 0; animation: introFade 0.7s var(--ease) 0.1s forwards;
}
.intro__lockup .brand__mark { width: 62px; height: 62px; flex: none; overflow: visible; }
.intro__lockup-text { display: flex; flex-direction: column; line-height: 1; }
.intro__lockup-name {
  font-family: var(--display); font-weight: 700;
  font-size: 1.62rem; letter-spacing: 0.07em;
  text-transform: uppercase; color: #fff;
}
.intro__lockup-sub {
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.4em;
  text-transform: uppercase; color: rgba(226, 231, 250, 0.86); margin-top: 0.52rem;
}

/* Top right, the three words the board runs as a strapline. */
.intro__meta {
  position: absolute; z-index: 3;
  top: clamp(1.9rem, 4.2vh, 3.1rem); right: clamp(1.4rem, 3.6vw, 3.4rem);
  display: flex; align-items: center; gap: 0.85rem;
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.3em;
  text-transform: uppercase; color: rgba(214, 220, 245, 0.78);
  opacity: 0; animation: introFade 0.7s var(--ease) 0.3s forwards;
}
.intro__meta i { font-style: normal; color: var(--indigo-400); opacity: 0.8; }
/* The board closes the strapline with a short rule running off the edge. */
.intro__meta::after {
  content: ""; width: clamp(2rem, 5vw, 4.5rem); height: 1px; margin-left: 0.4rem;
  background: linear-gradient(90deg, rgba(196, 181, 253, 0.75), transparent);
}

.intro__content {
  position: absolute; z-index: 3; left: 50%; top: 37.6%;
  transform: translate(-50%, -50%);
  /* Wide enough that "Sentrova Solutions" holds one line at full size, the
     way the board sets it. At 60rem it broke to three lines. */
  width: min(100%, 76rem); text-align: center; padding-inline: var(--gutter);
}

/* The eyebrow carries a rule on each side, drawn out from the word. */
.intro__eyebrow {
  display: flex; align-items: center; justify-content: center; gap: 1.1rem;
  font-family: var(--mono); font-size: clamp(0.6rem, 2.1vw, 0.98rem);
  letter-spacing: 0.34em; text-transform: uppercase; color: #F4F7FF;
  opacity: 0; animation: introFade 0.6s var(--ease) 0.2s forwards;
}
.intro__eyebrow::before,
.intro__eyebrow::after {
  content: ""; width: clamp(2rem, 7vw, 5.5rem); height: 1px;
  background: linear-gradient(90deg, transparent, rgba(167, 139, 250, 0.85));
  transform: scaleX(0); transform-origin: right center;
  animation: introRule 0.9s var(--ease) 0.45s forwards;
}
.intro__eyebrow::after {
  background: linear-gradient(90deg, rgba(167, 139, 250, 0.85), transparent);
  transform-origin: left center;
}

.intro__title {
  margin-top: 2.9rem;
  font-family: var(--display); font-weight: 800;
  font-size: clamp(2.5rem, 7.6vw, 6.2rem);
  /* Measured off the board: its two lines advance 90px at a 99px size. */
  line-height: 0.93; letter-spacing: -0.028em;
}
.intro__line { display: block; }
.intro__word {
  display: inline-block; margin-inline: 0.02em;
  /* The board's type is lit, not flat: a soft top-down sheen on the white
     words, and the brand gradient on the last one. */
  background: linear-gradient(176deg, #FFFFFF 26%, #B9C2E4 100%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  opacity: 0; transform: translateY(0.5em); filter: blur(9px);
  animation: introWord 0.75s var(--ease) forwards;
}
.intro__word--brand {
  background: linear-gradient(176deg, #C4B5FD 18%, #7C3AED 100%);
  -webkit-background-clip: text; background-clip: text;
}
.intro__line:nth-child(1) .intro__word:nth-child(1) { animation-delay: 0.45s; }
.intro__line:nth-child(1) .intro__word:nth-child(2) { animation-delay: 0.57s; }
.intro__line:nth-child(2) .intro__word:nth-child(1) { animation-delay: 0.69s; }
.intro__line:nth-child(2) .intro__word:nth-child(2) { animation-delay: 0.81s; }

.intro__sub {
  margin-top: 1.3rem;
  font-family: var(--mono); font-size: clamp(0.68rem, 1.3vw, 0.95rem);
  letter-spacing: 0.3em; text-transform: uppercase;
  color: rgba(238, 242, 255, 0.95);
  opacity: 0; animation: introFade 0.7s var(--ease) 1.15s forwards;
}

@keyframes introWord { to { opacity: 1; transform: translateY(0); filter: blur(0); } }
@keyframes introFade { to { opacity: 1; } }
@keyframes introRule { to { transform: scaleX(1); } }

@media (max-width: 40rem) {
  .intro__meta { display: none; }
  .intro__eyebrow { gap: 0.6rem; letter-spacing: 0.22em; }
  .intro__eyebrow::before,
  .intro__eyebrow::after { width: clamp(1rem, 5vw, 2.5rem); }
  .intro__lockup .brand__mark { width: 36px; height: 36px; }
  .intro__lockup-name { font-size: 0.92rem; }
  .intro__content { top: 27%; }
}

/* While the welcome sequence plays, the page behind it holds its breath:
   every entrance animation resumes from the top once it lifts. */
html[data-intro-active] :where(main, header, footer) * {
  animation-play-state: paused !important;
}

/* --- 16c. Reading progress ------------------------------------------------
   Two pixels of position feedback under the masthead. Transform only; the
   bar never animates on its own, it follows the scroll. */
.page-progress {
  position: fixed; top: 4.25rem; left: 0; right: 0; z-index: 99;
  height: 2px; background: var(--indigo);
  transform: scaleX(0); transform-origin: left center;
  pointer-events: none; opacity: 0;
}
.js .page-progress { opacity: 1; }

/* --- 16e. Figures ---------------------------------------------------------
   Technical plates: hairline frame, mono plate line, no decoration beyond
   the drawing itself. */
.figure {
  margin-top: clamp(2.25rem, 4vw, 3.25rem);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--panel);
}
.figure__img { width: 100%; height: auto; display: block; }
/* The live field sits inside the frame artwork's own drawing area (the
   plate's border and axis chrome stay static SVG; only the data lives). */
.figure__stage { position: relative; }
.figure__field-host {
  position: absolute;
  left: 4%; right: 4%; top: 18.571%; bottom: 17.143%;
  overflow: hidden;
}
.figure__field { width: 100%; height: 100%; display: block; }
.figure__cap {
  padding: 0.85rem 1.25rem;
  border-top: 1px solid var(--rule);
  color: var(--ink-3);
}

/* --- 16e2. Build-week cadence --------------------------------------------
   The week as five checkpoints on one rail, replacing a flat exported SVG
   that could not reflow, could not be read by a screen reader beyond its alt
   text, and went stale the moment the copy changed. Everything here is live
   markup on the house tokens.

   The rail is drawn by each step rather than by one absolutely positioned
   line: the segment is a flex sibling of the node, so it inherits the node's
   vertical centre for free and needs no magic offset that a type-size change
   would break. A negative margin carries it across the grid gap, and the
   last step has no segment, so the line stops at the final node instead of
   running on to the container edge. */
.cadence {
  --cadence-gap: 1.75rem;
  --cadence-node: 2.75rem;
  margin-top: clamp(2.25rem, 4vw, 3.25rem);
  padding: clamp(1.5rem, 3vw, 2.5rem);
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
}

.cadence__head {
  display: flex; flex-wrap: wrap; align-items: start;
  justify-content: space-between; gap: 1rem 1.5rem;
  padding-bottom: clamp(1.25rem, 2.4vw, 1.75rem);
  border-bottom: 1px solid var(--rule-soft);
}
.cadence__eyebrow {
  display: flex; align-items: center; gap: 0.75rem;
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--indigo-700);
}
/* The short stroke after the eyebrow, reused at the far end of the footer. */
.cadence__rule { display: block; width: 1.75rem; height: 1px; background: var(--indigo-200); flex: none; }
.cadence__heading {
  margin-top: 0.7rem;
  font-family: var(--display); font-weight: var(--w-heading);
  font-size: clamp(1.4rem, 2.4vw, 1.9rem);
  line-height: 1.2; letter-spacing: -0.01em;
  color: var(--ink); text-wrap: balance;
}
.cadence__heading-accent { color: var(--indigo-700); }
.cadence__count {
  flex: none;
  padding: 0.4rem 0.95rem;
  border: 1px solid var(--indigo-200);
  border-radius: var(--radius-pill);
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--indigo-700);
  min-height: 24px; display: inline-flex; align-items: center;
}

.cadence__steps {
  display: grid; gap: var(--cadence-gap);
  padding-block: clamp(1.75rem, 3vw, 2.5rem);
  grid-template-columns: 1fr;
  counter-reset: none;
}
.cadence__step { position: relative; min-width: 0; }

.cadence__meta { display: flex; align-items: baseline; gap: 0.6rem; }
.cadence__n {
  font-family: var(--mono); font-size: 0.6875rem;
  letter-spacing: 0.2em; color: var(--indigo-600);
  font-variant-numeric: tabular-nums;
}
.cadence__day {
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--ink);
}

.cadence__rail { display: flex; align-items: center; margin-top: 0.9rem; }
.cadence__node {
  flex: none;
  width: var(--cadence-node); height: var(--cadence-node);
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--indigo-700); color: #fff;
  /* A flat ring, not elevation: no blur, no offset. */
  box-shadow: 0 0 0 5px var(--indigo-100);
}
.cadence__node svg { width: 1.15rem; height: 1.15rem; }
.cadence__line { flex: 1; height: 1px; background: var(--indigo-300); }
.cadence__step:last-child .cadence__line { display: none; }

.cadence__text { margin-top: 1.4rem; }
.cadence__title {
  font-family: var(--display); font-weight: var(--w-heading);
  font-size: var(--fs-h3); line-height: 1.3; letter-spacing: -0.004em;
  color: var(--ink);
}
.cadence__body {
  margin-top: 0.55rem;
  color: var(--ink-2); line-height: 1.6;
  /* Uncapped, this ran to 107 characters a line once the timeline went full
     width, which is half again the length an eye tracks back reliably. */
  max-width: 52ch;
  text-wrap: pretty;
}
.cadence__tag {
  margin-top: 1rem;
  /* flex-wrap, not white-space: the label and its arrow are separate flex
     items, and flex items never wrap on white-space alone. Without this the
     pill held one line at any cost and ran 8px past the card at 320px. */
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 0.4rem;
  max-width: 100%;
  /* The longest pill sets the minimum column width for the whole five-up.
     At 0.12em and 0.8rem it measured 202px against a 201px column — over by
     a pixel at every width, because the shell caps at 78rem. Trimmed to
     190px, which leaves the row real slack. */
  padding: 0.35rem 0.7rem;
  background: var(--indigo-50);
  border-radius: var(--radius-pill);
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--indigo-700);
  min-height: 24px;
}

.cadence__foot {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 0.75rem 1.5rem;
  padding-top: clamp(1.1rem, 2.2vw, 1.5rem);
  border-top: 1px solid var(--rule-soft);
}
.cadence__foot-item {
  display: flex; align-items: center; gap: 0.7rem;
  font-family: var(--display); font-weight: var(--w-sub);
  font-size: var(--fs-label); letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-3);
}
.cadence__dot {
  flex: none; width: 0.85rem; height: 0.85rem;
  border-radius: 50%;
  border: 1px solid var(--indigo-300);
  background:
    radial-gradient(circle, var(--indigo-700) 0 38%, transparent 39%);
}

/* Below the five-up: the same five checkpoints as a vertical run, with the
   rail turned on its side so the line still carries the eye from one node to
   the next. The rail spans the whole step, so the segment reaches the node
   below it however tall the copy above runs. */
@media (max-width: 77.99rem) {
  .cadence__step {
    display: grid;
    grid-template-columns: var(--cadence-node) minmax(0, 1fr);
    column-gap: 1.1rem;
  }
  .cadence__rail {
    grid-column: 1; grid-row: 1 / -1;
    flex-direction: column; align-self: stretch;
    margin-top: 0;
  }
  .cadence__line {
    width: 1px; height: auto; flex: 1;
    margin-bottom: calc(var(--cadence-gap) * -1);
  }
  .cadence__meta { grid-column: 2; grid-row: 1; min-height: var(--cadence-node); align-items: center; }
  .cadence__text { grid-column: 2; grid-row: 2; margin-top: 0.6rem; }
}

/* 78rem is measured, not chosen, and it is where the shell stops growing.
   The widest pill is 193px. Below this the columns are narrower than that,
   so the pills quietly fold to two lines — at 1200px by a single pixel. From
   78rem the shell is capped, columns settle at 202px, and the row holds. */
@media (min-width: 78rem) {
  .cadence__steps {
    grid-template-columns: repeat(5, minmax(0, 1fr));
    column-gap: var(--cadence-gap);
    row-gap: 0;
  }
  /* Carries the segment across the gap to the next node. */
  .cadence__line { margin-right: calc(var(--cadence-gap) * -1); }

  /* Five checkpoints only read as one row if their five slots do. A step
     whose body runs to four lines would otherwise push its tag below the
     tags beside it, which is the defect the panel grids already fix this
     way. Subgrid hands the slots to the parent's rows, so each is as tall as
     the tallest in the row; the wrapper is dissolved with display:contents
     so its children can reach those rows. Spacing stays on the items rather
     than the gap, because the five steps are not evenly spaced. */
  @supports (grid-template-rows: subgrid) {
    .cadence__step {
      display: grid;
      grid-template-rows: subgrid;
      grid-row: span 5;
    }
    .cadence__text { display: contents; }
    .cadence__meta  { grid-row: 1; }
    .cadence__rail  { grid-row: 2; }
    .cadence__title { grid-row: 3; margin-top: 1.4rem; }
    .cadence__body  { grid-row: 4; }
    .cadence__tag   { grid-row: 5; align-self: start; justify-self: start; }
  }
  /* Only here does the pill need to hold one line: it is what sets the
     column width. Stacked, it has a whole row and may wrap, which is what
     keeps it inside a 320px card. */
  .cadence__tag { white-space: nowrap;
  }
}

/* --- 16e3. Structure pillars ----------------------------------------------
   The three claims on the about page's structure band. Their own component
   rather than `.panel` with modifiers: `.panel` carries a hairline border and
   a flat ground on six pages, and these want the opposite — no border, a
   lifted shadow, and an icon column standing beside the text rather than
   above it. Colours measured off the design: ground #FAFCFE, card #FFFFFF,
   the separation carried by the shadow rather than by a rule. */
#structure.band--panel { background: #FAFCFE; }

/* The design sets this band's eyebrow above the heading rather than in the
   left column, so the rule, the eyebrow and the heading share one left edge
   with the cards under them. Below 60rem `.band-head` is already a single
   column and stacks on its own. */
@media (min-width: 60rem) {
  .band-head--stacked {
    grid-template-columns: minmax(16rem, 1fr) minmax(0, 21rem);
    row-gap: 0.9rem;
  }
  .band-head--stacked > .label          { grid-column: 1; grid-row: 1; }
  .band-head--stacked .band-head__body  { grid-column: 1; grid-row: 2; }
  .band-head--stacked .band-head__aside { grid-column: 2; grid-row: 1 / span 2; }
}

.pillars {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.6rem;
}
.pillar {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.95rem;
  align-items: start;
  padding: 1.15rem 1.4rem;
  background: var(--panel);
  border-radius: 20px;
  /* Two shadows, not one: a tight contact edge so the card reads as sitting
     on the ground, and a wide soft fall so it reads as lifted off it. The
     design's ramp runs about 25px past the card, which is the second blur. */
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.05),
    0 14px 30px -12px rgba(20, 24, 48, 0.16);
}
.pillar__tile {
  display: grid;
  place-items: center;
  width: 4.2rem;
  height: 4.2rem;
  border-radius: 50%;
  /* #F1EDFE off the design, which is --indigo-100 to within a hair. */
  background: var(--indigo-100);
  color: var(--indigo-700);
}
.pillar__tile svg { width: 2.35rem; height: 2.35rem; display: block; }
/* The disc is lit from the top left, with a bright inner rim and a soft
   shadow under it, so the multi-tone marks sit on something with depth
   rather than on a flat lavender coin. */
.pillar__tile {
  background:
    radial-gradient(circle at 32% 28%, #FFFFFF 0%, rgba(255, 255, 255, 0) 46%),
    linear-gradient(145deg, #F5F2FF 0%, #E6DEFD 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.9),
    inset 0 -6px 12px -6px rgba(109, 40, 217, 0.18),
    0 10px 20px -12px rgba(76, 29, 149, 0.45);
}
.pillar__label { margin: 0 0 0.3rem; }
.pillar__title {
  font-family: var(--display);
  font-size: 1.0625rem;
  font-weight: var(--w-sub);
  line-height: 1.35;
  color: var(--ink);
  margin: 0 0 0.55rem;
}
.pillar__body { color: var(--ink-2); line-height: 1.65; margin: 0; }
.pillar__title, .pillar__body { overflow-wrap: anywhere; }

@media (min-width: 46rem) {
  .pillars { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The third card sits centred under the pair and a little wider than one
     column — 54% of the row, which is what the design measures. */
  .pillar--wide { grid-column: 1 / -1; justify-self: center; width: min(100%, 54%); }
  .pillar { padding: 1.2rem 1.45rem; }
}

/* A band left holding one claim rather than three. It has to come after the
   two-column rule above, or the lone card drops into the left column and the
   right half of the row sits empty; and it is capped rather than stretched,
   because a card run the full width of the shell reads as a banner. */
.pillars--solo { grid-template-columns: minmax(0, 1fr); justify-items: center; }
.pillars--solo .pillar { width: min(100%, 38rem); }

/* --- 16e4. The 45-day stages ---------------------------------------------
   The eight stages of the foundation build. They were rows of a `.spec`
   list divided by hairlines — the same component the measure band, the
   contact page and the services page use — so they could not be given cards
   without dragging six other places along. Ground, card, radius and the two
   shadows are the ones measured off the structure design; this is the same
   design system, so they are the same values. */
#implementation.band--panel { background: #FAFCFE; }

/* --- 16e3b. What a week goes into ----------------------------------------
   Five things the band says quietly eat a week. They were one unbroken line
   of mono type borrowed from the page-header component — five claims reading
   as one caption, with an inline margin to hold them off the paragraph under
   them. Five cards instead, each with the mark that names the act, across
   the half of the row that was empty. */
.drains {
  list-style: none; margin: 0 0 2.5rem; padding: 0;
  display: grid; gap: 0.7rem;
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
}
.drain {
  display: grid; gap: 0.6rem;
  align-content: start;
  padding: 1rem 1.05rem 1.1rem;
  background: var(--panel);
  border-radius: 16px;
  font-size: 0.9375rem;
  line-height: 1.4;
  color: var(--ink);
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.05),
    0 10px 24px -14px rgba(20, 24, 48, 0.16);
}
.drain__mark { display: block; width: 2.1rem; height: 2.1rem; }
.drain__mark svg { width: 100%; height: 100%; display: block; }

.stages { display: grid; gap: 0.7rem; margin: 0; }
.stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.55rem;
  padding: 1.05rem 1.2rem;
  background: var(--panel);
  border-radius: 16px;
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.05),
    0 10px 24px -14px rgba(20, 24, 48, 0.16);
}
.stage__days {
  justify-self: start;
  align-self: start;
  padding: 0.32rem 0.7rem;
  border-radius: var(--radius-pill);
  background: var(--indigo-100);
  color: var(--indigo-700);
  white-space: nowrap;
}
.stage__val { margin: 0; color: var(--ink-2); line-height: 1.6; }
.stage__title {
  display: block;
  font-family: var(--display);
  font-size: 1.0625rem;
  font-weight: var(--w-sub);
  line-height: 1.35;
  color: var(--ink);
  margin-bottom: 0.3rem;
}
/* The band promises a deliverable per stage. It was the last clause of each
   paragraph, where it reads as more prose; here it is a line of its own with
   the stage's mark beside it. */
.stage__gives {
  display: flex; align-items: flex-start; gap: 0.6rem;
  margin-top: 0.75rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--rule-soft);
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-2);
}
.stage__gives b {
  display: block;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--indigo-700);
}
.stage__mark { flex: none; display: grid; place-items: center; width: 1.9rem; height: 1.9rem; margin-top: 0.1rem; }
.stage__mark svg { width: 100%; height: 100%; display: block; }

@media (min-width: 48rem) {
  .stage {
    /* The pill column is fixed so all eight titles start on one left edge,
       and wide enough for the longest range before it wraps. */
    grid-template-columns: 8.5rem minmax(0, 1fr);
    gap: 0 1.5rem;
    padding: 1.1rem 1.45rem;
  }
  .stage__days { grid-column: 1; grid-row: 1; }
  .stage__val  { grid-column: 2; grid-row: 1; }
}

/* --- 16e5. Assurances -----------------------------------------------------
   Why the delivery model holds: two claims that are about ownership — no
   lock-in, and the IP is yours. Tracing and guardrails used to sit here too
   and were said again, more concretely, under Governance; they live there
   now. Each claim carries its own accent down a spine and around its mark,
   inside one tinted panel.

   Spines and tiles take the colours measured off the design. The eyebrow
   text does not: the design's orange (#FE9246) comes out at 2.2:1 on white,
   well under the AA floor this repo holds itself to. Decoration can carry
   it; words cannot. So each kind keeps a darker ink for its label, the same
   split the decision ladder uses. */
.assurances {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.9rem;
  padding: 0.9rem;
  background: #F5F8FE;
  border: 1px solid rgba(80, 110, 190, 0.10);
  border-radius: 22px;
}
@media (min-width: 52rem) {
  .assurances { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.05rem; padding: 1.05rem; }
}
.assurance {
  position: relative;
  overflow: hidden;
  padding: 1.1rem 1.3rem 1.2rem 1.45rem;
  background: var(--panel);
  border-radius: 14px;
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.04),
    0 8px 20px -12px rgba(20, 24, 48, 0.16);
}
/* The spine is clipped to the card's radius by the overflow above, so it
   curves with the corner instead of squaring it off. */
.assurance::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 6px;
  background: linear-gradient(180deg, var(--as-a), var(--as-b));
}
.assurance[data-kind="infrastructure"] { --as-a: #9B7BFF; --as-b: #7542FA; --as-ink: #6D28D9; --as-tile: #E9E5FD; }
.assurance[data-kind="ownership"]      { --as-a: #FFB37A; --as-b: #FE9246; --as-ink: #C2410C; --as-tile: #FEEBE2; }

.assurance__key { display: flex; align-items: center; gap: 0.75rem; margin: 0 0 0.7rem; }
.assurance__tile {
  flex: none;
  display: grid; place-items: center;
  width: 2.55rem; height: 2.55rem;
  border-radius: 50%;
  background: var(--as-tile);
  color: var(--as-ink);
}
.assurance__tile svg { width: 1.6rem; height: 1.6rem; display: block; }
.assurance__tile {
  width: 2.9rem; height: 2.9rem;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.85), 0 8px 16px -10px rgba(30, 27, 46, 0.35);
}
.assurance__label { color: var(--as-ink); }
.assurance__title {
  font-family: var(--display);
  font-size: 1.0625rem;
  font-weight: var(--w-sub);
  line-height: 1.35;
  color: var(--ink);
  margin: 0 0 0.45rem;
}
.assurance__body { color: var(--ink-2); line-height: 1.6; margin: 0; }
.assurance__title, .assurance__body { overflow-wrap: anywhere; }

/* A hairline beside the aside, not the solid bar the 45-day band uses. Only
   once the aside has a column of its own — stacked, a left rule reads as a
   quote. */
@media (min-width: 60rem) {
  .band-head__aside--ruled { border-left: 1px solid var(--rule-soft); padding-left: 1.4rem; }
}

/* Cooler and a shade deeper than the near-white band that follows it, so the
   two do not run together. */
/* The Approach band only: terms.html has a clause with the same anchor. */
#ownership.band { background: #F4F6FB; }

/* --- 16e6. Governance controls -------------------------------------------
   The three controls every workflow is built on. Ground, spines, tiles and
   the pill fills are sampled off the design; the inks are chosen against
   them, because a colour that reads on a spine does not always read as a
   word. The design sets the step numbers at #999AAA, which measures 2.64:1
   on the card — quiet is the point, but not at that price, so they take
   --ink-3 at 5.27:1 and stay quiet anyway. */
#governance.band--panel { background: #F7F7F5; }

.controls { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.1rem; }
@media (min-width: 56rem) {
  .controls { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.35rem; }
}
.control {
  position: relative;
  overflow: hidden;
  /* Four rows with the body on 1fr, so the three feet line up across the
     row however long each body runs. */
  display: grid;
  grid-template-rows: auto auto 1fr auto;
  padding: 1.15rem 1.3rem 1.2rem 1.5rem;
  background: linear-gradient(160deg, #F5F8FB, #FAFAFC 55%, #F9F9FB);
  border: 1px solid rgba(80, 110, 190, 0.10);
  border-radius: 16px;
  box-shadow:
    0 1px 2px rgba(20, 24, 48, 0.04),
    0 10px 26px -14px rgba(20, 24, 48, 0.18);
}
.control::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 6px;
  background: linear-gradient(180deg, var(--ctl-a), var(--ctl-b));
}
.control[data-kind="review"]  { --ctl-a: #7C5CF5; --ctl-b: #4426AE; --ctl-ink: #6D28D9; --ctl-tile: #E1DDF9; }
.control[data-kind="state"]   { --ctl-a: #4FA3E8; --ctl-b: #2271CE; --ctl-ink: #1D4ED8; --ctl-tile: #DAE8FA; }
.control[data-kind="logging"] { --ctl-a: #A78BFA; --ctl-b: #6D28D9; --ctl-ink: #6D28D9; --ctl-tile: #E2DDF9; }

.control__key { display: flex; align-items: center; gap: 0.8rem; margin: 0 0 0.85rem; padding-right: 2.4rem; }
.control__tile {
  flex: none;
  display: grid; place-items: center;
  width: 2.6rem; height: 2.6rem;
  border-radius: 12px;
  background: var(--ctl-tile);
  color: var(--ctl-ink);
}
.control__tile svg { width: 1.65rem; height: 1.65rem; display: block; }
.control__tile {
  width: 2.9rem; height: 2.9rem;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.85), 0 8px 16px -10px rgba(30, 27, 46, 0.35);
}
.control__label { color: var(--ctl-ink); }
.control__n {
  position: absolute; top: 1.15rem; right: 1.3rem;
  font-family: var(--mono); font-size: var(--fs-small);
  color: var(--ink-3); letter-spacing: 0.04em;
}
.control__title {
  font-family: var(--display); font-size: 1.0625rem; font-weight: var(--w-sub);
  line-height: 1.35; color: var(--ink); margin: 0 0 0.5rem;
}
.control__body { color: var(--ink-2); line-height: 1.6; margin: 0 0 1.15rem; }
.control__foot { display: flex; align-items: center; gap: 0.75rem; margin: 0; }
.control__disc {
  flex: none;
  display: grid; place-items: center;
  width: 2.1rem; height: 2.1rem;
  border-radius: 50%;
  background: #EAE9FA;
  color: var(--ctl-ink);
}
.control__disc svg { width: 0.95rem; height: 0.95rem; display: block; }
.control__out { color: var(--ctl-ink); }
.control__title, .control__body { overflow-wrap: anywhere; }

/* --- 16e7. Workflow flows -------------------------------------------------
   Each workflow area shows its own path across the card: a disc per step, an
   arrow between them, and the step named underneath. Colours are sampled off
   the design — disc #F6F3FE, glyph #7111FE, arrow #8D64CF, title #0A1235 —
   and the disc is sized as a share of the row so it holds its proportion to
   the card at every column count rather than being pinned to one width. */
.flow {
  /* A plain length first, because everything below depends on it. A
     percentage here resolves against whichever box the disc sits in — one
     step out of four — so it cannot hold the design's proportion; container
     units measure the row itself and do. But a browser without container
     queries drops `cqw` as invalid, and then `width: var(--disc)` falls back
     to `auto`: the discs came out 69 by 33, squashed ovals with the icon
     shrunk inside them. So the fixed size is the base and the proportional
     one is layered on only where it works. */
  --disc: 3.4rem;
  display: flex;
  align-items: flex-start;
  gap: 0.4rem;
  margin-top: 1.35rem;
}
.flow__step {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
}
.flow__disc {
  flex: none;
  width: var(--disc);
  height: var(--disc);
  display: grid;
  place-items: center;
  border-radius: 50%;
  /* Both sampled off the design: the ground is #EEEEFE, and the glyph runs
     30px across a 57px disc, which is 53% of it — not the 48% it had. */
  background: #EEEEFE;
}
.flow__disc svg { width: 53%; height: 53%; display: block; }
.flow__label {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.3;
  color: var(--ink-2);
  text-align: center;
  text-wrap: balance;
}
/* The arrow sits on the discs' centre line, not the column's — the labels
   below are different heights and would drag it off. */
.flow__arrow {
  flex: none;
  width: 0.85rem;
  height: 0.85rem;
  margin-top: calc(var(--disc) / 2 - 0.425rem);
  color: #8D64CF;
}
.flow__arrow svg { width: 100%; height: 100%; display: block; }

@supports (container-type: inline-size) {
  .flow { container-type: inline-size; --disc: min(4.4rem, 19.8cqw); }
}

/* The integrations card is not a line: things come in, meet in one place and
   go out. Two columns of chips either side of a hub. */
.hub {
  /* Flex rather than fixed columns, and each side given a basis wide enough
     for its longest chip. A one-word chip like "Spreadsheet" cannot wrap, so
     a layout that shaves the sides to fit cuts the word instead; this one
     wraps the whole arrangement — in, hub, out — the moment the three parts
     stop fitting side by side. */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  /* The three parts came to 331.6px inside a 331px card and the whole
     arrangement wrapped over half a pixel. The gap and the hub's caption
     give it room to stay on one line with a little to spare. */
  gap: 0.4rem;
  margin-top: 1.35rem;
}
/* Sized to their chips, not to a basis. A fixed basis of 7.6rem put the
   three parts 3px over the card and the whole arrangement wrapped —
   which read as in, hub, out stacked down the card instead of across
   it, and hid the fact that a chip would otherwise have been cut. */
.hub__side { flex: 0 1 auto; display: flex; flex-direction: column; gap: 0.45rem; min-width: 0; }
.hub__chip {
  display: flex; align-items: center; gap: 0.4rem;
  padding: 0.28rem 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--panel);
  /* The design lifts the chips off the card with a shadow instead of drawing
     a line around them, which is what keeps five of them from reading as a
     grid of boxes. */
  box-shadow: 0 1px 2px rgba(30, 27, 46, 0.05), 0 4px 12px rgba(30, 27, 46, 0.06);
  font-size: 0.75rem;
  color: var(--ink-2);
  min-width: 0;
}
/* Every glyph in the design sits on a rounded tile tinted to its own family:
   violet for what is ours to drive, blue for a system of record. */
.hub__tile {
  flex: none;
  display: grid; place-items: center;
  width: 1.3rem; height: 1.3rem;
  border-radius: 0.42rem;
}
.hub__tile--v { background: #E9E2FD; color: #6B2BDD; }
.hub__tile--b { background: #DCE7FD; color: #2F52D8; }
.hub__chip svg { flex: none; width: 0.82rem; height: 0.82rem; display: block; }
.hub__chip span { min-width: 0; }
.hub__core { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 0.5rem; }
.hub__disc {
  /* A fixed size, not a percentage: the core column is sized by its own
     content, so a percentage of it resolved to nothing and the disc sat on
     the floor of its clamp at every width. */
  width: 3rem;
  height: 3rem;
  display: grid; place-items: center;
  border-radius: 50%;
  /* The flow discs are cut circles; this one is not. In the design the core
     sits on a blob that fades out, which is what separates the meeting point
     from the steps that lead to it. */
  background: radial-gradient(circle at 50% 50%, #EDE8FE 0%, #EDE8FE 52%, rgba(237, 232, 254, 0) 78%);
}
.hub__disc svg { width: 58%; height: 58%; display: block; }
/* Wraps rather than setting the centre column's width: unwrapped it took
   enough room to push the chips either side into an ellipsis. */
.hub__label { margin: 0; max-width: 3.6rem; font-size: 0.75rem; line-height: 1.3; color: var(--ink-2); text-align: center; }

@media (max-width: 30rem) {
  /* Below this the discs are too small to read the glyph in. The path runs
     down the card instead, which keeps every step and its name. */
  .flow { flex-direction: column; align-items: stretch; gap: 0.5rem; --disc: 2.5rem; container-type: normal; }
  .flow__step { flex-direction: row; align-items: center; gap: 0.7rem; }
  .flow__label { text-align: left; }
  .flow__arrow { margin: 0 0 0 calc(var(--disc) / 2 - 0.425rem); transform: rotate(90deg); }
  .hub__chip { white-space: normal; }
}

/* --- 16e8. Delivery modes ------------------------------------------------
   Five ways to start, each on its own card: the step's number, its name, a
   mark, and what it is for. Ground #F8F8F8, card #FCFBFC, badge #EDE9FE and
   art panel #F2F0FE are sampled off the design; the badge colour turns out
   to be --indigo-100 to within a hair, so it uses the token.

   The design's marks are three-dimensional renders. These are the line marks
   the rest of this site uses — the same shapes, drawn the way every other
   icon here is drawn, rather than a poor imitation of a render. */
#engagements.band--tint { background: #F8F8F8; }

.modes {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.1rem;
}
@media (min-width: 46rem) { .modes { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 72rem) { .modes { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem; } }

.mode {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: 1.15rem 1.25rem 1.3rem;
  background: #FCFBFC;
  border: 1px solid rgba(96, 74, 180, 0.12);
  border-radius: 16px;
}
.mode__head { display: flex; align-items: center; gap: 0.7rem; margin: 0; }
.mode__n {
  flex: none;
  display: grid; place-items: center;
  min-width: 2.1rem; height: 1.7rem;
  padding-inline: 0.5rem;
  border-radius: var(--radius-pill);
  background: var(--indigo-100);
  color: var(--indigo-700);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
}
.mode__label { color: var(--indigo-700); }

/* Mark on the left, what it is for on the right, a hairline between. */
.mode__body {
  display: grid;
  grid-template-columns: auto 1px minmax(0, 1fr);
  gap: 0.95rem;
  align-items: start;
}
.mode__art {
  /* The design sets the drawing on a soft lavender blob, not inside a hard
     panel — the illustration carries its own colour and a box around it
     would fence it in. The blob is the same tint the panel was. */
  flex: none;
  display: grid; place-items: center;
  width: 5.4rem; height: 5.4rem;
  background: radial-gradient(circle at 50% 50%, #F2F0FE 0%, #F2F0FE 58%, rgba(242, 240, 254, 0) 72%);
}
/* Drawn at its own size: the illustrations are multi-colour and lose their
   detail if they are scaled down the way a single-stroke icon can be. */
.mode__art svg { width: 100%; height: 100%; display: block; }
.mode__rule { background: rgba(96, 74, 180, 0.14); align-self: stretch; }
.mode__text { margin: 0; color: var(--ink-2); line-height: 1.55; font-size: 0.875rem; }
.mode__text b { color: var(--ink); font-weight: var(--w-sub); }

/* The link out of the section, under a rule, as the design sets it. */
.modes__more {
  display: flex; align-items: center; gap: 0.9rem;
  margin-top: 1.8rem;
}
.modes__more::before {
  content: "";
  flex: none;
  width: 2.6rem; height: 3px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, var(--indigo-700), #4F46E5);
}

@media (max-width: 28rem) {
  /* The mark and the text stop fitting beside each other before the card
     does; stacking keeps both at a readable size. */
  .mode__body { grid-template-columns: minmax(0, 1fr); gap: 0.8rem; }
  .mode__rule { display: none; }
}

/* --- 16f. Small motion courtesies ---------------------------------------- */
/* Nav links draw their own underline on the way past. */
.nav__link::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: var(--indigo);
  transform: scaleX(0); transform-origin: left center;
  transition: transform 0.25s var(--ease);
}
.nav__link:hover::after { transform: scaleX(1); }
.nav__link[aria-current="page"]::after {
  height: 2px; background: var(--indigo);
  transform: scaleX(1); transition: none;
}
/* The mark answers the pointer with a half-unit lift, and the three nodes
   take a turn around the ring — the board's orbit effect.

   It runs on hover and focus rather than always. The masthead is sticky, so
   an orbit that started on its own would be in the corner of the eye for the
   whole session, and WCAG 2.2.2 asks for a way to stop any motion that runs
   past five seconds without being asked for. One turn on approach reads as
   alive; a permanent one reads as a distraction. */
.brand__mark { transition: transform 0.25s var(--ease); }
.brand:hover .brand__mark,
.brand:focus-visible .brand__mark { transform: translateY(-1px); }

/* view-box, not fill-box: the three nodes sit at 184deg, 15deg and 90deg, so
   the box that bounds them is off-centre and rotating about it would swing
   them off the orbit instead of around it. The viewBox centre is the centre
   of the circle they ride. */
.brand__mark [data-orbit] {
  transform-box: view-box;
  transform-origin: 50% 50%;
}
.brand:hover .brand__mark [data-orbit],
.brand:focus-visible .brand__mark [data-orbit] {
  animation: markOrbit 3.2s cubic-bezier(0.62, 0, 0.38, 1) 1;
}
@keyframes markOrbit { to { transform: rotate(360deg); } }

/* The nodes also warm very slightly, which is the board's "subtle glow" at
   a size where an actual blur would only muddy a 4px dot. */
.brand__mark [data-node-a] path,
.brand__mark [data-node-b] path,
.brand__mark [data-node-c] path { transition: opacity 0.25s var(--ease); }
.brand:hover .brand__mark [data-node-b] path { opacity: 0.85; }

@media (prefers-reduced-motion: reduce) {
  .brand:hover .brand__mark [data-orbit],
  .brand:focus-visible .brand__mark [data-orbit] { animation: none; }
}

/* --- 17. Print ----------------------------------------------------------- */
@media print {
  .masthead, .rail, .hero__field, .footer, .showcase__canvas, .showcase__overlay,
  .intro, .page-progress, .canvas-video { display: none; }
  .showcase { min-height: auto; background: #fff !important; }
  body { background: #fff; }
}

/* --- 17. Lean, light and signal --------------------------------------------
   Three small motions, all of them off under reduced motion and none of them
   carrying meaning the page does not already say in words.

   Lean: the lifted cards tip a few degrees toward a mouse, with a soft light
   under the pointer. The entrance animation holds `transform` with fill-mode
   forwards, so the lean is the separate `rotate` property — it composes with
   the entrance rather than being overridden by it. The light sits beneath the
   card's content (the card is its own stacking context), so it never washes
   over the type. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .pillars, .drains, .stages, .modes, .showcase__grid { perspective: 1100px; }
  .pillar, .drain, .stage, .mode, .sketch {
    position: relative;
    isolation: isolate;
    rotate: var(--tilt-x, 0) var(--tilt-y, 0) 0 var(--tilt-a, 0deg);
    transition: rotate 0.45s cubic-bezier(0.22, 1, 0.36, 1), box-shadow 0.35s ease;
  }
  .pillar::before, .drain::before, .stage::before, .mode::before, .sketch::before {
    content: "";
    position: absolute; inset: 0;
    z-index: -1;
    border-radius: inherit;
    background: radial-gradient(circle at var(--sheen-x, 50%) var(--sheen-y, 50%),
      rgba(167, 139, 250, 0.18), rgba(167, 139, 250, 0) 58%);
    opacity: 0;
    transition: opacity 0.35s ease;
    pointer-events: none;
  }
  .pillar[data-tilt]::before, .drain[data-tilt]::before, .stage[data-tilt]::before,
  .mode[data-tilt]::before, .sketch[data-tilt]::before { opacity: 1; }
  .pillar[data-tilt], .drain[data-tilt], .stage[data-tilt], .mode[data-tilt] {
    box-shadow:
      0 1px 2px rgba(20, 24, 48, 0.05),
      0 22px 40px -18px rgba(46, 16, 101, 0.28);
  }
}

/* Signal: a light that runs down the rail from the first stage to the last,
   one connector after another, then rests — and down the guardrails chain,
   from the model's proposal to the code that writes. It starts once the
   sequence has entered the screen, so it is never running out of sight. The
   resting position of the light is off the end of the line, so nothing shows
   before the first run. */
@media (prefers-reduced-motion: no-preference) {
  .step__rail::after {
    background:
      linear-gradient(180deg, rgba(139, 92, 246, 0) 0%, #8B5CF6 50%, rgba(139, 92, 246, 0) 100%) 0 -100% / 100% 45% no-repeat,
      linear-gradient(180deg, var(--indigo-200), var(--indigo-100));
  }
  .sequence[data-animated="true"] .step__rail::after {
    animation: signal-run 4.8s ease-in-out 0.6s infinite;
  }
  .sequence[data-animated="true"] .step:nth-child(2) .step__rail::after { animation-delay: 1.8s; }
  .sequence[data-animated="true"] .step:nth-child(3) .step__rail::after { animation-delay: 3s; }

  .chain__link {
    background:
      linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #E9DDFF 50%, rgba(255, 255, 255, 0) 100%) 0 -100% / 100% 60% no-repeat,
      linear-gradient(180deg, rgba(196, 181, 253, 0.55), rgba(196, 181, 253, 0.2));
    animation: signal-run 3.6s ease-in-out 1s infinite;
  }
  .chain__link:nth-of-type(4) { animation-delay: 1.9s; }
  .chain__link:nth-of-type(6) { animation-delay: 2.8s; }
}
@keyframes signal-run {
  0%        { background-position: 0 -100%, 0 0; }
  25%, 100% { background-position: 0 200%, 0 0; }
}

@keyframes iso-run-work {
  0% { left: 46%; top: 6%; }
  20% { left: 15.6%; top: 55.6%; }
  40% { left: 65.2%; top: 25.2%; }
  60% { left: 34.8%; top: 74.8%; }
  80% { left: 84.4%; top: 44.4%; }
  100% { left: 54%; top: 94%; }
}

/* --- 18. A phone in one hand --------------------------------------------
   Measured at 360 and 390 wide: the main buttons were 36px tall, the form
   fields 31px, footer links 24px — all under the 44–48px a thumb needs, and
   the size Google's mobile checks look for. A handful of labels were drawn
   at 10.9–11px. Desktop keeps its compact buttons; this is the phone only. */
@media (max-width: 49rem) {
  .btn { min-height: 48px; justify-content: center; }
  .form .btn[type="submit"] { width: 100%; justify-content: center; }
  .form-actions__note { width: 100%; text-align: center; }
  textarea.field__input { min-height: 8rem; }
  .link-mono { display: inline-flex; align-items: center; min-height: 44px; }
  .footer__list { gap: 0; }
  .footer__list a, .footer__legal a { min-height: 44px; display: inline-flex; align-items: center; }
  .chain__verdict, .record__row dt, .stage__gives b, .cadence__n, .field__hint { font-size: 0.75rem; }
  .artifact__rows span { font-size: 0.75rem; }
  /* Links that are a card's whole heading are targets, not words in a
     sentence, so they get the thumb's height too. */
  .panel .h3 a { display: inline-block; padding-block: 0.65rem; }
}
