/* Motion for the public site (#65).
 *
 * One rule governs this file: the markup's default state is the FINAL state.
 * Nothing here hides content. The "before" states live under `.motion`, a
 * class app/static/js/motion.js puts on <html> only when the visitor has an
 * IntersectionObserver and has not asked for reduced motion. So with
 * JavaScript off, with reduced motion on, and in print, every page shows its
 * finished self: the hero picture already shows its updated stock, the diagrams are
 * already drawn, the Copilot answer is already there.
 *
 * Tokens first -- durations, curves and distances -- so the whole site moves
 * with one feel. Only transform and opacity are animated.
 */

:root {
  --m-fast: 180ms;
  --m-base: 420ms;
  --m-slow: 700ms;
  --m-loop: 7s;
  --m-scene: 8s;   /* one turn of a channel-page picture */

  /* Out: things leaving or reacting to a pointer. Enter: things arriving --
     a long tail that settles rather than stops. Spring: the small overshoot
     a button or a chip gets when it is meant to feel touchable. */
  --m-ease-out: cubic-bezier(.22, 1, .36, 1);
  --m-ease-enter: cubic-bezier(.16, 1, .3, 1);
  --m-ease-spring: cubic-bezier(.34, 1.4, .64, 1);

  --m-rise: 24px;   /* how far a revealed block travels */
  --m-nudge: 4px;   /* hover lift, arrow nudge */
  --m-stagger: 70ms;
}

/* ---------------------------------------------------------------- reveal */
/* The existing .reveal (inline in base.html) still drives block entrances.
   These add the parts that need more than a fade-up. */

.motion [data-seq] > [data-step] {
  opacity: 0;
  transform: translateY(var(--m-rise));
  transition: opacity var(--m-slow) var(--m-ease-enter), transform var(--m-slow) var(--m-ease-enter);
  transition-delay: calc(var(--step, 0) * var(--m-stagger));
}
.motion [data-seq].m-in > [data-step] { opacity: 1; transform: none; }

/* A headline that arrives line by line from behind its own baseline. */
/* The padding keeps descenders (g, j, į) inside the clip on tight leading. */
.motion [data-lines] > span { display: block; overflow: hidden; padding-bottom: .14em; margin-bottom: -.14em; }
.motion [data-lines] > span > span {
  display: block;
  transform: translateY(105%);
  transition: transform var(--m-slow) var(--m-ease-enter);
  transition-delay: calc(var(--step, 0) * 90ms);
}
.motion [data-lines].m-in > span > span { transform: none; }

/* --------------------------------------------------------------- surfaces */

/* Aurora behind the hero grid. Two blurred blobs drifting; cheap because
   only transform moves and the element is composited on its own layer. */
.m-aurora { position: absolute; inset: -20% -10% auto; height: 120%; pointer-events: none; overflow: hidden; }
.m-aurora::before,
.m-aurora::after {
  content: ""; position: absolute; width: 46rem; height: 46rem; border-radius: 50%;
  filter: blur(80px); opacity: .5;
}
.m-aurora::before { background: radial-gradient(circle, rgba(14,165,233,.5), transparent 65%); top: -18rem; right: -10rem; }
.m-aurora::after  { background: radial-gradient(circle, rgba(0,95,184,.45), transparent 65%); top: 4rem; left: -16rem; }
.motion .m-aurora::before { animation: m-drift-a 28s ease-in-out infinite; }
.motion .m-aurora::after  { animation: m-drift-b 34s ease-in-out infinite; }

@keyframes m-drift-a {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(-4rem, 3rem, 0) scale(1.12); }
}
@keyframes m-drift-b {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1.05); }
  50%      { transform: translate3d(3rem, -2rem, 0) scale(.95); }
}

/* The gradient on the hero's key words moves, slowly. */
.motion .gradient-text { background-size: 220% 100%; animation: m-gradient 14s ease-in-out infinite; }
@keyframes m-gradient {
  0%, 100% { background-position: 0% 50%; }
  50%      { background-position: 100% 50%; }
}

/* --------------------------------------------------------------- pointer */

/* Tilt: motion.js writes --m-rx/--m-ry from the pointer. Desktop only, and
   the transition makes the return home eased rather than snapped. */
.motion [data-tilt] {
  transform: perspective(1100px) rotateX(var(--m-rx, 0deg)) rotateY(var(--m-ry, 0deg));
  transition: transform var(--m-base) var(--m-ease-out);
  transform-style: preserve-3d;
}

/* Spotlight border: a soft highlight following the pointer across a card.
   Used on the dark AI band only, where it reads as depth rather than noise. */
