/* Motion — the vocabulary, and the one place it can be switched off.

   DESIGN-DIRECTION section 6: motion explains CHANGES IN UNDERSTANDING. Not
   changes in layout, and not that something is available to click. Three moments
   in this product change what the reader knows, and each gets a named token in
   `tokens/semantic.tokens.json`:

     --motion-reveal    depth arrives on demand -- the basis of a claim
     --motion-replace   one understanding is exchanged for another -- the canvas
     --motion-settle    the reader's own selection registers

   WHAT THIS FILE HOLDS is the keyframes those moments share and the reduced
   motion switch. WHERE each is applied is the component's business and stays in
   `product-ui/`, the same split the Ask row and the button already follow.

   ENTRY IS ANIMATED, EXIT IS INSTANT, and that is a decision rather than a
   limitation to apologise for. What changed is that depth was revealed or an
   answer arrived; withdrawing it returns the reader to an unobstructed reading
   column and explains nothing by taking time over it. It is also what lets the
   whole vocabulary be CSS: `x-show` toggles `display`, which RESTARTS a keyframe
   animation on show -- verified in Chrome, not assumed -- and cannot transition
   on hide. So no `x-transition`, no durations in markup, and the design system
   stays independent of Alpine (section 1).

   NOTHING STAGGERS. A list whose items arrive one after another asserts an
   ordering, and ordering claims by importance is forbidden (section 6.6, and
   materiality is open at 15.5). Everything here moves at once or not at all. */

/* A thing arriving into rest. The displacement is small on purpose: enough to
   say "this came from somewhere", not enough to read as a slide-in. */
@keyframes reveal-from-left {
  from { opacity: 0; transform: translateX(8px); }
  to   { opacity: 1; transform: translateX(0); }
}

@keyframes reveal-from-below {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* One understanding replacing another. Shorter travel than a reveal and a
   longer duration: the point is the exchange, not the arrival. */
@keyframes exchange {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* A mark appearing beside something that has not moved. */
@keyframes mark {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Still working (plan cb3d1 task 52; DS-19 decided by the owner, 2026-10-15).
   THE ONE MOTION THAT IS NOT A CHANGE: while Ask works there are seconds in
   which nothing arrives, and a still page reads as a stalled one. One turn a
   second, linear, so it reads as steady rather than urgent; at zero duration,
   under reduced motion, the indicator stands still and the status text beside
   it carries the meaning. */
@keyframes working {
  to { transform: rotate(360deg); }
}

/* THE WHOLE VOCABULARY, OFF, IN ONE BLOCK.

   Every rule that moves names a `--motion-*` duration, so zeroing the tokens
   here stops all of it. A component therefore does not carry its own
   `prefers-reduced-motion` guard -- `statement.css` used to, and a guard that
   each component must remember is the one that gets forgotten on the fourth.

   NOT the usual `*, ::before, ::after { transition-duration: 0.01ms !important }`
   sweep: that silences rules which never opted in, and `!important` across every
   element on the page is unreviewable.

   THE COST, stated rather than hidden: these five names are declared in
   `tokens/semantic.tokens.json` and redefined here, so a `--motion-*` value now lives
   in two files. `tests/workspace/web/test_motion.py` fails when a motion token exists that
   this block does not zero, which is what stops the two drifting.

   Zero rather than a short duration. `0.01ms` is a trick for firing
   `transitionend`; nothing here listens for one. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-reveal: 0ms;
    --motion-replace: 0ms;
    --motion-settle: 0ms;
    --motion-working: 0ms;
  }
}
