/* BirthdayPartyPal design system: motion primitives (DESIGN_SYSTEM.md "Motion").
   Compositor-only: every keyframe here moves transform/translate/scale/rotate or opacity.
   Pair with ds/motion.js (BPPMotion) for the IntersectionObserver fallback, presses, particles,
   View Transitions, tick-ups, toasts and sheets. Needs ds/tokens.css first. */

/* Cross-document View Transitions between our own pages (Chrome 126+, Safari 18.2+). */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

@layer ds.motion {
  /* ---------------- keyframes ---------------- */
  @keyframes ds-fade-in { from { opacity: 0; } }
  @keyframes ds-fade-out { to { opacity: 0; } }
  @keyframes ds-reveal { from { opacity: 0; translate: 0 var(--ds-reveal-y, 24px); scale: var(--ds-reveal-scale, 1); } }
  @keyframes ds-rise { from { opacity: 0; translate: 0 8px; } }
  @keyframes ds-pop { 0% { scale: .6; opacity: 0; } 100% { scale: 1; opacity: 1; } }
  @keyframes ds-pulse { 0%, 100% { scale: 1; } 50% { scale: 1.06; } }
  @keyframes ds-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -8px; } }
  @keyframes ds-bob { 0%, 100% { translate: 0 0; rotate: 0deg; } 30% { translate: 0 -10%; rotate: -4deg; } 60% { translate: 0 0; rotate: 3deg; } }
  @keyframes ds-wiggle { 0%, 100% { rotate: 0deg; } 25% { rotate: -6deg; } 75% { rotate: 6deg; } }
  @keyframes ds-shake { 0%, 100% { translate: 0 0; } 20% { translate: -8px 0; } 40% { translate: 7px 0; } 60% { translate: -4px 0; } 80% { translate: 2px 0; } }
  @keyframes ds-spin { to { rotate: 1turn; } }
  @keyframes ds-shimmer { to { translate: 100% 0; } }
  @keyframes ds-twinkle { 0%, 100% { opacity: .25; scale: .8; } 50% { opacity: 1; scale: 1.1; } }
  @keyframes ds-ripple { from { opacity: .55; scale: .4; } to { opacity: 0; scale: 2.4; } }
  @keyframes ds-parallax { to { translate: 0 calc(var(--ds-depth, .2) * 240px); } }
  @keyframes ds-vt-out { to { opacity: 0; scale: .985; } }
  @keyframes ds-vt-in { from { opacity: 0; scale: 1.015; } }
  @keyframes ds-vt-swap-out { to { opacity: 0; scale: .94; } }
  @keyframes ds-vt-swap-in { from { opacity: 0; scale: 1.06; } }

  /* ---------------- reveal on scroll ----------------
     [data-reveal]: scroll-driven where animation-timeline exists (no JS at all); otherwise
     BPPMotion adds .ds-io to <html> and .is-in to each element as it enters. Without JS and
     without scroll timelines nothing is hidden. */
  @supports (animation-timeline: view()) {
    [data-reveal] { animation: ds-reveal linear both; animation-timeline: view(); animation-range: entry 0% entry 70%; }
    [data-reveal="pop"] { --ds-reveal-scale: .92; --ds-reveal-y: 12px; }
  }
  .ds-io [data-reveal]:not(.is-in) { opacity: 0; translate: 0 var(--ds-reveal-y, 24px); }
  .ds-io [data-reveal] { transition: opacity var(--ds-dur-slower) var(--ds-ease-out), translate var(--ds-dur-spring) var(--ds-spring); transition-delay: calc(var(--ds-i, 0) * var(--ds-stagger)); }

  /* ---------------- staggered entrances ----------------
     [data-stagger] > * enter one after another (BPPMotion.stagger sets --ds-i; nth-child covers
     the first 12 without JS). Add .is-in (or let reveal do it) to play. */
  [data-stagger] > :nth-child(2) { --ds-i: 1; } [data-stagger] > :nth-child(3) { --ds-i: 2; } [data-stagger] > :nth-child(4) { --ds-i: 3; }
  [data-stagger] > :nth-child(5) { --ds-i: 4; } [data-stagger] > :nth-child(6) { --ds-i: 5; } [data-stagger] > :nth-child(7) { --ds-i: 6; }
  [data-stagger] > :nth-child(8) { --ds-i: 7; } [data-stagger] > :nth-child(9) { --ds-i: 8; } [data-stagger] > :nth-child(10) { --ds-i: 9; }
  [data-stagger] > :nth-child(11) { --ds-i: 10; } [data-stagger] > :nth-child(n+12) { --ds-i: 11; }
  [data-stagger].is-in > * { animation: ds-reveal var(--ds-dur-spring) var(--ds-spring) both; animation-delay: calc(var(--ds-i, 0) * var(--ds-stagger)); }
  [data-stagger="pop"].is-in > * { animation-name: ds-pop; animation-timing-function: var(--ds-spring-bouncy); animation-duration: var(--ds-dur-bounce); }
  .ds-io [data-stagger]:not(.is-in) > * { opacity: 0; }

  /* ---------------- View Transitions (same-document and cross-document) ---------------- */
  ::view-transition-old(root) { animation: ds-vt-out var(--ds-dur-base) var(--ds-ease-in) both; }
  ::view-transition-new(root) { animation: ds-vt-in var(--ds-dur-slow) var(--ds-ease-out) both; }
  ::view-transition-group(*) { animation-duration: var(--ds-dur-spring); animation-timing-function: var(--ds-spring); }
  /* BPPMotion.swap(el, ...) names the element ds-swap for one transition. */
  ::view-transition-old(ds-swap) { animation: ds-vt-swap-out var(--ds-dur-base) var(--ds-ease-in) both; }
  ::view-transition-new(ds-swap) { animation: ds-vt-swap-in var(--ds-dur-spring) var(--ds-spring) both; }
  /* A local swap holds the rest of the page perfectly still. */
  :root.ds-vt-local::view-transition-old(root), :root.ds-vt-local::view-transition-new(root) { animation: none; }
  .ds-lock { overflow: hidden; }
  /* Animated SVG art off screen (BPPMotion marks it): stop paying for it every frame. */
  [data-ds-offscreen] * { animation-play-state: paused !important; }
  .ds-vt-hero { view-transition-name: ds-hero; }
  .ds-vt-title { view-transition-name: ds-title; }

  /* ---------------- press and magnetic ---------------- */
  [data-press] { transition: scale var(--ds-dur-spring) var(--ds-spring-snappy); -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
  [data-press]:active { scale: .96; transition-duration: var(--ds-dur-instant); }
  [data-magnetic] { transition: translate var(--ds-dur-spring) var(--ds-spring); }

  /* ---------------- parallax ----------------
     [data-parallax] with --ds-depth (0..1): scroll-driven where supported, else BPPMotion. */
  @supports (animation-timeline: scroll()) {
    [data-parallax] { animation: ds-parallax linear both; animation-timeline: scroll(root); animation-range: 0 100vh; }
  }

  /* ---------------- helpers ---------------- */
  .ds-float { animation: ds-float 4.5s var(--ds-ease-in-out) infinite; }
  .ds-bob { animation: ds-bob 1.6s var(--ds-ease-in-out) infinite; }
  .ds-wiggle { animation: ds-wiggle .5s var(--ds-ease-in-out) 2; }
  .ds-pulse { animation: ds-pulse 1.8s var(--ds-ease-in-out) infinite; }
  .ds-ripple { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: 0 0 0 6px var(--ds-accent); animation: ds-ripple 1.1s var(--ds-ease-out) both; }
  .ds-particles { position: fixed; inset: 0; inline-size: 100vw; block-size: var(--ds-vh); z-index: var(--ds-z-particles); pointer-events: none; contain: strict; }
  .ds-particles--top { z-index: calc(var(--ds-z-moment) + 1); }

  /* ---------------- reduced motion: nothing moves; fades stay ---------------- */
  @media (prefers-reduced-motion: reduce) {
    [data-reveal], [data-stagger] > *, [data-parallax] { animation: none !important; translate: none !important; scale: none !important; }
    .ds-io [data-reveal]:not(.is-in), .ds-io [data-stagger]:not(.is-in) > * { opacity: 1; }
    .ds-float, .ds-bob, .ds-wiggle, .ds-pulse, .ds-empty__art, .ds-skel::after, .ds-meter > span::after { animation: none !important; }
    .ds-chip, .ds-btn, [data-press], [data-magnetic], .ds-card--interactive, .ds-tabbar__item svg { transition-property: opacity !important; }
    .ds-ripple { display: none; }
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
  }
}