[data-spotlight] { position: relative; isolation: isolate; }
.motion [data-spotlight]::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none; z-index: -1;
  background: radial-gradient(18rem circle at var(--m-x, 50%) var(--m-y, 0%), rgba(125,211,252,.35), transparent 60%);
  opacity: 0; transition: opacity var(--m-base) var(--m-ease-out);
}
.motion [data-spotlight]:hover::before,
.motion [data-spotlight]:focus-within::before { opacity: 1; }

/* Magnetic primary CTA: motion.js offsets it a few pixels toward the cursor. */
.motion [data-magnetic] {
  transform: translate3d(var(--m-mx, 0), var(--m-my, 0), 0);
  transition: transform var(--m-base) var(--m-ease-out);
}
.motion [data-magnetic]:active { transform: translate3d(var(--m-mx, 0), var(--m-my, 0), 0) scale(.97); }

/* ------------------------------------------------------------------ chrome */

/* The glass header tightens and gains a border once the page has scrolled. */
.m-header { transition: box-shadow var(--m-base) var(--m-ease-out), background-color var(--m-base) var(--m-ease-out); }
.m-header.m-stuck { box-shadow: 0 1px 0 rgba(2,32,71,.08), 0 8px 24px -16px rgba(2,32,71,.4); }

/* Reading progress under the sticky nav. Width would relayout, so it scales. */
.m-progress {
  position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background: linear-gradient(90deg, #005FB8, #38bdf8);
  transform: scaleX(var(--m-progress, 0)); transform-origin: 0 50%;
}

/* The nav's active pill slides between items instead of blinking. */
.m-navpill {
  position: absolute; z-index: -1; border-radius: .5rem; background: rgb(239 246 255);
  transition: transform var(--m-base) var(--m-ease-out), width var(--m-base) var(--m-ease-out);
}

/* ------------------------------------------------------------------- cards */

.m-card { transition: transform var(--m-base) var(--m-ease-out), box-shadow var(--m-base) var(--m-ease-out); }
.motion .m-card:hover { transform: translateY(calc(var(--m-nudge) * -1)); }

/* The FAQ accordion opens with height and opacity together. */
.motion details[open] > :not(summary) { animation: m-open var(--m-base) var(--m-ease-out); }
@keyframes m-open {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* The highlighted plan is a white card with the brand outline the template
   gives it, nothing more. The travelling gradient border that used to sit
   here painted over the card's own background (a negative z-index child
   paints above its stacking context's background), so the whole card went
   blue -- removed 2026-10-01 (owner). */

/* --------------------------------------------- the living channel diagram */
/* A pulse travels each line in the direction that line actually carries, so
   the animation cannot contradict docs/claims-register.md: Amazon's only
   line runs inward. */

.m-wire { stroke-dasharray: var(--len, 200); stroke-dashoffset: 0; }
.motion .m-wire { stroke-dashoffset: var(--len, 200); transition: stroke-dashoffset var(--m-slow) var(--m-ease-enter); }
.motion .m-diagram.m-in .m-wire { stroke-dashoffset: 0; transition-delay: calc(var(--step, 0) * 90ms); }

/* Without motion the pulses sit at the start of their line and read as
   endpoints; with it, they travel. Either way they carry no information the
   text under the diagram does not also carry. */
.m-pulse { opacity: .5; }
.motion .m-diagram.m-in .m-pulse {
  opacity: 1;
  animation: m-travel 2.6s var(--m-ease-out) infinite;
  animation-delay: calc(.6s + var(--step, 0) * .4s);
}
@keyframes m-travel {
  0%       { offset-distance: 0%; opacity: 0; }
  12%, 80% { opacity: 1; }
  100%     { offset-distance: 100%; opacity: 0; }
}

/* ---------------------------------------------------- the self-typing chat */
/* The animated copy is aria-hidden; the real text stays in the mockup's
   role="img" label, so a screen reader never hears it letter by letter. */

.m-type { display: inline-block; }
.motion .m-type { clip-path: inset(0 100% 0 0); animation: m-type 1.4s steps(28, end) forwards; animation-delay: var(--delay, 0s); }
@keyframes m-type { to { clip-path: inset(0 0 0 0); } }

@keyframes m-blink { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }

/* ------------------------------------------------------------- provisioning */
/* The signup success page mirrors the real steps rather than spinning. */

.motion [data-steps] li { opacity: .45; transition: opacity var(--m-base) var(--m-ease-out); }
.motion [data-steps] li.m-active { opacity: 1; }
.motion [data-steps] li.m-active .m-dot { animation: m-pulse-dot 1.6s ease-in-out infinite; }
@keyframes m-pulse-dot { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.35); } }

