/* ==========================================================================
   base.css — reset, document defaults, typography, layout primitives.
   ========================================================================== */

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

html, body,
h1, h2, h3, h4, h5, h6,
p, figure, blockquote, dl, dd,
ul, ol { margin: 0; }

ul[class], ol[class] { padding: 0; list-style: none; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--nav-height) + var(--space-5));
  background: var(--ink);
  /* Grey flash on every tap on Android Chrome / iOS Safari. The controls all
     have their own :active and :focus-visible states, so the UA one is pure
     noise. */
  -webkit-tap-highlight-color: transparent;
}

/* Native smooth scroll only when GSAP's smoother is NOT driving the page,
   otherwise the two fight each other. anim.js sets .has-smoother. */
html:not(.has-smoother) { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  min-height: 100vh;
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--step-0);
  font-weight: 400;
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  /* Stops a rubber-band overscroll at the top of the page from triggering
     pull-to-refresh mid-swipe, which on the stream reads as the feed
     reloading itself. */
  overscroll-behavior-y: contain;
}

/* Scroll lock. Used by the mobile drawer and the report modal; `position` is
   left alone deliberately — the fixed-position lock trick loses scroll offset
   on iOS, and overflow:hidden on <body> is enough here because neither
   surface is taller than the viewport. */
body.is-locked { overflow: hidden; }

/* The preview banner is fixed to the bottom edge. Without this the last row
   of the footer sits underneath it, which on a phone is two or three lines of
   copy. main.js adds the class at the same moment it unhides the banner. */
body.has-preview-banner { padding-bottom: calc(5.25rem + var(--safe-b)); }

@media (min-width: 640px) {
  body.has-preview-banner { padding-bottom: calc(3.25rem + var(--safe-b)); }
}

img, picture, video, canvas, audio, svg {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
  background-color: var(--ink-2);
}

a { color: inherit; text-decoration: none; }

a:hover { text-decoration: none; }

input, button, textarea, select { font: inherit; color: inherit; }

button { background: none; border: 0; cursor: pointer; }

textarea { resize: vertical; }

:focus-visible {
  outline: 2px solid var(--sun);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

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

/* Scrollbar, tuned to the dark ground. */
* { scrollbar-color: var(--line-strong) transparent; scrollbar-width: thin; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: var(--radius-pill);
  border: 3px solid var(--ink);
}

/* ==========================================================================
   Typography — heavy, very tight. This compression is most of the look.
   ========================================================================== */
h1, h2, h3, h4 {
  font-weight: 700;
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); letter-spacing: var(--tracking-snug); line-height: var(--leading-snug); }
h4 { font-size: var(--step-1); letter-spacing: var(--tracking-snug); line-height: var(--leading-snug); }

p { text-wrap: pretty; }

strong { font-weight: 700; }

/* The oversized hero word. Set solid, hyphenation off. */
.display {
  font-size: var(--step-5);
  font-weight: 700;
  line-height: 0.86;
  letter-spacing: -0.045em;
  text-wrap: nowrap;
}

.lead {
  max-width: 54ch;
  font-size: var(--step-1);
  color: var(--text-soft);
  letter-spacing: -0.01em;
}

/* ==========================================================================
   Layout primitives
   ========================================================================== */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  /* Notch insets are added to the gutter rather than replacing it, so a phone
     held in landscape keeps the copy clear of the camera cutout without
     losing the normal margin on the other side. */
  padding-inline:
    calc(var(--page-x) + var(--safe-l))
    calc(var(--page-x) + var(--safe-r));
}

.container--narrow { max-width: var(--container-narrow); }
.container--feed { max-width: calc(var(--feed-width) + var(--page-x) * 2); }
/* Full-bleed: sections whose hairline grid must touch the viewport edges. */
.container--bleed { max-width: none; padding-inline: 0; }

.section { padding-block: var(--space-8); }

@media (min-width: 768px) {
  .section { padding-block: var(--space-9); }
}

.section--tight { padding-block: var(--space-7); }
.section--band { background: var(--ink-2); }

.stack > * + * { margin-top: var(--space-4); }
.stack-lg > * + * { margin-top: var(--space-6); }

/* A top hairline is how Vita separates most sections. */
.rule-top { border-top: 1px solid var(--line); }

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

.skip-link {
  position: absolute;
  top: 0; left: var(--space-4);
  z-index: calc(var(--z-nav) + 10);
  padding: var(--space-3) var(--space-5);
  border-radius: 0 0 var(--radius) var(--radius);
  background: var(--sun);
  color: var(--ink);
  font-weight: 700;
  transform: translateY(-110%);
  transition: transform var(--dur) var(--ease);
}

.skip-link:focus-visible { transform: translateY(0); }

.text-center { text-align: center; }
.text-soft { color: var(--text-soft); }
.text-muted { color: var(--text-muted); }
.text-meta {
  color: var(--text-muted);
  font-size: var(--step-sm);
  letter-spacing: -0.005em;
}
.text-sun { color: var(--sun); }

/* ---------- Eyebrow: asterisk glyph + tracked-out label ----------------- */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text);
  font-size: var(--step-sm);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.eyebrow::before {
  content: '✳';
  color: var(--sun);
  font-size: 1.05em;
  line-height: 1;
}

.eyebrow--plain::before { content: none; }

/* Small tracked-out caps label used inside cards and table rows. */
.label {
  display: block;
  color: var(--text-muted);
  font-size: var(--step-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* ---------- Numerals (stats, step numbers) ----------------------------- */
.numeral {
  font-size: var(--step-3);
  font-weight: 700;
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   Reveal-on-scroll

   Two systems, both gated on .js so a no-JS visitor sees finished content:

   1. [data-reveal]  — block fade + rise, driven by reveal.js (IntersectionObserver).
   2. [data-split]   — per-word colour wipe from --text-dim to --text, driven by
                       anim.js (GSAP SplitText + ScrollTrigger). anim.js sets
                       .gsap-ready once it has taken ownership; until then the
                       fallback below keeps the text fully legible.
   ========================================================================== */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition:
    opacity var(--dur-slow) var(--ease-out-expo),
    transform var(--dur-slow) var(--ease-out-expo);
  transition-delay: var(--reveal-delay, 0ms);
}

.js [data-reveal].is-inview {
  opacity: 1;
  transform: none;
}

/* Word spans created by SplitText. */
.split-word {
  display: inline-block;
  will-change: color;
}

/* Only dim the words once GSAP is actually driving them — otherwise a CDN
   failure would leave every heading unreadable at 22% opacity. */
.gsap-ready [data-split] .split-word { color: var(--text-dim); }

@media (prefers-reduced-motion: reduce) {
  .gsap-ready [data-split] .split-word { color: inherit; }
}
