/* ==========================================================================
   Itzal — page styles
   Requires brand/tokens.css, which must be linked first. No colour, font,
   radius or rhythm value is allowed to appear below: everything resolves to
   a token. That rule is what keeps the brand kit honest.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Base
   -------------------------------------------------------------------------- */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  background-image: var(--grain);
  color: var(--ink);
  font: 400 1rem/1.62 var(--body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.06;
  letter-spacing: -0.028em;
  text-wrap: balance;
}

p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

a { color: inherit; }

strong { font-weight: 600; }

code, pre, .mono { font-family: var(--mono); }

:focus-visible {
  outline: 2px solid var(--seal);
  outline-offset: 3px;
}

::selection { background: var(--seal-bg); color: var(--ink); }

hr { border: 0; border-top: 1px solid var(--line); margin: 0; }

.skip {
  position: absolute;
  left: -9999px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--ink);
  padding: .7rem 1rem;
  z-index: 100;
  font: 500 .75rem/1 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
}
.skip:focus { left: .75rem; top: .75rem; }

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

/* --------------------------------------------------------------------------
   2. Layout
   -------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gut);
}

section { position: relative; }
section > .wrap { padding-block: var(--sec); }

.rule-top { border-top: 1px solid var(--line); }

.measure { max-width: 60ch; }

/* --------------------------------------------------------------------------
   3. Type roles
   -------------------------------------------------------------------------- */

/* The one oversized voice on a page. Never two on the same screen. */
.display {
  font-size: clamp(2.6rem, 1.3rem + 5.4vw, 5.25rem);
  line-height: 0.95;
  letter-spacing: -0.038em;
  max-width: 22ch;
}

.h2 {
  font-size: clamp(1.85rem, 1.25rem + 2.4vw, 2.9rem);
  line-height: 1.02;
  letter-spacing: -0.032em;
  max-width: 18ch;
}

.h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.016em;
  line-height: 1.25;
}

.lede {
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.1875rem);
  line-height: 1.55;
  max-width: 48ch;
}

.sub { color: var(--slate); }

/* The mono micro-label that names a section. Leading rule, then the word. */
.label {
  display: flex;
  align-items: center;
  gap: .6rem;
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--seal);
  margin: 0 0 1.35rem;
}
.label::before {
  content: "";
  width: 18px;
  height: 1px;
  background: currentColor;
  flex: none;
}

.label-plain {
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--slate);
  margin: 0 0 1rem;
}

/* --------------------------------------------------------------------------
   4. Controls — sharp, mono, letterspaced. No pills, no gradients.
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  font: 500 .75rem/1 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  text-decoration: none;
  padding: .95rem 1.35rem;
  border: 1px solid var(--ink);
  border-radius: var(--r);
  background: transparent;
  color: var(--ink);
  transition: background .13s ease, color .13s ease, border-color .13s ease;
  white-space: nowrap;
  cursor: pointer;
}
.btn:hover { background: var(--ink); color: var(--paper); }
.btn .arrow { transition: transform var(--t1) var(--ease); }
.btn:hover .arrow { transform: translateX(3px); }

.btn-solid {
  background: var(--ink);
  color: var(--paper);
}
.btn-solid:hover { background: var(--seal); border-color: var(--seal); color: var(--band-ink); }

.btn-sm { padding: .6rem .9rem; font-size: .6875rem; }

/* On the band, controls invert to the band's own ink. */
.band .btn {
  border-color: var(--band-ink);
  color: var(--band-ink);
}
.band .btn:hover { background: var(--band-ink); color: var(--band); }
.band .btn-solid { background: var(--band-ink); color: var(--band); }
.band .btn-solid:hover { background: transparent; color: var(--band-ink); }

.actions { display: flex; flex-wrap: wrap; gap: .75rem; align-items: center; }

.arrow { font-size: 1em; line-height: 1; }

/* Inline text link — underline is the affordance, colour is the emphasis. */
.link {
  color: var(--seal);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 1px;
}
.link:hover { color: var(--ink); }
.band .link { color: var(--band-ink); }

/* --------------------------------------------------------------------------
   5. The band — full-bleed brand block
   -------------------------------------------------------------------------- */

.band {
  background: var(--band);
  background-image: var(--grain-band), var(--band-grad);
  color: var(--band-ink);
}
.band .sub { color: var(--band-dim); }
.band .label { color: var(--band-dim); }

/* --------------------------------------------------------------------------
   6. Masthead
   -------------------------------------------------------------------------- */

.masthead > .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 clamp(1rem, 3vw, 2.75rem);
  padding-block: 1.35rem;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  text-decoration: none;
  flex: none;
}
/* A filled square with its lower half masked out. Itzal is Basque for shadow;
   the mark is the redaction itself, not an abstract swoosh. */
.wordmark .mark {
  width: 24px; height: 24px;
  display: block;
  flex: none;
}
.wordmark b {
  font: 500 .875rem/1 var(--mono);
  letter-spacing: .18em;
  text-transform: uppercase;
}

.nav {
  order: 3;
  flex: 1 1 100%;
  min-width: 0;
  display: flex;
  gap: clamp(1rem, 3vw, 1.9rem);
  margin-top: 1.1rem;
  padding-top: .9rem;
  border-top: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}
.nav::-webkit-scrollbar { display: none; }
.band .nav { border-top-color: var(--band-line); }
@media (min-width: 1040px) {
  .nav {
    order: 0; flex: 0 1 auto; width: auto;
    margin-inline: auto;
    padding-top: 0; border-top: 0; overflow: visible;
  }
}
.nav a {
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  text-decoration: none;
  color: inherit;
  opacity: .72;
  padding: .3rem 0;
  white-space: nowrap;
}
.nav a:hover { opacity: 1; }

.masthead-end { display: flex; align-items: center; gap: .6rem; margin-inline-start: auto; }
@media (min-width: 1040px) { .masthead-end { margin-inline-start: 0; } }

.toggle {
  display: inline-grid;
  place-items: center;
  width: 38px; height: 38px;
  padding: 0;
  border: 1px solid currentColor;
  border-radius: var(--r);
  background: transparent;
  color: inherit;
  opacity: .72;
  cursor: pointer;
  flex: none;
}
.toggle:hover { opacity: 1; }
.toggle svg { display: block; }
.toggle .i-sun { display: none; }
:root[data-theme="dark"] .toggle .i-moon { display: none; }
:root[data-theme="dark"] .toggle .i-sun  { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .toggle .i-moon { display: none; }
  :root:not([data-theme="light"]) .toggle .i-sun  { display: block; }
}

/* --------------------------------------------------------------------------
   7. Panels
   -------------------------------------------------------------------------- */

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 1.6rem 1.7rem;
}
.panel-seal { border-color: var(--seal); background: var(--seal-bg); }
.panel-raw  { border-color: var(--raw);  background: var(--raw-bg); }

.panel-head {
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  margin: 0 0 1rem;
}
.panel-seal .panel-head { color: var(--seal); }
.panel-raw  .panel-head { color: var(--raw); }

.panel ul { margin: 0; padding-left: 1.05rem; }
.panel li { margin-bottom: .55rem; font-size: .9375rem; }
.panel li:last-child { margin-bottom: 0; }

