@layer animations {
  /* Every keyframe here is an arrival or a departure. The fall itself is not in
     this file and never should be: a word under gravity is integrated per frame
     in game/Storm.js, because its speed changes mid-flight as the weather turns
     and no CSS animation can express that.

     Nothing overshoots. @see --ease-drift */

  /* A cleared word's receipt: one ring, out and gone. */
  @keyframes ripple {
    from {
      opacity: 0.7;
      scale: 0.4;
    }

    to {
      opacity: 0;
      scale: 2.2;
    }
  }

  /* Silt arriving at the floor. Lands, rather than fades in. */
  @keyframes settle {
    from {
      opacity: 0;
      translate: 0 -0.5rem;
    }

    to {
      opacity: 1;
      translate: 0 0;
    }
  }

  @keyframes rise {
    from {
      opacity: 0;
      translate: 0 0.5rem;
    }

    to {
      opacity: 1;
      translate: 0 0;
    }
  }

  @keyframes fade {
    to {
      opacity: 0;
    }
  }

  /* The countdown beat. One per second, restarted by ui/Countdown.js reflowing
     the element — the number has to feel like it lands. */
  @keyframes beat {
    from {
      opacity: 0;
      scale: 1.12;
    }

    to {
      opacity: 1;
      scale: 1;
    }
  }

  .countdown__number {
    display: inline-block;
    animation: beat var(--dur-settle) var(--ease-drift) both;
  }

  /* A life lost. The lane flinches once — the only shake in the game, and it is
     two pixels, because this design does not shout. */
  @keyframes flinch {
    0%,
    100% {
      translate: 0 0;
    }

    25% {
      translate: -2px 0;
    }

    75% {
      translate: 2px 0;
    }
  }

  .lane--flinch .lane__sky {
    animation: flinch var(--dur-base) var(--ease-in-out);
  }

  /* Reduced motion: the fall survives, everything decorative does not.
     The token layer already collapses every duration to 1ms; these rules remove
     the animations outright so nothing is left mid-transform. */
  @media (prefers-reduced-motion: reduce) {
    .ripple,
    .silt,
    .toast,
    .countdown__number {
      animation: none;
    }

    .ripple {
      opacity: 0;
    }

    .lane--flinch .lane__sky {
      animation: none;
    }

    /* The word keeps its transform — that is the simulation, not decoration —
       but stops advertising itself to the compositor, since a motion-sensitive
       player has no smooth animation to protect. */
    .word {
      will-change: auto;
    }
  }
}
