/* S1 motion. One place for motion tokens and every S1 animation. Spec: MOTION.md.
 *
 * Rules (binding):
 * - Motion is triggered only by an explicit state change. The controller sets data-s1-motion="<name>" on the
 *   element after RWTRender.morph (RWTMotion.play does this and removes it on animationend). Nothing animates
 *   just because morph inserted or patched a node, so re-renders never replay motion on unchanged nodes.
 * - transform and opacity only. Two paint-only exceptions are named where they occur: the 8px status dot's
 *   background-color and the button background hover. One layout exception: .s1-collapse (grid-template-rows),
 *   used only on explicit user expand/collapse, on one element, contained.
 * - Nothing loops, nothing plays on idle, nothing delays input: the action fires on click; motion is only
 *   how the result arrives.
 * - prefers-reduced-motion: every movement becomes an instant state or a 120ms opacity crossfade.
 */

:root {
  --s1-dur-1: 120ms;   /* micro: press, hover, menu close, exit */
  --s1-dur-2: 180ms;   /* standard: tab content, menus, live region, row exit */
  --s1-dur-3: 260ms;   /* emphasized: phase sweep, FLIP settle, collapse, dialog */
  --s1-ease: cubic-bezier(.2, 0, 0, 1);          /* standard ease-out */
  --s1-ease-emph: cubic-bezier(.05, .7, .1, 1);  /* emphasized decelerate */
  --s1-stagger: 50ms;                            /* phase sweep per segment, capped at 4 segments */
}

/* ── Micro-feedback: hover and press. No bounce, no scale-up. ─────────────────────────────────── */
.s1-btn, .s1-chip, .s1-segmented-item, .s1-tab, .s1-menu-item, .s1-text-btn, .s1-step {
  transition: background-color var(--s1-dur-1) var(--s1-ease), color var(--s1-dur-1) var(--s1-ease),
              border-color var(--s1-dur-1) var(--s1-ease), transform var(--s1-dur-1) var(--s1-ease);
}
.s1-btn:active:not([disabled]), .s1-chip:active { transform: translateY(1px); }   /* pressed, not bounced */
.s1-tab[aria-selected="true"] { transition: color var(--s1-dur-2) var(--s1-ease), box-shadow var(--s1-dur-2) var(--s1-ease); }

