/* ============================================
   Scroll Reveal & Stagger
   See docs/motion-implementation-specification.md §03, §05, §10
   ============================================ */

/* Section-level reveal: fade + short translateY, triggered once by
   IntersectionObserver (js/main.js → initScrollReveal). */
.reveal {
  opacity: 0;
  transform: translateY(var(--motion-reveal-distance));
  transition: opacity var(--motion-reveal-duration) var(--motion-ease-out),
    transform var(--motion-reveal-duration) var(--motion-ease-out);
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Stagger: same fade+translateY as .reveal, but children step in with a
   short incremental delay (capped) once the parent .stagger-group enters
   the viewport. Delay index is set on each item via --i (see main.js). */
.stagger-item {
  opacity: 0;
  transform: translateY(var(--motion-reveal-distance));
  transition: opacity var(--motion-reveal-duration) var(--motion-ease-out),
    transform var(--motion-reveal-duration) var(--motion-ease-out);
  transition-delay: min(calc(var(--i, 0) * var(--motion-stagger-step)), var(--motion-stagger-max-delay));
}

.stagger-group.is-visible .stagger-item {
  opacity: 1;
  transform: translateY(0);
  width: 64%;
}

/* Typewriter intro (.welcome-title). Triggered once by IntersectionObserver
   (js/main.js → initWelcomeTitleTypewriter) when the title scrolls into
   view. Blinking cursor sits after whatever text has been typed so far —
   same look while typing and once finished, matching the reference. */
.welcome-title::after {
  content: '|';
  margin-left: 2px;
  opacity: 1;
  animation: welcomeTitleCursorBlink 0.7s infinite;
}

@keyframes welcomeTitleCursorBlink {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}

/* ============================================
   Hero Cover: Entrance
   ============================================ */

/* Wordmark, headline and delivery estimate fade in on page load — the hero
   sits fully above the fold (unlike .reveal/.stagger-item elsewhere), so
   this doesn't need scroll-triggering or an IntersectionObserver; a plain
   CSS animation just runs on paint. The sitewide prefers-reduced-motion
   rule (responsive.css) already collapses animation-duration to ~0 for
   every element, so this needs no separate reduced-motion handling.
   The hero photo itself is never animated — it's LCP-critical. */
@keyframes heroCoverEnter {
  from {
    opacity: 0;
    transform: translateY(var(--motion-reveal-distance));
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.hero-cover-enter {
  opacity: 0;
  animation: heroCoverEnter var(--motion-reveal-duration) var(--motion-ease-out) forwards;
  animation-delay: calc(var(--i, 0) * var(--motion-stagger-step));
}

/* .hero-scroll-btn carries its own translate(-50%,-50%) centering transform
   (layout.css, position:absolute) — animating a bare translateY like
   .hero-cover-enter would replace that transform outright instead of
   combining with it (transform isn't additive), knocking the button off
   center for the animation's duration. Same issue already fixed for its
   :hover/:active states; same fix here, its own keyframe that folds the
   entrance offset into the centering transform. */
@keyframes heroScrollBtnEnter {
  from {
    opacity: 0;
    transform: translate(-50%, calc(-50% + var(--motion-reveal-distance)));
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%);
  }
}

.hero-scroll-btn {
  opacity: 0;
  animation: heroScrollBtnEnter var(--motion-reveal-duration) var(--motion-ease-out) forwards;
  animation-delay: calc(2 * var(--motion-stagger-step));
}

/* Share-button toast (js/main.js → showFeedback). Own dedicated keyframes,
   not the generic slideUp/slideDown names — those already exist elsewhere
   (layout.css, responsive.css, both for .header-sticky's entrance) and
   only animate translateY. Reusing that name for this toast meant its own
   translateX(-50%) centering got clobbered for the animation's duration —
   transform isn't additive, whichever @keyframes with that name loads
   last wins outright — so the toast visibly started off-center and only
   snapped back to centered once the animation finished and the inline
   style took back over. Baking translateX(-50%) into both ends here
   fixes it at the source instead of patching the symptom. */
@keyframes feedbackSlideIn {
  from {
    opacity: 0;
    transform: translate(-50%, 20px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

@keyframes feedbackSlideOut {
  from {
    opacity: 1;
    transform: translate(-50%, 0);
  }
  to {
    opacity: 0;
    transform: translate(-50%, 20px);
  }
}

