/* Viyuh Security · site stylesheet
   Editorial, briefing-document look: 1px rules instead of boxes, serif display,
   one accent. Light theme only by design (brand spec). No framework, ~10 KB. */

/* Latin subsets only, self-hosted from Google Fonts' own files.
   Saves two cross-origin handshakes, which on Indian 4G is 100–300 ms of LCP. */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/fonts/geist-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Tinos";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/tinos-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: light;

  --ink: #0c2b2d;   /* deep teal-navy, shifted from #101820 toward the logo teal */
  --paper: #f7f5ef; /* warm ivory */
  --mist: #edf4f2;  /* alternate-section tint */
  --text: #14181c;
  --text-2: #3a4148;
  --muted: #5a616a;
  --rule: #c9cdd1;
  --rule-strong: #14181c;
  --accent: #0f766e;
  --accent-hover: #0b5e58;
  --logo-teal: #0d9488;

  /* On navy the brand teal drops to ~3.3:1, so dark bands use a lifted tint
     of the same hue (8.4:1 on --ink). Same accent, readable. */
  --accent-on-ink: #5cc2b6;
  --on-ink: #fcfcfa;
  --on-ink-2: #b9c1c9;
  --rule-on-ink: #2a4a4c;

  --serif: "Times New Roman", Tinos, Times, serif;
  --sans: Geist, "Helvetica Neue", Arial, sans-serif;

  --wrap: 73.75rem;
  --gutter: clamp(1rem, 4vw, 2rem);
  --section: clamp(4rem, 9vw, 7.5rem);
  --header-h: 4.25rem;

  /* Animation tempo: every entrance/scroll duration and delay is multiplied
     by this. 1 = original, higher = slower. Hover feedback is exempt. */
  --tempo: 1.6;
}

/* Fluid scale. Every size in this sheet is rem, so this one line scales the
   whole composition: 100% (the user's own base size, usually 16px) up to a
   1440px-wide viewport, then 1.1111vw (= 16px at 1440) so a 1920px screen
   renders the same layout 1.33x larger and a 2560px screen 1.78x larger,
   instead of a 1180px strip in the middle. Capped at 2.6x (a 4K-wide viewport
   still fills ~84%). Floor at
   100% keeps browser zoom and user font settings working (WCAG 1.4.4). */
html { font-size: clamp(100%, 1.1111vw, 260%); }

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--text);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.55;
  overflow-wrap: break-word; /* any unbreakable string (address, URL) wraps instead of widening the page */
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; }

h1, h2, h3, h4, p, ul, ol, dl, figure { margin: 0; }
address { font-style: normal; } /* browsers italicise <address> by default */

