/* AISU HERO · motion system (WP0). SPEC 4.1 tokens, 4.2 rules, 4.3 named keyframes, 4.8 reduced motion.
   Rules: keyframes touch transform and opacity only. Base styles are the FINAL state: entrances only have a
   `from` block and use fill `backwards`, so a UI whose animation never runs is still correct.
   Static poses use the individual `rotate` / `translate` / `scale` properties, so these `transform` keyframes compose
   with them instead of overwriting the tilt.

   How to animate (one override catches everything under reduced motion):
   - Entrances: add an .in-* class (presets below) and tune it with --dur / --at / --ease / --slide / --swing,
     or set your own `--anim: <animation shorthand>` on an element that has the .anim class.
   - Infinite loops: set `--loop: <animation shorthand with infinite>` on an element with the .loop class
     (or on any .in-* element: it runs after/with the entrance). An element may carry both.
   - Exits are written explicitly (e.g. body[data-prev] rules in base.css use ff-sink-out).
   --anim, --loop, --at, --dur and --ease are registered as NON-inherited: a child never picks up its parent's
   animation or delay (set --at on each element, as SPEC 4.5 asks for ceremony parts). */

@property --anim { syntax: '*'; inherits: false; }
@property --loop { syntax: '*'; inherits: false; }
@property --at   { syntax: '*'; inherits: false; }
@property --dur  { syntax: '*'; inherits: false; }
@property --ease { syntax: '*'; inherits: false; }

:root {
  /* durations */
  --t-instant: 90ms;     /* press, cursor hide */
  --t-quick: 140ms;      /* hover, cursor move, exits */
  --t-base: 220ms;       /* small reveals, toggles, tab ink */
  --t-panel: 340ms;      /* panels, toasts (320-380) */
  --t-stage: 520ms;      /* stall, boards, ema (480-520) */
  --t-ceremony: 680ms;   /* stamps, verdict (620-680) */
  --beat: .397s;         /* 60 / 151 BPM; beat.js sets it per song */
  --beat-delay: 0s;      /* beat.js: -(phase x beat) so loops land on the beat */

  /* easings */
  --ease-out: cubic-bezier(.22, 1, .36, 1);
  --ease-out-expo: cubic-bezier(.16, 1, .3, 1);
  --ease-snap: cubic-bezier(.2, .9, .25, 1);
  --ease-in: cubic-bezier(.4, 0, 1, 1);
  --ease-inout: cubic-bezier(.65, 0, .35, 1);
  --ease-back: cubic-bezier(.34, 1.56, .64, 1);
  --spring-pop: linear(0, 0.11, 0.363, 0.653, 0.907, 1.086, 1.182, 1.205, 1.177, 1.124, 1.064, 1.014, 0.979, 0.961, 0.958, 0.965, 0.976, 0.988, 0.998, 1.005, 1.008, 1.009, 1.007, 1.005, 1.002, 1, 0.999, 1);
  --spring-stamp: linear(0, 0.074, 0.25, 0.464, 0.673, 0.851, 0.983, 1.069, 1.114, 1.126, 1.117, 1.095, 1.067, 1.041, 1.019, 1.002, 0.991, 0.986, 0.984, 0.985, 0.988, 0.992, 0.995, 0.998, 1, 1.001, 1.002, 1);
  --spring-soft: linear(0, 0.046, 0.157, 0.3, 0.45, 0.592, 0.716, 0.819, 0.9, 0.959, 1, 1.026, 1.04, 1.046, 1.045, 1.041, 1.035, 1.028, 1.021, 1.015, 1.01, 1.006, 1.003, 1.001, 0.999, 0.998, 0.998, 1);
}
/* risk 8.8: browsers without linear() fall back to the back-ease overshoot */
@supports not (transition-timing-function: linear(0, 1)) {
  :root { --spring-pop: var(--ease-back); --spring-stamp: var(--ease-back); --spring-soft: var(--ease-back); }
}

