/* animations.css — the shared motion language. Keyframes plus the reusable
   entrance utilities (reveal, fade, slide-up, stagger, section entrance) and the
   keyframes referenced by components.css (spinner, modal). Values mirror the
   index page so every surface shares one easing and duration vocabulary.
   Reduced-motion is fully neutralised at the bottom. Depends on tokens.css. */

@keyframes sp{to{transform:rotate(360deg)}}
@keyframes modal-in{to{opacity:1;transform:none}}
@keyframes fade-in{from{opacity:0}to{opacity:1}}
@keyframes slide-up{from{opacity:0;transform:translateY(var(--reveal-shift))}to{opacity:1;transform:none}}
/* restrained section entrance — a small lift + fade, applied to a whole section
   container when it is switched in (never per panel/cell). Opacity + transform
   only, so it never triggers layout reflow. */
@keyframes section-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

/* reveal-on-scroll — hidden ONLY once the reveal runtime has confirmed it can
   restore the element (html.reveal-armed is set by animations.js after it has an
   IntersectionObserver). Without that guarantee the content simply stays visible,
   so a missing IntersectionObserver never leaves anything hidden. */
html.reveal-armed .reveal{opacity:0;transform:translateY(var(--reveal-shift));
  transition:opacity var(--dur-slow) var(--ease),transform var(--dur-slow) var(--ease);
  transition-delay:calc(var(--i,0)*var(--stagger))}
html.reveal-armed .reveal.in{opacity:1;transform:none}

/* one-shot entrance utilities (no runtime needed) */
.fade-in{animation:fade-in var(--dur-slow) var(--ease) both}
.slide-up{animation:slide-up var(--dur-slow) var(--ease) both}

@media(prefers-reduced-motion:reduce){
  html.reveal-armed .reveal,html.reveal-armed .reveal.in{transition:none!important;opacity:1!important;transform:none!important}
  .modal{animation:none;opacity:1;transform:none}
  .fade-in,.slide-up{animation:none}
  .btn,.icon-btn,.to-top,.toast,.mob-menu{transition:none!important}
}