a { color: var(--accent); text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
a[href^="mailto:"], a[href^="tel:"] { overflow-wrap: anywhere; } /* 200% text: long addresses wrap instead of scrolling */
a:hover { color: var(--accent-hover); }

strong { font-weight: 650; color: var(--text); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}
.band :focus-visible, .site-footer :focus-visible { outline-color: var(--accent-on-ink); }

.skip {
  position: absolute;
  left: 0.75rem;
  top: 0;
  transform: translateY(-110%); /* hidden by its own height at any scale */
  z-index: 100;
  padding: 0.625rem 0.875rem;
  background: var(--ink);
  color: var(--on-ink);
  font-weight: 600;
}
.skip:focus { transform: translateY(0.75rem); color: var(--on-ink); }

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

.wrap {
  width: 100%;
  max-width: calc(var(--wrap) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ── Type ─────────────────────────────────────────────────────────── */

.eyebrow {
  font-family: var(--sans);
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}
/* Brochure: on navy the eyebrow is quiet grey; the teal lives in a short rule above it. */
.band .eyebrow { color: var(--on-ink-2); }
.eyebrow, .legal h2 { text-wrap: balance; }
.eyebrow--ruled::before {
  content: "";
  display: block;
  width: 2.5rem;
  height: 2px;
  margin-bottom: 1.25rem;
  background: var(--logo-teal);
}

.display, .h2 { overflow-wrap: break-word; hyphens: auto; } /* a single long word must not push the page sideways at 200% text */
.display {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2.6rem, 1.4rem + 5.4vw, 5.6rem);
  line-height: 1.02;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
.display--page { font-size: clamp(2.3rem, 1.4rem + 3.8vw, 4.4rem); }

.h2 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(2rem, 1.35rem + 2.6vw, 3.25rem);
  line-height: 1.08;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.h3 {
  font-family: var(--sans);
  font-size: 1.125rem;
  font-weight: 650;
  line-height: 1.35;
}

.lede {
  font-size: clamp(1.1rem, 1rem + 0.45vw, 1.3rem);
  line-height: 1.5;
  color: var(--text-2);
  max-width: 34em;
  text-wrap: pretty;
}
.band .lede { color: var(--on-ink-2); }
.band .lede strong { color: var(--on-ink); }

.prose { max-width: 34em; color: var(--text-2); }
.prose p + p { margin-top: 1em; }

.muted { color: var(--muted); }
.small { font-size: 0.875rem; line-height: 1.5; }

/* ── Buttons ──────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 3rem;
  padding: 0.75rem 1.375rem;
  border: 1px solid transparent;
  border-radius: 2px;
  font: 600 1rem/1.2 var(--sans);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
.btn--primary { background: var(--accent); color: #fff; }
.btn--primary:hover { background: var(--accent-hover); color: #fff; }
.btn--ghost { border-color: var(--rule-strong); color: var(--text); background: transparent; }
.btn--ghost:hover { background: var(--text); color: var(--paper); }
.band .btn--ghost { border-color: var(--on-ink-2); color: var(--on-ink); }
.band .btn--primary { border-color: var(--accent-on-ink); } /* teal-on-teal-navy needs an edge */
.band .btn--ghost:hover { background: var(--on-ink); color: var(--ink); }
.btn--sm { min-height: 2.75rem; padding: 0.5rem 1rem; font-size: 0.9375rem; }

.actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.btn--icon { gap: 0.625rem; }
.btn .icon { flex: none; }
.dd-note { display: block; margin-top: 0.25rem; font-size: 0.875rem; color: var(--muted); }
.band .dd-note { color: var(--on-ink-2); }

.arrow-link {
  display: inline-block;
  padding-block: 0.5625rem;
  font-weight: 600;
  text-decoration: none;
}
.arrow-link::after { content: "\00a0\2192"; } /* nbsp keeps the arrow with its last word */
.arrow-link:hover { text-decoration: underline; }

/* ── Header ───────────────────────────────────────────────────────── */

/* Solid, never translucent: a backdrop blur over a dense rules-and-text page
   reads as noise and costs a compositing layer on every scroll frame. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  flex-wrap: wrap; /* at 200% text the menu button drops below instead of off-screen */
  min-height: var(--header-h);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--text);
  text-decoration: none;
}
.brand:hover { color: var(--text); }
.site-header .brand { transform-origin: left center; }
@media (prefers-reduced-motion: no-preference) {
  .site-header { transition: box-shadow 300ms ease, border-color 300ms ease; }
  .site-header .brand { transition: transform 300ms cubic-bezier(0.2, 0.7, 0.2, 1); }
}
/* Settled: the bar keeps its 4.25rem box (no content shift, CLS stays 0) and
   only reads as tighter: smaller mark, a hairline of depth. */
html.scrolled .site-header {
  border-bottom-color: transparent;
  box-shadow: 0 1px 0 rgb(12 43 45 / 0.10), 0 10px 24px -18px rgb(12 43 45 / 0.45);
}
html.scrolled .site-header .brand { transform: scale(0.92); }
.brand svg { width: 2.125rem; height: 2.125rem; flex: none; }
.brand__word {
  font: 700 0.875rem/1 var(--sans);
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.nav { display: flex; align-items: center; gap: 2rem; }
.nav__list {
  display: flex;
  gap: 1.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.nav__list a {
  color: var(--text-2);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
  padding-block: 0.375rem;
  border-bottom: 1px solid transparent;
}
.nav__list a:hover { color: var(--text); border-bottom-color: var(--rule-strong); }
.nav__list a[aria-current="page"] { color: var(--text); border-bottom-color: var(--accent); }

.menu-btn {
  display: none;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  background: transparent;
  color: var(--text);
  font: 600 0.875rem/1 var(--sans);
  cursor: pointer;
}
.menu-btn__bars,
.menu-btn__bars::before,
.menu-btn__bars::after {
  display: block;
  width: 1rem;
  height: 1.5px;
  background: currentColor;
}
.menu-btn__bars { position: relative; }
.menu-btn__bars::before,
.menu-btn__bars::after { content: ""; position: absolute; left: 0; }
.menu-btn__bars::before { top: -0.3125rem; }
.menu-btn__bars::after { top: 0.3125rem; }
.menu-btn[aria-expanded="true"] .menu-btn__bars { background: transparent; }
.menu-btn[aria-expanded="true"] .menu-btn__bars::before { top: 0; transform: rotate(45deg); }
.menu-btn[aria-expanded="true"] .menu-btn__bars::after { top: 0; transform: rotate(-45deg); }

@media (max-width: 56.25em) {
  .brand__word { font-size: 0.8125rem; letter-spacing: 0.16em; }
  .nav {
    position: absolute;
    inset: 100% 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0.5rem var(--gutter) 1.5rem;
    background: var(--paper);
    border-bottom: 1px solid var(--rule-strong);
  }
  .nav__list { flex-direction: column; gap: 0; }
  .nav__list a {
    display: block;
    padding: 0.875rem 0;
    font-size: 1.0625rem;
    border-bottom: 1px solid var(--rule);
  }
  .nav__list a[aria-current="page"] { border-bottom-color: var(--rule); color: var(--accent); }
  .nav .btn { margin-top: 1.25rem; }
  .nav[data-open] { height: calc(100svh - var(--header-h)); overflow-y: auto; overscroll-behavior: contain; }
}
/* Collapse the menu only when JS can open it again (early.js sets .js before
   first paint). Without JS the nav renders open under the header, so the site
   never strands a user. */
@media (max-width: 56.25em) {
  .js .menu-btn { display: inline-flex; }
  .js .nav:not([data-open]) { display: none; }
  html:not(.js) .site-header { position: static; }
  html:not(.js) .site-header__inner { flex-wrap: wrap; }
  html:not(.js) .nav { position: static; padding-inline: 0; border-bottom: 0; }
}
@media (max-width: 23.75em) {
  .brand__word { font-size: 0.75rem; letter-spacing: 0.12em; }
  .brand svg { width: 1.875rem; height: 1.875rem; }
}

/* ── Bands and sections ──────────────────────────────────────────── */

/* Blueprint: a faint 3rem grid ruled onto the band. These are 1px hard-stop
   lines, so it reads as drafting paper, not a gradient wash. */
.band {
  position: relative;
  background-color: var(--ink);
  background-image:
    linear-gradient(rgb(255 255 255 / 0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgb(255 255 255 / 0.045) 1px, transparent 1px);
  background-size: 3rem 3rem;
  background-position: -1px -1px;
  color: var(--on-ink);
}
.band > .wrap { position: relative; z-index: 1; }

/* Torch: a brighter copy of the grid, masked to a soft disc under the cursor.
   site.js feeds --mx/--my; only fine pointers with motion allowed ever see it. */
.band::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgb(92 194 182 / 0.22) 1px, transparent 1px),
    linear-gradient(90deg, rgb(92 194 182 / 0.22) 1px, transparent 1px);
  background-size: 3rem 3rem;
  background-position: -1px -1px;
  -webkit-mask-image: radial-gradient(16rem circle at var(--mx, -999px) var(--my, -999px), #000 0%, transparent 70%);
  mask-image: radial-gradient(16rem circle at var(--mx, -999px) var(--my, -999px), #000 0%, transparent 70%);
  opacity: 0;
  transition: opacity 400ms ease;
}
.band[data-torch]::before { opacity: 1; }

/* Ivory and mist alternate down the page so long scrolls have rhythm. */
main > section:not(.band):nth-of-type(odd) { background: var(--mist); }
.band strong { color: var(--on-ink); }
.band a:not(.btn) { color: var(--accent-on-ink); }

/* Width drives the rem scale; height must drive hero padding, or the stat row
   falls below the fold on every 16:9 screen (measured 1366×768 up to 4K). */
.hero { padding-block: clamp(2.5rem, 7svh, 8.75rem) clamp(2.5rem, 6svh, 6rem); }
.hero .eyebrow { margin-bottom: 1.75rem; }
.hero .lede { margin-top: 1.75rem; }
.hero .actions { margin-top: 2.5rem; }

.page-hero { padding-block: clamp(4rem, 9vw, 7rem) clamp(3rem, 7vw, 5rem); }
.page-hero .eyebrow { margin-bottom: 1.5rem; }
.page-hero .display { max-width: 16em; }
.page-hero .lede { margin-top: 1.5rem; }
/* Page heroes use the right half (a "deck") instead of leaving it blank. */
@media (min-width: 60em) {
  /* Left: eyebrow + title, bottom-aligned with the deck on the right. The first
     track soaks up spare height, so the eyebrow never floats away from its title. */
  .page-hero:has(.page-hero__deck) > .wrap {
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    grid-template-rows: 1fr auto auto;
    column-gap: 4rem;
  }
  .page-hero:has(.page-hero__deck) > .wrap > .eyebrow { grid-column: 1; grid-row: 2; align-self: end; }
  .page-hero:has(.page-hero__deck) > .wrap > :not(.page-hero__deck):not(.eyebrow) { grid-column: 1; grid-row: 3; }
  .page-hero__deck { grid-column: 2; grid-row: 1 / -1; align-self: end; }
  /* the incident line stacks inside the narrow deck: text, then the number */
  .page-hero__deck .alert-line { grid-template-columns: minmax(0, 1fr); }
  .page-hero__deck > :first-child { margin-top: 0; }
}
.page-hero__facts { display: grid; gap: 1rem; margin: 0; }
.page-hero__facts dt { font: 700 0.75rem/1.3 var(--sans); letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.band .page-hero__facts dt { color: var(--on-ink-2); }
.page-hero__facts dd { margin: 0.25rem 0 0; }
.page-hero .actions { margin-top: 2rem; }

.section { padding-block: var(--section); }
.section + .section { border-top: 1px solid var(--rule); }
.section--tight { padding-block: clamp(3rem, 6vw, 5rem); }

.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.125rem;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
  max-width: 46rem;
}
.section-head .lede { margin-top: 0.25rem; }

/* Two-column "label | content" layout from the brochure: the left column holds
   the eyebrow and heading, the right carries the body. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2rem;
}
@media (min-width: 60em) {
  .split { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 4rem; }
  .split--even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Ruled items (the brochure's core pattern: a strong 1px rule above) ─ */

.ruled {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2.5rem 2.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 42.5em) { .ruled--2, .ruled--3, .ruled--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 62.5em) { .ruled--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* 4 columns only from 1200px: at 1000–1199 they ran 23–27 characters a line. */
@media (min-width: 75em) { .ruled--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } }


.item {
  border-top: 1px solid var(--rule-strong);
  padding-top: 1.125rem;
}
.band .item { border-top-color: var(--rule-on-ink); }
.item__num {
  display: block;
  margin-bottom: 0.875rem;
  font: 700 0.8125rem/1 var(--sans);
  letter-spacing: 0.12em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.band .item__num { color: var(--accent-on-ink); }
.item .h3 + p,
.item .h3 + .item__body { margin-top: 0.625rem; }
.item p { color: var(--text-2); }
.band .item p { color: var(--on-ink-2); }
.item .arrow-link { display: inline-block; margin-top: 1rem; }

/* ── Numbers ──────────────────────────────────────────────────────── */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr)); /* 2 up on phones, 1 up when text is enlarged */
  gap: 2rem 1.5rem;
  margin: 0;
}
@media (min-width: 56.25em) { .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 2.5rem; } }
@media (min-width: 42.5em) { .stats--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 42.4375em) { .stats--3 .stat:last-child { grid-column: 1 / -1; } }

.stat {
  display: flex;
  flex-direction: column-reverse; /* <dt> label sits under the <dd> figure visually, reads label-first in AT */
  justify-content: flex-end;
  border-top: 1px solid var(--rule-strong);
  padding-top: 1rem;
}
.band .stat { border-top-color: var(--rule-on-ink); }
.stat dd {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(2.3rem, 1.6rem + 2.4vw, 3.6rem);
  line-height: 1;
  letter-spacing: -0.01em;
  font-variant-numeric: lining-nums;
}
.stat dt {
  margin-top: 0.625rem;
  font-size: 0.9375rem;
  line-height: 1.4;
  color: var(--text-2);
}
.band .stat dt { color: var(--on-ink-2); }

.stats-note { margin-top: 1.75rem; color: var(--muted); }
.band .stats-note { color: var(--on-ink-2); }
.hero .stats { margin-top: clamp(2rem, 5svh, 6rem); }

/* ── Services page ───────────────────────────────────────────────── */

.jump {
  position: sticky;
  top: var(--header-h);
  z-index: 40;
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}
.jump ol {
  position: relative;
  display: flex;
  gap: 1.75rem;
  margin: 0;
  padding: 1rem 0;
  list-style: none;
  counter-reset: jump;
  /* One row that scrolls sideways on phones: a wrapped, sticky bar would eat
     a third of a 360px screen. */
  overflow-x: auto;
  scrollbar-width: none;
}
.jump ol::-webkit-scrollbar { display: none; }
.jump li { flex: none; }
.jump a[aria-current] { color: var(--text); }
/* Scroll-spy indicator: 1px wide, stretched with scaleX so moving it never
   triggers layout. site.js sets the transform. */
.jump__ind {
  position: absolute;
  left: 0;
  bottom: 0.625rem;
  width: 1px;
  height: 2px;
  background: var(--accent);
  transform-origin: left;
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) { .jump__ind { transition: transform 380ms cubic-bezier(0.2, 0.7, 0.2, 1), opacity 200ms ease; } }
.jump__ind[data-on] { opacity: 1; }
.service { scroll-margin-top: calc(var(--header-h) + 4rem); }
.jump a { padding-block: 0.6875rem; }
@media (max-width: 42.4375em) {
  .jump ol { padding-block: 0; }
  .jump__ind { bottom: 0.25rem; }
}
/* Desktop: the intro column rides along while the long lists scroll past. */
@media (min-width: 60em) {
  .service .split > :first-child { position: sticky; top: calc(var(--header-h) + 5rem); align-self: start; }
}
.jump li { counter-increment: jump; }
.jump a {
  display: inline-block;
  padding-block: 0.375rem;
  color: var(--text-2);
  font-size: 0.9375rem;
  font-weight: 500;
  text-decoration: none;
}
.jump a::before {
  content: counter(jump, decimal-leading-zero) "  ";
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: pre;
}
.jump a:hover { color: var(--accent); }

.service { padding-block: var(--section); }
.service + .service { border-top: 1px solid var(--rule); }
.service__num {
  display: block;
  margin-bottom: 1.125rem;
  font: 400 clamp(2.4rem, 2rem + 1.4vw, 3.2rem)/1 var(--serif);
  color: var(--muted);
}
.service .h2 { margin-top: 0.875rem; }
.service .lede { margin-top: 1.125rem; }
.service .actions { margin-top: 1.75rem; }

.facts { display: grid; gap: 2.25rem; }
.facts h3 {
  padding-top: 0.875rem;
  border-top: 1px solid var(--rule-strong);
  font: 700 0.8125rem/1.3 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
}
.facts h3 + * { margin-top: 0.875rem; }
.facts p { color: var(--text-2); }

.list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.list li {
  position: relative;
  padding: 0.5625rem 0 0.5625rem 1.375rem;
  border-bottom: 1px solid var(--rule);
  color: var(--text-2);
}
.list li:last-child { border-bottom: 0; }
.list li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 1.05em;
  width: 0.5rem;
  height: 1px;
  background: var(--accent);
}
@media (min-width: 42.5em) { .list--cols { columns: 2; column-gap: 2.25rem; } .list--cols li { break-inside: avoid; } }

