/* ============================================================================
 * titan-motion.css — TITAN ONE motion kit (Legend rev §7, approved 2026-07-13)
 *
 * Load AFTER design-tokens.css, on any page that animates:
 *   <link rel="stylesheet" href="design-tokens.css">
 *   <link rel="stylesheet" href="titan-motion.css">
 *
 * Principle: animate TRANSFORM and OPACITY only — never width/height/top/left or
 * color, so every animation is compositor-cheap and never triggers layout. The
 * few "sweep"/"wash"/"shimmer" effects move a transformed pseudo-overlay, not a
 * background-position, for the same reason.
 *
 * Durations (Legend §7):
 *   --t1-fast  120ms  press / tap feedback
 *   --t1-base  220ms  toasts · sheets · banners
 *   --t1-slow  400ms  success · row arrival
 * Curves: --t1-ease standard, --t1-spring for pops/badges (slight overshoot).
 *
 * Usage: add a t1-* class to play a one-shot animation; remove it (or the whole
 * node) to stop. Looping states (t1-pulse / t1-dot-pulse) run until the class is
 * removed on acknowledge. RULE: at most ONE pulsing element on screen at a time.
 *
 * Accessibility: prefers-reduced-motion collapses every animation to a plain
 * opacity fade (or nothing for loops) — see the block at the foot of the file.
 * ========================================================================== */

:root {
  --t1-fast: 120ms;
  --t1-base: 220ms;
  --t1-slow: 400ms;
  --t1-ease:   cubic-bezier(.2, .9, .3, 1);
  --t1-spring: cubic-bezier(.2, .9, .3, 1.4);
}

/* ── press feedback — the one thing that goes on interactive elements globally.
 *    Opt in with .t1-press (buttons/tiles); :active scales down briefly. ── */
.t1-press { transition: transform var(--t1-fast) var(--t1-ease); }
.t1-press:active { transform: scale(.96); }

/* ── toasts ─────────────────────────────────────────────────────────────── */
@keyframes t1-toast-in  { from { opacity: 0; transform: translateY(12px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes t1-toast-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(8px) scale(.98); } }
.t1-toast-in  { animation: t1-toast-in  var(--t1-base) var(--t1-ease) both; }
.t1-toast-out { animation: t1-toast-out var(--t1-base) var(--t1-ease) both; }

/* ── notification bell + count badge ────────────────────────────────────── */
@keyframes t1-bell-ring {
  0%,100% { transform: rotate(0); }
  15% { transform: rotate(13deg); }  30% { transform: rotate(-11deg); }
  45% { transform: rotate(8deg); }   60% { transform: rotate(-6deg); }
  75% { transform: rotate(3deg); }
}
@keyframes t1-badge-pop { 0% { transform: scale(0); } 70% { transform: scale(1.25); } 100% { transform: scale(1); } }
.t1-bell-ring { animation: t1-bell-ring 640ms var(--t1-ease) both; transform-origin: 50% 12%; }
.t1-badge-pop { animation: t1-badge-pop var(--t1-base) var(--t1-spring) both; }

/* ── banners (the staging banner, late/alert banners) + drain countdown ──── */
@keyframes t1-banner-in { from { opacity: 0; transform: translateY(-100%); } to { opacity: 1; transform: none; } }
/* drain: a full-width bar inside the banner, transform-origin left, scaleX 1→0.
 * Set --t1-drain-ms inline to the countdown length. */
@keyframes t1-drain { from { transform: scaleX(1); } to { transform: scaleX(0); } }
.t1-banner-in { animation: t1-banner-in var(--t1-base) var(--t1-ease) both; }
.t1-drain { transform-origin: left center; animation: t1-drain var(--t1-drain-ms, 6000ms) linear both; }

/* ── row arrival + acknowledge wash (order rows, ledger rows) ────────────── */
@keyframes t1-row-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.t1-row-in { animation: t1-row-in var(--t1-slow) var(--t1-ease) both; }
/* wash: a one-shot accent highlight that fades in then out via a pseudo overlay
 * (opacity only). Host must be position:relative; overlay uses the module accent
 * tint. */
.t1-row-wash { position: relative; }
.t1-row-wash::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: var(--t1-wash-color, var(--t-active));
  opacity: 0; animation: t1-row-wash var(--t1-slow) var(--t1-ease) both;
}
@keyframes t1-row-wash { 0% { opacity: 0; } 35% { opacity: .55; } 100% { opacity: 0; } }

