/* =========================================================================
   animations.css — keyframes, reveal defaults, reduced-motion guard
   GSAP drives scroll/entrance timelines; this file holds the CSS-only motion.
   ========================================================================= */

/* ---------- Keyframes ---------------------------------------------------- */
@keyframes btnPulse {
  0%, 100% { box-shadow: 0 10px 30px -10px rgba(255, 46, 99, .7), 0 0 0 0 rgba(255, 46, 99, .45); }
  50%      { box-shadow: 0 12px 34px -10px rgba(255, 46, 99, .85), 0 0 0 14px rgba(255, 46, 99, 0); }
}

@keyframes floatParticle {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  33%  { transform: translate3d(14px, -26px, 0) scale(1.14); }
  66%  { transform: translate3d(-10px, -48px, 0) scale(.92); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}

@keyframes mobileItemIn {
  from { opacity: 0; transform: translateX(28px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes toastIn {
  from { opacity: 0; transform: translateX(28px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes toastOut {
  to { opacity: 0; transform: translateX(28px); }
}

@keyframes shimmer {
  0%   { background-position: -420px 0; }
  100% { background-position: 420px 0; }
}

@keyframes spin { to { transform: rotate(360deg); } }

@keyframes glowBreathe {
  0%, 100% { opacity: .5; }
  50%      { opacity: .95; }
}

/* One countdown digit changing value */
@keyframes countdownTick {
  0%   { transform: translateY(0);     opacity: 1; }
  40%  { transform: translateY(-38%);  opacity: 0; }
  41%  { transform: translateY(38%);   opacity: 0; }
  100% { transform: translateY(0);     opacity: 1; }
}

/* ---------- Utility animation classes ----------------------------------- */
.particle { animation: floatParticle linear infinite; }

/* Set by animations.js for one tick, then removed. */
.countdown-value.is-ticking { animation: countdownTick 420ms cubic-bezier(.4, 0, .2, 1); }

.spinner {
  width: 18px; height: 18px;
  border: 2px solid rgba(255, 255, 255, .2);
  border-top-color: var(--pink);
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}

.skeleton {
  background: linear-gradient(90deg, rgba(255,255,255,.04) 25%, rgba(255,255,255,.09) 37%, rgba(255,255,255,.04) 63%);
  background-size: 840px 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: var(--r-sm);
}

/* mobile nav item stagger */
.mobile-nav.is-open a:nth-child(1) { animation-delay: 60ms; }
.mobile-nav.is-open a:nth-child(2) { animation-delay: 110ms; }
.mobile-nav.is-open a:nth-child(3) { animation-delay: 160ms; }
.mobile-nav.is-open a:nth-child(4) { animation-delay: 210ms; }
.mobile-nav.is-open a:nth-child(5) { animation-delay: 260ms; }
.mobile-nav.is-open a:nth-child(6) { animation-delay: 310ms; }
.mobile-nav.is-open a:nth-child(7) { animation-delay: 360ms; }
.mobile-nav.is-open .btn         { animation: mobileItemIn 460ms cubic-bezier(.16,1,.3,1) 410ms forwards; opacity: 0; }

/* ---------- Reveal defaults ---------------------------------------------
   Elements marked [data-reveal] start hidden and are animated in by GSAP
   ScrollTrigger. The `.js-ready` class on <html> is set synchronously by the
   inline bootstrap in index.html, so no-JS visitors never see hidden content.
   ------------------------------------------------------------------------ */
html.js-ready [data-reveal] {
  --reveal-y: 50px;
  opacity: 0;
  transform: translateY(var(--reveal-y));
  will-change: opacity, transform;
}
/* The offset is a custom property, not a flat `transform: none`, so that
   components with a transform of their own (the tilting speaker cards) can
   compose it instead of being overwritten by it. */
html.js-ready [data-reveal].is-revealed,
html.no-gsap [data-reveal] {
  --reveal-y: 0px;
  opacity: 1;
  transition: opacity .8s cubic-bezier(.16,1,.3,1), transform .8s cubic-bezier(.16,1,.3,1);
}

/* Hero entrance holds — GSAP clears these inline */
html.js-ready .hero-lockup,
html.js-ready .hero-title .line > span,
html.js-ready .hero-subtitle,
html.js-ready .hero-meta,
html.js-ready .hero-buttons,
html.js-ready .hero-already,
html.js-ready .hero-rule { opacity: 0; }

html.no-gsap .hero-lockup,
html.no-gsap .hero-title .line > span,
html.no-gsap .hero-subtitle,
html.no-gsap .hero-meta,
html.no-gsap .hero-buttons,
html.no-gsap .hero-already,
html.no-gsap .hero-rule { opacity: 1; }

/* ---------- Reduced motion ---------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }

  html.js-ready [data-reveal],
  html.js-ready .hero-lockup,
  html.js-ready .hero-title .line > span,
  html.js-ready .hero-subtitle,
  html.js-ready .hero-meta,
  html.js-ready .hero-buttons,
  html.js-ready .hero-already,
  html.js-ready .hero-rule,
  html.js-ready [data-split] .split-word > span {
    opacity: 1 !important;
    transform: none !important;
  }

  .particles, .cursor-dot, .cursor-glow { display: none !important; }
  .btn--pulse { animation: none !important; }

  /* No splash screen and no pointer-driven motion when motion is unwanted. */
  html.js-ready .preloader { display: none !important; }
  .countdown-value.is-ticking { animation: none !important; }
  .speaker-card { --tilt-x: 0deg !important; --tilt-y: 0deg !important; }
  .btn--primary { --pull-x: 0px !important; --pull-y: 0px !important; }
}
