/* ==========================================================================
   CRVITAE — "The Living Résumé"  ·  main.css
   Design tokens derived from Branding/CRVITAE Design System (canonical).
   Rule: CSS never hides content. JS (when present) sets initial states and
   reveals — so no-JS, reduced-motion and crawlers always get the full page.
   Rule: layout is written in LOGICAL properties only (inline/block), so the
   Arabic route at /ar/ mirrors from `dir=rtl` alone. Grid and flex already
   follow `direction` — never add a [dir=rtl] override for those
   (PAGE-CONTRACT.md §7). The only exceptions below are transform-origin,
   which has no logical form, and one keyframe translate in chat.css.
   ========================================================================== */

:root {
  /* Primary (violet) */
  --violet-300: #a89bff;
  --violet-400: #8b7cff;
  --violet-500: #7c6ff6;
  --violet-600: #6b5be0;
  --violet-800: #3f2f9e;

  /* Plum neutrals */
  --ink-700: #221a3a;
  --ink-900: #100822;
  --ink-950: #0a0518;

  /* Stat ribbon accents */
  --mint:  #2ee5b4;
  --coral: #f46b6b;
  --amber: #f0bc4e;
  --iris:  #6f8afa;

  /* Surfaces */
  --bg:        var(--ink-900);
  --bg-elev:   #15092a;
  --card:      #1a1030;
  --card-hi:   #221540;
  --stroke:    rgba(255,255,255,0.06);
  --stroke-2:  rgba(255,255,255,0.12);

  --fg-1: #f3eefe;
  --fg-2: #b8aed1;
  /* Ink Muted. Was #7e7596, which computed to 4.20:1 on --card and 3.90:1 on
     --card-hi — below AA for normal-size text. Now matches DESIGN.md:185:
     5.83 / 5.42 / 6.27:1 on --card / --card-hi / --bg. */
  --fg-3: #968ead;

  /* The primary-action gradient. DESIGN.md:249 specifies #8b7cff → #7c6ff6 →
     #6b5be0, but its 15.2px/700 white label measures 3.27:1 on the light stop
     and 3.85:1 on the mid stop — below the 4.5:1 that size demands. Same
     135deg sweep, same hue, darkened until white clears 4.5:1 across the whole
     span: 4.70 / 5.54 / 6.87:1. */
  --grad-cta: linear-gradient(135deg, #6f60e4 0%, #6455d4 52%, #5747bf 100%);

  /* Signal glow. A STATE — focus, live, just-changed — never a resting finish
     (DESIGN.md:239, The Earned Glow Rule). */
  --glow: 0 0 0 1px rgba(124,111,246,0.45), 0 8px 28px rgba(124,111,246,0.25);
  --shadow-lg: 0 24px 60px rgba(0,0,0,0.5);
  --shadow-md: 0 8px 20px rgba(0,0,0,0.35);

  /* DESIGN.md radius scale */
  --r-xs: 6px;
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 18px;
  --r-xl: 24px;

  --sans: 'Cairo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: 'JetBrains Mono', Cairo, ui-monospace, Menlo, monospace;
  /* Three voices, one rule.
       --display  the narrative: headlines, and nothing else
       --sans     the product: all body copy, and ALL Arabic
       --mono     the data: figures, labels, identifiers, timestamps
     Cairo sits second in --display so an Arabic run inside a Latin headline
     resolves without a miss, and so the "all Arabic renders in Cairo" gate in
     verify-site sees Cairo in every computed family on the page. */
  --display: 'Fraunces', 'Cairo', Georgia, 'Times New Roman', serif;

  /* The type ramp, and the half of it this stylesheet actually reaches for.
     DESIGN.md documents 11 12 13 14 16 18 22 28 36 48 64; declared here are the
     six rungs that govern something, because a token nobody references is not a
     system, it is a comment that lies.

     The ramp governs FIXED type — labels, captions, body, the small end where a
     reader compares one size against another and notices a rung. Everything
     above 18px on this page is fluid: `clamp(2rem, 1.2rem + 3.4vw, 4.4rem)` is
     one size and two curve controls, and rounding the controls onto rungs would
     make the scale true at exactly two viewport widths and a fiction at every
     other. Display type interpolates by design; do not snap it.

     54 declarations moved onto these six, none by more than 1px. */
  --t-11: 0.6875rem; --t-12: 0.75rem;  --t-13: 0.8125rem;
  --t-14: 0.875rem;  --t-16: 1rem;     --t-18: 1.125rem;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* Three durations, and each rung is a MEANING rather than a number. There
     were eight across the three stylesheets — 0.15 0.16 0.18 0.2 0.25 0.3 0.4
     0.6 — which is not a scale, it is eight separate decisions nobody can
     reproduce. Nothing here got slower: 0.6s became 0.42s, and the rest moved
     by at most 0.04s.

       --dur-tap   something answering the pointer or the keyboard, now.
                   Hover fills, colour changes, a field taking focus.
       --dur-move  something changing position or elevation, so the eye can
                   follow it. A card lifting, a panel opening, a chevron turning.
       --dur-mood  an ambient change the reader is not waiting on. The header
                   scrim, the rail's chapter colour, the score ring's stroke.

     A fourth rung would mean the system has stopped being a system. */
  --dur-tap: 0.16s;
  --dur-move: 0.26s;
  --dur-mood: 0.42s;

  --pad-x: clamp(20px, 6vw, 96px);
  --sec-gap: clamp(96px, 14vh, 200px);
  --header-h: 72px;
}

/* ---------- Reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  scroll-behavior: smooth; -webkit-text-size-adjust: 100%;
  /* .hdr is fixed and 72px tall. Without this, Shift+Tab to a control above
     the viewport scrolls it flush to the top edge and the header covers it
     entirely -- WCAG 2.2 SC 2.4.11. Measured: five controls buried, now none. */
  scroll-padding-block-start: calc(var(--header-h) + 12px);
}
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg-2);
  font: 400 clamp(1rem, 0.95rem + 0.2vw, 1.0625rem)/1.7 var(--sans);
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
}
img, video, svg { max-width: 100%; height: auto; display: block; }
a { color: var(--violet-300); text-decoration: none; }
a:hover { color: var(--fg-1); }
button { font: inherit; cursor: pointer; }
h1, h2, h3, h4, p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
::selection { background: rgba(124,111,246,0.55); color: #fff; }

/* Arabic is a first-class direction, not a set of per-element patches. This
   also forces Cairo: JetBrains Mono ships no Arabic subset, so Arabic inside
   any --mono context silently fell through to the system monospace. A
   declared value always beats an inherited one, so this needs no specificity
   war with the mono rules it corrects. */
[lang="ar"] { direction: rtl; unicode-bidi: isolate; font-family: var(--sans); }

/* Focus ring. DESIGN.md:235 documents `0 0 0 3px rgba(124,111,246,0.40)`, but
   composited that ring measures 1.76:1 against --bg and 1.77:1 against
   --card-hi — it cannot carry SC 1.4.11 on its own. Same 3px geometry and the
   same violet at full opacity: 5.95:1 on --bg, 5.14:1 on --card-hi. Kept as
   `outline` (not box-shadow) so a component's own box-shadow — .btn--primary's,
   for one — can never suppress it. */
:focus-visible { outline: 3px solid var(--violet-400); outline-offset: 2px; border-radius: var(--r-xs); }

/* Screen-reader-only. Used twice in index.html; RTL-safe (no physical offset). */
.visually-hidden:not(:focus):not(:active) {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip: rect(0 0 0 0); clip-path: inset(50%);
}

/* Progressive enhancement: index.html swaps html.no-js → html.js on boot. */
html.js [data-tilt] { will-change: transform; transform-style: preserve-3d; }
html.no-js .chat { display: none; }

/* Subtle branded scrollbar (desktop) */
@media (pointer: fine) {
  ::-webkit-scrollbar { width: 10px; }
  ::-webkit-scrollbar-track { background: var(--ink-950); }
  ::-webkit-scrollbar-thumb { background: var(--ink-700); border-radius: var(--r-xs); border: 2px solid var(--ink-950); }
  ::-webkit-scrollbar-thumb:hover { background: var(--violet-600); }
}

/* ---------- Atmosphere ---------- */
.aurora {
  position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(52vw 42vh at 18% 8%,  rgba(124,111,246,0.16), transparent 62%),
    radial-gradient(46vw 40vh at 85% 30%, rgba(63,47,158,0.22),  transparent 64%),
    radial-gradient(60vw 50vh at 50% 105%, rgba(38,28,102,0.35), transparent 70%),
    var(--bg);
}
.aurora::after {
  content: ""; position: absolute; inset: 0; opacity: 0.035;
  background: url("../assets/img/noise.png"); background-size: 200px 200px;
}

/* Left progress rail — the "one line" that owns the page */
.rail {
  position: fixed; inset-inline-start: max(10px, calc(6vw - 34px)); inset-block: 0;
  width: 2px; z-index: 40;
  pointer-events: none;
}
.rail i {
  display: block; width: 100%; height: 100%;
  background: currentColor; color: var(--mint);
  transform-origin: top; transform: scaleY(0);
  transition: color var(--dur-mood) var(--ease-out);
}
@media (max-width: 900px) { .rail { display: none; } }

/* ---------- Header ---------- */
.hdr {
  position: fixed; inset-block-start: 0; inset-inline: 0; z-index: 90;
  display: flex; align-items: center; justify-content: space-between;
  height: var(--header-h); padding: 0 var(--pad-x);
  /* At rest the header floats over the hero photograph. The scrim was tuned
     against a master whose brightest pixel was #f5ffff, where the nav measured
     2.20:1 unscrimmed; the master is now a near-black colonnade that peaks at
     179/255, so the same scrim has an easier job than it was built for. It
     stays as it is: it costs nothing, and the next photograph to land here may
     be bright again. It holds >=0.80 alpha across the whole 22–50px text band
     and only then fades out, so the header still reads as transparent. */
  background: linear-gradient(180deg, rgba(10,5,24,0.94) 0%, rgba(10,5,24,0.80) 72%, rgba(10,5,24,0) 100%);
  transition: background var(--dur-mood), backdrop-filter var(--dur-mood);
}
/* 0.72 left 28% of an arbitrary screenshot showing through; 0.92 bounds it. */
.hdr.is-scrolled {
  background: rgba(16,8,34,0.92);
  backdrop-filter: blur(14px) saturate(1.2);
  border-block-end: 1px solid var(--stroke);
}
.hdr__logo img { height: 26px; width: auto; }
.hdr__nav { display: flex; gap: clamp(14px, 2vw, 30px); }
/* DESIGN.md:270 — nav items are Ink Secondary, brightening to Ink High. */
.hdr__nav a { font: 600 var(--t-12)/1 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-2); padding: 8px 2px; }
.hdr__nav a:hover, .hdr__nav a.is-active { color: var(--fg-1); }
/* Inserted by tools/build-ar.mjs on both editions. It had no rule of its own,
   so it rendered as a bare link in the header. */