/* Code well — recessed, never a floating dark slab. */
pre.well {
  font-family: var(--mono);
  font-size: .75rem;
  line-height: 1.7;
  background: var(--sunk);
  border-radius: var(--r);
  padding: 1rem 1.1rem;
  margin: 0;
  overflow-x: auto;
  color: var(--ink);
}
.panel-seal pre.well,
.panel-raw  pre.well { background: var(--surface); }

pre.well .k { color: var(--seal); }
pre.well .m { color: var(--raw); }
pre.well .c { color: var(--slate); }

.chip {
  display: inline-block;
  font: 500 .625rem/1 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: .35rem .6rem;
  border-radius: var(--r);
  white-space: nowrap;
}
.chip-seal { background: var(--seal-bg); color: var(--seal); }
.chip-raw  { background: var(--raw-bg);  color: var(--raw); }

/* --------------------------------------------------------------------------
   8. Grids
   -------------------------------------------------------------------------- */

.cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.75rem, 4vw, 3.5rem);
  align-items: start;
}
/* Two panels that are one comparison must share a bottom edge, or the reader
   reads the taller one as the more important one. */
.cols-even { align-items: stretch; }
@media (min-width: 900px) {
  .cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Bento: hairline-separated cells that read as one instrument panel, not as
   eight floating cards. The 1px gap is the grid showing through. */
.bento {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
}
@media (min-width: 620px)  { .bento { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .bento { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.cell {
  background: var(--surface);
  padding: 1.9rem 1.7rem 2.1rem;
  min-width: 0;
}
.cell {
  transition: background var(--t1) var(--ease);
}
.cell:hover { background: var(--seal-bg); }
.cell .ico {
  color: var(--seal);
  margin-bottom: 1.1rem;
  display: block;
  transition: transform var(--t2) var(--ease);
}
.cell:hover .ico { transform: translateY(-2px); }
.cell h3 { margin-bottom: .55rem; }
.cell p { font-size: .875rem; color: var(--slate); margin: 0; }

/* Checklist column, opposite a statement. */
.checks { list-style: none; margin: 0; padding: 0; }
.checks li {
  display: flex;
  gap: .85rem;
  align-items: baseline;
  padding: .85rem 0;
  border-bottom: 1px solid var(--line);
  font-size: .9375rem;
}
.checks li:last-child { border-bottom: 0; }
.checks .tick { color: var(--seal); flex: none; font-size: .8125rem; }

/* --------------------------------------------------------------------------
   9. The assurance strip — frameworks, not fabricated customer logos
   -------------------------------------------------------------------------- */

.strip {
  border-top: 1px solid var(--band-line);
  border-bottom: 1px solid var(--band-line);
}
.strip > .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem clamp(1.25rem, 4vw, 3rem);
  padding-block: 1.15rem;
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
}
.strip .lead { color: var(--band-dim); }
.strip span { color: var(--band-ink); }

/* --------------------------------------------------------------------------
   10. Statement — a single large sentence carrying a section on its own
   -------------------------------------------------------------------------- */

.statement {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.4rem, 1.05rem + 1.5vw, 2.05rem);
  line-height: 1.2;
  letter-spacing: -0.026em;
  max-width: 24ch;
  text-wrap: balance;
}
.statement em { font-style: normal; color: var(--seal); }

/* --------------------------------------------------------------------------
   11. FAQ
   -------------------------------------------------------------------------- */

.faq > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: .5rem clamp(1.5rem, 5vw, 4rem);
  padding: 1.6rem 0;
  border-top: 1px solid var(--line);
}
@media (min-width: 880px) {
  .faq > div { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); gap: 0 clamp(1.5rem, 5vw, 4rem); }
}
.faq p { font-size: .9375rem; color: var(--slate); }

/* --------------------------------------------------------------------------
   12. Footer
   -------------------------------------------------------------------------- */

.foot { border-top: 1px solid var(--line); background: var(--sunk); }
.foot > .wrap { padding-block: clamp(3rem, 6vw, 4.5rem) 2rem; }

.sitemap {
  display: grid;
  gap: 2.25rem clamp(1.5rem, 3vw, 2.5rem);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.sitemap h2 {
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--slate);
  margin: 0 0 1.05rem;
}
.sitemap ul { list-style: none; margin: 0; padding: 0; }
.sitemap li { margin-bottom: .6rem; }
.sitemap a {
  text-decoration: none;
  color: var(--ink);
  font-size: .875rem;
  line-height: 1.4;
  opacity: .82;
}
.sitemap a:hover { opacity: 1; text-decoration: underline; text-underline-offset: 3px; }

.colophon {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: .75rem 1.75rem;
  align-items: baseline;
  font: 400 .6875rem/1.5 var(--mono);
  letter-spacing: .06em;
  color: var(--slate);
}
.colophon .spacer { flex: 1; }
.colophon a { color: var(--slate); text-decoration: none; }
.colophon a:hover { color: var(--ink); }

/* --------------------------------------------------------------------------
   13. Motion / print
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }

  /* Reveal is a static hidden state, not an animation, so the blanket rule
     above would leave it hidden forever. Everything is simply visible. */
  .js .reveal,
  .js .reveal.in {
    opacity: 1 !important;
    transform: none !important;
  }

  /* The demonstration holds its finished state and never loops. */
  .js .verdict { display: flex !important; transform: none !important; opacity: 1 !important; }
  .js .demo .record { opacity: 1 !important; }
  .js .demo .scan { opacity: 0 !important; animation: none !important; }
  .js .demo .prompt .caret { display: none !important; }
}

@media print {
  .masthead, .toggle, .band { background: none; color: #000; }
  body { background: #fff; color: #000; }
}


/* ==========================================================================
   14. Motion
   The page is correct and complete before any of this runs. Reveal states are
   applied only once the document is marked .js, so a crawler — or a reader
   with scripting off — gets everything visible on first paint. Every rule
   here is switched off entirely under prefers-reduced-motion.
   ========================================================================== */

.js .reveal {
  opacity: 0;
  transform: translateY(14px);
}
.js .reveal.in {
  opacity: 1;
  transform: none;
  transition: opacity var(--t3) var(--ease), transform var(--t3) var(--ease);
}
/* Children of a revealed group arrive in sequence, not all at once. */
.js .reveal.in > * { animation: rise var(--t3) var(--ease) backwards; }
.js .reveal.in > :nth-child(1) { animation-delay: 0ms; }
.js .reveal.in > :nth-child(2) { animation-delay: 70ms; }
.js .reveal.in > :nth-child(3) { animation-delay: 140ms; }
.js .reveal.in > :nth-child(4) { animation-delay: 210ms; }

@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* The hero plays once, on load, without waiting for a scroll. */
.js .hero-in > * { animation: rise var(--t3) var(--ease) backwards; }
.js .hero-in > :nth-child(1) { animation-delay: 60ms; }
.js .hero-in > :nth-child(2) { animation-delay: 150ms; }
.js .hero-in > :nth-child(3) { animation-delay: 280ms; }
.js .hero-in > :nth-child(4) { animation-delay: 400ms; }

/* ==========================================================================
   15. The demonstration
   A loop showing one prompt being caught. It carries no information that
   isn't also in the figcaption, so it is aria-hidden and pausable.
   ========================================================================== */

.demo { margin: 0; }

.demo-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
@media (min-width: 980px) {
  .demo-grid { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); }
}

.win {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}

.win-bar {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .7rem .95rem;
  border-bottom: 1px solid var(--line);
  background: var(--sunk);
  font: 400 .6875rem/1 var(--mono);
  letter-spacing: .04em;
  color: var(--slate);
}
.win-bar .grow { flex: 1; }
.win-bar .dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--seal);
  flex: none;
}