/* ======================================================================= keyframes (4.3) */
/* entrances: `from` only (fill backwards) */
@keyframes ff-fade      { from { opacity: 0; } }
@keyframes ff-rise-in   { from { opacity: 0; transform: translateY(1rem); } }
@keyframes ff-slide-l   { from { opacity: 0; transform: translateX(calc(-1 * var(--slide, 108%))); } 25% { opacity: 1; } }
@keyframes ff-slide-r   { from { opacity: 0; transform: translateX(var(--slide, 108%)); } 25% { opacity: 1; } }
@keyframes ff-drop-in   { from { opacity: 0; transform: translateY(-120%) rotate(-6deg); } 30% { opacity: 1; } }
@keyframes ff-pop       { from { opacity: 0; transform: scale(.55); } 30% { opacity: 1; } }
@keyframes ff-stamp     { 0% { opacity: 0; transform: scale(1.9) rotate(-10deg); } 14% { opacity: 1; } 100% { opacity: 1; transform: none; } }
@keyframes ff-bar       { from { transform: scaleX(0); } }
@keyframes ff-judge     { from { opacity: 0; transform: scale(1.5); } }
@keyframes ff-don       { from { transform: scale(1.12); } }

/* one cycle, ends where it started */
@keyframes ff-swing     { 0%, 100% { transform: rotate(0deg); } 30% { transform: rotate(var(--swing, 1.2deg)); } 70% { transform: rotate(calc(-1 * var(--swing, 1.2deg))); } }
@keyframes ff-shake     { 0%, 100% { transform: translateX(0); } 17%, 50%, 83% { transform: translateX(-.2rem); } 33%, 67% { transform: translateX(.2rem); } }
@keyframes ff-hop       { 0% { transform: translateY(0) scale(1); } 45% { transform: translateY(-1.2rem) scale(.97, 1.04); } 78% { transform: translateY(0) scale(1.08, .9); } 100% { transform: translateY(0) scale(1); } }

/* exits and one-shots that end invisible or moved (use fill forwards) */
@keyframes ff-sink-out  { to { opacity: 0; transform: translateY(.5rem) scale(.98); } }
@keyframes ff-fade-out  { to { opacity: 0; } }                       /* helper (not in 4.3): bubbles, toasts, reduced-motion curtain */
@keyframes ff-shockwave { from { opacity: .9; transform: scale(.4); } to { opacity: 0; transform: scale(1.8); } }
@keyframes ff-curtain-l { from { transform: translateX(0); } to { transform: translateX(-102%); } }
@keyframes ff-curtain-r { from { transform: translateX(0); } to { transform: translateX(102%); } }
@keyframes ff-wipe      { from { transform: translateX(110vw) skewX(-20deg); } to { transform: translateX(-110vw) skewX(-20deg); } }
@keyframes ff-poof      { from { opacity: 1; transform: scale(.4); } to { opacity: 0; transform: scale(1.2); } }
@keyframes ff-burst     { 0% { opacity: 0; transform: scale(.15) rotate(0deg); } 8% { opacity: 1; } 55% { opacity: 1; transform: scale(1) rotate(8deg); } 100% { opacity: 0; transform: scale(1.06) rotate(10deg); } }

/* sprite strips: the strip is 4 frames wide; animate it with steps(4) */
@keyframes ff-walk      { from { transform: translateX(0); } to { transform: translateX(-100%); } }
@keyframes ff-appear    { from { transform: translateX(0); } to { transform: translateX(-100%); } }

/* loops (use with --loop / .loop; beat loops add animation-delay: var(--beat-delay)) */
@keyframes ff-squash    { 0% { transform: scale(1.05, .93); } 18% { transform: scale(.98, 1.03); } 42%, 100% { transform: scale(1); } }
@keyframes ff-glow      { from { opacity: .55; } to { opacity: 1; } }
@keyframes ff-tip       { from { transform: translateY(0); } to { transform: translateY(-.15rem); } }
@keyframes ff-breathe   { from { transform: scale(1); } to { transform: scale(1.045); } }
@keyframes ff-sway      { from { transform: rotate(calc(-1 * var(--sway, 1.5deg))); } to { transform: rotate(var(--sway, 1.5deg)); } }
@keyframes ff-bob       { from { transform: translateY(0); } to { transform: translateY(-.3rem); } }
@keyframes ff-flicker   { from { transform: scaleY(1) skewX(-3deg); } to { transform: scaleY(1.08) skewX(3deg); } }
@keyframes ff-shine     { 0% { transform: translateX(-120%) skewX(-20deg); } 62.5%, 100% { transform: translateX(120%) skewX(-20deg); } }
@keyframes ff-pulse-danger { from { opacity: .45; } to { opacity: 1; } }
@keyframes ff-spin      { to { transform: rotate(360deg); } }
@keyframes ff-ember     { 0% { opacity: 0; transform: translate(0, 0) scale(1); } 12% { opacity: 1; } 75% { opacity: .85; } 100% { opacity: 0; transform: translate(var(--drift, 2rem), -40vh) scale(.4); } }

