:root {
  --text: #f2f2f2;
  --muted: rgba(255,255,255,0.55);
  --accent: #ff1a1a;
  --accent-olive: #ffffff;
  --font-mono: 'Space Mono', 'SF Mono', 'Courier New', monospace;
  --font-display: 'Space Grotesk', sans-serif;
  --font-sans: 'Inter', sans-serif;
  /* Fallback only, used until js/header-height.js measures the real (fixed)
     header height and overwrites this - see .hero's padding-top. */
  --header-h: 90px;
  /* Subtle push of the fixed header down from the viewport's very top
     edge - reused by header::after (negated, so the blur zone still
     starts at y=0) and by .hero's padding-top (added on top of
     --header-h, so hero content still clears the header exactly). */
  --header-top-offset: 16px;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  height: 100%;
  background: #050505;
  color: var(--text);
  font-family: var(--font-sans), system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Mobile-only, additive: body is the page's real scrolling box (see
   js/drag-scroll.js's own comment on overflow-x:hidden above making it
   one) - a NON-root scroll container, not the viewport itself. iOS
   Safari gives smooth native momentum scrolling to non-root scrollers
   only with this explicit opt-in. Doesn't fix the ScrollTrigger render
   lag on its own (see js/touch-scroll-sync.js for that) - purely a
   touch-inertia quality improvement layered on top. */
@media (max-width: 767px) {
  body {
    -webkit-overflow-scrolling: touch;
  }
}

/* ========== PAGE ========== */
/* Full-bleed: page content spans the whole viewport width, only a
   consistent horizontal padding (shared by header/hero/sections),
   never a centered max-width column. */
.page {
  position: relative;
  z-index: 2;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: 22px 28px 40px;
}

/* Wraps everything in .page except <header> (see index.html's own
   comment on this element) so js/wave-ripple.js's filter: url(#wave-distort)
   can target it alone, without also making header - a position:fixed
   element - relative to a filtered ancestor instead of the real
   viewport. flex/flex-direction here exactly mirror .hero's own flex
   value at each breakpoint (see css/hero.css): header is position:fixed
   and takes no part in .page's flex layout, so before this wrapper
   existed, .hero was effectively the only flex item distributing
   .page's leftover vertical space - this wrapper now does that same job
   one level up, with .hero (still flex:1 internally) doing the same
   distribution it always did, just one level down. */
.content-wave {
  flex: 1;
  display: flex;
  flex-direction: column;
  /* Always attached, even when js/wave-ripple.js's ripple isn't running
     (feDisplacementMap's own scale defaults to 0 in index.html, i.e.
     genuinely doing nothing) - see that file's own comment for why
     toggling this to filter:none while idle was tried and reverted: no
     measured perf benefit, but switching an element into having a filter
     for the first time turned out to visibly shift .sections' children
     by a few pixels for one frame, right at that transition. */
  filter: url(#wave-distort);
}

/* ========== DESKTOP / TABLET ========== */
@media (min-width: 768px) {
  .page {
    padding: 28px 48px 50px;
    /* Shared with .hero and .sec (both children of .page, so both
       inherit it) - the fixed clearance content needs when it shares
       a row with a "— 00X" marker in column 1, so text doesn't land
       on top of it. Defined once, reused by --col-shift below and
       directly by .desc. */
    --marker-clearance: 48px;
  }

  /* Matches .hero's own flex:initial at this breakpoint (see
     css/hero.css) - see the base rule's comment above for why. */
  .content-wave {
    flex: initial;
  }
}

@media (min-width: 1100px) {
  .page {
    padding: 32px 56px 60px;
  }
}
