/* Disclosure Gate motion v4. Load after tokens.css.
   Motion announces a change and then gets out of the way: transform and opacity only, short, eased
   out, played once per change. The live dot and the level meters are the only things that repeat,
   because they are data. Under prefers-reduced-motion every duration token is zero and base.css
   collapses animations, so each state still reads without movement. */

@keyframes dg-spin { to { transform: rotate(360deg); } }
@keyframes dg-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes dg-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes dg-dialog-in { from { opacity: 0; transform: translateY(8px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes dg-drawer-in { from { opacity: 0.4; transform: translateX(24px); } to { opacity: 1; transform: none; } }
@keyframes dg-toast-in { from { opacity: 0; transform: translateY(8px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes dg-toast-out { to { opacity: 0; transform: translateY(4px) scale(0.98); } }
@keyframes dg-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@keyframes dg-shimmer { to { transform: translateX(100%); } }
@keyframes dg-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* M1 and M3 FLIP: start at the old position, then play to the new one. dom.mjs travel() and the
   shared renderer set --flip-dx and --flip-dy through CSSOM. */
.m-flip { transform: translate(var(--flip-dx, 0px), var(--flip-dy, 0px)); }
.m-flip.m-flip-play { transform: none; transition: transform var(--dur-withdraw) var(--ease-out); }
.m-flip.m-flip-play.m-cross { transition-duration: var(--dur-cross); transition-timing-function: var(--ease-spring); }

/* An item that just crossed the gate: a brief green wash that fades back to the surface. */
.m-cross { position: relative; }
.m-cross::before {
  content: "";
  position: absolute;
  inset: -4px -8px;
  border-radius: var(--radius-md);
  background: var(--ok-bg);
  box-shadow: inset 0 0 0 1px var(--ok-border);
  pointer-events: none;
  animation: dg-cross-wash 1.6s var(--ease-standard) forwards;
  z-index: -1;
}
@keyframes dg-cross-wash { 0% { opacity: 1; } 60% { opacity: 1; } 100% { opacity: 0; } }

/* M2 update tick: the number slides up into place. */
.m-tick { display: inline-block; font-variant-numeric: var(--figures-data); }
.m-tick.m-tick-play { animation: m-tick-in var(--dur-tick) var(--ease-out) both; }
@keyframes m-tick-in { from { transform: translateY(0.6em); opacity: 0; } to { transform: none; opacity: 1; } }

/* M3 withdraw: the ghost keeps the removed item's last position and fades out while the rest close up.
   The container needs position: relative; the ghost's box comes from --ghost-x/y/w through CSSOM. */
.m-ghost {
  position: absolute;
  left: var(--ghost-x, 0px);
  top: var(--ghost-y, 0px);
  width: var(--ghost-w, auto);
  margin: 0;
  pointer-events: none;
  animation: m-fade-out var(--dur-withdraw) var(--ease-out) forwards;
}
@keyframes m-fade-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: scale(0.98); } }

/* Crossfade: the struck source gives way to the sentence the team sees. */
.m-crossfade-in { animation: dg-fade-in var(--dur-state) var(--ease-out) both; }

/* Removal shown statically in the gate view: the words taken out above, the result below. */
.m-before del, .m-before s { text-decoration-line: line-through; text-decoration-thickness: 1px; color: var(--fg-subtle); }
.m-after { margin-top: var(--space-2); }

/* A state pill changing word: the old word lifts away, the new one settles (flap.mjs setFlap). */
.flap.is-flipping, .state-pill.is-flipping { animation: dg-pill-swap var(--dur-state) var(--ease-out); }
@keyframes dg-pill-swap { 0% { opacity: 0.2; transform: translateY(3px) scale(0.96); } 100% { opacity: 1; transform: none; } }

/* The update counter turns once per change. */
.flip-counter__digit.is-flipping { animation: dg-counter-turn var(--dur-tick) var(--ease-out); }
@keyframes dg-counter-turn { 0% { transform: translateY(-40%); opacity: 0; } 100% { transform: none; opacity: 1; } }

/* A status pill lands: a small press, once. */
.stamp.is-stamped, .chip.is-stamped, .badge.is-stamped { animation: dg-land var(--dur-stamp) var(--ease-out); }
@keyframes dg-land { 0% { transform: scale(1.12); opacity: 0; } 100% { transform: none; opacity: 1; } }

/* A section ruling draws in once. */
.rule.is-drawn { transform-origin: left center; animation: dg-rule-draw var(--dur-slow) var(--ease-out); }
@keyframes dg-rule-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* The gate pulses once as an item crosses it. */
.gate-threshold.is-crossing, .gate-pulse.is-crossing { animation: dg-gate-pulse 900ms var(--ease-out); }
@keyframes dg-gate-pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok-mark) 45%, transparent); }
  100% { box-shadow: 0 0 0 14px color-mix(in srgb, var(--ok-mark) 0%, transparent); }
}

/* Rows arriving on a live list or board. */
.is-arriving, .m-arrive { animation: dg-rise var(--dur-slow) var(--ease-out) both; }
