/* ============================================================
   Animations — transform/opacity only, GPU friendly.
   ============================================================ */

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

@keyframes slide-up {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; transform: none; }
}

@keyframes scale-in {
  from { opacity: 0; transform: scale(.94); }
  to   { opacity: 1; transform: none; }
}

@keyframes ripple-out {
  to { transform: scale(2.6); opacity: 0; }
}

@keyframes shimmer {
  from { background-position: 160% 0; }
  to   { background-position: -60% 0; }
}

@keyframes toast-in {
  from { opacity: 0; transform: translate3d(0, 18px, 0) scale(.96); }
  to   { opacity: 1; transform: none; }
}
@keyframes toast-out {
  to { opacity: 0; transform: translate3d(0, 10px, 0) scale(.96); }
}

@keyframes float-soft {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-8px); }
}

@keyframes count-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.06); }
  100% { transform: scale(1); }
}

/* Page/section entrance. `--i` staggers children.
   The delay is capped so items far down a long list still appear promptly
   instead of trickling in for seconds. */
.animate-in { animation: slide-up .5s var(--ease-out) backwards; }
.animate-fade { animation: fade-in .4s var(--ease-out) backwards; }
.animate-scale { animation: scale-in .4s var(--ease-spring) backwards; }
/* Must follow the shorthands above — `animation:` resets animation-delay. */
.animate-in,
.animate-fade,
.animate-scale {
  animation-delay: calc(min(var(--i, 0), 8) * 55ms);
}
.animate-float { animation: float-soft 5s var(--ease-in-out) infinite; }
.animate-pop { animation: count-pop .35s var(--ease-spring); }

/* Route change: content swaps with a short lift */
.route-enter { animation: slide-up .38s var(--ease-out); }

/* ------------------------------------------------------------
   Reduced motion: keep the UI functional, drop the movement.
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    /* Zero the stagger too: with `backwards` fill, a delay would leave content
       invisible until it elapsed — a blank screen, not a reduced animation. */
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  .skeleton { animation: none !important; background: var(--bg-tint); }
}

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