/* ============================================================================
   Motion tokens + the seven required moments (app-standards rule 03).
   Only transform and opacity are animated. Exits run ~0.7x the entrance.
   ========================================================================== */

:root {
  --dur-instant: 90ms;
  --dur-fast: 140ms;
  --dur-base: 220ms;
  --dur-slow: 320ms;
  --dur-slower: 460ms;
  --dur-exit-base: 150ms;
  --dur-exit-slow: 220ms;

  --ease-out: cubic-bezier(.32, .72, 0, 1);
  --ease-in: cubic-bezier(.4, 0, 1, 1);
  --ease-inout: cubic-bezier(.65, 0, .35, 1);
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);

  --stagger: 22ms;
  --lift-sm: 4px;
  --lift-md: 12px;
}

/* 1 — app entrance */
@keyframes app-in {
  from { opacity: 0; transform: scale(.99); }
  to { opacity: 1; transform: none; }
}
.app-root { animation: app-in var(--dur-slow) var(--ease-out) both; }

/* 2 — view / filter transition */
@keyframes view-in {
  from { opacity: 0; transform: translateY(var(--lift-sm)); }
  to { opacity: 1; transform: none; }
}
.view-in { animation: view-in var(--dur-base) var(--ease-out) both; }

/* 3 — list entry, staggered. The stagger is capped in JS at the first ~18 rows
   so a 90-row list doesn't take two seconds to finish appearing. */
@keyframes row-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
.row {
  animation: row-in var(--dur-base) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * var(--stagger));
}

/* 4 — overlays, in AND out */
@keyframes overlay-in {
  from { opacity: 0; transform: translateY(var(--lift-md)) scale(.98); }
  to { opacity: 1; transform: none; }
}
@keyframes overlay-out {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateY(6px) scale(.99); }
}
@keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes scrim-out { from { opacity: 1; } to { opacity: 0; } }

.sheet-scrim { animation: scrim-in var(--dur-base) var(--ease-out) both; }
.sheet-scrim[data-closing] { animation: scrim-out var(--dur-exit-base) var(--ease-in) both; }
.sheet { animation: overlay-in var(--dur-slow) var(--ease-out) both; }
.sheet-scrim[data-closing] .sheet { animation: overlay-out var(--dur-exit-slow) var(--ease-in) both; }

/* toasts animate out too — a toast that vanishes instantly is a bug */
@keyframes toast-in {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
  to { opacity: 1; transform: none; }
}
@keyframes toast-out {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateY(8px) scale(.98); }
}
.toast { animation: toast-in var(--dur-slow) var(--ease-spring) both; }
.toast[data-closing] { animation: toast-out var(--dur-exit-base) var(--ease-in) both; }

/* 6 — state change: the check mark landing, and the counter ticking over */
@keyframes check-pop {
  0% { transform: scale(.4); opacity: 0; }
  60% { transform: scale(1.12); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
.tick { animation: check-pop var(--dur-slow) var(--ease-spring) both; }

@keyframes count-bump {
  0% { transform: translateY(0); }
  40% { transform: translateY(-4px); }
  100% { transform: translateY(0); }
}
.bump { animation: count-bump var(--dur-slow) var(--ease-out); }

@keyframes bar-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* a row that was just checked in flashes green, then settles */
@keyframes row-confirm {
  0% { background-color: var(--success-soft); }
  100% { background-color: transparent; }
}
.row[data-just] { animation: row-in var(--dur-base) var(--ease-out) both, row-confirm 1.1s var(--ease-out) 1 forwards; }

/* skeleton shimmer for the loading state */
@keyframes shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* spinner for in-flight sync */
@keyframes spin { to { transform: rotate(360deg); } }
.spin { animation: spin 900ms linear infinite; }

/* pulse for the live/offline dot */
@keyframes dot-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .45; transform: scale(.82); }
}
.dot[data-live] { animation: dot-pulse 2.4s var(--ease-inout) infinite; }

/* ---- reduced motion: shorter and non-moving, but state stays legible ----- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    transition-duration: 1ms !important;
  }
  /* keep the fades so appearing/disappearing is still readable */
  .app-root, .view-in, .row, .sheet, .sheet-scrim, .toast, .tick {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .toast[data-closing], .sheet-scrim[data-closing] { opacity: 0 !important; transition: opacity 120ms linear; }
  .dot[data-live] { animation: none !important; }
}