.hdr__lang {
  flex: none; display: inline-flex; align-items: center;
  min-block-size: 34px; padding-inline: 12px; border-radius: 999px;
  color: var(--fg-2); font: 600 var(--t-13)/1 var(--sans); text-decoration: none;
}
.hdr__lang:hover { color: var(--fg-1); background: rgba(255,255,255,0.06); }
.hdr__lang:focus-visible { outline: 3px solid var(--violet-400); outline-offset: 2px; }

.hdr__cta {
  font: 700 var(--t-13)/1 var(--mono); letter-spacing: 0.08em;
  color: #fff; background: var(--grad-cta);
  border: 0; border-radius: 999px; padding: 11px 20px;
  box-shadow: 0 4px 18px rgba(124,111,246,0.35);
  transition: transform var(--dur-move) var(--ease-out), box-shadow var(--dur-move);
}
.hdr__cta:hover { transform: translateY(-2px); box-shadow: 0 8px 26px rgba(124,111,246,0.5); }
@media (max-width: 900px) { .hdr__nav { display: none; } }
/* At 320px the logo, language switch, motion control and CTA summed to exactly
   the available 280px — touching, with no gap at all. Give them room. */
@media (max-width: 420px) {
  .hdr { padding-inline: 12px; gap: 8px; }
  .hdr__logo img { height: 22px; }
  .hdr__cta { padding-inline: 12px; font-size: var(--t-11); }
  .hdr__lang { padding-inline: 8px; font-size: var(--t-12); }
}

/* ---------- Section primitives ---------- */
.sec { position: relative; padding: var(--sec-gap) var(--pad-x); max-width: 1560px; margin: 0 auto; }
.sec--full { max-width: none; }

/* ==========================================================================
   THE GRID — twelve columns, and four ways to open a chapter
   ==========================================================================
   Measured before this existed: every section headline rendered at exactly
   664px and every lede at exactly 613px on a 1512px viewport, and eight of the
   ten chapter openers were byte-identical in shape — ghost numeral, eyebrow,
   headline, ribbon, lede, content. Only the colour and the number changed. Past
   1020px nothing responded at all: one desktop layout from 1021px to 2560px,
   with 500px of gutter sitting dead on each side.

   The uniformity is why this is CSS and not surgery. `.sec` becomes a twelve
   column grid, its children default to the full span, and each chapter is
   assigned an opener with data-layout. No wrappers, no reordering, no new
   elements — which also means the /ar/ parity gate and every translation key
   survive untouched.

   Every placement is grid-column, which is INLINE-direction: column 1 is the
   left on /, the right on /ar/, and the whole system mirrors from dir=rtl with
   no override. Never add a [dir=rtl] rule here.

   The four openers, assigned so no two consecutive chapters share a shape:

     wide      01 chaos · 04 machine · 08 engine
               Headline across ten columns at the largest size on the page, the
               lede tucked under its left half. The chapter arrives.
     masthead  02 signal · 06 money
               Headline in the left seven, lede in the right four and set to the
               headline's own baseline. Editorial; uses the full measure.
     rail      03 conversation · 07 field
               A narrow, tall headline in five columns against a wide lede.
               Quieter — these two chapters carry their weight in the evidence.
     centre    05 answers · 09 faq
               Centred in the middle six. Both are chapters of questions.
   ========================================================================== */
.sec {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: clamp(16px, 1.6vw, 30px);
  align-content: start;
}
/* The default span is everything. Anything not placed below — every stage,
   split, beat row, statwall and form on the page — keeps the full width it
   had, so this cannot silently narrow content.

   min-inline-size: 0 is not decoration. A grid item's default min-width is
   `auto`, which is its MIN-CONTENT size, and `minmax(0, 1fr)` constrains the
   track but not the item sitting in it. A figure whose min-content is 390px
   therefore stayed 390px wide inside a 280px track on a 320px phone and pushed
   the document sideways — measured, +90px. The same guard already existed on
   .split and .beat children for the same reason; this is it for every other
   child of a section. */
.sec > * { grid-column: 1 / -1; min-inline-size: 0; }

@media (min-width: 1000px) {
  /* ---- masthead: the default opener ---- */
  .sec > .eyebrow { grid-column: 1 / -1;      grid-row: 1; }
  .sec > .hline   { grid-column: 1 / span 7;  grid-row: 2; }
  .sec > .ribbon  { grid-column: 1 / span 7;  grid-row: 3; }
  /* The lede sits beside the headline and hangs from the same baseline, which
     is what align-self: end buys: two blocks of different height that agree at
     the bottom rather than at the top. */
  .sec > .lede    { grid-column: 9 / -1;      grid-row: 2 / span 2;
                    align-self: end; margin-block-start: 0; max-width: 42ch; }

  /* ---- wide: the chapter arrives ---- */
  .sec[data-layout="wide"] > .hline {
    grid-column: 1 / span 10; grid-row: 2; max-width: 21ch;
    font-size: clamp(2rem, 0.6rem + 4.6vw, 5.4rem);
  }
  .sec[data-layout="wide"] > .ribbon { grid-column: 1 / span 10; grid-row: 3; }
  .sec[data-layout="wide"] > .lede {
    grid-column: 1 / span 6; grid-row: 4; align-self: start;
    margin-block-start: 30px; max-width: 52ch;
  }

  /* ---- rail: narrow and tall, against a wide lede ---- */
  .sec[data-layout="rail"] > .eyebrow { grid-column: 1 / span 5; grid-row: 1; }
  .sec[data-layout="rail"] > .hline {
    grid-column: 1 / span 5; grid-row: 2; max-width: 12ch;
    font-size: clamp(1.9rem, 0.9rem + 2.6vw, 3.6rem);
  }
  .sec[data-layout="rail"] > .ribbon { grid-column: 1 / span 5; grid-row: 3; }
  .sec[data-layout="rail"] > .lede {
    grid-column: 7 / -1; grid-row: 2 / span 2;
    align-self: start; margin-block-start: 0.4em; max-width: 54ch;
    font-size: clamp(1.08rem, 1rem + 0.5vw, 1.35rem);
  }

  /* ---- centre: the chapters that ask questions ---- */
  .sec[data-layout="centre"] > .eyebrow { grid-column: 4 / span 6; grid-row: 1; justify-content: center; }
  .sec[data-layout="centre"] > .hline {
    grid-column: 4 / span 6; grid-row: 2; max-width: 20ch;
    margin-inline: auto; text-align: center;
  }
  .sec[data-layout="centre"] > .ribbon { grid-column: 4 / span 6; grid-row: 3; margin-inline: auto; }
  .sec[data-layout="centre"] > .lede {
    grid-column: 4 / span 6; grid-row: 4; align-self: start;
    margin: 26px auto 0; text-align: center; max-width: 56ch;
  }
  /* The ribbon's transform-origin is a licensed physical offset (see the RTL
     note beside .ribbon). Centring it means the scale-in has to grow from the
     middle, not from the inline start. */
  .sec[data-layout="centre"] > .ribbon { transform-origin: center; }

  /* The executive report, set as a page: ten columns of twelve, centred by the
     grid rather than by auto margins. Below this breakpoint it is 1 / -1 like
     everything else and fills the phone. */
  #money > .shot { grid-column: 2 / span 10; }
}

.eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font: 600 var(--t-12)/1 var(--mono); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--mint); margin-block-end: 22px;
}
.eyebrow::before { content: ""; width: 26px; height: 1px; background: currentColor; opacity: 0.7; }
.eyebrow[data-tone="coral"] { color: var(--coral); }
.eyebrow[data-tone="amber"] { color: var(--amber); }
.eyebrow[data-tone="iris"]  { color: var(--iris); }
.eyebrow[data-tone="violet"]{ color: var(--violet-300); }

.hline {
  font-family: var(--display); font-weight: 700; font-synthesis-weight: none;
  color: var(--fg-1); letter-spacing: -0.014em;
  font-size: clamp(2rem, 1.2rem + 3.4vw, 4.4rem); line-height: 1.08;
  max-width: 17ch; text-wrap: balance;
}
/* Arabic has no Fraunces cut and is not getting a bad one. It keeps Cairo, and
   gets its display treatment the way Arabic type actually takes it: more
   weight, a fraction less tracking, and the taller line box it needs anyway. */
html[lang="ar"] .hero__title,
html[lang="ar"] .hline,
html[lang="ar"] .cta h2 { font-weight: 900; letter-spacing: -0.005em; }
.hline em { font-style: normal; color: var(--violet-300); }

/* Arabic display type needs a taller line box than Latin at the same size.
   1.04/1.08 were tuned for Latin: in Cairo an Arabic glyph box measures about
   1.45x the font size, so at 91.2px the ink is 132px against a 94.8px line. The
   overflow:hidden on the reveal used to hide that by clipping 37px off every
   line; once the clipping was fixed the lines collided with each other instead.
   This is what "Arabic is not a translation" means in practice — the same words
   in the same size need different leading. */
html[lang="ar"] .hero__title { line-height: 1.34; }
html[lang="ar"] .hline { line-height: 1.36; }
html[lang="ar"] .cta h2 { line-height: 1.34; }
.hline .u-mint  { color: var(--mint); }
.hline .u-coral { color: var(--coral); }
.hline .u-amber { color: var(--amber); }
.hline .u-iris  { color: var(--iris); }
.lede { max-width: 58ch; margin-block-start: 22px; color: var(--fg-2); font-size: clamp(1.02rem, 1rem + 0.25vw, 1.18rem); }
.lede b, .body b { color: var(--fg-1); font-weight: 700; }

/* Ghost section numerals.
   Weight 700, not 800: assets/fonts/ carries JetBrains Mono at 400/500/700
   only, so every 800 var(--mono) on this page was synthetic emboldening.
   Same correction everywhere --mono asked for 800 below. */
.ghost {
  position: absolute; inset-block-start: clamp(20px, 6vh, 70px); inset-inline-end: var(--pad-x); z-index: 0;
  font: 700 clamp(4rem, 10vw, 9rem)/1 var(--mono); letter-spacing: -0.05em;
  font-variant-numeric: tabular-nums;
  color: transparent; -webkit-text-stroke: 1px rgba(124,111,246,0.16);
  user-select: none; pointer-events: none;
}

/* Reveal ribbon under headlines (signature #3) */
.ribbon {
  display: block; width: 92px; height: 3px; margin-block-start: 26px; border-radius: 2px;
  background: currentColor; color: var(--mint);
  transform-origin: left;
}
/* transform-origin has no logical keyword — one of two honest RTL overrides. */
[dir="rtl"] .ribbon { transform-origin: right; }

/* ---------- Glass ---------- */
.glass {
  position: relative;
  background: linear-gradient(160deg, rgba(34,21,64,0.72), rgba(26,16,48,0.86));
  border: 1px solid var(--stroke-2);
  border-radius: var(--r-lg);
  backdrop-filter: blur(16px) saturate(1.15);
  box-shadow: var(--shadow-md);
}
.glass::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: url("../assets/img/noise.png"); background-size: 200px 200px; opacity: 0.03;
}

/* ---------- Bespoke browser chrome for product shots ---------- */
.shot {
  position: relative; border-radius: var(--r-lg); overflow: hidden;
  background: var(--bg-elev);
  border: 1px solid var(--stroke-2);
  box-shadow: var(--shadow-lg);
  transition: box-shadow var(--dur-move) var(--ease-out), border-color var(--dur-move);
}
/* Earned Glow Rule: the violet ring reports pointer/keyboard state, not rest. */
.shot:hover, .shot:focus-within { border-color: rgba(124,111,246,0.45); box-shadow: var(--shadow-lg), var(--glow); }
.shot__bar {
  /* A flex item's default min-width:auto is the content's min-content size, and
     the URL inside has no break opportunity — 369px on a 390px phone, which
     pinned the whole figure wider than the viewport and overflowed the page.
     Let it shrink; the URL is decoration and ellipsises. */
  display: flex; align-items: center; gap: 12px; min-inline-size: 0;
  padding: 11px 16px; background: rgba(16,8,34,0.9);
  border-block-end: 1px solid var(--stroke);
}
.shot__dots { display: flex; gap: 6px; flex: 0 0 auto; }
.shot__dots i { width: 9px; height: 9px; border-radius: 50%; display: block; }
.shot__dots i:nth-child(1) { background: var(--coral); }
.shot__dots i:nth-child(2) { background: var(--amber); }
.shot__dots i:nth-child(3) { background: var(--mint); }
.shot__url {
  flex: 1; min-inline-size: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-align: center; font: 500 var(--t-11)/1 var(--mono); letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--fg-3); background: rgba(255,255,255,0.04); border-radius: 999px; padding: 6px 14px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.shot img { width: 100%; }
/* Ink Floor Rule (DESIGN.md:194): these 15 captions are prose, not labels. */
/* A MEASURE, not a width. Making the evidence full-width stretched its captions
   with it: 1,286px of 14px prose is about 184 characters a line, where 45-75 is
   comfortable and 90 is the usual ceiling. The figure keeps its full width; the
   sentence under it stops where a sentence should. */
.shot__cap {
  max-inline-size: 76ch;
  padding: 14px 18px; font-size: var(--t-14); line-height: 1.55; color: var(--fg-2);
  background: rgba(16,8,34,0.65); border-block-start: 1px solid var(--stroke);
}
.shot__cap b { color: var(--fg-1); font-weight: 700; }

/* ---------- Enlarge ----------
   The affordance lives in the fake browser chrome, where a reader already
   expects window controls, rather than floating over the screenshot. The
   <figure> is not made focusable and gets no role: the button carries the
   keyboard and the accessible name, and the pointer handler on the figure is
   pure convenience on top of it. */
.shot--zoomable picture, .shot--zoomable img { cursor: zoom-in; }
.shot__zoom {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 30px; height: 30px; margin-inline-start: 10px; padding: 0;
  border: 1px solid var(--stroke-2); border-radius: 8px;
  background: rgba(255,255,255,0.05); color: var(--fg-2); cursor: zoom-in;
  transition: background var(--dur-tap), color var(--dur-tap), border-color var(--dur-tap);
}
.shot__zoom svg { width: 15px; height: 15px; }
.shot__zoom:hover { background: rgba(124,111,246,0.22); color: var(--fg-1); border-color: rgba(124,111,246,0.55); }

.lightbox {
  border: 0; padding: 0; background: transparent; color: var(--fg-1);
  max-width: min(1400px, 94vw); max-height: 94vh; overflow: visible;
}
.lightbox::backdrop { background: rgba(8,4,18,0.88); backdrop-filter: blur(8px); }
.lightbox__fig {
  margin: 0; border-radius: var(--r-xl); overflow: hidden;
  background: var(--card); border: 1px solid var(--stroke-2); box-shadow: var(--shadow-lg);
}
.lightbox__stage { display: block; background: var(--ink-950); }
.lightbox__stage img { display: block; width: 100%; height: auto; max-height: 80vh; object-fit: contain; }
.lightbox__cap {
  padding: 16px clamp(16px, 2.4vw, 28px);
  font-size: var(--t-14); line-height: 1.6; color: var(--fg-2);
  border-block-start: 1px solid var(--stroke);
}
.lightbox__cap b { color: var(--fg-1); font-weight: 700; }
.lightbox__close {
  position: absolute; inset-block-start: -52px; inset-inline-end: 0;
  width: 44px; height: 44px; display: grid; place-items: center; padding: 0;
  border-radius: 50%; border: 1px solid var(--stroke-2);
  background: rgba(255,255,255,0.07); color: var(--fg-1); cursor: pointer;
}
.lightbox__close svg { width: 20px; height: 20px; }
.lightbox__close:hover { background: rgba(255,255,255,0.14); }
/* On a short screen there is no room above the frame for the close button, so
   it moves inside. Escape closes it either way. */
@media (max-height: 620px), (max-width: 560px) {
  .lightbox__close { inset-block-start: 8px; inset-inline-end: 8px; background: rgba(16,8,34,0.8); }
}