.win-state {
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--seal);
  transition: color var(--t2) var(--ease);
}
.demo[data-stage="blocked"] .win-state { color: var(--raw); }

/* The composer */
.composer {
  padding: 1.35rem 1.4rem 1.5rem;
  min-height: 9.5rem;
  position: relative;
}
.composer-label {
  font: 500 .625rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--slate);
  margin: 0 0 .9rem;
}
.prompt {
  font-size: .9375rem;
  line-height: 1.7;
  margin: 0;
  min-height: 3.4em;
}
.prompt .caret {
  display: none;
  width: 2px;
  height: 1.05em;
  background: var(--ink);
  vertical-align: text-bottom;
  margin-left: 1px;
}
.js .demo[data-stage="typing"] .prompt .caret {
  display: inline-block;
  animation: blink 1s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* A found value: cherry while raw, petrol once sealed. */
.hit {
  background: var(--seal-bg);
  color: var(--seal);
  box-shadow: inset 0 -1px 0 var(--seal);
  padding: .1em .22em;
  margin: 0 -.06em;
  border-radius: var(--r);
  transition: background var(--t2) var(--ease), color var(--t2) var(--ease);
}
.demo[data-stage="found"] .hit,
.demo[data-stage="masking"] .hit {
  background: var(--raw-bg);
  color: var(--raw);
  box-shadow: inset 0 -1px 0 var(--raw);
}
.demo[data-stage="masked"] .hit,
.demo[data-stage="blocked"] .hit,
.demo[data-stage="sent"] .hit {
  background: var(--seal-bg);
  color: var(--seal);
  box-shadow: inset 0 -1px 0 var(--seal);
}

/* The scan sweep — one pass, left to right, over the composer only. */
.scan {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(90deg,
    transparent 0%,
    color-mix(in srgb, var(--seal-2) 22%, transparent) 45%,
    color-mix(in srgb, var(--seal-2) 34%, transparent) 50%,
    color-mix(in srgb, var(--seal-2) 22%, transparent) 55%,
    transparent 100%);
  background-size: 55% 100%;
  background-repeat: no-repeat;
}
.js .demo[data-stage="scanning"] .scan {
  opacity: 1;
  animation: sweep 900ms var(--ease) 1 forwards;
}
@keyframes sweep {
  from { background-position: -60% 0; }
  to   { background-position: 160% 0; }
}

/* The enforcement bar */
.verdict {
  display: flex;
  align-items: center;
  gap: .75rem;
  flex-wrap: wrap;
  padding: .85rem 1.4rem;
  border-top: 1px solid var(--raw);
  background: var(--raw-bg);
  color: var(--raw);
  font-size: .875rem;
}
.js .verdict { display: none; transform: translateY(100%); opacity: 0; }
.js .demo[data-stage="blocked"] .verdict,
.js .demo[data-stage="sent"] .verdict {
  display: flex;
  transform: none;
  opacity: 1;
  transition: transform var(--t2) var(--ease), opacity var(--t2) var(--ease);
}
.verdict b { font-weight: 600; }
.verdict .meta { color: var(--raw); opacity: .8; font-family: var(--mono); font-size: .75rem; letter-spacing: .04em; }

/* The record that leaves, arriving line by line */
.record { transition: opacity var(--t2) var(--ease); }
.js .demo:not([data-stage="sent"]) .record { opacity: .32; }
.js .demo[data-stage="sent"] .record-line { animation: rise 420ms var(--ease) backwards; }
.js .demo[data-stage="sent"] .record-line:nth-child(1) { animation-delay: 0ms; }
.js .demo[data-stage="sent"] .record-line:nth-child(2) { animation-delay: 60ms; }
.js .demo[data-stage="sent"] .record-line:nth-child(3) { animation-delay: 120ms; }
.js .demo[data-stage="sent"] .record-line:nth-child(4) { animation-delay: 180ms; }
.js .demo[data-stage="sent"] .record-line:nth-child(5) { animation-delay: 240ms; }
.js .demo[data-stage="sent"] .record-line:nth-child(6) { animation-delay: 300ms; }

.record-line { display: block; }

/* Pause control — required for anything that loops (WCAG 2.2.2). */
.demo-controls {
  display: none;
  align-items: center;
  gap: .75rem;
  margin-top: 1rem;
}
.js .demo-controls { display: flex; }
.demo-note { font-size: .8125rem; color: var(--slate); margin: 0; }

/* ==========================================================================
   16. Flow — the plain-language explanation
   Full-width alternating rows. A screenshot needs about 60% of the container
   to stay legible; three of them side by side does not work at any viewport
   this site supports.
   ========================================================================== */

.flow { display: grid; gap: clamp(2.75rem, 6vw, 5rem); }

.flowrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.4rem, 3vw, 3.5rem);
  align-items: center;
}
/* Text left, image right, every row. The wide track is the second one, and the
   image is second in the markup — so source order matches visual order and no
   `order` property is needed. Reordering with CSS would leave a keyboard user
   tabbing through the row in a sequence that doesn't match what they see. */
@media (min-width: 920px) {
  .flowrow { grid-template-columns: minmax(0, 1fr) minmax(0, 1.55fr); }
}

.flow-media { min-width: 0; }
.flow-media img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
}

.flow-body { min-width: 0; }
.flow-n {
  display: block;
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .14em;
  color: var(--seal);
  margin-bottom: 1rem;
}
.flow-body h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.15rem, .95rem + .6vw, 1.5rem);
  letter-spacing: -.022em;
  line-height: 1.15;
  margin-bottom: .85rem;
}
.flow-body p { color: var(--slate); margin: 0; }

/* ==========================================================================
   17. Audience panels
   ========================================================================== */

.who { padding: 1.9rem 1.8rem 2rem; }
.who .who-tag {
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--seal);
  margin: 0 0 1rem;
}
.who h3 { font-size: 1.25rem; margin-bottom: .9rem; letter-spacing: -0.02em; }
.who > p { font-size: .9375rem; color: var(--slate); }
.who .checks li { font-size: .875rem; padding: .7rem 0; }

/* ==========================================================================
   18. Media placeholders
   Every slot states its own spec, so whoever supplies the asset doesn't have
   to guess a ratio or a format. The drop-in markup sits in an HTML comment
   directly above each one. These are meant to be deleted, not styled around.
   ========================================================================== */

.ph {
  position: relative;
  display: block;
  width: 100%;
  border: 1px dashed var(--seal);
  background: var(--seal-bg);
  border-radius: var(--r);
  overflow: hidden;
  color: var(--ink);
}

.ph-16x9  { aspect-ratio: 16 / 9; }
.ph-16x10 { aspect-ratio: 16 / 10; }
.ph-4x3   { aspect-ratio: 4 / 3; }

