Slot swap — 220ms ease-out
Outgoing slots fade + translate in 220ms. Fast enough to feel snappy, slow enough for the eye to track the swap without confusion.
Width morph — 350ms ease-in-out
The container width eases symmetrically — slower than the content slots so it reads as a settled destination, not a race to catch up.
Check draw — 450ms spring (34,1.56,64,1)
A slight overshoot on the stroke draw gives the checkmark a moment of personality — it "lands" rather than just appearing. Under reduced-motion the check is shown instantly; only the animation is removed.
Error shake — 500ms ease-out, once
Shake fires once and stops — it's information, not punishment. Under prefers-reduced-motion, the shake is skipped entirely; the red color + error label still communicate the failure.
Hover lift — 150ms ease-out
Hover uses translateY(-1px) + glow ring rather than scale, so the button doesn't visually grow into its neighbor. The glow is opacity-only — compositor-friendly.
Interruptibility
All state is gated by a lock flag. Spam-clicking during loading or a transition is silently ignored. The lock is always released, even on uncaught errors, via a finally block.