/* Scroll-triggered fade-in-up animations (user-requested motion — the ONLY
 * intentional deviation from the pixel-exact-static design, motion-only).
 *
 * LAYOUT-SAFETY: these are CLASS-ONLY. They add opacity/transform/transition to
 * an element that already exists (grid cards keep className added directly to the
 * card element — NO wrapper div is inserted between a grid/flex container and its
 * item, so responsive.css's `>*` / `:first-child` / grid-collapse selectors keep
 * matching the real cards). `transform` + `opacity` do not affect layout box size
 * or grid participation, so grid-template-columns collapse + min-width:0 + the
 * sticky-switcher `aside>div:first-child` selector all still fire exactly as before.
 *
 * A single page-level IntersectionObserver (components/ScrollAnimator.tsx) adds
 * `.visible` to each `.fade-in-up` when it enters the viewport — once (unobserved
 * after). Elements start hidden; if JS is disabled or the observer never runs, the
 * no-JS fallback below reveals everything so content is never permanently hidden.
 */
.fade-in-up {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.6s ease-out, transform 0.6s ease-out;
  will-change: opacity, transform;
}
.fade-in-up.visible {
  opacity: 1;
  transform: none;
}

/* Stagger utilities for grid cards / alternating rows */
.fade-in-up.delay-100 {
  transition-delay: 100ms;
}
.fade-in-up.delay-200 {
  transition-delay: 200ms;
}
.fade-in-up.delay-300 {
  transition-delay: 300ms;
}
.fade-in-up.delay-400 {
  transition-delay: 400ms;
}

/* Accessibility: users who prefer reduced motion see everything instantly, no anim. */
@media (prefers-reduced-motion: reduce) {
  .fade-in-up {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* No-JS / observer-never-runs fallback: if the ScrollAnimator client component
 * doesn't hydrate, reveal all fade elements so content is never stuck hidden.
 * The <html> element gets class `js` added by ScrollAnimator on mount; until then
 * (and forever, if JS is off) `html:not(.js)` reveals everything. */
html:not(.js) .fade-in-up {
  opacity: 1;
  transform: none;
  transition: none;
}

/* Hero on-load animation (above the fold — NOT scroll-triggered).
 * Applied via class `hero-fade` on hero content; animates once on page load. */
@keyframes hero-fade-in-up {
  from {
    opacity: 0;
    transform: translateY(24px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.hero-fade {
  animation: hero-fade-in-up 0.7s ease-out both;
}
.hero-fade.hero-delay-1 {
  animation-delay: 0.08s;
}
.hero-fade.hero-delay-2 {
  animation-delay: 0.16s;
}
@media (prefers-reduced-motion: reduce) {
  .hero-fade {
    animation: none;
  }
}