.ph-inner {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .7rem;
  text-align: center;
  padding: 1.25rem;
}

.ph-name {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.015em;
  margin: 0;
}
.ph-spec {
  font: 400 .6875rem/1.6 var(--mono);
  letter-spacing: .04em;
  color: var(--slate);
  margin: 0;
  max-width: 52ch;
}
.ph-tag {
  position: absolute;
  top: .7rem; left: .8rem;
  font: 500 .5625rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--seal);
  background: var(--surface);
  border: 1px solid var(--seal);
  border-radius: var(--r);
  padding: .3rem .5rem;
}

/* Compact variant for the step cells */
.ph-sm .ph-name { font-size: .8125rem; }
.ph-sm .ph-spec { font-size: .625rem; }
.ph-sm .ph-tag  { font-size: .5rem; top: .5rem; left: .55rem; padding: .22rem .4rem; }

/* The play affordance. Real once a poster frame replaces the placeholder. */
.ph-play {
  width: 62px; height: 62px;
  border-radius: 50%;
  border: 1px solid var(--seal);
  background: var(--surface);
  color: var(--seal);
  display: grid;
  place-items: center;
  transition: transform var(--t2) var(--ease), background var(--t2) var(--ease),
              color var(--t2) var(--ease);
}
.ph-video:hover .ph-play,
.ph-video:focus-within .ph-play {
  transform: scale(1.07);
  background: var(--seal);
  color: var(--band-ink);
}

/* A slow sweep, so a placeholder never reads as a broken image. */
.js .ph::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(115deg,
    transparent 38%,
    color-mix(in srgb, var(--seal-2) 12%, transparent) 50%,
    transparent 62%);
  background-size: 260% 100%;
  animation: shimmer 5.5s var(--ease) infinite;
}
@keyframes shimmer {
  from { background-position: 180% 0; }
  to   { background-position: -80% 0; }
}

.media-note {
  font: 400 .8125rem/1.55 var(--body);
  color: var(--slate);
  margin: 1rem 0 0;
  max-width: 62ch;
}

/* ==========================================================================
   19. Condensed bar
   Appears once the brand band has scrolled away, so the primary action is
   never more than a glance up. Purely additive: without script it never
   appears, and the masthead in the band keeps doing its job.
   ========================================================================== */

.stickybar {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 60;
  display: none;
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
  transform: translateY(-100%);
}
.js .stickybar { display: block; }
.stickybar.show {
  transform: none;
  transition: transform var(--t2) var(--ease);
}
.stickybar > .wrap {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-block: .65rem;
}
.stickybar .spacer { flex: 1; }

/* ==========================================================================
   20. Additional motion
   ========================================================================== */

/* The band breathes — one hue, a 30s drift, no hue rotation and no parallax.
   Slow enough that it reads as depth rather than as something happening. */
.js .band {
  background-size: auto, 200% 200%;
  animation: drift 30s ease-in-out infinite alternate;
}
@keyframes drift {
  from { background-position: 0% 0%, 0% 30%; }
  to   { background-position: 0% 0%, 0% 70%; }
}

/* Paired columns arrive from opposite sides — a small cue that they are one
   comparison, not two unrelated blocks. */
.js .reveal-l { opacity: 0; transform: translateX(-16px); }
.js .reveal-r { opacity: 0; transform: translateX(16px); }
.js .reveal-l.in,
.js .reveal-r.in {
  opacity: 1;
  transform: none;
  transition: opacity var(--t3) var(--ease), transform var(--t3) var(--ease);
}

/* Inline links grow their underline rather than switching it on. */
.link {
  border-bottom: 0;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% 1px;
  transition: background-size var(--t2) var(--ease), color var(--t1) var(--ease);
}
.link:hover { background-size: 100% 2px; }

/* Bento cells lift a hairline of petrol as the pointer arrives. */
.cell { position: relative; }
.cell::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  height: 2px;
  width: 100%;
  background: var(--seal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t2) var(--ease);
}
.cell:hover::after { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  .js .band { animation: none !important; }
  .js .ph::after { animation: none !important; opacity: 0 !important; }
  .js .reveal-l, .js .reveal-r,
  .js .reveal-l.in, .js .reveal-r.in {
    opacity: 1 !important;
    transform: none !important;
  }
  .stickybar { transition: none !important; }
}

/* ==========================================================================
   21. Plans
   Three cards on one hairline grid, so they read as one instrument rather
   than three floating offers. The middle card is emphasised with the accent
   border only — never a bigger card, never a different radius.
   ========================================================================== */

.plans {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
}
@media (min-width: 900px) { .plans { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.plan {
  background: var(--surface);
  padding: 1.9rem 1.8rem 2.1rem;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.plan-pick { background: var(--seal-bg); }

.plan-head {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  margin-bottom: .9rem;
  flex-wrap: wrap;
}
.plan-name {
  font: 500 .75rem/1 var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--seal);
  margin: 0;
}
.plan-tag {
  font: 500 .5625rem/1 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--seal);
  border: 1px solid var(--seal);
  border-radius: var(--r);
  padding: .3rem .45rem;
}

.plan h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.2rem;
  letter-spacing: -.022em;
  line-height: 1.2;
  margin-bottom: .7rem;
}
.plan .who-for { font-size: .9375rem; color: var(--slate); margin-bottom: 1.5rem; }

.plan .actions { margin-bottom: 1.6rem; }
.plan .btn { width: 100%; justify-content: center; }

.plan-list { list-style: none; margin: 0; padding: 0; }
.plan-list li {
  display: flex;
  gap: .7rem;
  align-items: baseline;
  font-size: .875rem;
  padding: .55rem 0;
  border-top: 1px solid var(--line);
  color: var(--slate);
}
.plan-list li:first-child { border-top: 0; }
.plan-list .tick { color: var(--seal); flex: none; font-size: .75rem; }
.plan-list b { color: var(--ink); font-weight: 500; }

.plan-foot {
  margin-top: auto;
  padding-top: 1.4rem;
  font: 400 .8125rem/1.5 var(--body);
  color: var(--slate);
}

/* Row alignment across the three cards.
   Each card contributes the same six rows — label, heading, who-it's-for,
   action, feature list, footnote — and subgrid makes the tallest cell in each
   row set that row's height for all three. So the buttons sit on one line
   whether a heading wraps to one line or two, with no hard-coded height to go
   stale the next time the copy changes.

   This block must come after every other .plan rule: @supports and @media add
   no specificity, so the plain `.plan { display: flex }` above would otherwise
   win on source order. Flex stays the fallback where subgrid is unavailable —
   the cards just size independently, exactly as they did before.

   row-gap drops to 0 here because .plans paints its 1px grid lines with a
   background; across six rows that would draw five hairlines through every
   card. The column gap stays, so the cards remain separated. */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 900px) {
    .plans {
      grid-template-rows: auto auto auto auto auto auto;
      row-gap: 0;
      column-gap: 1px;
    }
    .plan {
      display: grid;
      grid-row: span 6;
      grid-template-rows: subgrid;
      row-gap: 0;
    }
    /* Inside a subgrid the row supplies the spacing, so the flex-era bottom
       margins would double it. */
    .plan .plan-head,
    .plan h3,
    .plan .who-for,
    .plan .actions { margin-bottom: 0; }
    .plan .plan-head { align-self: start; }
    .plan h3        { align-self: start; padding-block: .9rem 1.1rem; }
    .plan .who-for  { align-self: start; padding-bottom: 1.5rem; }
    .plan .actions  { align-self: end;   padding-bottom: 1.6rem; }
    .plan .plan-list { align-self: start; }
    .plan .plan-foot { margin-top: 0; align-self: end; }
  }
}