/* ---------- Phone chrome ---------- */
.phone {
  position: relative; width: min(300px, 78vw); border-radius: 34px; overflow: hidden;
  background: var(--ink-950); border: 1px solid var(--stroke-2);
  box-shadow: var(--shadow-lg);
  padding: 10px;
}
.phone::before {
  /* Symmetric centring: physical left + translateX is direction-neutral.
     inset-inline-start would double up with the translate under dir=rtl. */
  content: ""; position: absolute; top: 18px; left: 50%; transform: translateX(-50%);
  width: 86px; height: 20px; border-radius: 999px; background: var(--ink-950); z-index: 2;
}
.phone img { border-radius: var(--r-xl); width: 100%; }

/* ==========================================================================
   SCENE 1 — HERO
   ========================================================================== */
.hero {
  /* One rhythm for the whole scene, and it answers to the SHORT axis.
     The hero used to be sized entirely off width — 5vh/12vh padding and a
     width-only type clamp — so on a 1440x797 laptop it stood 1011px tall in
     Arabic and pushed its own call to action 7px below the fold. Every gap
     below is a multiple of this one value, so the scene stays in proportion
     as it shrinks instead of collapsing unevenly. */
  --rhythm: clamp(16px, 3.2svh, 34px);
  position: relative; min-height: 100svh; display: grid; align-content: center;
  padding: calc(var(--header-h) + var(--rhythm) * 1.2) var(--pad-x) calc(var(--rhythm) * 2.8);
  max-width: none;
  grid-template-areas: "kicker" "title" "lede" "ctas" "chip";
  grid-template-columns: minmax(0, 1fr);
}
.hero__kicker { grid-area: kicker; }
.hero__title  { grid-area: title; }
.hero__lede   { grid-area: lede; }
.hero__ctas   { grid-area: ctas; }
.hero .leadchip { grid-area: chip; margin-block-start: calc(var(--rhythm) * 1.6); }

/* Wide enough for the helix to have a column of its own: Fahad steps out of
   the 3D stack and becomes a real record beside it. This is the one card in
   the hero a reader can select, read and copy — the rest are geometry. It
   also lifts 110px out of the vertical stack, which is what finally brings
   the Arabic hero inside one screen on a short laptop.

   grid-template-areas is inline-direction, so /ar/ mirrors it from dir=rtl
   with no override — the text column moves to the right, the helix to the
   left, and the 3D group follows via the RTL branch in hero3d.js. */
@media (min-width: 1180px) {
  .hero {
    grid-template-areas:
      "kicker ."
      "title  chip"
      "lede   chip"
      "ctas   chip";
    /* Both tracks floor at 0, never at a pixel minimum. A `minmax(320px, ...)`
       right-hand track cannot shrink below its floor, so on a 1180px screen it
       would hold its width and the chip inside it would push the document
       wider than the viewport — a horizontal scrollbar on the hero. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.52fr);
    column-gap: clamp(32px, 4vw, 80px);
  }
  .hero .leadchip {
    align-self: center; justify-self: center; margin-block-start: 0;
    max-width: 100%;
  }
  /* Narrow column, long record line: wrap it rather than widen the chip — but
     wrap it EVENLY. The record line wants 305px and the column gives it 218-284
     from 1180px to 1599px, which is the whole laptop range, so "DEMO RECORD"
     was orphaned onto a second line and read as a break rather than a label.
     Balanced, the two lines look like what they are. The string itself stays:
     "DEMO RECORD" is the marker that says this record is synthetic. */
  .hero .leadchip__meta span { line-height: 1.5; text-wrap: balance; }
}
.hero__canvas, .hero__poster {
  position: absolute; inset: 0; z-index: -1; overflow: hidden;
}
.hero__canvas canvas { width: 100%; height: 100%; display: block; }
/* The opacity and the scrim below are a CONTRAST budget, not a look.
   The original master contained near-white pixels (#f5ffff): at 0.55 opacity
   behind a 0.25 scrim the hero lede measured 3.35:1 and the kicker 1.71:1, and
   0.36 behind the four-stop scrim was what brought every horizontal band of the
   photograph to >=10.2:1 for --fg-1 and >=5.5:1 for --fg-2.

   The master is now a near-black colonnade peaking at 179/255 — the figures can
   only have improved, and tools/verify-contrast.cjs confirms it from painted
   pixels. Do not read the new image as permission to raise the opacity: this
   budget is what lets ANY photograph sit here, and the next one may be bright.
   If you change it, measure, do not estimate. */
.hero__poster img, .hero__poster picture { width: 100%; height: 100%; object-fit: cover; opacity: 0.36; }
.hero__poster::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg,
    rgba(16,8,34,0.48) 0%,
    rgba(16,8,34,0.60) 34%,
    rgba(16,8,34,0.72) 62%,
    var(--bg) 88%);
}
/* Guaranteed backdrop: opaque --card, so the chip's 5.83:1 does not depend on
   which pixel of the photograph happens to land behind it. */
.hero__kicker {
  /* .hero is a grid, which blockifies inline-flex and stretches it to the full
     column. Harmless while this was transparent; the moment it gained an opaque
     background for contrast it drew a full-width bar instead of a pill.
     `start` is logical, so it is correct under RTL too. */
  display: inline-flex; justify-self: start; align-items: center; gap: 12px; margin-block-end: var(--rhythm);
  padding: 9px 16px; border-radius: 999px;
  background: var(--card); border: 1px solid var(--stroke-2);
  font: 600 var(--t-12)/1 var(--mono); letter-spacing: 0.18em; text-transform: uppercase; color: var(--fg-3);
}
.hero__kicker .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--mint); box-shadow: 0 0 12px var(--mint); flex: none; }
/* Wraps to three lines on a phone; the dot belongs beside the first line,
   not floating against the middle of the block. */
@media (max-width: 600px) {
  .hero__kicker { align-items: flex-start; line-height: 1.7; letter-spacing: 0.12em; }
  .hero__kicker .dot { margin-block-start: 0.5em; }
}
.hero__title {
  font-family: var(--display); font-weight: 700; font-synthesis-weight: none;
  color: var(--fg-1); letter-spacing: -0.018em;
  /* min() so the short axis can veto the long one: 91px of display type is
     right on a 1080px screen and absurd on a 797px one. On a phone the width
     term is always the smaller of the two, so nothing changes there. */
  /* The width term is gentler than it was (0.9rem + 5vw, not 1.3rem + 5vw)
     because Fraunces sets wider than Cairo at the same size: at 40px on a
     390px phone "Every relationship" wrapped to two lines, which turned a
     three-line headline into four and added 84px to the hero. */
  font-size: clamp(2rem, min(0.9rem + 5vw, 1rem + 7.1svh), 5.7rem);
  line-height: 1.04; max-width: 19ch;
  /* Flex column, and the reason is margin collapsing, not layout.
     The lines below carry padding for the Arabic glyph box and an equal
     negative margin to give the space back. Between two block-level siblings
     those two negative margins are ADJOINING, so they collapse to the single
     largest — -0.42em, not -0.84em — and only half the padding is ever clawed
     back. Measured: a 133px line step against a 94.8px line-height, +38px on
     every line of the headline. Flex items never collapse margins, so here the
     compensation lands in full and the step is exactly the line-height. */
  display: flex; flex-direction: column; align-items: stretch;
}
/* The reveal slides each line up out of an overflow:hidden box. At 91.2px,
   Cairo's Arabic glyph box is 132px against a 94.8px line box, so the
   Arabic headline lost 37px off every line while Latin looked fine. The
   padding gives the glyphs room; the matching negative margin keeps the
   layout height unchanged, and the span still starts below the padded box. */
.hero__title .line { display: block; overflow: hidden;
  padding-block: 0.42em; margin-block: -0.42em; }
.hero__title .line > span { display: inline-block; }
.hero__title em { font-style: normal; color: var(--violet-300); }
.hero__lede { max-width: 54ch; margin-block-start: var(--rhythm); font-size: clamp(1.05rem, 1rem + 0.4vw, 1.3rem); color: var(--fg-2); }
.hero__ctas { display: flex; flex-wrap: wrap; gap: 16px; margin-block-start: calc(var(--rhythm) * 1.5); }
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  border-radius: 999px; padding: 16px 30px; border: 0;
  font: 700 var(--t-16)/1 var(--sans); letter-spacing: 0.01em;
  transition: transform var(--dur-move) var(--ease-out), box-shadow var(--dur-move);
}
.btn--primary {
  color: #fff; background: var(--grad-cta);
  box-shadow: 0 6px 26px rgba(124,111,246,0.4);
}
.btn--primary:hover { transform: translateY(-2px); box-shadow: 0 12px 36px rgba(124,111,246,0.55); color: #fff; }
.btn--ghost { color: var(--fg-1); background: rgba(255,255,255,0.05); border: 1px solid var(--stroke-2); }
.btn--ghost:hover { background: rgba(255,255,255,0.09); }
.hero__scroll {
  position: absolute; inset-block-end: 28px; left: 50%; transform: translateX(-50%);
  font: 600 var(--t-11)/1 var(--mono); letter-spacing: 0.22em; text-transform: uppercase; color: var(--fg-2);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.hero__scroll::after {
  content: ""; width: 1px; height: 44px;
  background: linear-gradient(var(--violet-400), transparent);
  animation: scrollhint 2.2s var(--ease-out) infinite;
}
@keyframes scrollhint { 0% { transform: scaleY(0); transform-origin: top; } 55% { transform: scaleY(1); transform-origin: top; } 56% { transform-origin: bottom; } 100% { transform: scaleY(0); transform-origin: bottom; } }

/* Lead chip — Fahad appears in the hero and recurs */
.leadchip {
  /* Same grid stretch. This one has been drawing a 1,672px outlined bar on
     production since July -- the border was visible, the emptiness was not. */
  display: inline-flex; justify-self: start; align-items: center; gap: 12px;
  margin-block-start: 46px; padding-block: 12px; padding-inline: 12px 20px; border-radius: 999px;
}
.leadchip__avatar {
  width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center;
  /* White initials at 14.4px/700 measured 3.85:1 on the old violet-500 stop. */
  background: linear-gradient(135deg, var(--violet-600), var(--violet-800));
  color: #fff; font: 700 var(--t-14)/1 var(--sans);
}
.leadchip__meta { display: grid; gap: 2px; }
.leadchip__meta b { font-size: var(--t-14); color: var(--fg-1); }
.leadchip__meta span { font: 500 var(--t-11)/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--fg-3); letter-spacing: 0.06em; }
.leadchip__score { font: 700 1.3rem/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--mint); margin-inline-start: 10px; }