/* ======================================================================= utilities
   Entrance presets. Tune per element: --at (delay), --dur, --ease, --slide (ff-slide-*), --swing (ff-swing). */
.anim, .loop,
.in-fade, .in-rise, .in-slide-l, .in-slide-r, .in-drop, .in-pop, .in-stamp, .in-bar, .in-judge, .in-don,
.in-swing, .in-shake, .in-hop, .in-shock, .in-burst, .in-poof {
  animation: var(--anim, none), var(--loop, none);
}
.in-fade    { --anim: ff-fade     var(--dur, 200ms)              var(--ease, linear)             var(--at, 0ms) backwards; }
.in-rise    { --anim: ff-rise-in  var(--dur, var(--t-base))      var(--ease, var(--ease-out))    var(--at, 0ms) backwards; }
.in-slide-l { --anim: ff-slide-l  var(--dur, var(--t-stage))     var(--ease, var(--spring-soft)) var(--at, 0ms) backwards; }
.in-slide-r { --anim: ff-slide-r  var(--dur, var(--t-stage))     var(--ease, var(--spring-soft)) var(--at, 0ms) backwards; }
.in-drop    { --anim: ff-drop-in  var(--dur, var(--t-stage))     var(--ease, var(--spring-soft)) var(--at, 0ms) backwards; }
.in-pop     { --anim: ff-pop      var(--dur, 380ms)              var(--ease, var(--spring-pop))  var(--at, 0ms) backwards; }
.in-stamp   { --anim: ff-stamp    var(--dur, var(--t-ceremony))  var(--ease, var(--spring-stamp)) var(--at, 0ms) backwards; }
.in-bar     { --anim: ff-bar      var(--dur, 400ms)              var(--ease, var(--ease-out))    var(--at, 0ms) backwards; }
.in-judge   { --anim: ff-judge    var(--dur, 300ms)              var(--ease, var(--spring-pop))  var(--at, 0ms) backwards; }
.in-don     { --anim: ff-don      var(--dur, var(--t-base))      var(--ease, var(--spring-pop))  var(--at, 0ms) backwards; }
.in-swing   { --anim: ff-swing    var(--dur, 600ms)              var(--ease, var(--ease-inout))  var(--at, 0ms) both; }
.in-shake   { --anim: ff-shake    var(--dur, var(--t-base))      var(--ease, linear)             var(--at, 0ms) both; }
.in-hop     { --anim: ff-hop      var(--dur, 360ms)              var(--ease, var(--ease-out))    var(--at, 0ms) both; }
.in-shock   { --anim: ff-shockwave var(--dur, 500ms)             var(--ease, var(--ease-out))    var(--at, 0ms) both; }
.in-burst   { --anim: ff-burst    var(--dur, 900ms)              var(--ease, var(--ease-out))    var(--at, 0ms) both; }
.in-poof    { --anim: ff-poof     var(--dur, 600ms)              var(--ease, var(--ease-out))    var(--at, 0ms) both; }

/* ======================================================================= reduced motion (4.8)
   Every entrance becomes a 120ms fade (no delay, so content is there at once); every loop stops.
   Particles (fireworks, embers, poofs, the wipe, rays) are removed in base.css. Components with their own
   reduced-motion needs (cursor, mascot, curtain, judgements, ceremony) add rules in their own files. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    --anim: ff-fade 120ms ease-out backwards !important;
    --loop: none !important;
  }
  .loop { animation: var(--anim, none) !important; }
}
