/* ==========================================================================
   BASE — reset, typography, utilities
   ========================================================================== */

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

html {
  /* No native smooth-scroll: GSAP ScrollTrigger drives the pinned/scrubbed
     hero and fights native smooth-scroll's own scroll-position animation. */
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--size-body);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

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

h1, h2, h3, h4, p, figure { margin: 0; }

.u-container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.u-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-family: var(--font-display);
  font-size: var(--size-label);
  font-weight: 500;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--color-accent);
}

.u-eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: currentColor;
  opacity: 0.7;
}

/* Shared lead paragraph under a section heading — same treatment as the
   hero/cinematic leads, generalized so section-head copy blocks don't
   each need their own one-off rule. */
.u-section-lead {
  margin-top: var(--space-sm);
  max-width: 52ch;
  font-size: var(--size-lead);
  color: var(--color-ink-dim);
}

.u-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Reveal primitives — toggled by IntersectionObserver in main.js.
   Elements are visible by default (progressive enhancement / no-JS safe),
   JS adds .is-ready to <html> before wiring animations. */
.r-fade,
.r-mask > * ,
.r-rise {
  opacity: 1;
  transform: none;
}

html.is-ready .r-fade { opacity: 0; transition: opacity var(--dur-slow) var(--ease-out); }
html.is-ready .r-fade.in-view { opacity: 1; }

html.is-ready .r-rise { opacity: 0; transform: translateY(2.5rem); transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
html.is-ready .r-rise.in-view { opacity: 1; transform: none; }

html.is-ready .r-slide-left { opacity: 0; transform: translateX(-4.5rem); transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
html.is-ready .r-slide-left.in-view { opacity: 1; transform: none; }

html.is-ready .r-mask { overflow: hidden; }
html.is-ready .r-mask > * { transform: translateY(110%); transition: transform var(--dur-slow) var(--ease-out); }
html.is-ready .r-mask.in-view > * { transform: none; }

@media (prefers-reduced-motion: reduce) {
  html.is-ready .r-fade,
  html.is-ready .r-rise,
  html.is-ready .r-slide-left,
  html.is-ready .r-mask > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* Word-stagger reveal (.r-words) — see main.js splitIntoWords().
   Purely structural: with no JS/GSAP the words sit inline exactly as
   normal text, so this degrades safely. */
.split-word { display: inline-block; overflow: hidden; vertical-align: top; }
.split-word-inner { display: inline-block; will-change: transform, opacity; }

/* Overlay push hand-off (.c-hgallery exit phase in main.js). While the
   billboard finale slides off and the NEXT section is shown early via its
   fixed overlay, that section's scroll-triggered reveals (word-stagger
   title/eyebrow, card grid) have NOT fired yet — their GSAP ScrollTriggers
   key off real scrollY, which hasn't reached them, so the title would be
   missing with a gap above the body. This forces them to their revealed
   end-state for the DURATION OF THE OVERLAY ONLY, layered over (never
   erasing) GSAP's own inline styles via !important. Removing the class
   hands full control straight back to the normal GSAP triggers untouched,
   so a natural arrival (direct link, mid-page refresh, scroll-back-then-
   forward) still animates exactly as before. (The .r-fade lead is driven
   by an IntersectionObserver, which the overlay already satisfies, so it
   reveals on its own — covered here too only as a belt-and-braces guard.) */
.is-push-revealed .split-word-inner { transform: none !important; opacity: 1 !important; }
.is-push-revealed .u-section-lead { opacity: 1 !important; }
.is-push-revealed .c-mockup-grid__list > * { transform: none !important; opacity: 1 !important; }
