/* Scroll-reveal + micro-interactions.
 *
 * Content is visible by default (opacity:1, no transform) — the ".reveal"
 * hidden pre-state is only ever applied by JS itself, and only to elements
 * that are NOT already on-screen when assets/js/main.js runs (see
 * initReveal()). This matters on real hosting: caching/optimization
 * plugins (e.g. LiteSpeed Cache's "Delay JS Execution", common on
 * Hostinger) can defer script execution by seconds after first paint —
 * with the old "hidden until JS says otherwise" approach, that meant
 * above-the-fold hero content sat invisible/mid-fade for that whole delay.
 * With this approach, worst case if JS never runs at all is simply "no
 * fade-in animation" — never hidden content. */

.reveal {
  opacity: 1;
  transform: none;
  transition: opacity var(--duration-slow) var(--ease-standard),
              transform var(--duration-slow) var(--ease-standard);
}
.reveal.reveal-armed {
  opacity: 0;
  transform: translateY(22px);
  will-change: opacity, transform;
}
.reveal.reveal-armed.is-inview {
  opacity: 1;
  transform: translateY(0);
  will-change: auto;
}

/* Stagger children of a revealed group: each card carries its own
   --stagger-i custom property (set inline in the template), so cards that
   cross the IntersectionObserver threshold in the same frame still fan
   out instead of all animating in unison. */
.reveal-group > .is-inview { transition-delay: calc(var(--stagger-i, 0) * 90ms); }

@media (prefers-reduced-motion: reduce) {
  .reveal.reveal-armed { opacity: 1; transform: none; transition: none; }
}