/* ==========================================================================
   SCENE 2 — THE CHAOS
   ========================================================================== */
.chaos { min-height: 100svh; }
.chaos__stage { position: relative; margin-block-start: 60px; min-height: 520px; }
.frag {
  position: absolute; border-radius: var(--r-md); padding: 14px 18px;
  font: 500 var(--t-12)/1.5 var(--mono); font-variant-numeric: tabular-nums; color: var(--fg-2);
  background: var(--card); border: 1px solid var(--stroke-2); box-shadow: var(--shadow-md);
  max-width: 300px;
}
.frag--csv    { inset-block-start: 4%;  inset-inline-start: 2%;  color: var(--fg-3); }
.frag--wa     { inset-block-start: 12%; inset-inline-end: 6%;    background: #1f2c24; border-color: rgba(46,229,180,0.25); }
.frag--wa2    { inset-block-start: 44%; inset-inline-end: 18%;   background: #1f2c24; border-color: rgba(46,229,180,0.25); }
.frag--note   { inset-block-start: 58%; inset-inline-start: 12%; background: #3a331b; border-color: rgba(240,188,78,0.35); color: #e8d9a8; font-family: var(--sans); }
.frag--missed { inset-block-start: 30%; inset-inline-start: 30%; color: var(--coral); border-color: rgba(244,107,107,0.35); }
.frag--xls    { inset-block-end: 6%;    inset-inline-end: 4%;    color: var(--fg-3); }
.frag--dead   { inset-block-end: 2%;    inset-inline-start: 34%; color: var(--fg-3); }
/* opacity:.8 compounded onto an already-muted colour: 3.13:1 on --card and
   3.14:1 on the amber sticky note. Ink Secondary at full opacity instead. */
.frag small { display: block; margin-block-start: 6px; font-size: var(--t-11); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-2); }
.chaos__mark {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);
  width: clamp(120px, 16vw, 200px); opacity: 0.9; z-index: 2;
  filter: drop-shadow(0 0 40px rgba(124,111,246,0.45));
}
.chaos__stats { display: flex; flex-wrap: wrap; gap: clamp(20px, 4vw, 60px); margin-block-start: 70px; }
.chaos__stats div b { display: block; font: 700 clamp(1.8rem, 3vw, 2.6rem)/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--coral); }
.chaos__stats div b span { font: inherit; color: inherit; }
.chaos__stats div > span { font-size: var(--t-14); color: var(--fg-3); }

@media (max-width: 900px) {
  .chaos__stage { min-height: 0; display: grid; gap: 14px; }
  .frag { position: static; max-width: none; }
  .chaos__mark { display: none; }
}

/* ==========================================================================
   SCENE 3 — THE SIGNAL
   ========================================================================== */
/* Grid items default to min-width:auto, which is min-content, so any long
   unbreakable string inside one of these columns pushes the whole page wider
   than the viewport. Every one of these is content that may legitimately
   shrink and wrap. */
.split > *, .beat > *, .field__wrap > *, .machine__beats > * { min-inline-size: 0; }

/* 4fr/8fr, not 5fr/7fr, and the reason is measured rather than aesthetic.
   The right-hand track is where every product screenshot on this page lives,
   and `apply-pictures --measure` reported it laying them out at 391-669px on a
   1600px viewport. A full application screenshot at 669px is texture: its own
   14px interface type lands at 6px. The crops declared in img-sources.json cut
   each shot to about 800 CSS pixels of content, and this track is what has to
   be 800 pixels wide for that to arrive at 1:1 instead of half size. */
.split { display: grid; grid-template-columns: minmax(300px, 4fr) 8fr; gap: clamp(26px, 3.4vw, 56px); align-items: center; }

/* A .split with no copy in it is not a split — it is two pieces of evidence,
   and evidence needs width. The 4fr track is for a column of prose beside a
   screenshot; put a second screenshot in it and one of them renders at 330px
   while its twin gets 730px, which is neither a comparison nor legible. Both
   go full width, one above the other, and their crops are cut to match. */
.split--evidence { grid-template-columns: 1fr; gap: clamp(40px, 6vh, 80px); }
@media (max-width: 1020px) { .split { grid-template-columns: 1fr; } }

.scorecard { padding: 26px; border-radius: var(--r-xl); max-width: 420px; }
.scorecard__head { display: flex; align-items: center; gap: 14px; }
/* Exactly the bug that `.wa__head span` had, in the section next door, left
   unfixed because only one of the two was measured. `.scorecard__head span`
   (0,1,1) out-specifies `.leadchip__avatar` (0,1,0), so the initials were
   painted in 11px Ink Muted mono on the violet avatar — 2.22:1, found by
   tools/verify-contrast.cjs. The avatar's own ink, restored: 8.6:1. */
.scorecard__head .leadchip__avatar {
  width: 48px; height: 48px;
  font: 700 var(--t-16)/1 var(--sans); color: #fff;
}
.scorecard__head b { color: var(--fg-1); font-size: var(--t-16); display: block; }
.scorecard__head span { font: 500 var(--t-11)/1.4 var(--mono); font-variant-numeric: tabular-nums; color: var(--fg-3); }
.scorering { position: relative; width: 168px; height: 168px; margin: 26px auto 10px; }
.scorering svg { transform: rotate(-90deg); }
.scorering__track { stroke: rgba(255,255,255,0.07); }
.scorering__fill { stroke: var(--mint); stroke-linecap: round; transition: stroke var(--dur-mood); }
.scorering__num {
  position: absolute; inset: 0; display: grid; place-items: center;
  font: 700 3.2rem/1 var(--mono); font-variant-numeric: tabular-nums;
  color: var(--fg-1); letter-spacing: -0.04em;
}
/* This label is wide enough to run under the mint progress arc, where --fg-3
   measured 1.92:1 against the #2ee5b4 stroke. An opaque chip pins it to
   --card (5.83:1) wherever the arc happens to have travelled to. */
.scorering__num small {
  font-size: var(--t-11); font-weight: 600; letter-spacing: 0.2em; color: var(--fg-3);
  margin-block-start: 6px; padding: 3px 10px; border-radius: 999px;
  background: var(--card); box-shadow: 0 0 0 1px var(--stroke);
}
.sigchips { display: grid; gap: 10px; margin-block-start: 22px; }
.sigchip {
  display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-radius: var(--r-sm);
  background: rgba(255,255,255,0.035); border: 1px solid var(--stroke);
  font-size: var(--t-14); color: var(--fg-2);
}
.sigchip i {
  flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center; font-size: 11px; color: var(--ink-900);
  background: var(--mint); font-style: normal; font-weight: 800;
}
.sigchip b { margin-inline-start: auto; font: 700 var(--t-13)/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--mint); }
.bands { display: flex; gap: 8px; margin-block-start: 26px; }
.band { flex: 1; padding: 10px 4px; text-align: center; border-radius: var(--r-sm); font: 700 var(--t-11)/1 var(--mono); font-variant-numeric: tabular-nums; letter-spacing: 0.05em; text-transform: uppercase; white-space: nowrap; }
.band--cold { background: rgba(111,138,250,0.12); color: var(--iris); }
.band--warm { background: rgba(240,188,78,0.12); color: var(--amber); }
.band--hot  { background: rgba(46,229,180,0.14); color: var(--mint); border: 1px solid rgba(46,229,180,0.4); }

/* ==========================================================================
   SCENE 4 — THE CONVERSATION (WhatsApp thread)
   ========================================================================== */
.wa { width: min(400px, 100%); border-radius: var(--r-xl); overflow: hidden; }
.wa__head { display: flex; align-items: center; gap: 12px; padding: 14px 18px; background: rgba(16,8,34,0.85); border-block-end: 1px solid var(--stroke); }
/* `.wa__head span` (0,1,1) out-specifies `.leadchip__avatar` (0,1,0), so the
   initials were being painted mint on the violet avatar — 3.89:1 measured.
   Restore the avatar's own ink here: 6.37:1. */
