/* ============================================================
   HELIOCASTLE — ANIMATION
   The movement system: scroll reveals, the loaded-state intro and
   the ?snap capture mode. Each section keeps its own @keyframes
   next to the thing they move.
   ============================================================ */

/* ============================================================
   REVEAL ANIMATIONS
   Three flavours of the same arrival, differing only in where they
   start from: --rise-from.
   ============================================================ */

.reveal,
.reveal-nav,
.reveal-up {
  opacity: 0;
  transform: translateY(var(--rise-from, 28px));
  will-change: opacity, transform;
}

.reveal-nav { --rise-from: -12px; }
.reveal-up  { --rise-from: 34px; }

/* The settled state is a real rule, not a filling animation.

   An animation left filling `forwards` sits above every normal
   declaration underneath it, so `transform: none` held by the finished
   reveal outranked `:hover { transform: translateY(-6px) }` and no card
   on the site could lift once it had arrived. `backwards` holds the
   hidden state through the delay instead, and lets go at the end. */
.is-loaded .reveal,
.is-loaded .reveal-nav,
.reveal-up.in-view {
  opacity: 1;
  transform: none;
}

.is-loaded .reveal {
  animation: rise 1.1s var(--ease) backwards;
  animation-delay: var(--d, 0s);
}
.is-loaded .reveal-nav {
  animation: rise 1s var(--ease) 0.05s backwards;
}
.reveal-up.in-view {
  animation: rise 1s var(--ease) backwards;
  animation-delay: var(--d, 0s);
}

@keyframes rise {
  from { opacity: 0; transform: translateY(var(--rise-from, 28px)); }
  to   { opacity: 1; transform: none; }
}

/* parallax layers move via JS transform — keep them smooth */
.parallax { transition: transform 0.9s cubic-bezier(0.16, 1, 0.3, 1); }
