/* ============================================================================
   Repark Motion — the single source of truth for timing, easing and the shared
   keyframes. Loaded FIRST so every stylesheet can use the tokens; the
   reduced-motion guard at the bottom uses !important so it wins from here.

   Before this file: 51 keyframes across 10 stylesheets implementing the same
   handful of ideas, at 11 durations and 8 easings, with 37 of them ignoring
   prefers-reduced-motion entirely.

   Rules for anything new:
     - Never write a raw ms value. Use --t-*.
     - Never write a raw cubic-bezier. Use --e-*.
     - Never write a new @keyframes for shimmer / spin / pop / fade / pulse.
       They exist below. Reuse them.
   ========================================================================== */
:root{
  /* ── Durations. Three, deliberately. ────────────────────────────────────
     fast  — state flips the eye shouldn't notice: hover, focus, pill colour
     base  — things entering or leaving: toasts, rows, modals, popovers
     slow  — things that travel a distance: sheets, drawers, progress bars   */
  --t-fast:120ms;
  --t-base:200ms;
  --t-slow:320ms;
  /* Loops run long on purpose — they must not read as urgent. */
  --t-shimmer:1300ms;
  --t-spin:800ms;
  --t-pulse:1400ms;
  /* A flash is a NOTIFICATION, not a transition, so it does not belong on the
     --t-fast/base/slow scale. It has to still be there when your eye arrives —
     you're looking at the button you clicked, not the row that changed. The
     app's own flashes already ran 600ms (fulFlashOk) to 2s (vfRowFlash); 900ms
     sits in that range. Shipped this at --t-slow (320ms) first and it was over
     before anyone saw it. */
  --t-flash:900ms;

  /* ── Easings. Three, deliberately. ──────────────────────────────────────
     out    — the default. Anything entering, or responding to a click.
     in-out — anything that both starts and ends on screen (loops, bars).
     spring — a deliberate overshoot. Confirmations only; use sparingly.     */
  --e-out:cubic-bezier(.2,.8,.2,1);
  --e-in-out:cubic-bezier(.4,0,.2,1);
  --e-spring:cubic-bezier(.2,.9,.3,1.15);
  --e-linear:linear;                 /* spinners only — anything else looks mechanical */

  /* Skeleton fill. One gradient, one geometry, one direction (right-to-left).
     Consumers set `background:var(--rms-skel-fill); background-size:400% 100%`. */
  --rms-skel-fill:linear-gradient(90deg,#eef2f7 25%,#f7fafc 37%,#eef2f7 63%);
}
html[data-theme="dark"]{
  --rms-skel-fill:linear-gradient(90deg,rgba(255,255,255,.05) 25%,rgba(255,255,255,.10) 37%,rgba(255,255,255,.05) 63%);
}

/* NOTE: responsive.css (--t-tap/--t-ui/--ease) and mobile-kit.css
   (--rk-t-tap/--rk-t-ui/--rk-ease) invented their own motion tokens before this
   file existed. They now alias onto the values above, declared at their own
   definition sites — aliasing them from HERE silently failed, because this file
   loads first and their local definitions simply won. One definition each. */

/* ══ The shared keyframes ══════════════════════════════════════════════════
   Each one below replaced a group of byte-identical duplicates. Groups that
   only LOOKED alike were left alone — see the note at the foot of this file. */

/* Replaces: aplus-sh · dashShimmer · ecshimmer · lb-sh · mt-sh · plshimmer
             rkShimmer · rmsShimmer · sc-sh · ui-shimmer   (10 → 1)
   Those ran at 1.2s and 1.3s, across ease / ease-in-out / linear, and in two
   opposite directions. This is the right-to-left one, which 8 of the 10 used. */
@keyframes rms-shimmer{ 0%{ background-position:100% 0 } 100%{ background-position:0 0 } }

/* Replaces: ecspin · ful-spin · fulSpin · lb-spin · plspin · vf-spin (6 → 1)
   All six were byte-identical; two of them lived in the same stylesheet. */
@keyframes rms-spin{ to{ transform:rotate(360deg) } }

/* Replaces: aplus-pop · lb-pop · mt-pop · sc-pop (4 → 1) — byte-identical. */
@keyframes rms-pop{ from{ opacity:0; transform:translateY(10px) scale(.985) } to{ opacity:1; transform:none } }

/* Replaces: aplus-fade · mt-fade · sc-fade (3 → 1) — byte-identical.
   NOT fulFade, which also translates; that one is a rise, not a fade. */
@keyframes rms-fade{ from{ opacity:0 } to{ opacity:1 } }

/* Replaces: mcPulse · rkPulse (2 → 1) — byte-identical opacity blink.
   NOT whPulse / fulPulse / fulPulseDot / bld-hl-pulse — those pulse
   filter:brightness or box-shadow, which is a different effect entirely. */
@keyframes rms-pulse{ 0%,100%{ opacity:1 } 50%{ opacity:.35 } }

/* Parametrised flash — "this row just changed". Callers set --flash. Holds to
   60% so the eye catches it, then fades.

   Uses an INSET BOX-SHADOW, not `background`, and that is deliberate. Animation
   declarations sit BELOW important author declarations in the cascade, so any
   `background: … !important` beats an animated background outright — no selector
   can win that fight. Two such rules exist and both cover table cells:
       style.css  .data-row:hover td      { background:#f5f9ff !important }
       dark.css   [data-theme=dark] td    { background:… !important }
   With `background` this flash was invisible on hover, and invisible ALWAYS in
   dark mode. An inset shadow paints over the background whoever owns it, and
   nothing in the app !importants box-shadow on a cell.
   Absorbed fulFlashGreen. NOT vfRowFlash (linear, no hold) or fulFlashOk/Err
   (they also animate border-color to a specific end state). */
@keyframes rms-flash{
  0%,60%{ box-shadow:inset 0 0 0 100vmax var(--flash,#dcfce7) }
  100%  { box-shadow:inset 0 0 0 100vmax transparent }
}

/* Parametrised rise. Callers set --rise (default 8px).
   Replaces: cmdp-in(-8) · fulExpand(-4) · fulTickIn(-6) · fulFade(3)
             rkToastIn(14) · rmsFadeIn(6) · ui-row-in(3) · ui-toast-in(8)  (8 → 1)
   The distances were arbitrary drift, not intent — but they're preserved
   per-call-site via --rise so nothing shifts visually. */
@keyframes rms-rise{ from{ opacity:0; transform:translateY(var(--rise,8px)) } to{ opacity:1; transform:none } }
@keyframes rms-rise-out{ to{ opacity:0; transform:translateY(var(--rise,8px)) } }

/* ══ The guard ═════════════════════════════════════════════════════════════
   One rule, covering every animation and transition in the app. Previously 37
   of 51 keyframes had no guard at all — style.css alone had 24 and zero.
   !important so it wins regardless of where this file sits in the cascade. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:1ms !important;
    animation-iteration-count:1 !important;
    transition-duration:1ms !important;
    scroll-behavior:auto !important;
  }
}

/* ── Deliberately NOT merged ──────────────────────────────────────────────
   These look like duplicates by name and are not:
     whPulse        filter:brightness   (warehouse block highlight)
     bld-hl-pulse   box-shadow ring     (builder highlight)
     fulPulseDot    box-shadow ripple   (live dot)
     fulPulse       box-shadow + var    (fulfilment row)
     pkShake        translateX shake    (packing error)
     rkSheetUp      translateY(100%)    (bottom sheet — a travel, not a rise)
     rkDash         stroke-dashoffset   (warehouse map route)
     rkLoadbarSlide left offset         (indeterminate bar)
   Each animates a different property or distance class. Collapsing them would
   be churn with breakage risk and no user-visible gain. They now draw their
   timing from the tokens above; their keyframes stay where they are.        */

/* ── A2: stock row patched in place after a save ──────────────────────────
   Ties the confirmation to the row you touched. A toast in the corner tells you
   something saved; it can't tell you WHICH row. Reuses rms-flash — no new keyframe. */
tr.data-row.stk-saved > td{
  --flash:#dcfce7;
  animation:rms-flash var(--t-flash) var(--e-out);
}
html[data-theme="dark"] tr.data-row.stk-saved > td{ --flash:rgba(74,222,128,.18); }

/* ── A3: determinate SFTP poll progress ───────────────────────────────────
   Fixed panel: two different tabs trigger a poll and both report here. Uses
   rms-spin/rms-rise from above — no new keyframes. */
.amz-poll{
  position:fixed; right:20px; bottom:20px; z-index:4200; width:min(330px,calc(100vw - 32px));
  background:#fff; border:1px solid #e2e8f0; border-left:4px solid #0588b6; border-radius:12px;
  padding:13px 15px; box-shadow:0 14px 34px rgba(15,37,64,.16);
  font-size:13px; color:#0f2740;
  --rise:8px; animation:rms-rise var(--t-base) var(--e-out);
}
.amz-poll.is-done{ border-left-color:#16a34a; }
.amz-poll.is-bad{ border-left-color:#dc2626; }
.amz-poll-h{ display:flex; align-items:center; gap:9px; font-size:13.5px; }
.amz-poll-dot{
  width:13px; height:13px; flex:0 0 auto; border-radius:50%;
  border:2px solid #cbd5e1; border-top-color:#0588b6;
  animation:rms-spin var(--t-spin) var(--e-linear) infinite;
}
.amz-poll-dot.ok{ border:0; background:#16a34a; animation:none; }
.amz-poll-dot.bad{ border:0; background:#dc2626; animation:none; }
.amz-poll-file{
  font-family:ui-monospace,Consolas,monospace; font-size:11px; color:#5b7183;
  margin-top:6px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.amz-poll-bar{ height:6px; border-radius:3px; background:#eef2f7; overflow:hidden; margin:9px 0 7px; }
.amz-poll-bar i{ display:block; height:100%; background:linear-gradient(90deg,#03658e,#0588b6);
  transition:width var(--t-slow) var(--e-in-out); }
.amz-poll-meta{ display:flex; gap:10px; flex-wrap:wrap; font-size:11px; color:#5b7183; font-weight:600; }
.amz-poll-meta .ok{ color:#16a34a; } .amz-poll-meta .bad{ color:#dc2626; }
.amz-poll-err{ margin-top:7px; font-size:11.5px; color:#dc2626; }
html[data-theme="dark"] .amz-poll{ background:#101f2e; border-color:rgba(255,255,255,.1); color:#dfeaf4; }
html[data-theme="dark"] .amz-poll-bar{ background:rgba(255,255,255,.08); }

/* ── A6: PO status timeline ───────────────────────────────────────────────
   Uses rms-rise from above — no new keyframe. */
.amz-tl{ border:1px solid #e2e8f0; border-radius:12px; background:#fff; padding:14px 16px; margin:14px 0; }
.amz-tl-h{ font-size:11px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; color:#5b7183; margin-bottom:12px; }
.amz-tl-load,.amz-tl-empty{ font-size:12.5px; color:#8fa3b4; }
.amz-tl-i{ display:grid; grid-template-columns:15px 1fr; gap:12px; padding-bottom:14px; position:relative;
  --rise:4px; animation:rms-rise var(--t-base) var(--e-out) backwards; }
.amz-tl-i:nth-child(2){ animation-delay:0ms } .amz-tl-i:nth-child(3){ animation-delay:60ms }
.amz-tl-i:nth-child(4){ animation-delay:120ms } .amz-tl-i:nth-child(5){ animation-delay:180ms }
.amz-tl-i:last-child{ padding-bottom:0; }
.amz-tl-i:not(:last-child)::before{ content:""; position:absolute; left:7px; top:16px; bottom:0; width:1.5px; background:#e2e8f0; }
.amz-tl-d{ width:11px; height:11px; border-radius:50%; background:#cbd5e1; margin-top:4px; z-index:1; }
.amz-tl-i.done .amz-tl-d{ background:#16a34a; }
.amz-tl-i.now .amz-tl-d{ background:#0588b6; box-shadow:0 0 0 4px #e4f4fb; }
.amz-tl-t{ font-size:13px; font-weight:700; color:#0f2740; text-transform:capitalize; }
.amz-tl-from{ font-weight:500; color:#8fa3b4; text-transform:none; font-size:11.5px; }
.amz-tl-s{ font-size:11.5px; color:#5b7183; margin-top:2px; line-height:1.5; }
html[data-theme="dark"] .amz-tl{ background:#101f2e; border-color:rgba(255,255,255,.1); }
html[data-theme="dark"] .amz-tl-t{ color:#dfeaf4; }
html[data-theme="dark"] .amz-tl-i:not(:last-child)::before{ background:rgba(255,255,255,.12); }
html[data-theme="dark"] .amz-tl-i.now .amz-tl-d{ box-shadow:0 0 0 4px #0d2c3d; }