.wa__head .leadchip__avatar { width: 38px; height: 38px; font-size: var(--t-13); color: #fff; }
.wa__head b { color: var(--fg-1); font-size: var(--t-14); display: block; }
.wa__head span { font: 500 var(--t-11)/1 var(--mono); color: var(--mint); }
.wa__body { padding: 20px 16px 24px; display: grid; gap: 12px; min-height: 380px; align-content: start; }
.msg {
  max-width: 84%; padding: 11px 15px; border-radius: var(--r-md);
  font-size: var(--t-14); line-height: 1.65;
}
/* The bubble tails are logical, so they point the right way in an Arabic
   thread without a single [dir=rtl] override. */
.msg--in  { justify-self: start; background: var(--card-hi); border: 1px solid var(--stroke-2); border-end-start-radius: var(--r-xs); color: var(--fg-1); }
.msg--out { justify-self: end; background: linear-gradient(135deg, rgba(124,111,246,0.32), rgba(107,91,224,0.4)); border: 1px solid rgba(124,111,246,0.4); border-end-end-radius: var(--r-xs); color: var(--fg-1); }
.msg [lang="ar"] { display: block; }
/* --fg-3 measured 2.50:1 on the violet .msg--out gradient and 3.90:1 on
   --card-hi. Ink Secondary: 5.32:1 and 8.01:1. The translation line is prose. */
.msg .tr { display: block; margin-block-start: 6px; font-size: var(--t-12); color: var(--fg-2); }
.msg time { display: block; margin-block-start: 5px; font: 500 var(--t-11)/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--fg-2); text-align: end; }
.msg--sys {
  justify-self: center; text-align: center; font: 600 var(--t-11)/1.5 var(--mono);
  letter-spacing: 0.08em; color: var(--mint); background: rgba(46,229,180,0.08);
  border: 1px solid rgba(46,229,180,0.3); border-radius: 999px; padding: 8px 18px;
}
.wave { display: inline-flex; align-items: center; gap: 2.5px; height: 22px; margin-block: 2px 0; margin-inline: 0 8px; vertical-align: middle; }
.wave i { width: 2.5px; border-radius: 2px; background: var(--violet-300); height: 30%; }
.wave i:nth-child(2n)  { height: 75%; }
.wave i:nth-child(3n)  { height: 50%; }
.wave i:nth-child(5n)  { height: 95%; }
.typing { display: inline-flex; gap: 4px; padding: 4px 2px; }
.typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--fg-3); animation: blink 1.2s infinite; }
.typing i:nth-child(2) { animation-delay: 0.2s; }
.typing i:nth-child(3) { animation-delay: 0.4s; }
@keyframes blink { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }

/* ==========================================================================
   SCENE 5 — THE MACHINE (reconcile → deal room → matchmaker)
   ========================================================================== */
.machine__beats { display: grid; gap: clamp(60px, 9vh, 120px); margin-block-start: clamp(50px, 8vh, 100px); }
.beat { display: grid; grid-template-columns: minmax(300px, 4fr) 8fr; gap: clamp(26px, 4vw, 70px); align-items: center; }
/* Named, not counted. `.beat:nth-child(2)` meant the reversal belonged to a
   POSITION rather than to a beat: insert a beat anywhere above it, or reorder
   two, and the wrong one flips with nothing to show for it in the diff. */
.beat--rev { grid-template-columns: 8fr minmax(300px, 4fr); }
.beat--rev .beat__copy { order: 2; }
/* The matchmaker beat carries two figures — a phone and a console — and side by
   side inside one column the console measured 391px, which is where its own
   14px interface type lands at 5px. Stacked, it gets the whole track. */
.beat--pair .field__wrap { grid-template-columns: 1fr; justify-items: center; row-gap: clamp(28px, 4vh, 52px); }
@media (max-width: 1020px) { .beat, .beat--rev { grid-template-columns: 1fr; } .beat--rev .beat__copy { order: 0; } }
.beat__num { font: 700 var(--t-14)/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--violet-300); letter-spacing: 0.14em; margin-block-end: 14px; display: block; }
.beat h3 { color: var(--fg-1); font-size: clamp(1.35rem, 1.1rem + 1vw, 1.9rem); font-weight: 800; line-height: 1.2; max-width: 24ch; }
.beat p { margin-block-start: 14px; max-width: 46ch; font-size: var(--t-16); }

/* Mini kanban that animates its own card */
.kanban { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; padding: 20px; border-radius: var(--r-xl); min-inline-size: 0; }
.kanban > * { min-inline-size: 0; }
.kcol { background: rgba(255,255,255,0.028); border: 1px solid var(--stroke); border-radius: var(--r-md); padding: 12px; min-height: 210px; }
.kcol .kcol__t { display: block; font: 700 var(--t-11)/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; margin-block-end: 4px; }
.kcol span:not(.kcol__t) { font: 600 var(--t-12)/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--fg-3); }
.kcol--avail .kcol__t { color: var(--mint); } .kcol--init .kcol__t { color: var(--iris); }
.kcol--resv .kcol__t { color: var(--amber); } .kcol--cont .kcol__t { color: var(--violet-300); }
.kcard {
  margin-block-start: 10px; padding: 10px 12px; border-radius: var(--r-sm);
  background: var(--card-hi); border: 1px solid var(--stroke-2); box-shadow: var(--shadow-md);
  font-size: var(--t-12); line-height: 1.5;
}
.kcard b { display: block; color: var(--fg-1); font: 700 var(--t-12)/1.3 var(--mono); font-variant-numeric: tabular-nums; }
.kcard span { color: var(--fg-3); font: 500 var(--t-12)/1.4 var(--mono); font-variant-numeric: tabular-nums; }
/* The mint border already marks B-104 as the tracked unit. The glow is the
   ARRIVAL state — scroll.js animates box-shadow in when the card lands in
   Contracted — so it is no longer a resting finish (Earned Glow Rule). */
.kcard--hero { border-color: rgba(46,229,180,0.5); position: relative; z-index: 2; }
.kcard--hero.is-active, .kcard--hero:hover, .kcard--hero:focus-within { box-shadow: 0 0 24px rgba(46,229,180,0.18), var(--shadow-md); }
.kcard--ghost { opacity: 0.45; }
@media (max-width: 700px) { .kanban { grid-template-columns: repeat(2, 1fr); padding: 14px; } .kcol { min-height: 150px; } }
/* Two columns of unit cards still measured 437px against 343px of available
   width on a 390px phone. One column below that. */
@media (max-width: 520px) { .kanban { grid-template-columns: 1fr; } .kcol { min-height: 0; } }

/* Reconcile match visual */
.recon { padding: 24px; border-radius: var(--r-xl); }
.recon__row { display: grid; grid-template-columns: 1fr 60px 1fr; align-items: center; gap: 8px; padding: 9px 0; }
.recon__cell { font: 500 var(--t-12)/1.5 var(--mono); font-variant-numeric: tabular-nums; padding: 10px 14px; border-radius: var(--r-sm); background: rgba(255,255,255,0.03); border: 1px solid var(--stroke); color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.recon__link { height: 2px; background: linear-gradient(90deg, var(--coral), var(--mint)); border-radius: 2px; transform-origin: left; }
[dir="rtl"] .recon__link { transform-origin: right; }
.recon__pct { text-align: center; margin-block-start: 18px; font: 700 1.6rem/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--mint); }
.recon__pct small { display: block; font: 600 var(--t-11)/1.6 var(--mono); letter-spacing: 0.14em; color: var(--fg-3); text-transform: uppercase; }

/* ==========================================================================
   SCENE 6 — THE ANSWERS (Ask CRVITAE demo → chat is born here)
   ========================================================================== */
.askdemo { max-width: 760px; margin: 60px auto 0; border-radius: var(--r-xl); padding: 26px; }
.askdemo__bar {
  display: flex; align-items: center; gap: 12px; padding: 15px 20px; border-radius: var(--r-md);
  background: rgba(255,255,255,0.045); border: 1px solid var(--stroke-2);
  font: 500 var(--t-16)/1.4 var(--sans); color: var(--fg-1);
}
.askdemo__bar .k { margin-inline-start: auto; font: 600 var(--t-11)/1 var(--mono); color: var(--fg-3); border: 1px solid var(--stroke-2); border-radius: var(--r-xs); padding: 4px 7px; }
.askdemo__q { min-height: 1.4em; }
.askdemo__q .caret { display: inline-block; width: 1px; height: 1.05em; background: var(--mint); vertical-align: -0.18em; margin-inline-start: 2px; animation: blink 1.1s steps(1) infinite; }
.askdemo__a { margin-block-start: 18px; padding: 18px 20px; border-radius: var(--r-md); background: rgba(124,111,246,0.08); border: 1px solid rgba(124,111,246,0.25); font-size: var(--t-16); }
.askdemo__a b { color: var(--fg-1); }
.askdemo__a .num { font: 700 0.9em/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--mint); }
.askdemo__src { margin-block-start: 12px; font: 500 var(--t-11)/1.6 var(--mono); color: var(--fg-3); letter-spacing: 0.05em; max-inline-size: 88ch; }
/* #askHint reuses .askdemo__src but carries prose, not a source label. */
#askHint { color: var(--fg-2); letter-spacing: 0.02em; }

