/* animations.css — GSAP ScrollTrigger animation states
 * Pairs with js/animations.js and js/animations/ sub-modules.
 * Must be loaded before animations.js so initial states apply
 * before JS runs (prevents FOUC on .will-reveal elements).
 */

/* === INITIAL HIDDEN STATES ===
 * Applied programmatically by animations.js after confirming GSAP loaded.
 * NOT set in HTML so no-JS / GSAP-fail users always see content.
 */
.will-reveal{opacity:0;transform:translateY(40px)}
.will-reveal-x{opacity:0;transform:translateX(-30px)}
.will-reveal-scale{opacity:0;transform:scale(0.94)}

/* === CHARACTER SPLIT ===
 * Wrapper for individual character spans created by splitChars().
 * overflow:hidden on parent clips the chars as they slide up.
 */
.split-line{display:block;overflow:hidden;line-height:inherit}
.anim-char{display:inline-block;will-change:transform,opacity}
.anim-word{display:inline-block;will-change:transform,opacity}

/* === IMAGE MASK REVEAL ===
 * Wrapper must have overflow:hidden. Inner image starts translated
 * so it appears to wipe in from the left.
 */
.anim-mask{overflow:hidden;display:block}
.anim-mask img,.anim-mask model-viewer{
  will-change:transform,clip-path;
  transform-origin:left center
}

/* === STAGGER CONTAINER ===
 * Used on destination cards, service rows, place-cards.
 * JS adds .will-reveal to each child before triggering.
 */
.stagger-container>.stagger-item{opacity:0;transform:translateY(30px)}

/* === COUNTER ANIMATION ===
 * Targets stat values that count up on scroll.
 */
.anim-counter{display:inline-block;will-change:contents}

/* === PINNED SECTION ===
 * Booking section gets pinned. Extra padding-top prevents
 * content jumping during pin trigger calculation.
 */
.pin-spacer{box-sizing:border-box}

/* === CLIP-PATH REVEAL (editorial image wipe) ===
 * Initial state only — no will-change here. GSAP promotes the layer
 * automatically at animation time, keeping offscreen images cheap.
 */
.clip-reveal{clip-path:inset(0 100% 0 0)}
.clip-reveal.is-revealed{clip-path:inset(0 0% 0 0)}

/* === REDUCED MOTION OVERRIDE (CRITICAL)
 * If GSAP fails or prefers-reduced-motion, all elements must be visible.
 * This is the safety net — never remove it.
 */
@media (prefers-reduced-motion:reduce){
  .will-reveal,
  .will-reveal-x,
  .will-reveal-scale,
  .anim-char,
  .anim-word,
  .stagger-container>.stagger-item,
  .clip-reveal{
    opacity:1 !important;
    transform:none !important;
    clip-path:none !important;
    transition:none !important;
    animation:none !important;
  }
}

/* Fallback: if JS has not run yet and body lacks [data-anim-ready],
 * keep hidden states applied only to elements inside [data-anim-ready].
 * Before JS: everything visible (no data-anim-ready on body).
 * After JS init: body gets data-anim-ready and hidden states are applied.
 */
body:not([data-anim-ready]) .will-reveal,
body:not([data-anim-ready]) .will-reveal-x,
body:not([data-anim-ready]) .will-reveal-scale,
body:not([data-anim-ready]) .stagger-container>.stagger-item,
body:not([data-anim-ready]) .clip-reveal{
  opacity:1 !important;
  transform:none !important;
  clip-path:none !important;
}