/* ------------------------------------------------------- channel scenes */
/* The pictures on a channel's own page (templates/public/_channel_scenes.html).
   Each [data-scene] loops only while motion.js marks it `.m-play` -- on
   screen, tab visible -- so a picture scrolled past costs nothing (the canvas
   prototype of the Pigu page crashed phones by running them all at once).
   Every keyframe ends where the markup already is. `.m-before` pieces -- an
   old price, a pending badge, ripples, the scanner beam -- and the travelling
   dots exist for the loop only: without motion they are not shown. One turn
   of every loop is --m-scene; the bridge hero's lanes (m-lane-*) take half a
   turn each, in the lane's own direction, a little out of step. */

[data-scene] .m-before,
[data-scene] .m-dot,
[data-scene] .m-ring { display: none; }
.motion [data-scene].m-play .m-before,
.motion [data-scene].m-play .m-dot,
.motion [data-scene].m-play .m-ring { display: block; }
/* A loop-only piece that lays out its own content (a placeholder centring its
   label, a skeleton row) keeps its own display instead of the block above. */
.motion [data-scene].m-play .m-before.flex { display: flex; }
.motion [data-scene].m-play .m-before.grid { display: grid; }

.m-mover { position: absolute; inset: 0; pointer-events: none; }
.m-dot { position: absolute; width: .75rem; height: .75rem; border-radius: 9999px; }
.m-dot-in { background: #059669; box-shadow: 0 0 0 4px rgba(5,150,105,.18); }
.m-dot-out { background: #005FB8; box-shadow: 0 0 0 4px rgba(0,95,184,.18); }
.m-ring { position: absolute; inset: -2px; border: 2px solid #005FB8; pointer-events: none; }
.m-fill { display: inline-block; transform-origin: 0 50%; }
.m-fill-y { transform-origin: 50% 0; }
.m-shrink { transform-origin: 0 50%; }
.m-turn, .m-orbit { transform-box: view-box; transform-origin: 50px 50px; }
.m-draw, .m-gauge { stroke-dasharray: 1; stroke-dashoffset: 0; }
/* The fanned deck: --fan is the finished spread (a row; on phones a 2x2
   grid), and the loop deals the cards into it. */
.m-fan { transform: var(--fan); }
.m-fan-1 { --fan: translate(-160%, 12%) rotate(-7deg); }
.m-fan-2 { --fan: translate(-54%, 0) rotate(-2deg); }
.m-fan-3 { --fan: translate(54%, 0) rotate(2deg); }
.m-fan-4 { --fan: translate(160%, 12%) rotate(7deg); }
@media (max-width: 639px) {
  .m-fan-1 { --fan: translate(-56%, 0) rotate(-3deg); }
  .m-fan-2 { --fan: translate(56%, 0) rotate(2deg); }
  .m-fan-3 { --fan: translate(-56%, 112%) rotate(2deg); }
  .m-fan-4 { --fan: translate(56%, 112%) rotate(-3deg); }
}

.motion [data-scene].m-play .m-in-x1 { animation: m-in-x1 var(--m-scene) ease-in-out infinite; }
.motion [data-scene].m-play .m-in-x2 { animation: m-in-x2 var(--m-scene) ease-in-out infinite; }
.motion [data-scene].m-play .m-in-x3 { animation: m-in-x3 var(--m-scene) ease-in-out infinite; }
.motion [data-scene].m-play .m-in-x4 { animation: m-in-x4 var(--m-scene) ease-in-out infinite; }
.motion [data-scene].m-play .m-out-x { animation: m-out-x var(--m-scene) ease-in-out infinite; }
.motion [data-scene].m-play .m-at1 { animation: m-at1 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-at2 { animation: m-at2 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-at3 { animation: m-at3 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-at4 { animation: m-at4 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-at5 { animation: m-at5 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-at6 { animation: m-at6 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-at7 { animation: m-at7 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-at8 { animation: m-at8 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-gone2 { animation: m-gone2 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-gone4 { animation: m-gone4 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-gone6 { animation: m-gone6 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-w0 { animation: m-w0 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-w1 { animation: m-w1 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-w2 { animation: m-w2 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-w3 { animation: m-w3 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-press { animation: m-press var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-press-late { animation: m-press-late var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-pop { animation: m-pop var(--m-scene) var(--m-ease-spring) infinite; }
.motion [data-scene].m-play .m-fill { animation: m-fill1 var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-fill-y { animation: m-fill-y var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-ring { animation: m-ring var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-turn { animation: m-turn var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-orbit { animation: m-turn 40s linear infinite; }
.motion [data-scene].m-play .m-halo { animation: m-halo 2.4s ease-out infinite; }
.motion [data-scene].m-play .m-draw { animation: m-draw var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-gauge { animation: m-gauge var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-shrink { animation: m-shrink var(--m-scene) var(--m-ease-out) infinite; }
.motion [data-scene].m-play .m-scan { animation: m-scan var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-breathe { animation: m-blink 2.4s ease-in-out infinite; }
.motion [data-scene].m-play .m-ripple-1 { animation: m-ripple1 var(--m-scene) var(--m-ease-out) infinite; }
.motion [data-scene].m-play .m-ripple-2 { animation: m-ripple2 var(--m-scene) var(--m-ease-out) infinite; }
.motion [data-scene].m-play .m-ripple-3 { animation: m-ripple3 var(--m-scene) var(--m-ease-out) infinite; }
.motion [data-scene].m-play .m-fan-1 { animation: m-fan var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-fan-2 { animation: m-fan var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-fan-3 { animation: m-fan var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-fan-4 { animation: m-fan var(--m-scene) var(--m-ease-enter) infinite; }
.motion [data-scene].m-play .m-fill-2 { animation-name: m-fill2; }
.motion [data-scene].m-play .m-fill-3 { animation-name: m-fill3; }
.motion [data-scene].m-play .m-draw-2 { animation-name: m-draw2; }
.motion [data-scene].m-play .m-draw-3 { animation-name: m-draw3; }
.motion [data-scene].m-play .m-arc1 { animation-name: m-arc1; }
.motion [data-scene].m-play .m-arc2 { animation-name: m-arc2; }
.motion [data-scene].m-play .m-arc3 { animation-name: m-arc3; }
.motion [data-scene].m-play .m-arc4 { animation-name: m-arc4; }

@keyframes m-in-x1 { 0%, 0% { transform: translateX(0); opacity: 0; } 2% { opacity: 1; } 16% { transform: translateX(-100%); opacity: 1; } 18%, 100% { transform: translateX(-100%); opacity: 0; } }
@keyframes m-in-x2 { 0%, 10% { transform: translateX(0); opacity: 0; } 12% { opacity: 1; } 26% { transform: translateX(-100%); opacity: 1; } 28%, 100% { transform: translateX(-100%); opacity: 0; } }
@keyframes m-in-x3 { 0%, 20% { transform: translateX(0); opacity: 0; } 22% { opacity: 1; } 36% { transform: translateX(-100%); opacity: 1; } 38%, 100% { transform: translateX(-100%); opacity: 0; } }
@keyframes m-in-x4 { 0%, 30% { transform: translateX(0); opacity: 0; } 32% { opacity: 1; } 46% { transform: translateX(-100%); opacity: 1; } 48%, 100% { transform: translateX(-100%); opacity: 0; } }
@keyframes m-out-x { 0%, 55% { transform: translateX(-100%); opacity: 0; } 57% { opacity: 1; } 71% { transform: translateX(0); opacity: 1; } 73%, 100% { transform: translateX(0); opacity: 0; } }
@keyframes m-at1 { 0%, 8% { opacity: 0; transform: translateY(8px); } 12%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-at2 { 0%, 18% { opacity: 0; transform: translateY(8px); } 22%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-at3 { 0%, 28% { opacity: 0; transform: translateY(8px); } 32%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-at4 { 0%, 38% { opacity: 0; transform: translateY(8px); } 42%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-at5 { 0%, 48% { opacity: 0; transform: translateY(8px); } 52%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-at6 { 0%, 58% { opacity: 0; transform: translateY(8px); } 62%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-at7 { 0%, 68% { opacity: 0; transform: translateY(8px); } 72%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-at8 { 0%, 78% { opacity: 0; transform: translateY(8px); } 82%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-gone2 { 0%, 16% { opacity: 1; } 22%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-gone4 { 0%, 45% { opacity: 1; } 50%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-gone6 { 0%, 54% { opacity: 1; } 60%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-w0 { 0%, 18% { opacity: 1; } 21%, 100% { opacity: 0; } }
@keyframes m-w1 { 0%, 18% { opacity: 0; } 21%, 28% { opacity: 1; } 31%, 100% { opacity: 0; } }
@keyframes m-w2 { 0%, 28% { opacity: 0; } 31%, 38% { opacity: 1; } 41%, 100% { opacity: 0; } }
@keyframes m-w3 { 0%, 38% { opacity: 0; } 41%, 48% { opacity: 1; } 51%, 100% { opacity: 0; } }
@keyframes m-press { 0%, 27% { transform: none; } 30% { transform: scale(.93); } 34%, 100% { transform: none; } }
@keyframes m-press-late { 0%, 40% { transform: none; } 43% { transform: scale(.93); } 47%, 100% { transform: none; } }
@keyframes m-pop { 0%, 47% { opacity: 0; transform: scale(.6); } 53%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-fill1 { 0%, 4% { transform: scaleX(0); } 22%, 90% { transform: scaleX(1); } 96%, 100% { transform: scaleX(0); } }
@keyframes m-fill2 { 0%, 10% { transform: scaleX(0); } 28%, 90% { transform: scaleX(1); } 96%, 100% { transform: scaleX(0); } }
@keyframes m-fill3 { 0%, 16% { transform: scaleX(0); } 34%, 90% { transform: scaleX(1); } 96%, 100% { transform: scaleX(0); } }
@keyframes m-fill-y { 0%, 4% { transform: scaleY(0); } 52%, 90% { transform: scaleY(1); } 96%, 100% { transform: scaleY(0); } }
@keyframes m-ring { 0%, 70% { opacity: 0; } 73%, 84% { opacity: 1; } 88%, 100% { opacity: 0; } }
@keyframes m-turn { to { transform: rotate(360deg); } }
@keyframes m-halo { 0% { transform: scale(.96); opacity: .7; } 70%, 100% { transform: scale(1.4); opacity: 0; } }
@keyframes m-draw { 0%, 8% { stroke-dashoffset: 1; } 36%, 90% { stroke-dashoffset: 0; } 97%, 100% { stroke-dashoffset: 1; } }
@keyframes m-draw2 { 0%, 16% { stroke-dashoffset: 1; } 44%, 90% { stroke-dashoffset: 0; } 97%, 100% { stroke-dashoffset: 1; } }
@keyframes m-draw3 { 0%, 24% { stroke-dashoffset: 1; } 52%, 90% { stroke-dashoffset: 0; } 97%, 100% { stroke-dashoffset: 1; } }
@keyframes m-arc1 { 0%, 36% { stroke-dashoffset: 1; } 42%, 90% { stroke-dashoffset: 0; } 97%, 100% { stroke-dashoffset: 1; } }
@keyframes m-arc2 { 0%, 42% { stroke-dashoffset: 1; } 48%, 90% { stroke-dashoffset: 0; } 97%, 100% { stroke-dashoffset: 1; } }
@keyframes m-arc3 { 0%, 48% { stroke-dashoffset: 1; } 54%, 90% { stroke-dashoffset: 0; } 97%, 100% { stroke-dashoffset: 1; } }
@keyframes m-arc4 { 0%, 54% { stroke-dashoffset: 1; } 60%, 90% { stroke-dashoffset: 0; } 97%, 100% { stroke-dashoffset: 1; } }
@keyframes m-gauge { 0%, 6% { stroke-dashoffset: 1; } 30%, 54% { stroke-dashoffset: .2; } 62%, 90% { stroke-dashoffset: 0; } 97%, 100% { stroke-dashoffset: 1; } }
@keyframes m-shrink { 0%, 45% { transform: scaleX(1.078); } 52%, 90% { transform: scaleX(1); } 97%, 100% { transform: scaleX(1.078); } }
@keyframes m-fan { 0%, 8% { transform: translate(0, -38%) rotate(0deg) scale(.92); } 30%, 90% { transform: var(--fan); } 97%, 100% { transform: translate(0, -38%) rotate(0deg) scale(.92); } }
@keyframes m-scan { 0%, 4% { transform: translateY(0); opacity: 0; } 7% { opacity: 1; } 32% { transform: translateY(100%); opacity: 1; } 35%, 100% { transform: translateY(100%); opacity: 0; } }
@keyframes m-ripple1 { 0%, 30% { transform: scale(.35); opacity: 0; } 33% { opacity: .7; } 60%, 100% { transform: scale(1.45); opacity: 0; } }
@keyframes m-ripple2 { 0%, 38% { transform: scale(.35); opacity: 0; } 41% { opacity: .7; } 68%, 100% { transform: scale(1.45); opacity: 0; } }
@keyframes m-ripple3 { 0%, 46% { transform: scale(.35); opacity: 0; } 49% { opacity: .7; } 76%, 100% { transform: scale(1.45); opacity: 0; } }

.motion [data-scene].m-play .m-lane-in { animation: m-lane-in calc(var(--m-scene) / 2) ease-in-out infinite; }
.motion [data-scene].m-play .m-lane-out { animation: m-lane-out calc(var(--m-scene) / 2) ease-in-out infinite; }
.motion [data-scene].m-play .m-lane-d2 { animation-delay: -1s; }
.motion [data-scene].m-play .m-lane-d3 { animation-delay: -2s; }
.motion [data-scene].m-play .m-lane-d4 { animation-delay: -3s; }
.motion [data-scene].m-play .m-trail-back { animation: m-trail-back var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-trail-long { animation: m-trail-long var(--m-scene) linear infinite; }

@keyframes m-lane-in { 0% { transform: translateX(0); opacity: 0; } 8% { opacity: 1; } 60% { transform: translateX(100%); opacity: 1; } 68%, 100% { transform: translateX(100%); opacity: 0; } }
@keyframes m-lane-out { 0% { transform: translateX(0); opacity: 0; } 8% { opacity: 1; } 60% { transform: translateX(-100%); opacity: 1; } 68%, 100% { transform: translateX(-100%); opacity: 0; } }
@keyframes m-trail-long { 0%, 4% { stroke-dashoffset: .14; } 56%, 100% { stroke-dashoffset: -1; } }
@keyframes m-trail-back { 0%, 44% { stroke-dashoffset: .14; } 60%, 100% { stroke-dashoffset: -1; } }

/* ---------------------------------------------------------- home scenes */
/* The home page's pictures (templates/public/_home_scenes.html) play by the
   same rules as the channel scenes above and reuse their pieces. What they
   add: a pulse that runs along an SVG line (a short dash walked along the
   path, so any curve works), the hero's four stock updates arriving one
   after another, cards that fall from a scatter into line, text that types
   itself (`m-typing`: its own name, because the page-wide `.motion .m-type`
   runs from page load, and a shared name would keep that clock when the
   picture starts playing), and the vault's two drops. */

.m-trail { stroke-dasharray: .14 2; }
.m-dial { transform-box: view-box; transform-origin: 20px 20px; }
/* --mess is where a card lies before it is tidied; the markup is the line. */
.m-tidy { transform: none; }
.m-mess-1 { --mess: translate(-3%, 8%) rotate(-5deg); }
.m-mess-2 { --mess: translate(5%, 4%) rotate(4deg); }
.m-mess-3 { --mess: translate(-6%, -6%) rotate(-3deg); }
.m-mess-4 { --mess: translate(4%, 10%) rotate(5deg); }
.m-mess-5 { --mess: translate(-3%, -4%) rotate(-4deg); }

.motion [data-scene].m-play .m-trail-in { animation: m-trail-in var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-trail-o1 { animation: m-trail-o1 var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-trail-o2 { animation: m-trail-o2 var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-trail-o3 { animation: m-trail-o3 var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-trail-o4 { animation: m-trail-o4 var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-trail-o5 { animation: m-trail-o5 var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-trail-o6 { animation: m-trail-o6 var(--m-scene) linear infinite; }
.motion [data-scene].m-play .m-sw1 { animation: m-sw1 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-sw2 { animation: m-sw2 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-sw3 { animation: m-sw3 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-sw4 { animation: m-sw4 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-sw5 { animation: m-sw5 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-sw6 { animation: m-sw6 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-ring1 { animation: m-ring1 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-ring2 { animation: m-ring2 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-tidy { animation: m-tidy var(--m-scene) var(--m-ease-out) infinite; }
.motion [data-scene].m-play .m-type { animation: m-typing var(--m-scene) steps(24, end) infinite; }
.motion [data-scene].m-play .m-press7 { animation: m-press7 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-pop7 { animation: m-pop7 var(--m-scene) var(--m-ease-spring) infinite; }
.motion [data-scene].m-play .m-down1 { animation: m-down1 var(--m-scene) ease-in-out infinite; }
.motion [data-scene].m-play .m-down2 { animation: m-down2 var(--m-scene) ease-in-out infinite; }
.motion [data-scene].m-play .m-dial { animation: m-dial var(--m-scene) var(--m-ease-out) infinite; }

@keyframes m-trail-in { 0%, 3% { stroke-dashoffset: .14; } 19%, 100% { stroke-dashoffset: -1; } }
@keyframes m-trail-o1 { 0%, 27% { stroke-dashoffset: .14; } 39%, 100% { stroke-dashoffset: -1; } }
@keyframes m-trail-o2 { 0%, 30% { stroke-dashoffset: .14; } 42%, 100% { stroke-dashoffset: -1; } }
@keyframes m-trail-o3 { 0%, 33% { stroke-dashoffset: .14; } 45%, 100% { stroke-dashoffset: -1; } }
@keyframes m-trail-o4 { 0%, 36% { stroke-dashoffset: .14; } 48%, 100% { stroke-dashoffset: -1; } }
@keyframes m-trail-o5 { 0%, 39% { stroke-dashoffset: .14; } 51%, 100% { stroke-dashoffset: -1; } }
@keyframes m-trail-o6 { 0%, 42% { stroke-dashoffset: .14; } 54%, 100% { stroke-dashoffset: -1; } }
@keyframes m-sw1 { 0%, 38% { opacity: 1; } 41%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-sw2 { 0%, 41% { opacity: 1; } 44%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-sw3 { 0%, 44% { opacity: 1; } 47%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-sw4 { 0%, 47% { opacity: 1; } 50%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-sw5 { 0%, 50% { opacity: 1; } 53%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-sw6 { 0%, 53% { opacity: 1; } 56%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-ring1 { 0%, 2% { opacity: 0; } 5%, 18% { opacity: 1; } 24%, 100% { opacity: 0; } }
@keyframes m-ring2 { 0%, 18% { opacity: 0; } 22%, 34% { opacity: 1; } 42%, 100% { opacity: 0; } }
@keyframes m-tidy { 0%, 8% { transform: var(--mess); } 24%, 92% { transform: none; } 98%, 100% { transform: var(--mess); } }
@keyframes m-typing { 0%, 4% { clip-path: inset(0 100% 0 0); } 22%, 90% { clip-path: inset(0 0 0 0); } 96%, 100% { clip-path: inset(0 100% 0 0); } }
@keyframes m-press7 { 0%, 67% { transform: none; } 70% { transform: scale(.93); } 74%, 100% { transform: none; } }
@keyframes m-pop7 { 0%, 70% { opacity: 0; transform: scale(.6); } 76%, 90% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes m-down1 { 0%, 4% { transform: translateY(0); opacity: 0; } 6% { opacity: 1; } 20% { transform: translateY(100%); opacity: 1; } 22%, 100% { transform: translateY(100%); opacity: 0; } }
@keyframes m-down2 { 0%, 52% { transform: translateY(0); opacity: 0; } 54% { opacity: 1; } 70% { transform: translateY(100%); opacity: 1; } 72%, 100% { transform: translateY(100%); opacity: 0; } }
@keyframes m-dial { 0%, 70% { transform: rotate(0deg); } 80%, 100% { transform: rotate(90deg); } }

/* ------------------------------------------------------------ DI scenes */
/* The pictures on /di (templates/public/_ai_scenes.html) reuse the pieces
   above and run on a longer turn (the frame sets --m-scene: 10s), because
   each one tells a whole exchange. What they add:
   - `m-offN`, the mirror of `m-atN`: a "still being done" layer drawn over
     the finished piece (a skeleton, a spinner, "tikrinama") lifts off at the
     moment the piece would arrive, so a picture never waits empty;
   - a person's pointer (`m-cursor7`) that lands on a button and presses it
     together with `m-press7`, just before the approved state pops in;
   - a "writing" placeholder with thinking dots (`m-think`, `m-d2`/`m-d3`),
     a spinner, and a skeleton shimmer while a draft is being written;
   - an action the agent does not have: the agent bumps into it (`m-bumpN`),
     the row shakes its head and flashes red, one after another, so "cannot"
     reads as a refusal rather than a greyed-out button. */

.m-shake1, .m-shake2, .m-shake3 { transform: none; }
.m-spinner { transform-origin: 50% 50%; }
.m-shimmer { background-image: linear-gradient(90deg, rgb(var(--c-slate-100)) 25%, rgb(var(--c-slate-200)) 37%, rgb(var(--c-slate-100)) 63%); background-size: 400% 100%; }
.motion [data-scene].m-play .m-off1 { animation: m-off1 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-off2 { animation: m-off2 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-off3 { animation: m-off3 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-off4 { animation: m-off4 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-off5 { animation: m-off5 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-off6 { animation: m-off6 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-off7 { animation: m-off7 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-off8 { animation: m-off8 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-cursor7 { animation: m-cursor7 var(--m-scene) ease-in-out infinite; }
.motion [data-scene].m-play .m-think { animation: m-think var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-spinner { animation: m-turn .9s linear infinite; }
.motion [data-scene].m-play .m-shimmer { animation: m-shimmer 1.4s ease infinite; }
.motion [data-scene].m-play .m-d2 { animation-delay: .2s; }
.motion [data-scene].m-play .m-d3 { animation-delay: .4s; }
.motion [data-scene].m-play .m-shake1 { animation: m-shake1 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-shake2 { animation: m-shake2 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-shake3 { animation: m-shake3 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-deny1 { animation: m-deny1 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-deny2 { animation: m-deny2 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-deny3 { animation: m-deny3 var(--m-scene) ease infinite; }
.motion [data-scene].m-play .m-bump1 { animation: m-bump1 var(--m-scene) ease-out infinite; }
.motion [data-scene].m-play .m-bump2 { animation: m-bump2 var(--m-scene) ease-out infinite; }
.motion [data-scene].m-play .m-bump3 { animation: m-bump3 var(--m-scene) ease-out infinite; }

@keyframes m-off1 { 0%, 8% { opacity: 1; } 12%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-off2 { 0%, 18% { opacity: 1; } 22%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-off3 { 0%, 28% { opacity: 1; } 32%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-off4 { 0%, 38% { opacity: 1; } 42%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-off5 { 0%, 48% { opacity: 1; } 52%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-off6 { 0%, 58% { opacity: 1; } 62%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-off7 { 0%, 68% { opacity: 1; } 72%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-off8 { 0%, 78% { opacity: 1; } 82%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes m-cursor7 { 0%, 50% { opacity: 0; transform: translate(64px, 48px); } 55% { opacity: 1; transform: translate(64px, 48px); } 66% { opacity: 1; transform: translate(0, 0); } 69% { transform: translate(0, 0) scale(.8); } 72%, 84% { opacity: 1; transform: translate(0, 0) scale(1); } 90%, 100% { opacity: 0; transform: translate(0, 0); } }
@keyframes m-think { 0%, 20% { opacity: 0; } 23%, 37% { opacity: 1; } 41%, 100% { opacity: 0; } }
@keyframes m-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@keyframes m-shake1 { 0%, 28% { transform: none; } 30% { transform: translateX(-4px); } 32% { transform: translateX(4px); } 34% { transform: translateX(-3px); } 36% { transform: translateX(2px); } 38%, 100% { transform: none; } }
@keyframes m-shake2 { 0%, 43% { transform: none; } 45% { transform: translateX(-4px); } 47% { transform: translateX(4px); } 49% { transform: translateX(-3px); } 51% { transform: translateX(2px); } 53%, 100% { transform: none; } }
@keyframes m-shake3 { 0%, 58% { transform: none; } 60% { transform: translateX(-4px); } 62% { transform: translateX(4px); } 64% { transform: translateX(-3px); } 66% { transform: translateX(2px); } 68%, 100% { transform: none; } }
@keyframes m-deny1 { 0%, 27% { opacity: 0; } 29%, 40% { opacity: 1; } 46%, 100% { opacity: 0; } }
@keyframes m-deny2 { 0%, 42% { opacity: 0; } 44%, 55% { opacity: 1; } 61%, 100% { opacity: 0; } }
@keyframes m-deny3 { 0%, 57% { opacity: 0; } 59%, 70% { opacity: 1; } 76%, 100% { opacity: 0; } }
@keyframes m-bump1 { 0%, 19% { opacity: 0; transform: translateX(-14px); } 22% { opacity: 1; transform: translateX(-14px); } 28% { opacity: 1; transform: translateX(4px); } 31% { transform: translateX(0); } 37% { opacity: 1; transform: translateX(0); } 41%, 100% { opacity: 0; transform: translateX(0); } }
@keyframes m-bump2 { 0%, 34% { opacity: 0; transform: translateX(-14px); } 37% { opacity: 1; transform: translateX(-14px); } 43% { opacity: 1; transform: translateX(4px); } 46% { transform: translateX(0); } 52% { opacity: 1; transform: translateX(0); } 56%, 100% { opacity: 0; transform: translateX(0); } }
@keyframes m-bump3 { 0%, 49% { opacity: 0; transform: translateX(-14px); } 52% { opacity: 1; transform: translateX(-14px); } 58% { opacity: 1; transform: translateX(4px); } 61% { transform: translateX(0); } 67% { opacity: 1; transform: translateX(0); } 71%, 100% { opacity: 0; transform: translateX(0); } }

/* ------------------------------------------------------------------ escape */
/* Belt and braces: even if `.motion` were set by mistake, these two wipe the
   file out for anyone who asked for less movement, and for print. */

@media (prefers-reduced-motion: reduce) {
  .motion *, .motion *::before, .motion *::after {
    animation: none !important;
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
    clip-path: none !important;
    stroke-dashoffset: 0 !important;
  }
}

@media print {
  .motion *, .motion *::before, .motion *::after {
    animation: none !important;
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
    clip-path: none !important;
  }
  [data-scene] .m-before, [data-scene] .m-dot, [data-scene] .m-ring { display: none !important; }
}

/* Phones get the ideas, not the machinery: no pinned columns, no pointer
   effects, no parallax. */
@media (max-width: 1023px) {
  .motion [data-tilt] { transform: none; }
  .motion [data-magnetic] { transform: none; }
  .m-pinned { position: static !important; }
}