/* ==========================================================================
   SCENE 7 — THE MONEY
   ========================================================================== */
.moneyline { display: grid; grid-template-columns: 1fr auto 1fr; gap: clamp(16px, 3vw, 44px); align-items: center; margin: 60px 0 30px; }
.moneyline__cell { text-align: center; padding: 30px 20px; border-radius: var(--r-xl); }
.moneyline__cell b { display: block; font: 700 clamp(1.7rem, 1.2rem + 2.4vw, 3.2rem)/1 var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -0.03em; color: var(--fg-1); }
.moneyline__cell b span { font: inherit; color: inherit; }
.moneyline__cell > span { display: block; margin-block-start: 10px; font: 600 var(--t-11)/1.5 var(--mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg-3); }
.moneyline__x { font: 700 clamp(2rem, 4vw, 3.6rem)/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--mint); text-shadow: 0 0 34px rgba(46,229,180,0.4); text-align: center; }
.moneyline__x small { display: block; font-size: var(--t-11); font-weight: 600; letter-spacing: 0.18em; color: var(--fg-2); margin-block-start: 8px; }
@media (max-width: 780px) { .moneyline { grid-template-columns: 1fr; } }
.funnel { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-block-start: 26px; }
.funnel span { font: 600 var(--t-12)/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--fg-2); padding: 10px 16px; border-radius: 999px; background: rgba(255,255,255,0.04); border: 1px solid var(--stroke); }
.funnel span b { color: var(--amber); }
.funnel i { align-self: center; color: var(--fg-3); font-style: normal; }

/* ==========================================================================
   CHAPTER 06 — THE PROOF, WITH THE LIGHTS ON
   ==========================================================================
   Nine chapters of dark plum and then, for the one chapter that is nothing but
   evidence, the page inverts. DESIGN.md:228 already specifies a light theme for
   the product, so this extends the system rather than inventing a second one.

   It is a full second palette, not a filter. Every token below is re-declared
   on #money itself, so anything inside it that reads var(--fg-2) or var(--card)
   picks up the light value with no new class names — and anything that does NOT
   go through a token (.glass's hardcoded gradient, .shot__bar's plum, the
   funnel's white-on-white pills) is overridden explicitly underneath. There is
   no third path: if a surface here looks wrong it is because it is painted with
   a literal instead of a token, and that is the thing to fix.

   THE ACCENTS ARE NOT THE SAME COLOURS. Mint #2ee5b4 measures 1.7:1 on this
   ground and amber #f0bc4e measures 1.6:1 — both are display colours for a dark
   page and neither is text on a light one. The Ribbon Rule binds the MEANING to
   the hue, not the hex, so each keeps its identity at a luminance that can
   actually be read. Every pair below is measured from composited pixels, not
   computed from the declarations, because backdrop-filter cannot be resolved
   from CSS alone — that is how this site once scored 98 on Lighthouse
   accessibility with body text at 2.50:1.

   Full-bleed by padding, not by a 100vw pseudo-element: 100vw includes the
   scrollbar, which is a horizontal overflow on the one page that gates on
   having none. The padding formula keeps the content column identical to every
   other chapter while the ground runs edge to edge. */
#money {
  --bg:       #f2eff8;
  --bg-elev:  #ffffff;
  --card:     #ffffff;
  --card-hi:  #faf8fe;
  --stroke:   rgba(20, 9, 42, 0.10);
  --stroke-2: rgba(20, 9, 42, 0.17);

  --fg-1: #14092a;
  --fg-2: #3e3358;
  --fg-3: #5b5077;

  --mint:  #0b8a66;
  --amber: #8f5d0a;
  --coral: #bf2f2f;
  --iris:  #3b4fbe;
  --violet-300: #5344b8;
  --violet-400: #5344b8;

  --shadow-lg: 0 26px 60px rgba(20, 9, 42, 0.12);
  --shadow-md: 0 10px 26px rgba(20, 9, 42, 0.09);
  --glow: 0 0 0 1px rgba(83, 68, 184, 0.5), 0 8px 28px rgba(83, 68, 184, 0.18);

  max-width: none;
  padding-inline: max(var(--pad-x), calc((100% - 1560px) / 2 + var(--pad-x)));
  background: var(--bg);
  color: var(--fg-2);
}

/* The ghost numeral is a hairline stroke tuned to sit just above a dark
   ground's noise floor. At the same alpha on cream it disappears entirely. */
#money .ghost { -webkit-text-stroke-color: rgba(20, 9, 42, 0.13); }

/* .glass paints a literal dark gradient and blurs what is behind it. On an
   opaque light ground there is nothing behind it worth blurring, and the blur
   is what makes a contrast figure unmeasurable — so here it is a real card. */
#money .glass {
  background: var(--card);
  border-color: var(--stroke-2);
  box-shadow: var(--shadow-md);
  backdrop-filter: none;
}
#money .glass::after { opacity: 0.02; mix-blend-mode: multiply; }

/* The funnel pills were white at 4% on plum. On cream that is white on white. */
#money .funnel span { background: #ffffff; border-color: var(--stroke-2); }

/* The screenshots stay dark — they are the product, and the product is dark.
   What changes is the frame around them: a light chrome bar reads as a window
   onto the application rather than as more of the same surface. */
#money .shot { background: var(--card); }
#money .shot__bar { background: #ebe6f5; border-block-end-color: var(--stroke-2); }
#money .shot__cap { background: var(--card); border-block-start-color: var(--stroke); }
#money .shot__zoom { background: rgba(20, 9, 42, 0.05); }
#money .shot__zoom:hover { background: rgba(83, 68, 184, 0.16); border-color: rgba(83, 68, 184, 0.5); }
#money .shot:hover, #money .shot:focus-within { border-color: rgba(83, 68, 184, 0.45); }

/* The ROAS is the loudest number on the site and it earned its glow on a dark
   ground. A mint halo on cream is a smudge; the figure carries itself. */
#money .moneyline__x { text-shadow: none; }

/* (The evidence stack is a general rule now — see .split--evidence.) */

/* The executive report is a document, not a screen: an A4 page, cropped to
   itself and set at the width a page wants to be read at.

   Centred by GRID PLACEMENT, never by `margin-inline: auto`. Auto margins take
   a grid item out of `stretch` and into shrink-to-fit, and shrink-to-fit asks
   the content for its MIN-CONTENT width — which for this figure is its chrome
   bar, and the URL in that bar is `white-space: nowrap`. So the figure sized
   itself to 390px inside a 280px track on a 320px phone and pushed the document
   sideways: measured, +90px, and green everywhere above 400px so it looked
   fine. Spanning columns keeps the item stretched and the bar free to ellipsise
   as it was designed to. */
#money > .shot { margin-block-start: clamp(26px, 4vh, 54px); }

/* Links inside the chapter, and the focus ring, both of which are declared once
   at :root against the dark palette. */
#money a { color: var(--violet-300); }
#money :focus-visible { outline-color: var(--violet-400); }

/* ==========================================================================
   SCENE 8 — THE FIELD
   ========================================================================== */
.field__wrap { display: grid; grid-template-columns: auto 1fr; gap: clamp(40px, 6vw, 100px); align-items: center; }
@media (max-width: 1020px) {
  .field__wrap { grid-template-columns: 1fr; justify-items: center; }
  /* justify-items:center sizes each item to its own content rather than to
     the track, so a wide child stops being bounded by the column and pushes
     past the viewport. Bound it explicitly. */
  .field__wrap > * { max-inline-size: 100%; }
}
.phone--scrollin { overflow: hidden; }
.phone--scrollin .phone__viewport { height: 520px; overflow: hidden; border-radius: var(--r-xl); }
.phone--scrollin img { border-radius: 0; }
.coach { margin-block-start: 26px; padding: 20px 22px; border-radius: var(--r-lg); max-width: 460px; }
.coach b { display: block; font: 700 var(--t-11)/1 var(--mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--mint); margin-block-end: 10px; }
.coach p { font-size: var(--t-14); }
/* The direction/text-align patch that used to live here is gone — the general
   [lang="ar"] rule at the top of this file covers it now. */
.coach p[lang="ar"] { margin-block-start: 8px; color: var(--fg-1); }

/* ==========================================================================
   SCENE 9 — ENGINE & TRUST
   ========================================================================== */
.engine { position: relative; overflow: clip; border-radius: 0; }
.engine__bg { position: absolute; inset: 0; z-index: -1; }
/* The chapter is ~2,000px tall and the plate is 1.79:1, so stretching the
   image over the whole section made `cover` crop a narrow centre strip and
   scale it ~2.6x — the corridor read as a flat smudge. Pin it to one viewport
   instead: the perspective stays at its intended scale and parallaxes as the
   chapter scrolls past. `clip` (not `hidden`) on .engine is what lets sticky
   still resolve against the viewport. */