/* ==========================================================================
   22. Comparison table
   ========================================================================== */

/* A table cannot crush below its min-content width, so it scrolls inside its
   own box rather than widening the page. Every wide table on the site needs
   this wrapper — without it the comparison alone pushed the document 294px
   past the viewport at phone width. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

.compare { width: 100%; border-collapse: collapse; font-size: .875rem; min-width: 660px; }

.compare th, .compare td {
  text-align: left;
  padding: .8rem 1rem .8rem 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.compare thead th {
  font: 500 .6875rem/1.3 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--seal);
  border-bottom: 1px solid var(--ink);
  padding-bottom: .7rem;
  text-align: center;
}
.compare thead th:first-child { color: var(--slate); text-align: left; }
.compare tbody th {
  font-weight: 400;
  color: var(--ink);
  padding-right: 2rem;
  width: 37%;
  text-align: left;
}
.compare tbody th small { display: block; color: var(--slate); font-size: .8125rem; margin-top: .2rem; }
/* Plan columns are centred. Left-aligned in a 380px column, a single glyph
   sits adrift from its own header and from the marks above and below it — the
   eye reads a ragged edge where there is meant to be a rhythm. Centring gives
   the three columns one axis each, and text values ("Opt-in", "All eight")
   land on the same axis as the ticks. */
.compare td {
  color: var(--slate);
  width: 21%;
  text-align: center;
  padding-right: 0;
  padding-inline: .75rem;
}
.compare .grp td { padding-inline: 0; }

/* Group heading row */
.compare .grp th {
  font: 500 .625rem/1 var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--slate);
  background: var(--sunk);
  padding: .7rem 1rem .7rem .8rem;
  border-bottom: 1px solid var(--line);
}
.compare .grp td { background: var(--sunk); border-bottom: 1px solid var(--line); }

/* Marks. Never colour alone — each carries a label for assistive tech, and
   the dash is a visible glyph rather than an empty cell. */
/* Marks share a box so a tick, a dash and a word all sit on one baseline;
   colour is never the only carrier — each has a label for assistive tech. */
/* Marks inherit the cell's font size deliberately. Bumping the tick even to
   .9375rem gives it a taller line box than a plain text value like "Opt-in",
   and the two then sit on different baselines in the same row. Colour carries
   the emphasis instead. */
.yes, .no {
  font: inherit;
}
.yes { color: var(--seal); }
.no  { color: var(--slate); opacity: .6; }

.compare .col-pick { background: var(--seal-bg); }

/* Status markers for a coverage-style table. A square plus the word — colour
   is never the only carrier, and each carries an aria-label besides.
   Partial is deliberately slate rather than a third accent: petrol means
   sealed and cherry means raw, and inventing a third would cost both of them
   their meaning (see the brand kit's two-colour rule). */
.status {
  display: inline-flex;
  align-items: baseline;
  gap: .5rem;
  font: 500 .75rem/1.5 var(--mono);
  letter-spacing: .03em;
  text-align: left;
}
.status::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 1px;
  background: currentColor;
  flex: none;
  translate: 0 -1px;
}
.s-on   { color: var(--seal); }
.s-part { color: var(--slate); }
.s-off  { color: var(--raw); }

/* ==========================================================================
   23. Callout
   A single paragraph that needs to sit apart from the prose around it —
   a caveat, a qualification, the sentence a reader must not skim past.
   ========================================================================== */

.callout {
  border: 1px solid var(--line);
  border-left: 2px solid var(--seal);
  border-radius: 0 var(--r) var(--r) 0;
  background: var(--seal-bg);
  padding: 1.15rem 1.35rem;
  font-size: .9375rem;
  color: var(--slate);
}
.callout strong { color: var(--ink); font-weight: 600; }

/* Any image placed directly in prose. Was inline style on every <img>, which
   meant the border and radius could drift per page. */
.media {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: var(--r);
}

/* ==========================================================================
   24. Identifier marks
   For showing a piece of text with the sensitive parts called out in place.
   Cherry means raw — the same meaning it carries everywhere else in the
   system — so this must not be used to highlight anything that isn't
   sensitive data.
   ========================================================================== */

.sample {
  font-family: var(--mono);
  font-size: .9375rem;
  line-height: 2.1;
  background: var(--sunk);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 1.35rem 1.5rem;
  margin: 0;
}
.idmark {
  background: var(--raw-bg);
  color: var(--raw);
  box-shadow: inset 0 -1px 0 var(--raw);
  border-radius: var(--r);
  padding: .12em .3em;
}

/* A value that is not true yet — a reviewer who has not signed off, a fact
   nobody has supplied. Cherry, because "not yet true" is exactly what cherry
   means everywhere else in this system. */
.pending {
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--raw);
  border: 1px dashed var(--raw);
  border-radius: var(--r);
  padding: .28rem .5rem;
  display: inline-block;
  white-space: nowrap;
}

/* An inline list of terms, hairline-separated, that reads as one set. */
.terms {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
}
.terms li {
  font: 400 .8125rem/1.4 var(--body);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: .4rem .6rem;
}

/* ==========================================================================
   25. Stages — a numbered sequence that is not three-abreast
   ========================================================================== */

.stages { list-style: none; margin: 0; padding: 0; counter-reset: stage; }

.stages li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: .35rem;
  padding: 1.3rem 0;
  border-top: 1px solid var(--line);
}
.stages li:first-child { border-top: 1px solid var(--ink); }
@media (min-width: 760px) {
  .stages li { grid-template-columns: 3.5rem minmax(0, 15rem) minmax(0, 1fr); gap: 0 clamp(1rem, 3vw, 2.5rem); align-items: baseline; }
}
.stages .n {
  counter-increment: stage;
  font: 500 .6875rem/1.6 var(--mono);
  letter-spacing: .14em;
  color: var(--seal);
}
.stages h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -.018em;
  line-height: 1.5;
}
.stages p { font-size: .9375rem; color: var(--slate); margin: 0; }

/* ==========================================================================
   26. Forms
   Every control gets a real <label>, and the help text is tied to its field
   with aria-describedby rather than floated nearby. Native validation does
   the work — a hand-rolled JS validator is one more thing to get wrong, and
   it fails closed when the script doesn't load.
   ========================================================================== */

/* Fieldsets need clear air between them, or "what you need" reads as another
   row of "who you are". */
.form { display: grid; gap: clamp(2rem, 4vw, 2.75rem); }

fieldset {
  border: 0;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1.5rem;
}
/* A <legend> is lifted out of grid flow by the browser, so its own margin is
   the only thing separating it from the first field. */
legend {
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--seal);
  padding: 0;
  margin-bottom: 1.1rem;
}

/* align-content: start matters. Side-by-side fields share a row height, and
   without it the grid stretches the shorter field's input to fill the space
   left by its neighbour's help text — so two inputs on one row end up
   different heights. */