.why {
  padding: 1.25rem 0 0;
  border-top: 2px solid var(--accent);
}
.why p { color: var(--text); }

/* ── Capability index (platform) ─────────────────────────────────── */

.index-list { margin: 0; padding: 0; list-style: none; border-bottom: 1px solid var(--rule); }
.index-list > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.375rem 2.5rem;
  padding: 1.375rem 0;
  border-top: 1px solid var(--rule);
}
.index-list > li:first-child { border-top-color: var(--rule-strong); }
@media (min-width: 51.25em) { .index-list > li { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); } }
.index-list h3 { font: 650 1.125rem/1.35 var(--sans); }
.index-list p { color: var(--text-2); max-width: 35em; }

.chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
.chips li {
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--rule);
  border-radius: 2px;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-2);
}

/* ── Contact ─────────────────────────────────────────────────────── */

.alert-line {
  display: grid;
  gap: 1.125rem;
  padding: clamp(1.5rem, 4vw, 2.25rem) 0;
  border-top: 1px solid var(--rule-on-ink);
}
@media (min-width: 51.25em) { .alert-line { grid-template-columns: 1fr auto; align-items: center; } }
.alert-line__phone {
  font: 400 clamp(2rem, 1.5rem + 2vw, 3rem)/1.05 var(--serif);
  color: var(--on-ink);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.band a.alert-line__phone { color: var(--on-ink); }
.band a.alert-line__phone:hover { color: var(--accent-on-ink); }

.contact-grid { display: grid; gap: 3.5rem; }
@media (min-width: 60em) { .contact-grid { grid-template-columns: minmax(0, 7fr) minmax(0, 4fr); gap: 5rem; } }

/* Contact options: one ruled column of email / WhatsApp / call. */
.contact-options { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
.contact-options .actions { margin-top: 1rem; }

.contact-aside { display: grid; gap: 1.75rem; align-content: start; }
.contact-aside dl { display: grid; gap: 1.375rem; margin: 0; }
.contact-aside dt {
  font: 700 0.8125rem/1.3 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.contact-aside dd { margin: 0.375rem 0 0; font-size: 1.125rem; }
.contact-aside dd a { color: var(--text); }
.contact-aside dd a:hover { color: var(--accent); }

/* ── Closing CTA band ────────────────────────────────────────────── */

.cta { padding-block: clamp(4.5rem, 10vw, 8rem); }
.cta .h2 { max-width: 14em; font-size: clamp(2.2rem, 1.4rem + 3vw, 3.8rem); }
.cta .lede { margin-top: 1.25rem; }
.cta .actions { margin-top: 2.25rem; }
.cta__contact {
  display: grid;
  gap: 1.5rem;
  margin-top: clamp(3rem, 6vw, 4.5rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule-on-ink);
}
@media (min-width: 45em) { .cta__contact { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 68.75em) { .cta__contact { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.cta__contact dt {
  font: 700 0.75rem/1.3 var(--sans);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--on-ink-2);
}
.cta__contact dd { margin: 0.5rem 0 0; font-size: 1.125rem; color: var(--on-ink); }
.band .cta__contact dd a { color: var(--on-ink); }
.band .cta__contact dd a:hover { color: var(--accent-on-ink); }

/* ── Legal pages ─────────────────────────────────────────────────── */

.legal-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 3rem; }
@media (min-width: 64em) {
  .legal-layout { grid-template-columns: 14rem minmax(0, 1fr); gap: 4.5rem; }
  .legal-toc { position: sticky; top: calc(var(--header-h) + 1.5rem); align-self: start; }
}
.legal-toc summary { cursor: pointer; list-style: none; padding-block: 0.6875rem; }
.legal-toc summary::-webkit-details-marker { display: none; }
.legal-toc summary::after { content: " +"; color: var(--muted); }
.legal-toc details[open] summary::after { content: " \2212"; }
@media (min-width: 64em) { .legal-toc summary { padding-block: 0; } }
.legal-toc ol { margin: 1rem 0 0; padding: 0; list-style: none; counter-reset: toc; }
.legal-toc li { counter-increment: toc; border-top: 1px solid var(--rule); }
.legal-toc a {
  display: block;
  padding: 0.5rem 0;
  font-size: 0.875rem;
  color: var(--text-2);
  text-decoration: none;
}
.legal-toc a::before { content: counter(toc, decimal-leading-zero) "  "; white-space: pre; color: var(--muted); font-variant-numeric: tabular-nums; }
.legal-toc a:hover { color: var(--accent); }

.legal { max-width: 38rem; }
.legal h2 {
  margin-top: 3rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule-strong);
  font: 650 1.25rem/1.3 var(--sans);
  scroll-margin-top: calc(var(--header-h) + 1rem);
}
.legal h2:first-child { margin-top: 0; }
.legal h3 { margin-top: 1.5rem; font: 650 1rem/1.4 var(--sans); color: var(--text); }
.legal p { margin-top: 0.875rem; color: var(--text-2); }
.legal ul { margin: 0.875rem 0 0; padding-left: 1.25rem; color: var(--text-2); }
.legal li + li { margin-top: 0.5rem; }
.legal-contact { display: grid; gap: 1rem; margin-top: 1rem; }
@media (min-width: 40em) { .legal-contact { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.legal-contact dt { font: 700 0.75rem/1.3 var(--sans); letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
.legal-contact dd { margin: 0.375rem 0 0; color: var(--text); }
.placeholder {
  display: inline-block;
  padding: 0.125rem 0.375rem;
  border: 1px dashed var(--accent);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.85em;
  color: var(--accent);
  background: #f2f8f7;
}

/* ── Footer ──────────────────────────────────────────────────────── */

.site-footer {
  background: var(--ink);
  color: var(--on-ink-2);
  font-size: 0.9375rem;
  border-top: 1px solid var(--rule-on-ink);
}
.site-footer a { color: var(--on-ink); text-decoration: none; }
.site-footer a:hover { color: var(--accent-on-ink); text-decoration: underline; }
.site-footer .brand, .site-footer .brand:hover { color: var(--on-ink); text-decoration: none; }
.footer-grid {
  display: grid;
  gap: 2.5rem;
  padding-block: 4rem 3rem;
}
@media (min-width: 51.25em) { .footer-grid { grid-template-columns: minmax(0, 5fr) repeat(3, minmax(0, 2.4fr)); } }
.footer-grid p { margin-top: 1.125rem; max-width: 26em; }
.footer-grid h2 {
  margin-bottom: 0.875rem;
  font: 700 0.75rem/1.3 var(--sans);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--on-ink-2);
}
.footer-grid ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 0; }
.footer-grid ul a { display: inline-block; padding-block: 0.6875rem; }
.footer-grid ul li:not(:has(a)) { padding-block: 0.6875rem; }
@media (min-width: 35em) and (max-width: 51.1875em) {
  .footer-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .footer-grid > :first-child { grid-column: 1 / -1; }
}
.footer-legal {
  margin: 0 0 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule-on-ink);
  max-width: 62em;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--on-ink-2);
  text-wrap: pretty;
}
.footer-legal a { text-decoration: underline; text-underline-offset: 0.18em; white-space: nowrap; } /* not colour alone (1.4.1) */
.footer-legal .placeholder { background: transparent; color: var(--accent-on-ink); border-color: var(--accent-on-ink); }
.footer-base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
  padding-block: 1.25rem 1.75rem;
  border-top: 1px solid var(--rule-on-ink);
  font-size: 0.875rem;
}
.footer-base ul { display: flex; flex-wrap: wrap; gap: 0 1.25rem; margin: 0; padding: 0; list-style: none; }
.footer-base a { display: inline-block; padding-block: 0.6875rem; }

/* ── Utilities ───────────────────────────────────────────────────── */

.mt-s { margin-top: 1rem; }
.mt-m { margin-top: 2rem; }
.mt-l { margin-top: clamp(2.5rem, 5vw, 4rem); }
.measure { max-width: 40em; }


/* ── Hero mark: the logo redrawn as a blueprint line drawing ─────────
   Placed by layout, not by eye: the art column is as tall as the copy beside
   it (grid stretch), and the SVG fills that box with xMaxYMid meet. Result:
   the shield's right edge sits on the content edge (same line as the header
   CTA and the stats rule), its top meets the headline's top and its bottom
   meets the buttons' bottom. The viewBox is cropped to the shield itself so
   no invisible padding offsets those edges. */
.hero__copy .display { max-width: 13em; }
.hero__art { display: none; }
/* From 960px; the art column scales with the screen so it never crowds the headline. */
@media (min-width: 60em) {
  .hero__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) clamp(12rem, 22vw, 18.75rem);
    column-gap: 4rem;
  }
  .hero__art { display: block; position: relative; }
  .hero-mark {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none;
  }
  .hero-mark__shield, .hero-mark__v {
    fill: none;
    stroke: var(--accent-on-ink);
    stroke-linecap: round;
    stroke-linejoin: round;
    /* Widths in viewBox units: ~288 px / 47.2 ≈ 6.1 px per unit, so 0.21 ≈ 1.3 px.
       (non-scaling-stroke would break pathLength dashes in Chrome.) */
    stroke-width: 0.21;
  }
  .hero-mark__shield { opacity: 0.55; }
  .hero-mark__v { opacity: 0.9; stroke-width: 0.27; }
  .hero-mark__dot { fill: var(--accent-on-ink); }
}


