/* ─── Motion foundation ───────────
 * Progressive enhancement: every element is visible by default. Animations
 * either fire on page load (for above-the-fold hero + first sections) or
 * are triggered by IntersectionObserver adding .is-visible (for deep-scroll
 * reveals that we WANT to time to viewport entry). The CSS never leaves
 * content hidden if JS/IO never runs. */

@keyframes fade-up {
  from { opacity: 0; transform: translateY(1rem); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes image-reveal {
  from { clip-path: inset(0 0 100% 0); }
  to   { clip-path: inset(0 0 0% 0); }
}

@keyframes slide-in-left {
  from { opacity: 0; transform: translateX(-2rem); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes word-rise {
  from { transform: translateY(105%); }
  to   { transform: translateY(0); }
}

@keyframes marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Above-fold hero: eyebrow + lead fade up, headline handled by
   .word-stagger (Modern) or shown plainly (Classic). */
@media (prefers-reduced-motion: no-preference) {
  .hero-entrance {
    animation: fade-up var(--transition-slow) ease-out both;
  }
  .hero-entrance-delay {
    animation: fade-up var(--transition-slow) ease-out both;
    animation-delay: 200ms;
  }
  [data-theme="modern"] .hero-entrance-delay {
    animation: image-reveal var(--transition-slow) cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: 250ms;
  }
}

/* Word-level rise for LAIN-style headlines — plays on load, Modern only.
   Classic reverts (see editorial.css). Split into .word > span by
   reveal.js; falls back to plain text if JS never runs. */
.word-stagger .word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
[data-theme="modern"] .word-stagger .word > span {
  display: inline-block;
}
@media (prefers-reduced-motion: no-preference) {
  [data-theme="modern"] html.js-word-stagger .word-stagger .word > span {
    animation: word-rise 900ms cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: calc(var(--i, 0) * 60ms + 80ms);
  }
}

/* Deep-scroll reveals — hidden only once IO has confirmed it will observe
   them, and only if reduce-motion is off. Failsafe: if IO doesn't fire
   within 2s of page load, all pending reveals are shown by
   .js-reveal-timeout applied by reveal.js. */
@media (prefers-reduced-motion: no-preference) {
  html.js-reveal-armed .reveal:not(.is-visible) {
    opacity: 0;
    transform: translateY(1rem);
  }
  html.js-reveal-armed .reveal.is-visible {
    animation: fade-up var(--transition-slow) ease-out forwards;
  }

  html.js-reveal-armed .reveal-image:not(.is-visible) {
    clip-path: inset(0 0 100% 0);
  }
  html.js-reveal-armed .reveal-image.is-visible {
    animation: image-reveal var(--transition-slow) cubic-bezier(0.16, 1, 0.3, 1) both;
  }

  html.js-reveal-armed .reveal-stagger:not(.is-visible) > * {
    opacity: 0;
    transform: translateY(0.75rem);
  }
  html.js-reveal-armed .reveal-stagger.is-visible > * {
    animation: fade-up var(--transition-base) ease-out both;
    animation-delay: calc(var(--stagger-i, 0) * 80ms);
  }

  html.js-reveal-armed .reveal-slide-l:not(.is-visible) {
    opacity: 0;
    transform: translateX(-2rem);
  }
  html.js-reveal-armed .reveal-slide-l.is-visible {
    animation: slide-in-left var(--transition-slow) cubic-bezier(0.16, 1, 0.3, 1) both;
  }
}

/* Editorial image parallax — Modern only, full-bleed banner images.
   Driven entirely by the native scroll-driven-animations API (no scroll JS,
   no rAF loop): the browser ties the keyframe's progress to the image's
   position in the viewport off the main thread. Unsupported browsers just
   never run the animation — the image sits at its normal resting frame, so
   this is a strict progressive enhancement layered on top of .reveal-image. */
@keyframes image-parallax {
  from { transform: scale(1.12) translateY(2%); }
  to   { transform: scale(1) translateY(0); }
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-theme="modern"] .image-parallax > img {
      animation: image-parallax linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 40%;
    }
  }
}

/* Universal safety net for reduced-motion. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* ─── Theme toggle icon swap ───────────*/
.theme-toggle-icon {
  position: absolute;
  opacity: 0;
  transform: rotate(-90deg) scale(0.5);
  transition: opacity var(--transition-base) ease, transform var(--transition-base) ease;
}
[data-theme="modern"] .theme-toggle-icon--modern,
[data-theme="classic"] .theme-toggle-icon--classic {
  opacity: 1;
  transform: rotate(0deg) scale(1);
}

/* ─── Mobile menu icon swap ───────────*/
.menu-icon {
  position: absolute;
  opacity: 1;
  transform: rotate(0deg) scale(1);
  transition: opacity var(--transition-fast) ease, transform var(--transition-fast) ease;
}
.menu-icon--close {
  opacity: 0;
  transform: rotate(-90deg) scale(0.5);
}
details[open] > summary .menu-icon--open {
  opacity: 0;
  transform: rotate(90deg) scale(0.5);
}
details[open] > summary .menu-icon--close {
  opacity: 1;
  transform: rotate(0deg) scale(1);
}

@media (prefers-reduced-motion: no-preference) {
  details[open] > nav {
    animation: fade-up var(--transition-fast) ease-out both;
  }
}