.field {
  display: grid;
  gap: .5rem;
  align-content: start;
  min-width: 0;
}

.field > label {
  font-size: .9375rem;
  font-weight: 500;
  color: var(--ink);
}
.field .opt {
  font: 400 .8125rem/1 var(--body);
  color: var(--slate);
  font-weight: 400;
  margin-left: .4rem;
}
.field .help {
  font-size: .8125rem;
  line-height: 1.5;
  color: var(--slate);
  margin: 0;
  order: 3;
}

input[type="text"],
input[type="email"],
input[type="tel"],
select,
textarea {
  font: 400 .9375rem/1.5 var(--body);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: .7rem .85rem;
  width: 100%;
  min-width: 0;
  transition: border-color var(--t1) var(--ease), background var(--t1) var(--ease);
  appearance: none;
}
textarea { resize: vertical; min-height: 7.5rem; }

select {
  /* The arrow is drawn rather than loaded, so the control needs no image
     request and inverts with the theme on its own. */
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 18px) 55%, calc(100% - 13px) 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 2.25rem;
}

input:hover, select:hover, textarea:hover { border-color: var(--slate); }
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--seal);
  outline-offset: 1px;
  border-color: var(--seal);
}
/* :user-invalid, not :invalid — :invalid paints a required field red before
   anyone has had a chance to type in it. */
input:user-invalid, select:user-invalid, textarea:user-invalid {
  border-color: var(--raw);
}

.row2 { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
@media (min-width: 620px) { .row2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.consent {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .75rem;
  align-items: start;
  border-top: 1px solid var(--line);
  padding-top: 1.35rem;
}
.consent input[type="checkbox"] {
  width: 1.05rem;
  height: 1.05rem;
  margin: .15rem 0 0;
  accent-color: var(--seal);
  flex: none;
}
.consent label { font-size: .875rem; line-height: 1.55; color: var(--slate); }
.consent label b { color: var(--ink); font-weight: 500; }

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  border-top: 1px solid var(--line);
  padding-top: 1.5rem;
}
.form-actions .btn { padding-inline: 1.9rem; }
.form-actions .note { font-size: .8125rem; color: var(--slate); margin: 0; }

/* ==========================================================================
   27. Pronunciation
   The name is the one thing on the site people will have to say out loud in
   a meeting. Give it the room to be read once and remembered.
   ========================================================================== */

.pronounce {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  padding: clamp(1.75rem, 4vw, 2.75rem);
}

.pronounce .word {
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(3rem, 1.6rem + 6vw, 5.5rem);
  line-height: 1;
  letter-spacing: -.04em;
  margin: 0 0 1.1rem;
}
/* The stressed syllable is coloured, not bolded — the whole word is already
   at its heaviest weight, so weight has nothing left to say. */
.pronounce .word .stress { color: var(--seal); }

.pronounce .say {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .5rem 1.4rem;
  padding-bottom: 1.4rem;
  margin-bottom: 1.4rem;
  border-bottom: 1px solid var(--line);
}
.pronounce .respell {
  font: 500 1.125rem/1 var(--mono);
  letter-spacing: .02em;
  color: var(--ink);
}
.pronounce .ipa {
  font: 400 1rem/1 var(--mono);
  color: var(--slate);
}
.pronounce .tag {
  font: 500 .625rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--slate);
}

.pronounce dl { margin: 0; display: grid; gap: .9rem; }
.pronounce dt {
  font: 500 .625rem/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--slate);
  margin-bottom: .35rem;
}
.pronounce dd { margin: 0; font-size: .9375rem; color: var(--ink); }
.pronounce dd em { font-style: italic; color: var(--slate); }


/* ==========================================================================
   27. Nav submenu
   Opens on hover and on :focus-within, so it works with no JavaScript at all
   — tab to the trigger and the panel is already open. The script only keeps
   aria-expanded honest; it is not what makes the menu function.
   ========================================================================== */

.nav-sub { position: relative; display: flex; align-items: center; }

.nav-sub-trigger {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  background: none;
  border: 0;
  padding: .3rem 0;
  font: 500 .6875rem/1 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: inherit;
  opacity: .72;
  cursor: pointer;
  white-space: nowrap;
}
.nav-sub-trigger:hover,
.nav-sub:focus-within .nav-sub-trigger { opacity: 1; }
.nav-sub-trigger svg { transition: transform var(--t1) var(--ease); }
.nav-sub:hover .nav-sub-trigger svg,
.nav-sub:focus-within .nav-sub-trigger svg { transform: rotate(180deg); }

.nav-sub > ul {
  position: absolute;
  top: 100%;
  left: 50%;
  translate: -50% 0;
  margin: .55rem 0 0;
  padding: .4rem;
  min-width: 13rem;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow-lift);
  z-index: 70;
  opacity: 0;
  visibility: hidden;
  translate: -50% -4px;
  transition: opacity var(--t1) var(--ease), translate var(--t1) var(--ease),
              visibility 0s linear var(--t1);
}
.nav-sub:hover > ul,
.nav-sub:focus-within > ul {
  opacity: 1;
  visibility: visible;
  translate: -50% 0;
  transition-delay: 0s;
}

.nav-sub > ul a {
  display: block;
  padding: .55rem .7rem;
  border-radius: var(--r);
  font: 400 .8125rem/1.4 var(--body);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  opacity: 1;
  white-space: nowrap;
}
.nav-sub > ul a:hover,
.nav-sub > ul a:focus-visible { background: var(--seal-bg); color: var(--seal); }
.nav-sub > ul a[aria-current="page"] { color: var(--seal); }

/* Below the desktop breakpoint the nav is already a scrolling strip, so a
   floating panel would be worse than no panel. The submenu flattens into the
   strip instead — every destination stays one tap away and nothing overlays
   anything. display:contents lifts the links up to be flex items of .nav. */
@media (max-width: 1039px) {
  .nav-sub { display: contents; }
  .nav-sub-trigger { display: none; }
  .nav-sub > ul {
    display: contents;
    position: static;
    opacity: 1;
    visibility: visible;
    translate: none;
    background: none;
    border: 0;
    box-shadow: none;
    padding: 0;
    margin: 0;
  }
  .nav-sub > ul li { display: contents; }
  .nav-sub > ul a {
    display: inline-block;
    padding: .3rem 0;
    font: 500 .6875rem/1 var(--mono);
    letter-spacing: .12em;
    text-transform: uppercase;
    color: inherit;
    opacity: .72;
    background: none;
  }
  .nav-sub > ul a:hover { opacity: 1; background: none; }
}

/* ==========================================================================
   28. Hero figure
   An animated inline SVG of the mechanism the page claims: a message is
   written, scanned on the device, its sensitive spans are masked, and only a
   small record crosses the boundary.

   It is a diagram, not decoration — it shows the thing the headline asserts.
   All animation lives here rather than inside the <svg>, which carries no
   <style> or <script> of its own and stays self-contained.
   ========================================================================== */

.hero-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: center;
  margin-top: 1.6rem;
}
@media (min-width: 980px) {
  .hero-split { grid-template-columns: minmax(0, 1fr) minmax(0, .92fr); }
}