/* ── live pulse (unread dot / active status) — LOOPS until class removed.
 *    Only one pulsing element per screen (Legend §7). ── */
@keyframes t1-pulse     { 0%,100% { opacity: 1; } 50% { opacity: .45; } }
@keyframes t1-dot-pulse { 0% { transform: scale(1); opacity: .9; } 70% { transform: scale(1.9); opacity: 0; } 100% { opacity: 0; } }
.t1-pulse { animation: t1-pulse 1.6s var(--t1-ease) infinite; }
/* dot: host is the solid dot (position:relative); ::after is the expanding ring. */
.t1-dot-pulse { position: relative; }
.t1-dot-pulse::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: currentColor; animation: t1-dot-pulse 1.8s var(--t1-ease) infinite;
}

/* ── shake — validation / rejected action, once per event ───────────────── */
@keyframes t1-shake {
  0%,100% { transform: translateX(0); }
  20% { transform: translateX(-6px); } 40% { transform: translateX(5px); }
  60% { transform: translateX(-4px); } 80% { transform: translateX(2px); }
}
.t1-shake { animation: t1-shake 380ms var(--t1-ease) both; }

/* ── boot pulse — one-time splash heartbeat on app boot (2.2s) ───────────── */
@keyframes t1-boot { 0% { opacity: .35; transform: scale(.94); } 50% { opacity: 1; transform: scale(1.02); } 100% { opacity: 1; transform: scale(1); } }
.t1-boot { animation: t1-boot 2.2s var(--t1-ease) both; }

/* ── progress sweep — indeterminate bar; inner .t1-sweep-bar slides across.
 *    Track: overflow:hidden. Bar: 40% width. ── */
@keyframes t1-sweep { 0% { transform: translateX(-120%); } 100% { transform: translateX(320%); } }
.t1-sweep { overflow: hidden; }
.t1-sweep > .t1-sweep-bar { animation: t1-sweep 1.15s var(--t1-ease) infinite; }

/* ── skeleton shimmer — loading placeholders; a light band sweeps across via a
 *    transformed pseudo overlay (no background-position animation). ── */
.t1-skeleton { position: relative; overflow: hidden; background: var(--t-active); }
.t1-skeleton::after {
  content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.35), transparent);
  animation: t1-shimmer 1.3s linear infinite;
}
:root[data-theme="dark"] .t1-skeleton::after { background: linear-gradient(90deg, transparent, rgba(255,255,255,.08), transparent); }
@keyframes t1-shimmer { 100% { transform: translateX(100%); } }

/* ── reduced motion — collapse to fades; kill loops and travel ───────────── */
@media (prefers-reduced-motion: reduce) {
  .t1-press { transition: none; }
  .t1-press:active { transform: none; }
  .t1-toast-in, .t1-toast-out, .t1-banner-in, .t1-row-in, .t1-boot,
  .t1-badge-pop, .t1-bell-ring, .t1-shake {
    animation-duration: 1ms; animation-timing-function: linear;
  }
  .t1-pulse, .t1-dot-pulse::after, .t1-drain,
  .t1-sweep > .t1-sweep-bar, .t1-skeleton::after {
    animation: none;
  }
  .t1-bell-ring, .t1-shake, .t1-badge-pop { transform: none; }
  .t1-row-wash::after { animation: none; opacity: 0; }
}
