/* animations.css — keyframes + motion utilities
 * Rule: motion is wrapped by the prefers-reduced-motion guard below.
 */

@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes fade-up {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes spin { to { transform: rotate(360deg); } }

.fade-in { animation: fade-in 0.3s ease forwards; }
.fade-up { animation: fade-up 0.45s ease forwards; }

/* Reveal-on-scroll: start hidden, .is-visible (set by IntersectionObserver) reveals.
 * The hidden state is gated on the `js` class (set in <head> before paint) so that
 * without JS the content is simply VISIBLE rather than stuck at opacity 0 — these
 * marketing pages are server-rendered precisely to work without JS. */
.js .reveal { opacity: 0; transform: translateY(16px); transition: opacity 0.5s ease, transform 0.5s ease; }
.js .reveal.is-visible { opacity: 1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration:        0.01ms !important;
    animation-iteration-count: 1      !important;
    transition-duration:       0.01ms !important;
  }
  .reveal, .js .reveal { opacity: 1; transform: none; }
}