.hero-fig { margin: 0; min-width: 0; }
.hero-fig svg { display: block; width: 100%; max-width: 30rem; height: auto; }
.hero-fig figcaption {
  font: 400 .75rem/1.5 var(--mono);
  letter-spacing: .02em;
  color: var(--band-dim);
  margin-top: 1rem;
  max-width: 34ch;
}

/* ---- static appearance (this is what a reduced-motion reader sees) ------- */
.h-card  { fill: none; stroke: var(--band-line); stroke-width: 1; }
.h-cap   { fill: var(--band-dim); font: 500 10px var(--mono); letter-spacing: .14em; }
.h-line  { fill: var(--band-ink); opacity: .42; }
.h-hit   { fill: var(--band-raw); opacity: 0; }
.h-mask  { fill: var(--band-seal); opacity: .85; }
.h-sweep { fill: var(--band-seal); opacity: 0; }
.h-bound { stroke: var(--band-seal); stroke-width: 1.5; stroke-dasharray: 5 4; opacity: .7; }
.h-cross { stroke: var(--band-seal); stroke-width: 1.5; opacity: .9; }
.h-token { fill: var(--band-seal); opacity: 0; }
.h-rec   { fill: var(--band-ink); opacity: .46; }
.h-arrow { fill: var(--band-seal); }

/* transform-box: fill-box is load-bearing, not tidiness.
   On an SVG element transform-box defaults to view-box, so transform-origin
   resolves against the whole canvas rather than the element. A bar with
   `transform-origin: left center` then scales toward the SVG's left edge —
   it slides sideways instead of growing in place, and never reads as empty
   at scaleX(0). Every animated element here is pinned to its own box. */
.hero-fig .h-line,
.hero-fig .h-hit,
.hero-fig .h-mask,
.hero-fig .h-sweep,
.hero-fig .h-token,
.hero-fig .h-rec { transform-box: fill-box; }

/* ---- the loop ----------------------------------------------------------
   One 5.5s cycle shared by every element, retimed so something is always in
   motion — the first version held each element still for three quarters of its
   cycle, which reads as a static picture no matter how correct it is. Stagger comes from animation-delay
   rather than from per-element keyframes: the offset stays fixed on every
   repetition, so the sequence never drifts out of step. */
.js .hero-fig .h-line,
.js .hero-fig .h-hit,
.js .hero-fig .h-mask,
.js .hero-fig .h-sweep,
.js .hero-fig .h-token,
.js .hero-fig .h-rec {
  animation-duration: 5.5s;
  animation-timing-function: var(--ease);
  animation-iteration-count: infinite;
}

/* 1 · the message is written — 0 to 1.2s */
.js .hero-fig .h-line {
  transform-origin: left center;
  animation-name: hWrite;
}
.js .hero-fig .h-lines rect:nth-of-type(2) { animation-delay: .10s; }
.js .hero-fig .h-lines rect:nth-of-type(3) { animation-delay: .20s; }
.js .hero-fig .h-lines rect:nth-of-type(4) { animation-delay: .30s; }
.js .hero-fig .h-lines rect:nth-of-type(5) { animation-delay: .40s; }
@keyframes hWrite {
  0%   { transform: scaleX(0); opacity: 0; }
  12%  { transform: scaleX(1); opacity: .42; }
  86%  { transform: scaleX(1); opacity: .42; }
  95%  { transform: scaleX(1); opacity: 0; }
  100% { transform: scaleX(0); opacity: 0; }
}

/* 2 · the scan passes over it — 1.4 to 2.4s */
.js .hero-fig .h-sweep { animation-name: hSweep; }
@keyframes hSweep {
  0%, 14%  { transform: translateX(-46px); opacity: 0; }
  18%      { opacity: .42; }
  28%      { opacity: .42; }
  32%      { transform: translateX(206px); opacity: 0; }
  100%     { transform: translateX(206px); opacity: 0; }
}

/* 3 · two spans are found — 2.4 to 3.4s */
.js .hero-fig .h-hit { transform-origin: center; animation-name: hFound; }
.js .hero-fig .h-hits rect:nth-of-type(2) { animation-delay: .12s; }
@keyframes hFound {
  0%, 27% { opacity: 0; transform: scaleY(1); }
  31%     { opacity: 1; transform: scaleY(1.9); }
  40%     { opacity: 1; transform: scaleY(1.9); }
  44%     { opacity: 0; transform: scaleY(1); }
  100%    { opacity: 0; transform: scaleY(1); }
}

/* 4 · and masked in place — from 3.4s */
.js .hero-fig .h-mask { transform-origin: left center; animation-name: hMask; }
.js .hero-fig .h-masks rect:nth-of-type(2) { animation-delay: .12s; }
@keyframes hMask {
  0%, 42% { opacity: 0; transform: scaleX(.45); }
  47%     { opacity: 1; transform: scaleX(1); }
  88%     { opacity: 1; transform: scaleX(1); }
  95%     { opacity: 0; transform: scaleX(1); }
  100%    { opacity: 0; transform: scaleX(.45); }
}

/* 5 · one record crosses — 4.2 to 5.2s */
.js .hero-fig .h-token { animation-name: hCross; }
@keyframes hCross {
  0%, 50% { transform: translateX(0) scale(.5);  opacity: 0; }
  54%     { transform: translateX(0) scale(1);   opacity: 1; }
  66%     { transform: translateX(62px) scale(1); opacity: 1; }
  70%     { transform: translateX(62px) scale(.5); opacity: 0; }
  100%    { transform: translateX(62px) scale(.5); opacity: 0; }
}

/* 6 · and lands in the console — 5.2s on */
.js .hero-fig .h-rec {
  transform-origin: left center;
  animation-name: hLand;
}
.js .hero-fig .h-recs rect:nth-of-type(2) { animation-delay: .09s; }
.js .hero-fig .h-recs rect:nth-of-type(3) { animation-delay: .18s; }
.js .hero-fig .h-recs rect:nth-of-type(4) { animation-delay: .27s; }
@keyframes hLand {
  0%, 64% { transform: scaleX(0); opacity: 0; }
  71%     { transform: scaleX(1); opacity: .46; }
  88%     { transform: scaleX(1); opacity: .46; }
  95%     { transform: scaleX(1); opacity: 0; }
  100%    { transform: scaleX(0); opacity: 0; }
}

/* Reduced motion keeps the finished frame: message written, spans masked,
   record landed. The claim survives without a single thing moving. */
@media (prefers-reduced-motion: reduce) {
  .js .hero-fig * { animation: none !important; transform: none !important; }
  .js .hero-fig .h-line,
  .js .hero-fig .h-rec  { opacity: .32 !important; }
  .js .hero-fig .h-mask { opacity: .85 !important; }
  .js .hero-fig .h-hit,
  .js .hero-fig .h-sweep { opacity: 0 !important; }
  .js .hero-fig .h-token { opacity: 1 !important; }
}

/* ==========================================================================
   29. Hero figure — additional primitives
   Every page gets a figure built from these, chosen to match what the page
   argues. They share the 8s cycle and the fill-box rule above, so a figure is
   composed rather than hand-timed.
   ========================================================================== */

.hero-fig .h-tile,
.hero-fig .h-col,
.hero-fig .h-pulse,
.hero-fig .h-drop { transform-box: fill-box; }

