/* ==========================================================================
   SLASHED Animations — v0.6.25.0
   Named @keyframes library + .animate-* utility classes.

   Opt-in — link separately when needed. REQUIRES one of tokens-default.css,
   tokens-legacy.css, or slashed-full.css to be loaded first; this file
   consumes --sf-duration-*, --sf-ease-*, --sf-spin-duration tokens with no
   fallbacks per the framework's anti-break-fallbacks policy. Loaded
   standalone, the .animate-* utilities resolve invalid:
     <link rel="stylesheet" href="slashed-full.css">         <!-- required -->
     <link rel="stylesheet" href="slashed-animations.css">   <!-- opt-in -->

   Layer: slashed.animations — sits between slashed.visual and slashed.a11y.
   The slashed.a11y layer beats this one, which means .motion-reduce:* and
   prefers-reduced-motion overrides in slashed-core.css always win.

   Motion safety: all .animate-* utility classes are wrapped in
   @media (prefers-reduced-motion: no-preference) so they are inert for
   users who have requested reduced motion. The @keyframes themselves are
   always defined (they must exist even if not used, so references in
   slashed.base and slashed.a11y resolve correctly).
   ========================================================================== */

@layer slashed.reset, slashed.base, slashed.layout, slashed.components, slashed.utilities, slashed.visual, slashed.animations, slashed.a11y, slashed.overrides;

/* ==========================================================================
   @KEYFRAMES — always defined, no media-query gate
   (CSS requires the name to be present for animation: references to resolve)
   ========================================================================== */

/* ----- Fade ----- */
@keyframes fade-in       { from { opacity: 0; } }
@keyframes fade-out      { to   { opacity: 0; } }

/* ----- Slide ----- */
@keyframes slide-in-up    { from { opacity: 0; translate: 0  1rem; } }
@keyframes slide-in-down  { from { opacity: 0; translate: 0 -1rem; } }
@keyframes slide-in-left  { from { opacity: 0; translate: -1rem 0; } }
@keyframes slide-in-right { from { opacity: 0; translate:  1rem 0; } }

/* ----- Scale ----- */
@keyframes scale-in  { from { opacity: 0; scale: 0.95; } }
@keyframes scale-out { to   { opacity: 0; scale: 0.95; } }

/* ----- Spin ----- */
@keyframes spin { to { rotate: 360deg; } }

/* ----- Pulse ----- */
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.5; }
}

/* ----- Ping (ripple-out) ----- */
@keyframes ping {
  75%, 100% { scale: 2; opacity: 0; }
}

/* ----- Bounce ----- */
@keyframes bounce {
  0%, 100% { translate: 0 0;      animation-timing-function: cubic-bezier(0.8, 0, 1, 1); }
  50%       { translate: 0 -25%;  animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }
}

/* ----- Shake (attention-seeker) ----- */
@keyframes shake {
  0%, 100% { translate: 0; }
  10%, 50%, 90% { translate: -4px 0; }
  30%, 70%      { translate:  4px 0; }
}

/* ==========================================================================
   .animate-* UTILITY CLASSES
   Guarded by prefers-reduced-motion: no-preference — inert for reduced-motion.
   All use `animation-fill-mode: both` so the start state is applied immediately
   and the end state persists after the animation completes.
   ========================================================================== */

@layer slashed.animations {

  /* ----- Base animation variables ----- */
  /* Tokens are declared unconditionally so consumers can read them in any
     context. Reduced-motion is enforced by slashed-core.css via
     `animation-duration: 0.01ms !important` under @media (prefers-reduced-motion:
     reduce); the utility wrapper below remains as belt-and-braces. */
  :root {
    --sf-anim-duration: var(--sf-duration-normal);
    --sf-anim-easing:   var(--sf-ease-out);
    --sf-anim-delay:    0s;
    --sf-anim-fill:     both;
  }

  @media (prefers-reduced-motion: no-preference) {

    /* ----- Fade ----- */
    .sf-animate-fade-in  { animation: fade-in  var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }
    .sf-animate-fade-out { animation: fade-out var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }

    /* ----- Slide ----- */
    .sf-animate-slide-in-up    { animation: slide-in-up    var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }
    .sf-animate-slide-in-down  { animation: slide-in-down  var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }
    .sf-animate-slide-in-left  { animation: slide-in-left  var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }
    .sf-animate-slide-in-right { animation: slide-in-right var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }

    /* ----- Scale ----- */
    .sf-animate-scale-in  { animation: scale-in  var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }
    .sf-animate-scale-out { animation: scale-out var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }

    /* ----- Continuous ----- */
    /* Duration instance tokens allow per-element speed override:
       style="--sf-spin-duration: 0.5s"  — fast spinner
       style="--sf-pulse-duration: 3s"   — slow pulse     */
    .sf-animate-spin   { animation: spin   var(--sf-spin-duration,   1s) linear infinite; }
    .sf-animate-pulse  { animation: pulse  var(--sf-pulse-duration,  2s) var(--sf-ease-in-out) infinite; }
    .sf-animate-ping   { animation: ping   var(--sf-ping-duration,   1s) var(--sf-ease-out) infinite; }
    .sf-animate-bounce { animation: bounce var(--sf-bounce-duration, 1s) infinite; }

    /* ----- Attention ----- */
    .sf-animate-shake { animation: shake var(--sf-anim-duration) var(--sf-anim-easing) var(--sf-anim-delay) var(--sf-anim-fill); }

  }

  /* ----- Modifiers (intent-independent — work for any animation, including
     consumer-authored ones, regardless of prefers-reduced-motion). ----- */

  /* ----- Duration overrides ----- */
  .sf-anim-fast   { --sf-anim-duration: var(--sf-duration-fast); }
  .sf-anim-normal { --sf-anim-duration: var(--sf-duration-normal); }
  .sf-anim-slow   { --sf-anim-duration: var(--sf-duration-slow); }

  /* ----- Delay modifiers ----- */
  .sf-anim-delay-75   { --sf-anim-delay: 75ms;  }
  .sf-anim-delay-150  { --sf-anim-delay: 150ms; }
  .sf-anim-delay-300  { --sf-anim-delay: 300ms; }
  .sf-anim-delay-500  { --sf-anim-delay: 500ms; }
  .sf-anim-delay-700  { --sf-anim-delay: 700ms; }
  .sf-anim-delay-1000 { --sf-anim-delay: 1000ms; }

  /* ----- Fill mode ----- */
  .sf-anim-fill-none    { --sf-anim-fill: none; }
  .sf-anim-fill-forward { --sf-anim-fill: forwards; }
  .sf-anim-fill-both    { --sf-anim-fill: both; }

  /* ----- Play state ----- */
  .sf-anim-paused  { animation-play-state: paused; }
  .sf-anim-running { animation-play-state: running; }

  /* ----- Iteration ----- */
  .sf-anim-once     { animation-iteration-count: 1; }
  .sf-anim-infinite { animation-iteration-count: infinite; }

}