.engine__bg picture { display: block; position: sticky; top: 0; height: 100vh; height: 100svh; }
.engine__bg img { width: 100%; height: 100%; object-fit: cover; opacity: 0.42; }
.engine__bg::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, var(--bg) 0%, rgba(16,8,34,0.55) 22%, rgba(16,8,34,0.6) 78%, var(--bg) 100%); }
.statwall { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; margin-block-start: 60px; }
/* The benchmark conditions live here rather than inside the 50,000 tile.
   Crammed into the tile they ran to four lines of 11px mono and left that
   card 33px taller than its neighbours on a phone; the brochure carries the
   same caveat as a footnote for the same reason. A figure quoted bare is a
   figure quoted wrongly, but the tile is not where the sentence goes. */
.statwall__note { margin-block-start: 14px; font-size: var(--t-12); line-height: 1.6; color: var(--fg-3); max-inline-size: 92ch; }
.stat { padding: 26px 20px; border-radius: var(--r-lg); text-align: center; }
.stat b { display: block; font: 700 clamp(1.7rem, 2.6vw, 2.6rem)/1 var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; color: var(--fg-1); }
.stat b span { font: inherit; color: inherit; }
.stat > span { display: block; margin-block-start: 10px; font: 600 var(--t-11)/1.5 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-3); }
.stat[data-tone="mint"] b { color: var(--mint); } .stat[data-tone="amber"] b { color: var(--amber); }
.stat[data-tone="iris"] b { color: var(--iris); } .stat[data-tone="coral"] b { color: var(--coral); }
.stat[data-tone="violet"] b { color: var(--violet-300); }
/* The iris glow reported no state at rest; the stroke and the tinted fill
   already say "this panel is the sovereignty claim" (Earned Glow Rule). */
.vault {
  margin-block-start: 60px; padding: clamp(26px, 4vw, 46px); border-radius: var(--r-xl);
  border: 1px solid rgba(111,138,250,0.35);
  background: linear-gradient(160deg, rgba(111,138,250,0.1), rgba(26,16,48,0.9));
  box-shadow: var(--shadow-md);
  display: grid; grid-template-columns: auto 1fr; gap: 26px; align-items: start;
}
.vault__icon { width: 52px; height: 52px; border-radius: var(--r-md); display: grid; place-items: center; background: rgba(111,138,250,0.16); color: var(--iris); }
.vault h3 { color: var(--fg-1); font-size: 1.3rem; font-weight: 800; }
.vault p { margin-block-start: 10px; max-width: 68ch; font-size: var(--t-16); }
@media (max-width: 700px) { .vault { grid-template-columns: 1fr; } }
.partners { display: flex; flex-wrap: wrap; gap: 14px; margin-block-start: 34px; }
.partners span { font: 700 var(--t-12)/1 var(--mono); letter-spacing: 0.1em; color: var(--fg-2); padding: 12px 22px; border-radius: 999px; border: 1px solid var(--stroke-2); background: rgba(255,255,255,0.03); }

/* ==========================================================================
   COMPARISON
   ========================================================================== */
.cmp-wrap { overflow-x: auto; margin-block-start: 50px; border-radius: var(--r-lg); border: 1px solid var(--stroke-2); }
table.cmp { width: 100%; min-width: 760px; border-collapse: collapse; background: rgba(26,16,48,0.6); }
.cmp th, .cmp td { padding: 16px 18px; text-align: start; font-size: var(--t-14); border-block-end: 1px solid var(--stroke); vertical-align: top; }
.cmp thead th { font: 700 var(--t-12)/1.4 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-3); background: rgba(16,8,34,0.8); }
.cmp thead th:last-child { color: var(--mint); }
.cmp td.axis { font-weight: 700; color: var(--fg-1); min-width: 170px; }
.cmp td:last-child { color: var(--fg-1); background: rgba(46,229,180,0.045); border-inline-start: 1px solid rgba(46,229,180,0.25); }
.cmp td .no  { color: var(--coral); font-weight: 700; }
.cmp td .meh { color: var(--amber); font-weight: 700; }
.cmp tbody tr:last-child td { border-block-end: 0; }
/* Prose, not a label — Ink Floor Rule. */
.cmp__foot { font-size: var(--t-12); color: var(--fg-2); padding: 14px 18px; max-inline-size: 92ch; }

/* ==========================================================================
   FAQ
   ========================================================================== */
.faq { max-width: 860px; }
.faq details {
  border-block-end: 1px solid var(--stroke-2); padding: 6px 0;
}
.faq summary {
  cursor: pointer; list-style: none; display: flex; justify-content: space-between; gap: 20px;
  padding: 20px 4px; font-weight: 700; color: var(--fg-1); font-size: var(--t-16);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; font: 700 1.3rem/1 var(--mono); color: var(--violet-300); transition: transform var(--dur-move) var(--ease-out); flex: 0 0 auto; }
.faq details[open] summary::after { transform: rotate(45deg); }
.faq details p { padding: 0 4px 22px; max-width: 70ch; }

/* ==========================================================================
   SCENE 10 — SIGNATURE / CTA / FOOTER
   ========================================================================== */
.cta {
  position: relative; border-radius: var(--r-xl); overflow: hidden;
  padding: clamp(46px, 7vw, 100px) clamp(26px, 6vw, 90px);
  text-align: center;
}
.cta__bg { position: absolute; inset: 0; z-index: 0; }
.cta__bg img { width: 100%; height: 100%; object-fit: cover; opacity: 0.4; }
.cta__bg::after { content: ""; position: absolute; inset: 0; background: radial-gradient(90% 100% at 50% 100%, rgba(16,8,34,0.3), rgba(16,8,34,0.88)); }
.cta > *:not(.cta__bg) { position: relative; z-index: 1; }
.cta h2 {
  font-family: var(--display); font-weight: 700; font-synthesis-weight: none;
  font-size: clamp(2rem, 1.4rem + 3vw, 4rem); color: var(--fg-1);
  /* It had no line-height at all, so 64px display type inherited body's 1.7 and
     the closing headline stood 109px a line apart. The Arabic edition was
     already corrected here; the Latin one never was, because Cairo at 900 made
     the gap look almost deliberate. */
  line-height: 1.06;
  letter-spacing: -0.014em; max-width: 18ch; margin: 0 auto; text-wrap: balance;
}
.cta h2 em { font-style: normal; color: var(--violet-300); }
/* DIRECT children only. `.cta p` also matched every <p class="field"> in the
   demo form and .demoform__legal below it, capping each field at 52ch and
   centring it with `margin: auto` — which, on a flex column, is shrink-to-fit.
   Every input sat 181px wide in a 260px track, and the form read as a scatter
   of boxes rather than a row. */
.cta > p { max-width: 52ch; margin: 20px auto 0; }
.cta__row { display: flex; flex-wrap: wrap; gap: 16px; justify-content: center; margin-block-start: 40px; }
.cta__contact { display: flex; flex-wrap: wrap; gap: clamp(14px, 3vw, 40px); justify-content: center; margin-block-start: 34px; font: 600 var(--t-13)/1 var(--mono); }
/* These three are the whole contact route now that the telephone and WhatsApp
   links are gone, and they were 13px tall — WCAG 2.5.8 wants 24x24 minimum.
   Padding gets them to 44px without moving anything visually, because the
   flex row's gap already leaves the space. */
.cta__contact a { color: var(--fg-2); display: inline-flex; align-items: center; gap: 8px;
  min-block-size: 44px; padding-inline: 10px; border-radius: 10px; }
.cta__contact a:focus-visible { background: var(--card-hi); }
.cta__contact a:hover { color: var(--mint); }

.footer { padding: 70px var(--pad-x) 46px; border-block-start: 1px solid var(--stroke); margin-block-start: var(--sec-gap); }
.footer__grid { max-width: 1560px; margin: 0 auto; display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 40px; }
@media (max-width: 900px) { .footer__grid { grid-template-columns: 1fr; } }
.footer__brand img { height: 40px; width: auto; }
.footer__brand p { margin-block-start: 18px; font-size: var(--t-14); max-width: 46ch; }
.footer h3 { font: 700 var(--t-11)/1 var(--mono); letter-spacing: 0.14em; text-transform: uppercase; color: var(--fg-3); margin-block-end: 16px; }
.footer li { margin-block: 9px; font-size: var(--t-14); }
.footer__legal {
  max-width: 1560px; margin: 50px auto 0; padding-block-start: 26px; border-block-start: 1px solid var(--stroke);
  display: flex; flex-wrap: wrap; gap: 16px; justify-content: space-between; align-items: center;
  font: 500 var(--t-11)/1.6 var(--mono); font-variant-numeric: tabular-nums; color: var(--fg-3);
}
.footer__legal img { height: 20px; width: auto; opacity: 0.75; }

/* ---------- Scene backdrops (generated imagery, shallow-depth geometric) -- */
.scenebg { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.scenebg img { width: 100%; height: 100%; object-fit: cover; opacity: 0.16; }
.scenebg::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, var(--bg), transparent 30%, transparent 70%, var(--bg)); }

/* The Tabular Rule (DESIGN.md:218). The animated counters jitter visibly
   through their intermediate values without it. */
[data-count], [data-format="comma"], [data-decimals] { font-variant-numeric: tabular-nums; }

/* ---------- Reduced motion & no-JS ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.001s !important; animation-iteration-count: 1 !important; transition-duration: 0.001s !important; }
  .hero__scroll::after { animation: none; }
}
