/* ─── Scroll reveal initial state ─── */
/*
  GSAP owns the transform/y for `.reveal` elements.
  We only set opacity:0 here so elements are hidden before GSAP initialises.
  GSAP's fromTo() will set and animate both opacity and y via inline styles,
  which always override CSS class rules.
*/
.reveal { opacity: 0; }

/* Fallback: when GSAP is absent (no-JS / CDN fail), JS adds .revealed */
.reveal.revealed {
  opacity: 1;
  transition: opacity 0.6s ease;
}

/* ─── Chip floats ─── */
@keyframes floatA {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-10px); }
}

@keyframes floatB {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-8px); }
}

@keyframes floatC {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-12px); }
}

.fa { animation: floatA 5s ease-in-out infinite; }
.fb { animation: floatB 5s ease-in-out 1.7s infinite; }
.fc { animation: floatC 5s ease-in-out 3.2s infinite; }

/* ─── Spine node pulse ─── */
@keyframes nodePulse {
  0%, 100% { opacity: 0.3; }
  50%       { opacity: 0.7; }
}

.spine-nd { animation: nodePulse 3s ease-in-out infinite; }

/* ─── Reduced-motion override ─── */
@media (prefers-reduced-motion: reduce) {
  /* JS will add .revealed immediately; also show them via CSS as a safety net */
  .reveal { opacity: 1; }

  .btn-cta,
  .btn-ghost,
  .nav-lnk,
  .lift,
  .tab-btn,
  .store-badge-link {
    transition: none;
  }

  .fa, .fb, .fc { animation: none; }
  .spine-nd      { animation: none; opacity: 0.5; }
}
