/* ============================================================
   ANIMATION UTILITIES
   JS (animations.js) toggles these classes via IntersectionObserver
   or GSAP. Everything here is inert without JS, and everything
   respects prefers-reduced-motion (see style.css global rule too).
   ============================================================ */

.reveal{
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .8s cubic-bezier(.16,.8,.24,1),
              transform .8s cubic-bezier(.16,.8,.24,1);
  will-change: transform, opacity;
}
.reveal.is-visible{ opacity: 1; transform: translateY(0); }

.reveal-stagger > *{
  opacity: 0; transform: translateY(24px);
  transition: opacity .7s cubic-bezier(.16,.8,.24,1),
              transform .7s cubic-bezier(.16,.8,.24,1);
}
.reveal-stagger.is-visible > *{ opacity:1; transform:translateY(0); }
.reveal-stagger.is-visible > *:nth-child(1){ transition-delay: .02s; }
.reveal-stagger.is-visible > *:nth-child(2){ transition-delay: .08s; }
.reveal-stagger.is-visible > *:nth-child(3){ transition-delay: .14s; }
.reveal-stagger.is-visible > *:nth-child(4){ transition-delay: .2s; }
.reveal-stagger.is-visible > *:nth-child(5){ transition-delay: .26s; }
.reveal-stagger.is-visible > *:nth-child(6){ transition-delay: .32s; }
.reveal-stagger.is-visible > *:nth-child(7){ transition-delay: .38s; }
.reveal-stagger.is-visible > *:nth-child(8){ transition-delay: .44s; }

@media (prefers-reduced-motion: reduce){
  .reveal, .reveal-stagger > *{
    opacity: 1 !important; transform:none !important; transition:none !important;
  }
}

/* hero text reveal: characters/lines wiped up on load */
.hero h1 .line{
  display:block; overflow:hidden;
}
.hero h1 .line span{
  display:block; transform: translateY(105%);
  animation: line-up .9s cubic-bezier(.16,.8,.24,1) forwards;
}
.hero h1 .line:nth-child(2) span{ animation-delay: .08s; }
.hero h1 .line:nth-child(3) span{ animation-delay: .16s; }
@keyframes line-up{ to{ transform: translateY(0); } }
@media (prefers-reduced-motion: reduce){
  .hero h1 .line span{ animation: none; transform:none; }
}

/* page loader (brief pill-mark pulse, not a full spinner takeover) */
.page-loader{
  position: fixed; inset: 0; z-index: 9999;
  background: var(--paper);
  display:flex; align-items:center; justify-content:center;
  transition: opacity .5s var(--ease-out), visibility 0s .5s;
}
.page-loader .mark{
  width: 44px; height: 44px; border-radius: 12px;
  background: linear-gradient(135deg, var(--signal), #8a7dff);
  animation: pulse 1.1s ease-in-out infinite;
}
.page-loader.is-done{ opacity: 0; visibility: hidden; pointer-events:none; }
@keyframes pulse{
  0%, 100%{ transform: scale(1); opacity:1; }
  50%{ transform: scale(.82); opacity:.7; }
}
@media (prefers-reduced-motion: reduce){
  .page-loader .mark{ animation: none; }
}

/* counters (value swapped by JS; this just guards layout shift) */
.stat .num{ font-variant-numeric: tabular-nums; }

/* image scale-on-hover already in .card-media img (style.css); this adds
   a generic hook for non-card media */
.hover-zoom{ overflow:hidden; border-radius: var(--radius-m); }
.hover-zoom img{ transition: transform .7s var(--ease-out); }
.hover-zoom:hover img{ transform: scale(1.05); }