/* ── Product screenshots ──────────────────────────────────────────────
   Real platform captures at 2x, WebP. Width/height attributes on every
   <img> reserve the box before decode, so screenshots never shift layout. */
.shot { margin: 0; min-width: 0; }
.shot__frame {
  overflow: hidden;
  border-radius: 0.625rem;
  background: #f4f6fa;                         /* the app's own light canvas while decoding */
  border: 1px solid #d5dbe1;                   /* a hairline so white UI never melts into ivory */
  box-shadow:
    0 1.5rem 3rem -1.75rem rgb(4 22 24 / 0.35),
    0 3.75rem 7.5rem -3.75rem rgb(4 22 24 / 0.4);
}
.band .shot__frame {
  border-color: rgb(255 255 255 / 0.14);
  box-shadow:
    0 1.5rem 3rem -1.75rem rgb(0 0 0 / 0.55),
    0 3.75rem 7.5rem -3.75rem rgb(0 0 0 / 0.6);
}
.shot img { display: block; width: 100%; height: auto; }
.shot figcaption { margin-top: 0.75rem; font-size: 0.875rem; line-height: 1.45; color: var(--text-2); } /* text-2: ≥6:1 even over the shadow */
.shot figcaption strong { color: var(--text); font-weight: 600; }
.band .shot figcaption { color: var(--on-ink-2); }
.band .shot figcaption strong { color: var(--on-ink); }

