/* Elements staged for scroll animation begin hidden and shifted down by 16px to create calm ascension. */
[data-animate] {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity var(--dur-slow) var(--ease-drift),
    transform var(--dur-slow) var(--ease-drift);
  will-change: opacity, transform;
}

/* Intersection observer applies visible class to finalize element entry and release compositor layer. */
[data-animate].visible {
  opacity: 1;
  transform: translateY(0);
  will-change: auto;
}

/* Hero title letters rise sequentially from black depths. */
@keyframes letterRise {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Scroll prompt pulses steadily to beckon reader into the descent. */
@keyframes promptPulse {
  0%,
  100% {
    opacity: 0.4;
  }
  50% {
    opacity: 1;
  }
}

/* Souls materialize into the shallow wells when triggered into view. */
@keyframes soulAppear {
  0% {
    opacity: 0;
    transform: scale(0);
  }
  65% {
    opacity: 1;
    transform: scale(1.15);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}

/* Leaderboard loading state breathes gently while awaiting remote tally resolution. */
@keyframes pulseLoading {
  0%,
  100% {
    opacity: 0.3;
  }
  50% {
    opacity: 0.7;
  }
}

/* Mist clouds pulse gently at rest while waiting for the reader to descend. */
@keyframes mistPulseLeft {
  0%,
  100% {
    transform: scale(1);
    opacity: 0.98;
  }
  50% {
    transform: scale(1.02);
    opacity: 0.90;
  }
}

@keyframes mistPulseRight {
  0%,
  100% {
    transform: scale(1);
    opacity: 0.98;
  }
  50% {
    transform: scale(1.02);
    opacity: 0.90;
  }
}

/* Reduced motion preference strips transitions to respect accessibility requirements. */
@media (prefers-reduced-motion: reduce) {
  [data-animate] {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .letter,
  .scroll-prompt,
  .soul-orb,
  .leaderboard-loading,
  .mist-bank {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}