/* Static appearance — also the reduced-motion still. */
.h-tile      { fill: var(--band-ink); opacity: .30; }
.h-tile-off  { fill: none; stroke: var(--band-raw); stroke-width: 1; stroke-dasharray: 3 3; opacity: .75; }
.h-col       { fill: var(--band-ink); opacity: .34; transform-origin: bottom center; }
.h-col-pick  { fill: var(--band-seal); opacity: .8; }
.h-pulse     { fill: none; stroke: var(--band-seal); stroke-width: 1.25; opacity: 0; }
.h-drop      { fill: var(--band-raw); opacity: 0; }
.h-node      { fill: var(--band-ink); opacity: .3; }
.h-edge      { stroke: var(--band-line); stroke-width: 1; }

.js .hero-fig .h-tile,
.js .hero-fig .h-col,
.js .hero-fig .h-pulse,
.js .hero-fig .h-drop {
  animation-duration: 5.5s;
  animation-timing-function: var(--ease);
  animation-iteration-count: infinite;
}

/* Tiles light up one after another; the uncovered ones never do. */
.js .hero-fig .h-tiles rect { animation-name: hTile; }
.js .hero-fig .h-tiles rect:nth-of-type(2) { animation-delay: .14s; }
.js .hero-fig .h-tiles rect:nth-of-type(3) { animation-delay: .28s; }
.js .hero-fig .h-tiles rect:nth-of-type(4) { animation-delay: .42s; }
.js .hero-fig .h-tiles rect:nth-of-type(5) { animation-delay: .56s; }
.js .hero-fig .h-tiles rect:nth-of-type(6) { animation-delay: .70s; }
.js .hero-fig .h-tiles rect:nth-of-type(7) { animation-delay: .84s; }
.js .hero-fig .h-tiles rect:nth-of-type(8) { animation-delay: .98s; }
@keyframes hTile {
  0%   { opacity: 0;   transform: scale(.78); }
  10%  { opacity: .55; transform: scale(1.04); }
  16%  { opacity: .30; transform: scale(1); }
  86%  { opacity: .30; transform: scale(1); }
  95%  { opacity: 0;   transform: scale(1); }
  100% { opacity: 0;   transform: scale(.78); }
}

/* Columns grow from the baseline. */
.js .hero-fig .h-cols rect { animation-name: hCol; }
.js .hero-fig .h-cols rect:nth-of-type(2) { animation-delay: .16s; }
.js .hero-fig .h-cols rect:nth-of-type(3) { animation-delay: .32s; }
.js .hero-fig .h-cols rect:nth-of-type(4) { animation-delay: .48s; }
.js .hero-fig .h-cols rect:nth-of-type(5) { animation-delay: .64s; }
@keyframes hCol {
  0%   { transform: scaleY(0);    opacity: 0; }
  16%  { transform: scaleY(1.06); opacity: 1; }
  22%  { transform: scaleY(1);    opacity: 1; }
  86%  { transform: scaleY(1);    opacity: 1; }
  95%  { transform: scaleY(1);    opacity: 0; }
  100% { transform: scaleY(0);    opacity: 0; }
}

/* A ring expanding once per cycle — used where something is detected. */
.js .hero-fig .h-pulse { transform-origin: center; animation-name: hPulse; }
.js .hero-fig .h-pulse:nth-of-type(2) { animation-delay: .5s; }
@keyframes hPulse {
  0%, 26% { opacity: 0; transform: scale(.35); }
  31%     { opacity: 1; transform: scale(.7); }
  48%     { opacity: 0; transform: scale(1.7); }
  100%    { opacity: 0; transform: scale(.35); }
}

/* Something falling out of scope — the uncovered case. */
.js .hero-fig .h-drop { animation-name: hDrop; }
.js .hero-fig .h-drops *:nth-of-type(2) { animation-delay: .3s; }
.js .hero-fig .h-drops *:nth-of-type(3) { animation-delay: .6s; }
@keyframes hDrop {
  0%, 30% { opacity: 0;  transform: translateY(-10px) scale(.7); }
  40%     { opacity: 1;  transform: translateY(0) scale(1); }
  84%     { opacity: 1;  transform: translateY(0) scale(1); }
  93%     { opacity: 0;  transform: translateY(10px) scale(.7); }
  100%    { opacity: 0;  transform: translateY(-10px) scale(.7); }
}

@media (prefers-reduced-motion: reduce) {
  .js .hero-fig .h-tile  { opacity: .22 !important; }
  .js .hero-fig .h-col   { opacity: .26 !important; }
  .js .hero-fig .h-col-pick { opacity: .8 !important; }
  .js .hero-fig .h-pulse { opacity: 0 !important; }
  .js .hero-fig .h-drop  { opacity: .9 !important; }
}

/* ==========================================================================
   30. Hero figure — the shadow
   For the about page, which is about the name. A solid form is written, light
   crosses it, and its outline appears on the far side: the same shape,
   carrying none of the substance. That is the sentence the page opens with,
   and it is what `itzal` means.
   ========================================================================== */

.hero-fig .h-ghost,
.hero-fig .h-ray { transform-box: fill-box; }

.h-ghost { fill: none; stroke: var(--band-ink); stroke-width: 1.25; opacity: .5; }
.h-ray   { stroke: var(--band-seal); stroke-width: 1; opacity: .5; transform-origin: left center; }

.js .hero-fig .h-ghost,
.js .hero-fig .h-ray {
  animation-duration: 5.5s;
  animation-timing-function: var(--ease);
  animation-iteration-count: infinite;
}

/* the light crosses, row by row */
.js .hero-fig .h-rays line { transform-origin: left center; animation-name: hRay; }
.js .hero-fig .h-rays line:nth-of-type(2) { animation-delay: .08s; }
.js .hero-fig .h-rays line:nth-of-type(3) { animation-delay: .16s; }
.js .hero-fig .h-rays line:nth-of-type(4) { animation-delay: .24s; }
@keyframes hRay {
  0%, 18% { transform: scaleX(0); opacity: 0; }
  26%     { transform: scaleX(1); opacity: .9; }
  44%     { transform: scaleX(1); opacity: .9; }
  56%     { transform: scaleX(1); opacity: 0; }
  100%    { transform: scaleX(0); opacity: 0; }
}

/* the outline arrives after the light reaches it, and stays */
.js .hero-fig .h-ghosts * { transform-origin: left center; animation-name: hGhost; }
.js .hero-fig .h-ghosts *:nth-of-type(2) { animation-delay: .09s; }
.js .hero-fig .h-ghosts *:nth-of-type(3) { animation-delay: .18s; }
.js .hero-fig .h-ghosts *:nth-of-type(4) { animation-delay: .27s; }
@keyframes hGhost {
  0%, 30% { opacity: 0; transform: scaleX(.3); }
  40%     { opacity: .6; transform: scaleX(1); }
  86%     { opacity: .6; transform: scaleX(1); }
  95%     { opacity: 0; transform: scaleX(1); }
  100%    { opacity: 0; transform: scaleX(.3); }
}

@media (prefers-reduced-motion: reduce) {
  .js .hero-fig .h-ghost { opacity: .5 !important; }
  .js .hero-fig .h-ray   { opacity: .35 !important; }
}