/* Hero screenshot straddles the band edge: the band reserves space below
   its content, the figure pulls itself up into it. */
.product-hero { padding-bottom: clamp(9.375rem, 24vw, 21.25rem); }
.straddle { position: relative; z-index: 2; margin-top: calc(-1 * clamp(7.5rem, 20vw, 18.75rem)); }
.straddle + .section { border-top: 0; }

/* Text beside a screenshot */
.feature { display: grid; gap: 2.5rem; align-items: center; }
@media (min-width: 60em) {
  .feature { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 4rem; }
  .feature--flip { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .feature--flip > :first-child { order: 2; }
}

/* Gallery: justified rows. Each tile's flex-grow equals its image's
   width/height, with flex-basis 0, so every image in a row renders at
   exactly the same height: h = (row width - gaps) / sum(aspect ratios). */
.gallery { display: grid; gap: clamp(1.75rem, 3vw, 2.5rem); }
.gallery__row { display: flex; gap: clamp(1rem, 2vw, 1.5rem); align-items: flex-start; }
.gallery__row > .shot { flex-basis: 0; }
.g-globe { flex-grow: 1.350; } /* 1164 × 862 */
.g-spiral { flex-grow: 1.524; } /* 1164 × 764 */
.g-map { flex-grow: 1.257; } /* 1164 × 926 */
.g-countrymonth { flex-grow: 1.423; } /* 1746 × 1227 */
.g-forecast { flex-grow: 1.450; } /* 966 × 666 */
.g-score { flex-grow: 1.153; } /* 768 × 666 */
.g-sectors { flex-grow: 1.594; } /* 966 × 606 */
.g-timeline { flex-grow: 1.778; } /* 1362 × 766 */
.g-insights { flex-grow: 0.923; } /* 768 × 832 */
.g-topgroups { flex-grow: 1.055; } /* 966 × 916 */
/* Phones: one swipeable row per theme instead of a 2,900px stack. */
@media (max-width: 44.9375em) {
  .gallery__row {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: 0.75rem;
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: var(--gutter);
    scroll-padding-inline: var(--gutter);
  }
  .gallery__row > .shot { flex: 0 0 82%; scroll-snap-align: start; }
}

/* Phones: a full-width app screenshot shrunk to 343px would put its text at
   ~3px. Instead the frame pans: the image keeps ~75% of real size (56.25rem
   for a 1,200px app crop) and the reader swipes across it. */
@media (max-width: 44.9375em) {
  .shot--pan .shot__frame { overflow-x: auto; overscroll-behavior-x: contain; }
  .shot--pan .shot__frame img { width: 56.25rem; max-width: none; }
  .shot--pan figcaption::after { content: " Swipe to explore."; color: var(--muted); }
}
.shot__frame:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.band .shot__frame:focus-visible { outline-color: var(--accent-on-ink); }

/* Home page teaser screenshot */
.teaser .shot { margin-top: clamp(2.5rem, 5vw, 4rem); }

/* ── Extras ────────────────────────────────────────────────────────── */

/* Section eyebrows on light sections carry a short teal rule, echoing the
   brochure's dark-band rule. Drawn in when the eyebrow scrolls into view. */
main > section:not(.band) .eyebrow:not(.why .eyebrow) {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
main > section:not(.band) .eyebrow:not(.why .eyebrow)::before {
  content: "";
  flex: none;
  width: 1.5rem;
  height: 2px;
  background: var(--accent);
  transform-origin: left;
}


/* Email / call chooser: built by site.js on mouse-driven screens only. No
   `display` here, or it would override the UA's hiding of a closed popover. */
.reach {
  position: fixed;
  inset: auto;
  margin: 0;
  width: min(18rem, calc(100vw - 2rem));
  padding: 1rem 0.75rem 0.75rem;
  background: var(--paper);
  color: var(--text);
  border: 1px solid var(--rule-strong);
  box-shadow: 0 1rem 2.5rem rgb(12 43 45 / 0.2);
}
.reach__title { padding: 0 0.625rem; font: 600 0.9375rem/1.35 var(--sans); overflow-wrap: anywhere; }
.reach__hint { padding: 0 0.625rem; margin-top: 0.25rem; font-size: 0.8125rem; color: var(--muted); }
.reach ul { list-style: none; margin: 0.625rem 0 0; padding: 0; }
.reach__opt {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  padding: 0.5625rem 0.625rem;
  font: 500 0.9375rem/1.3 var(--sans);
  color: var(--text);
  text-align: left;
  text-decoration: none;
  background: none;
  border: 0;
  border-top: 1px solid var(--rule);
  cursor: pointer;
}
.reach__opt::after { content: "→"; color: var(--accent); }
.reach__opt:hover, .reach__opt:focus-visible { background: var(--mist); color: var(--accent-hover); }

/* Copy-email button, inserted by site.js next to mailto links. */
.copy-btn {
  margin-left: 0.625rem;
  padding: 2px 0.5rem;
  border: 1px solid currentColor;
  border-radius: 2px;
  background: transparent;
  color: var(--muted);
  font: 600 0.75rem/1.6 var(--sans);
  letter-spacing: 0.04em;
  cursor: pointer;
  vertical-align: middle;
}
.copy-btn { position: relative; }
.copy-btn::after { content: ""; position: absolute; inset: -0.625rem -0.375rem; } /* 44px hit area, same look */
.copy-btn:hover { color: var(--accent); }
.band .copy-btn { color: var(--on-ink-2); }
.band .copy-btn:hover { color: var(--accent-on-ink); }
.copy-btn[data-done] { color: var(--accent); border-color: var(--accent); }
.band .copy-btn[data-done] { color: var(--accent-on-ink); border-color: var(--accent-on-ink); }

/* Reading progress: a 2px teal line along the header's bottom edge, driven
   by the scroll position itself (scroll-driven animation, no JS, runs on
   the compositor). Browsers without support simply don't show it. */
@supports (animation-timeline: scroll()) {
@media (prefers-reduced-motion: no-preference) {
  @keyframes vy-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
  .site-header::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    background: var(--accent);
    transform-origin: left;
    animation-name: vy-progress;
    animation-duration: auto;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: scroll(root block);
  }
}}


/* ── Motion ─────────────────────────────────────────────────────────
   Only transform + opacity (compositor-only, no layout or paint per frame).
   Everything sits behind prefers-reduced-motion; without JS nothing is hidden. */

@media (prefers-reduced-motion: no-preference) {
  /* Hero entrance: pure CSS so it starts at first paint, no JS flash. */
  @keyframes vy-rise { from { opacity: 0; transform: translateY(0.875rem); } }
  @keyframes vy-draw { from { transform: scaleX(0); } }

  .hero > .wrap > :not(.hero__row), .hero__copy > *, .page-hero > .wrap > * {
    animation: vy-rise calc(900ms * var(--tempo)) cubic-bezier(0.2, 0.7, 0.2, 1) both;
  }
  .page-hero > .wrap > :nth-child(2) { animation-delay: calc(90ms * var(--tempo)); }
  .page-hero > .wrap > :nth-child(3) { animation-delay: calc(180ms * var(--tempo)); }
  .page-hero > .wrap > :nth-child(4) { animation-delay: calc(270ms * var(--tempo)); }
  .page-hero > .wrap > :nth-child(5) { animation-delay: calc(360ms * var(--tempo)); }
  /* home hero: eyebrow → headline → lede → buttons → stats */
  .hero__copy > :nth-child(1) { animation-delay: calc(90ms * var(--tempo)); }
  .hero__copy > :nth-child(2) { animation-delay: calc(180ms * var(--tempo)); }
  .hero__copy > :nth-child(3) { animation-delay: calc(270ms * var(--tempo)); }
  .hero > .wrap > .stats { animation-delay: calc(360ms * var(--tempo)); }
  .eyebrow--ruled::before {
    transform-origin: left;
    animation: vy-draw calc(1100ms * var(--tempo)) cubic-bezier(0.2, 0.7, 0.2, 1) calc(150ms * var(--tempo)) both;
  }

  /* Scroll reveal: site.js adds .reveal only to blocks still below the fold. */
  .reveal {
    opacity: 0;
    transform: translateY(1rem);
    transition: opacity calc(700ms * var(--tempo)) ease, transform calc(800ms * var(--tempo)) cubic-bezier(0.2, 0.7, 0.2, 1);
    transition-delay: calc(var(--d, 0ms) * var(--tempo));
  }
  .reveal.is-in { opacity: 1; transform: none; }
  .reveal:focus-within { opacity: 1; transform: none; transition: none; }

  /* Ruled items: the 1px rule draws across as the block arrives. */
  .item.reveal, .stat.reveal { position: relative; border-top-color: transparent; }
  .item.reveal::before, .stat.reveal::before {
    content: "";
    position: absolute;
    top: -1px; left: 0; right: 0;
    height: 1px;
    background: var(--rule-strong);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform calc(1000ms * var(--tempo)) cubic-bezier(0.2, 0.7, 0.2, 1);
    transition-delay: calc((var(--d, 0ms) + 120ms) * var(--tempo));
  }
  .band .item.reveal::before, .band .stat.reveal::before { background: var(--rule-on-ink); }
  .item.reveal.is-in::before, .stat.reveal.is-in::before { transform: none; }


  /* Eyebrow rule draws in with its eyebrow. */
  .eyebrow.reveal::before { transform: scaleX(0); transition: transform calc(900ms * var(--tempo)) cubic-bezier(0.2, 0.7, 0.2, 1) calc(200ms * var(--tempo)); }
  .eyebrow.reveal.is-in::before { transform: none; }


  /* Micro-interactions */
  .arrow-link::after { display: inline-block; transition: transform 200ms ease; }
  .arrow-link:hover::after { transform: translateX(0.1875rem); }
  .btn { transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease, transform 160ms ease; }
  .btn:active { transform: translateY(1px); }

  /* The rest of the hero waits for the last bar (~1.8s) instead of competing with it. */


  /* Hero mark draws itself: shield outline traces (1.6 s), then the V. */
  @keyframes vy-trace { from { stroke-dashoffset: 1; } }
  html:not(.vy-home-seen) .hero-mark__shield,
  html:not(.vy-home-seen) .hero-mark__v { stroke-dasharray: 1; }
  html:not(.vy-home-seen) .hero-mark__shield { animation: vy-trace calc(1800ms * var(--tempo)) cubic-bezier(0.6, 0, 0.25, 1) calc(300ms * var(--tempo)) both; }
  html:not(.vy-home-seen) .hero-mark__v { animation: vy-trace calc(900ms * var(--tempo)) cubic-bezier(0.6, 0, 0.25, 1) calc(1700ms * var(--tempo)) both; }
  html:not(.vy-home-seen) .hero-mark__dot { transform-box: fill-box; transform-origin: center; animation: vy-pop calc(360ms * var(--tempo)) cubic-bezier(0.2, 0.7, 0.2, 1.4) both; }
  html:not(.vy-home-seen) .hero-mark__dot:nth-of-type(1) { animation-delay: calc(1600ms * var(--tempo)); }
  html:not(.vy-home-seen) .hero-mark__dot:nth-of-type(2) { animation-delay: calc(2450ms * var(--tempo)); }
  /* ── Shield draw-on (first page of a session) ─────────────────────
     V path length = 2·√(12² + 24.5²) ≈ 54.6, so a 55 dash covers it exactly. */
  @keyframes vy-stroke { from { stroke-dashoffset: 55; } }
  @keyframes vy-pop { from { opacity: 0; transform: scale(0.2); } }
  html:not(.vy-returning) .site-header .logo-v {
    stroke-dasharray: 55;
    animation: vy-stroke calc(900ms * var(--tempo)) cubic-bezier(0.6, 0, 0.2, 1) calc(250ms * var(--tempo)) both;
  }
  html:not(.vy-returning) .site-header .logo-dot {
    transform-box: fill-box;
    transform-origin: center;
    animation: vy-pop calc(320ms * var(--tempo)) cubic-bezier(0.2, 0.7, 0.2, 1.4) both;
  }
  html:not(.vy-returning) .site-header .logo-dot:nth-of-type(1) { animation-delay: calc(150ms * var(--tempo)); }
  html:not(.vy-returning) .site-header .logo-dot:nth-of-type(2) { animation-delay: calc(1100ms * var(--tempo)); }

  /* ── Headline line reveal ─────────────────────────────────────────
     site.js splits a below-the-fold .h2 into measured lines; each line
     rises out of its own mask. Restored to plain text once finished. */
  .line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.1em;   /* keep serif descenders (g, y, p) out of the clip */
    margin-bottom: -0.1em;
  }
  .line__in {
    display: inline-block;
    transform: translateY(110%);
    transition: transform calc(950ms * var(--tempo)) cubic-bezier(0.2, 0.7, 0.2, 1);
    transition-delay: calc((var(--l, 0) * 95ms) * var(--tempo));
  }
  .lines-in .line__in { transform: none; }

  /* ── Page transitions (Chrome/Edge 126+, Safari 18.2+; others: plain nav) */
  @view-transition { navigation: auto; }
  @keyframes vy-out { to { opacity: 0; } }
  @keyframes vy-in { from { opacity: 0; } }
  .site-header { view-transition-name: site-header; } /* header stays put across pages */
  ::view-transition-old(root) { animation: vy-out 160ms ease both; }
  ::view-transition-new(root) { animation: vy-in 260ms ease 60ms both; }
}

/* ── Row hover: only where hover is real (not tap-emulated on phones) */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .index-list > li { position: relative; }
  .index-list > li::after {
    content: "";
    position: absolute;
    left: 0; right: 0; top: -1px;
    height: 1px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 520ms cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  .index-list > li:hover::after { transform: none; }
  .index-list h3 { transition: transform 300ms cubic-bezier(0.2, 0.7, 0.2, 1), color 300ms ease; }
  .index-list > li:hover h3 { transform: translateX(0.25rem); color: var(--accent); }

  .list li::before { transform-origin: left; transition: transform 300ms cubic-bezier(0.2, 0.7, 0.2, 1); }
  .list li:hover::before { transform: scaleX(2); }
}

@media print {
  .reveal, .reveal::before { opacity: 1 !important; transform: none !important; }
  .line__in { transform: none !important; }
  .site-header, .site-footer, .cta, .jump { display: none; }
  .band { background: none; color: #000; }
  body { font-size: 12pt; }
}
