/* admin-motion.css — every @keyframes the backoffice uses, and the one switch that
 * turns all of them off.
 *
 * Kept in a single file on purpose. Animation is the part of a design system that
 * rots quietest: a keyframe defined next to the component that first needed it
 * gets copied, renamed, and subtly diverged, and nobody notices because six
 * near-identical fades all look fine. One file means one definition, and it means
 * the prefers-reduced-motion escape hatch at the bottom provably covers the lot.
 *
 * Named `bo-*` so a keyframe can never collide with one in the shared kit.
 *
 * MUST BE LINKED LAST. The reduced-motion block is a blanket !important reset and
 * has to outrank whatever a later sheet might add. */

/* Rows and list items arriving. Up and in — the mockup's trIn. */
@keyframes bo-row-in {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}

/* Something appearing in place: an empty state, a validation message. */
@keyframes bo-pop {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: none; }
}

/* A banner pushing the page down. */
@keyframes bo-slide-down {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}

/* The modal. Scale from .96, never from 0 — a dialog that grows from nothing
 * reads as a zoom, not as a panel arriving. */
@keyframes bo-dialog-in {
  from { opacity: 0; transform: translateY(12px) scale(0.96); }
  to   { opacity: 1; transform: none; }
}

/* A menu unfolding from its own corner. transform-origin is set by the rule that
 * uses this, not here, because the profile menu opens from top-right and a
 * future menu may not. */
@keyframes bo-menu-in {
  from { opacity: 0; transform: scale(0.96) translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* Confirmation that a value changed — the pager's current page, a counter
 * ticking over. Overshoots and settles; a linear scale reads as a glitch. */
@keyframes bo-bump {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* Rejection. Four beats, 5px, no rotation. The point is peripheral vision: a
 * field that fails validation below the fold is findable because something
 * moved, not because something is red. */
@keyframes bo-shake {
  0%, 100%  { translate: 0; }
  20%, 60%  { translate: -5px 0; }
  40%, 80%  { translate: 5px 0; }
}

/* Work in flight. Travels right-to-left across a placeholder surface. */
@keyframes bo-shimmer {
  to { background-position: -220% 0; }
}

/* The in-flight spinner on a button. */
@keyframes bo-spin {
  to { transform: rotate(360deg); }
}

/* A live indicator — an open shift, a connected till. Expands and fades; it does
 * not flash, because a flashing dot on a page someone stares at all shift is an
 * accessibility problem, not a feature. */
@keyframes bo-pulse {
  0%   { box-shadow: 0 0 0 0 currentColor; opacity: 1; }
  100% { box-shadow: 0 0 0 6px transparent; opacity: 0.85; }
}

/* ---- the switch ----------------------------------------------------------
 *
 * prefers-reduced-motion is a medical setting, not a preference. Vestibular
 * disorders make transform-based motion genuinely nauseating, and this console
 * is looked at for a whole shift.
 *
 * The blanket form is deliberate. A per-animation opt-out is a list that goes
 * stale the first time someone adds a keyframe and forgets, and the failure is
 * invisible to everyone who does not have the setting on. This cannot go stale:
 * anything animated, anywhere in the backoffice, stops.
 *
 * `animation: none` rather than a zero duration, so `both`-filled animations do
 * not strand an element at its `from` state — a row that never plays bo-row-in
 * must be visible, not stuck at opacity 0. Same reason `transform` is reset. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
  table.data tbody tr,
  .empty,
  .table-empty,
  .banner,
  .error-box,
  dialog[open],
  .menu:not([hidden]) {
    opacity: 1 !important;
    transform: none !important;
  }
  #live-status {
    transform: none !important;
  }
  .htmx-request .table-scroll::after { display: none; }
}