/* ── Keyframes ────────────────────────────────────────────────────────────────────────────────── */
@keyframes s1-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes s1-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes s1-rise-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@keyframes s1-exit-up { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(-4px); } }
@keyframes s1-pop-in { from { opacity: 0; transform: scale(.97); } to { opacity: 1; transform: none; } }
@keyframes s1-sheet-in { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes s1-sweep { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes s1-check-in { 0% { opacity: 0; transform: scale(.6); } 60% { opacity: 1; transform: scale(1); } 100% { opacity: 1; transform: none; } }
@keyframes s1-strike { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes s1-ok-out { 0%, 70% { opacity: 1; } 100% { opacity: 0; } }

/* ── Tab switch: the new panel's content rises 4px and fades in. The header never moves. ─────────── */
[data-s1-motion="tab-enter"] { animation: s1-rise-in var(--s1-dur-2) var(--s1-ease) both; }

/* ── Expand / collapse (details folds, "Show more", system fold). The one layout exception. ──────── */
.s1-collapse { display: grid; grid-template-rows: 1fr; opacity: 1; contain: layout paint;
  transition: grid-template-rows var(--s1-dur-3) var(--s1-ease), opacity var(--s1-dur-2) var(--s1-ease); }
.s1-collapse > * { min-height: 0; overflow: hidden; }
.s1-collapse[data-collapsed="true"] { grid-template-rows: 0fr; opacity: 0; }

/* ── Status control: the tone dot crossfades (paint-only, 8px), then the live region confirms. ───── */
.s1-status-control::before { transition: background-color var(--s1-dur-3) var(--s1-ease); }
[data-s1-motion="confirm"].s1-status-control { animation: s1-pop-in var(--s1-dur-2) var(--s1-ease-emph); }

/* ── Phase stepper advance: newly done segments fill left→right, staggered (cap 4). ─────────────── */
.s1-stepper[data-s1-motion="advance"] > li.is-filling .s1-step::before {
  transform-origin: left center;
  animation: s1-sweep var(--s1-dur-3) var(--s1-ease-emph) both;
  animation-delay: calc(min(var(--s1-i, 0), 4) * var(--s1-stagger));
}
.s1-stepper[data-s1-motion="advance"] > li.is-current .s1-step-label,
.s1-stepper[data-s1-motion="advance"] > li.is-current .s1-step-mark { animation: s1-fade-in var(--s1-dur-2) var(--s1-ease) both; }

/* ── For-you item resolved: Done becomes a brief green check, then the line leaves; siblings FLIP. ── */
.s1-resolved-check { display: inline-grid; place-items: center; width: 28px; height: 28px; color: var(--s1-ok); }
.s1-resolved-check > .s1-icon { width: 20px; height: 20px; stroke-width: 2.4; }
[data-s1-motion="resolved"] .s1-resolved-check { animation: s1-check-in var(--s1-dur-3) var(--s1-ease-emph) both, s1-ok-out 900ms linear both; }
[data-s1-motion="exit"] { animation: s1-exit-up var(--s1-dur-2) var(--s1-ease) both; pointer-events: none; }

/* ── Task done: check pops in, title strike draws, row settles (FLIP, see RWTMotion.flip). ───────── */
.s1-strike { position: relative; }
.s1-strike::after { content: ""; position: absolute; left: 0; right: 0; top: 55%; height: 1.5px; background: currentColor; transform: scaleX(0); transform-origin: left center; pointer-events: none; }
.is-complete .s1-strike { color: var(--s1-mute); }
.is-complete .s1-strike::after { transform: scaleX(1); }
[data-s1-motion="complete"] .s1-strike::after { animation: s1-strike var(--s1-dur-3) var(--s1-ease) both; }
[data-s1-motion="complete"] .s1-check-mark { animation: s1-check-in var(--s1-dur-2) var(--s1-ease-emph) both; }

/* ── Board drag: lift (2px up, shadow fades in via opacity) and settle (FLIP from the drop point). ── */
.s1-task { position: relative; }
.s1-task::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: 0 8px 20px rgba(17, 19, 24, .16); opacity: 0; pointer-events: none; transition: opacity var(--s1-dur-1) var(--s1-ease); }
.s1-task.is-dragging, .s1-task[aria-grabbed="true"] { transform: translateY(-2px); opacity: 1; transition: transform var(--s1-dur-1) var(--s1-ease); }
.s1-task.is-dragging::after, .s1-task[aria-grabbed="true"]::after { opacity: 1; }
.s1-board-col { transition: border-color var(--s1-dur-1) var(--s1-ease); }

/* ── Live region ("toast"): enters 4px up with a fade; leaves with a fade. Saved clears after 2 s. ── */
[data-s1-motion="live-enter"] { animation: s1-rise-in var(--s1-dur-2) var(--s1-ease) both; }
[data-s1-motion="live-exit"] { animation: s1-fade-out var(--s1-dur-1) var(--s1-ease) both; }

/* ── Loading: no shimmer, no pulse (both loop). The "Loading {section}…" line fades in only after
 *    300ms, once, so fast loads never flash it. ─────────────────────────────────────────────────── */
.s1-loading { animation: s1-fade-in var(--s1-dur-2) var(--s1-ease) 300ms both; }

/* ── Menus and dialogs: scale in from their origin; closing is instant (never delays focus return). ─ */
.s1-menu:not([hidden]) { transform-origin: top right; animation: s1-pop-in var(--s1-dur-2) var(--s1-ease-emph); }
.s1-menu[data-origin="left"] { transform-origin: top left; }
.s1-dialog[open] { animation: s1-pop-in var(--s1-dur-3) var(--s1-ease-emph); }
.s1-dialog[open]::backdrop, .s1-dialog-shade { animation: s1-fade-in var(--s1-dur-2) var(--s1-ease); }

/* ── Reduced motion: no movement anywhere. State changes are instant or a 120ms crossfade. ────────── */
@media (prefers-reduced-motion: reduce) {
  :root { --s1-dur-2: 120ms; --s1-dur-3: 120ms; --s1-stagger: 0ms; }
  .s1-btn:active:not([disabled]), .s1-chip:active, .s1-task.is-dragging, .s1-task[aria-grabbed="true"] { transform: none; }
  [data-s1-motion], [data-s1-motion] *, .s1-menu:not([hidden]), .s1-dialog[open], .s1-dialog-shade, .s1-dialog-shade > .s1-dialog, .s1-loading {
    animation-name: s1-fade-in !important; animation-duration: var(--s1-dur-1) !important; animation-delay: 0ms !important;
  }
  [data-s1-motion="exit"], [data-s1-motion="live-exit"] { animation-name: s1-fade-out !important; }
  [data-s1-motion="resolved"] .s1-resolved-check { animation: none !important; }
  .s1-stepper[data-s1-motion="advance"] > li.is-filling .s1-step::before, [data-s1-motion="complete"] .s1-strike::after { animation: none !important; }
  .s1-collapse { transition: opacity var(--s1-dur-1) linear; }
}

/* ── Calendar: the month/week grid slides 14px in the direction of travel; the day popover rises in. ───── */
@keyframes s1-cal-next { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
@keyframes s1-cal-prev { from { opacity: 0; transform: translateX(-14px); } to { opacity: 1; transform: none; } }
[data-s1-motion="cal-next"] { animation: s1-cal-next var(--s1-dur-2) var(--s1-ease) both; }
[data-s1-motion="cal-prev"] { animation: s1-cal-prev var(--s1-dur-2) var(--s1-ease) both; }
[data-s1-motion="cal-pop"] { animation: s1-rise-in var(--s1-dur-1) var(--s1-ease) both; }
