/* Character motion for the loader and the 4.45-second bonus introduction.
   Travel belongs to the actor; the sprite alone supplies the footfall lift. */
#subwayChase .actor,
#loader .load-actor {
  --stride-time: .52s;
  --stride-offset: 0s;
  filter: drop-shadow(0 3px 3px #03071980);
}

#subwayChase .chase-police,
#loader .load-police {
  --stride-time: .60s;
  --stride-offset: -.15s;
}

/* The existing sheets put the soles 4.5% above the frame's bottom edge. */
#subwayChase .actor::before,
#loader .load-actor::before {
  content: '';
  position: absolute;
  left: 22%;
  bottom: 2.5%;
  width: 56%;
  height: 4%;
  background: radial-gradient(ellipse, #020715b3 15%, #02071555 50%, transparent 72%);
  transform-origin: center;
  pointer-events: none;
}

#subwayChase .actor-idle {
  bottom: 2%;
  transform-origin: 50% 100%;
}

/* A brief reaction during the existing whistle cue, before the run at 850ms. */
#subwayChase.is-alert .chase-runner .actor-idle {
  animation: chase-runner-ready .85s ease-in-out both;
}

#subwayChase.is-alert .chase-police .actor-idle {
  animation: chase-police-ready .85s ease-in-out both;
}

#subwayChase .whistle-mark {
  animation: chase-whistle .48s ease-out both;
}

@keyframes chase-runner-ready {
  0%, 45% { transform: none; }
  65% { transform: translateX(-.6%) rotate(-.8deg); }
  100% { transform: translateX(1%) rotate(1.4deg); }
}

@keyframes chase-police-ready {
  0%, 62% { transform: none; }
  100% { transform: translateX(.6%) rotate(1deg); }
}

@keyframes chase-whistle {
  0% { opacity: 0; scale: .85; }
  45% { opacity: 1; scale: 1.04; }
  100% { opacity: 1; scale: 1; }
}

#subwayChase.is-running .actor-run,
#loader .load-actor > i {
  transform-origin: 50% 96%;
  animation: subway-run var(--stride-time) steps(4) var(--stride-offset) infinite,
    character-footfall var(--stride-time) ease-in-out var(--stride-offset) infinite;
}

#subwayChase.is-running .actor::before,
#loader .load-actor::before {
  animation: character-contact var(--stride-time) ease-in-out var(--stride-offset) infinite;
}

/* Two light lifts per four-frame cycle; no whole-body shaking or squashing. */
@keyframes character-footfall {
  0%, 50%, 100% { transform: translateY(0); }
  25%, 75% { transform: translateY(-.65%); }
}

@keyframes character-contact {
  0%, 50%, 100% { transform: scaleX(1); opacity: .85; }
  25%, 75% { transform: scaleX(.88); opacity: .65; }
}

/* The lead accelerates first; the officer follows 120ms later. All travel stays
   on the same ground line, and the train cue/flash retain their JS timing. */
#subwayChase.is-running .chase-runner {
  animation: chase-runner-travel 2.65s linear both;
}

#subwayChase.is-running .chase-police {
  animation: chase-police-travel 2.55s linear .12s both;
}

@keyframes chase-runner-travel {
  0% { transform: translateX(0); }
  15% { transform: translateX(3vw); }
  35% { transform: translateX(12vw); }
  60% { transform: translateX(29vw); }
  85% { transform: translateX(47vw); }
  100% { transform: translateX(62vw); }
}

@keyframes chase-police-travel {
  0% { transform: translateX(0); }
  15% { transform: translateX(3vw); }
  35% { transform: translateX(14vw); }
  60% { transform: translateX(36vw); }
  85% { transform: translateX(64vw); }
  100% { transform: translateX(90vw); }
}

/* Each loading actor crosses at a steady pace. The cycle resets only after
   that actor has left the clipped track, never in the visible middle. */
#loader .load-chase { --character-track-width: min(760px, 94vw); }
#loader .load-pack { animation: none; transform: none; }
#loader .load-runner,
#loader .load-police {
  left: 0;
  animation: character-loading-travel 4.8s linear infinite;
}
#loader .load-runner { animation-delay: -2.9s; }
#loader .load-police { animation-delay: -1.6s; }
#loader .load-police::after {
  top: 5%;
  font-size: clamp(25px, 5vmin, 44px);
}

@keyframes character-loading-travel {
  from { transform: translateX(-110%); }
  to { transform: translateX(calc(var(--character-track-width) + 10%)); }
}

/* Use the same foot line and existing safe composition on narrow screens. */
@media (orientation: portrait) {
  #subwayChase .actor { bottom: max(26%, calc(env(safe-area-inset-bottom) + 100px)); }
}

body.page-hidden #subwayChase .actor,
body.page-hidden #subwayChase .actor > i,
body.page-hidden #subwayChase .actor::before,
body.page-hidden #subwayChase .whistle-mark,
body.page-hidden #loader .load-actor,
body.page-hidden #loader .load-actor > i,
body.page-hidden #loader .load-actor::before {
  animation-play-state: paused !important;
}

@media (prefers-reduced-motion: reduce) {
  #subwayChase .actor,
  #subwayChase .actor > i,
  #subwayChase .actor::before,
  #subwayChase .whistle-mark,
  #loader .load-actor,
  #loader .load-actor > i,
  #loader .load-actor::before {
    animation: none !important;
    transform: none;
  }
  #loader .load-police { left: 9%; }
  #loader .load-runner { left: 52%; }
}
