/* Elements are visible by default. Hiding only applies once JS marks the body
   ready, so a failed script can never leave the page blank. */
/* GSAP owns the pre-reveal state when it is driving. The v1 CSS pre-hide is kept
   only for the IntersectionObserver path so content is never hidden with no
   animator present to reveal it. */
.motion-ready:not(.gsap-ready) [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1) var(--reveal-delay, 0ms),
    transform 0.6s cubic-bezier(0.22, 1, 0.36, 1) var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.char { display: inline-block; will-change: transform, opacity; }

.motion-ready [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .motion-ready [data-reveal] { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
  /* .char is produced by Phase B's character-split reveal (not yet built); this
     rule is dead-but-harmless until then and pins the no-motion contract early. */
  .char { opacity: 1 !important; transform: none !important; }
  .hero__photo { transform: none !important; }
}
