/* ============================================================
   ANIMATIONS
   ============================================================ */

@keyframes screen-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes card-in {
  from { opacity: 0; transform: translateY(14px) scale(0.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes title-drop {
  from { opacity: 0; transform: translateY(-28px) scale(0.86); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes light-flicker {
  0%, 100% { opacity: 0.75; }
  46% { opacity: 1; }
  52% { opacity: 0.62; }
}

@keyframes flag-wave {
  0%, 100% { transform: skewY(0deg) scaleY(1); }
  50% { transform: skewY(-2.5deg) scaleY(1.03); }
}
.np-flag-right { animation-name: flag-wave-r; }
@keyframes flag-wave-r {
  0%, 100% { transform: scaleX(-1) skewY(0deg); }
  50% { transform: scaleX(-1) skewY(-2.5deg) scaleY(1.03); }
}

@keyframes fly-across {
  0%   { left: -50px; top: 30%; transform: rotate(0deg) scale(0.8); opacity: 0; }
  12%  { opacity: 1; }
  50%  { top: 18%; transform: rotate(420deg) scale(1.15); }
  100% { left: 106%; top: 34%; transform: rotate(900deg) scale(0.8); opacity: 0.9; }
}

@keyframes confetti-drift {
  from { transform: translateY(-10vh) rotate(0deg); opacity: 0; }
  10%  { opacity: 0.9; }
  to   { transform: translateY(110vh) rotate(620deg); opacity: 0; }
}

@keyframes confetti-fall {
  from { transform: translateY(0) rotate(0deg); opacity: 1; }
  to   { transform: translateY(120vh) rotate(560deg); opacity: 0; }
}

@keyframes dot-blink {
  0%, 80%, 100% { opacity: 0.2; }
  40% { opacity: 1; }
}

@keyframes loading-fill {
  from { width: 0; }
  to   { width: 100%; }
}

/* Bowler drives in toward the crease as the ball is released. */
@keyframes bowler-runup {
  0%   { transform: translateX(-50%) translateY(-6px) scale(0.92); }
  60%  { transform: translateX(-50%) translateY(3px) scale(1.05); }
  100% { transform: translateX(-50%) translateY(0) scale(1); }
}

/* ---------------------------------------------------------------------------
   BALL FLIGHT — composited.

   These animate only `transform` and `opacity`, so they run on the compositor
   instead of forcing layout + paint on the main thread every frame. That
   matters here beyond smoothness: the ball is what the player watches to time
   their tap, so main-thread jank directly cost them timing accuracy.

   Travel distances arrive as CSS variables set per delivery in game.js, because
   transform percentages resolve against the element's own 13px box rather than
   against the pitch panel.
   --------------------------------------------------------------------------- */
@keyframes ball-deliver {
  0%   { transform: translate3d(0, 0, 0) scale(0.7); opacity: 1; }
  100% { transform: translate3d(0, var(--deliver-y, 200px), 0) scale(1.45); opacity: 1; }
}
.ball.deliver { animation: ball-deliver linear forwards; opacity: 1; }

/* SIX — ball launches up and clears the stadium. */
@keyframes ball-launch {
  0%   { transform: translate3d(0, 0, 0) scale(1.3); opacity: 1; }
  30%  { transform: translate3d(calc(var(--fly-x, 0px) * 0.30),
                                calc(var(--fly-y, -300px) * 0.38), 0) scale(1.05); opacity: 1; }
  65%  { transform: translate3d(calc(var(--fly-x, 0px) * 0.68),
                                calc(var(--fly-y, -300px) * 0.72), 0) scale(0.78); opacity: 1; }
  100% { transform: translate3d(var(--fly-x, 0px), var(--fly-y, -300px), 0) scale(0.42); opacity: 0; }
}
.ball.launch { animation: ball-launch 1.05s cubic-bezier(0.25, 0.55, 0.5, 1) forwards; opacity: 1; }

/* FOUR — ball races along the ground to the boundary rope. */
@keyframes ball-boundary {
  0%   { transform: translate3d(0, 0, 0) scale(1.3); opacity: 1; }
  100% { transform: translate3d(var(--fly-x, -120px), var(--fly-y, 80px), 0) scale(1.6); opacity: 0; }
}
.ball.boundary { animation: ball-boundary 0.7s cubic-bezier(0.15, 0.7, 0.4, 1) forwards; opacity: 1; }

/* WICKET — the stumps are knocked back and topple over. Pivots about the base
   so they fall rather than slide. */
@keyframes stumps-topple {
  0%   { transform: translateX(-50%) rotate(0deg); }
  16%  { transform: translateX(-50%) rotate(-7deg) translateY(-3px); }
  45%  { transform: translateX(-50%) rotate(-34deg); }
  75%  { transform: translateX(-50%) rotate(-62deg); }
  100% { transform: translateX(-50%) rotate(-78deg); opacity: 0.85; }
}

/* WICKET — drawn-pitch stumps scatter (photo pitch uses the graphic above). */
@keyframes stump-fly-l {
  to { transform: translate(-22px, 10px) rotate(-72deg); opacity: 0; }
}
@keyframes stump-fly-c {
  to { transform: translate(3px, 16px) rotate(18deg); opacity: 0; }
}
@keyframes stump-fly-r {
  to { transform: translate(22px, 11px) rotate(78deg); opacity: 0; }
}
.stumps.broken span { transform-origin: 50% 100%; }
.stumps.broken span:nth-child(1) { animation: stump-fly-l 0.6s ease-out forwards; }
.stumps.broken span:nth-child(2) { animation: stump-fly-c 0.6s ease-out forwards; }
.stumps.broken span:nth-child(3) { animation: stump-fly-r 0.6s ease-out forwards; }

/* Photo-pitch trajectories — matched to the photograph's perspective,
   where the far wicket sits near 56% and the batting crease near 82%. */
@keyframes ball-deliver-photo {
  0%   { transform: translate3d(0, 0, 0) scale(0.55); opacity: 1; }
  100% { transform: translate3d(0, var(--deliver-y, 180px), 0) scale(1.35); opacity: 1; }
}
@keyframes ball-launch-photo {
  0%   { transform: translate3d(0, 0, 0) scale(1.3); opacity: 1; }
  30%  { transform: translate3d(calc(var(--fly-x, 0px) * 0.30),
                                calc(var(--fly-y, -300px) * 0.38), 0) scale(1); opacity: 1; }
  65%  { transform: translate3d(calc(var(--fly-x, 0px) * 0.68),
                                calc(var(--fly-y, -300px) * 0.72), 0) scale(0.7); opacity: 1; }
  100% { transform: translate3d(var(--fly-x, 0px), var(--fly-y, -300px), 0) scale(0.38); opacity: 0; }
}
@keyframes ball-boundary-photo {
  0%   { transform: translate3d(0, 0, 0) scale(1.3); opacity: 1; }
  100% { transform: translate3d(var(--fly-x, -120px), var(--fly-y, -80px), 0) scale(0.65); opacity: 0; }
}

/* Composited too — this is the other thing the player is watching while
   timing the shot, so it must not compete for the main thread. */
@keyframes indicator-sweep {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(var(--tm-travel, 300px), 0, 0); }
}

@keyframes flash-pop {
  0%   { opacity: 0; transform: scale(0.55); }
  28%  { opacity: 1; transform: scale(1.14); }
  66%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.02); }
}

@keyframes outcome-in {
  0%   { opacity: 0; transform: scale(0.7); }
  12%  { opacity: 1; transform: scale(1.08); }
  24%  { transform: scale(1); }
  76%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.05); }
}

@keyframes shake {
  0%, 100% { transform: translate(0, 0); }
  18% { transform: translate(-7px, 3px); }
  36% { transform: translate(7px, -3px); }
  54% { transform: translate(-5px, 2px); }
  72% { transform: translate(5px, -2px); }
  88% { transform: translate(-2px, 1px); }
}
.pitch.shake { animation: shake 0.38s ease; }

@keyframes score-pop {
  0%   { transform: scale(1); }
  46%  { transform: scale(1.4); color: var(--gold-lt); }
  100% { transform: scale(1); }
}
.hud-value.pop { animation: score-pop 0.42s ease; }

@keyframes hit-pulse {
  0%, 100% {
    box-shadow: 0 12px 32px rgba(217,35,46,0.5), 0 0 0 4px rgba(217,35,46,0.18),
                inset 0 2px 0 rgba(255,255,255,0.35), inset 0 -3px 10px rgba(0,0,0,0.35);
  }
  50% {
    box-shadow: 0 12px 38px rgba(217,35,46,0.7), 0 0 0 9px rgba(217,35,46,0.1),
                inset 0 2px 0 rgba(255,255,255,0.35), inset 0 -3px 10px rgba(0,0,0,0.35);
  }
}

@keyframes pill-pulse {
  0%, 100% { box-shadow: 0 0 14px rgba(217,35,46,0.6); }
  50% { box-shadow: 0 0 24px rgba(217,35,46,0.95); }
}

/* ---------------------------- BOWLING DELIVERY ----------------------------
   The batting keyframes grow the ball as it comes at you (0.55 -> 1.35).
   Bowling sends it away, so the scale runs the other way and --deliver-y
   is negative. Same duration and easing, so the timing meter still lines
   up exactly with what the eye sees. */
/* `--deliver-x` carries the ball in from the bowler's hand to the centre line,
   because the release point is off to his side while the far wicket is centred.
   It is 0 in the first-person view, where the ball already starts centred. */
/* `--arc-y` is the mid-flight lift and `--hop-y` the smaller lift after the
   bounce. --arc-y exists because releasing from the hand
   makes the delivery almost flat on screen — hand height and the far batsman's
   height are nearly the same — so without an arc the ball reads as sliding
   sideways rather than being bowled away. It is 0 in the first-person view,
   which already has a long vertical travel of its own. */
/* The delivery PITCHES. `BOUNCE_AT` (0.733) is where the straight line from
   the release point to the batsman crosses the target ring, so the ball
   touches down exactly on the mark the player was aiming at — game.js derives
   the ring's depth from the same constant, and the two cannot drift apart.

   Keyframe percentages equal the down-pitch fraction and the timing function
   stays linear, so `--deliver-y * <that fraction>` keeps the recede perfectly
   even; only the ADDED height term arcs. Height is therefore 0 at both ends
   (the endpoints are already the hand and the bat) and 0 at the bounce (the
   ball is on the ground there) — it is negative in between, which is up the
   frame and reads as "above the pitch". */
@keyframes ball-deliver-bowl {
  0%   { transform: translate3d(0, 0, 0) scale(1.25); opacity: 1; }
  40%  { transform: translate3d(calc(var(--deliver-x, 0px) * 0.40),
                                calc(var(--deliver-y, -200px) * 0.40 + var(--arc-y, 0px)), 0)
                   scale(1.02); opacity: 1; }
  /* pitching — on the ground, no lift */
  73.3% { transform: translate3d(calc(var(--deliver-x, 0px) * 0.733),
                                calc(var(--deliver-y, -200px) * 0.733), 0)
                   scale(0.86); opacity: 1; }
  84%  { transform: translate3d(calc(var(--deliver-x, 0px) * 0.84),
                                calc(var(--deliver-y, -200px) * 0.84 + var(--hop-y, 0px)), 0)
                   scale(0.80); opacity: 1; }
  100% { transform: translate3d(var(--deliver-x, 0px), var(--deliver-y, -200px), 0) scale(0.70); opacity: 1; }
}
@keyframes ball-deliver-photo-bowl {
  0%   { transform: translate3d(0, 0, 0) scale(1.15); opacity: 1; }
  40%  { transform: translate3d(calc(var(--deliver-x, 0px) * 0.40),
                                calc(var(--deliver-y, -180px) * 0.40 + var(--arc-y, 0px)), 0)
                   scale(0.95); opacity: 1; }
  /* pitching — on the ground, no lift */
  73.3% { transform: translate3d(calc(var(--deliver-x, 0px) * 0.733),
                                calc(var(--deliver-y, -180px) * 0.733), 0)
                   scale(0.76); opacity: 1; }
  84%  { transform: translate3d(calc(var(--deliver-x, 0px) * 0.84),
                                calc(var(--deliver-y, -180px) * 0.84 + var(--hop-y, 0px)), 0)
                   scale(0.70); opacity: 1; }
  100% { transform: translate3d(var(--deliver-x, 0px), var(--deliver-y, -180px), 0) scale(0.55); opacity: 1; }
}
.pitch.bowl-mode .ball.deliver { animation-name: ball-deliver-bowl; }
.pitch.bowl-mode.photo .ball.deliver { animation-name: ball-deliver-photo-bowl; }

/* Pitch target — breathing pulse while the ball is live, and a flare when
   the delivery actually lands on the mark. */
@keyframes bt-pulse {
  0%, 100% { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.07); }
}
@keyframes bt-flare {
  0%   { opacity: 1;   transform: translate(-50%, -50%) scale(1); }
  55%  { opacity: 0.9; transform: translate(-50%, -50%) scale(1.45); }
  100% { opacity: 0;   transform: translate(-50%, -50%) scale(1.75); }
}

/* Bat swing impulse — a short weight-shift on the batsman container while
   the sprite frames swap. The frames already carry the bat angle, so this
   only supplies momentum; rotating the artwork on top would double it up.
   translateX(-50%) must be repeated in every step or the sprite jumps out
   of its centred position for the duration of the animation. */
@keyframes bat-swing {
  0%   { transform: translateX(-50%) translateY(0)      scale(1); }
  20%  { transform: translateX(-50%) translateY(-1.8%)  scale(1.035); }
  52%  { transform: translateX(-50%) translateY(0.6%)   scale(0.994); }
  100% { transform: translateX(-50%) translateY(0)      scale(1); }
}
.batsman.swinging { animation: bat-swing 300ms cubic-bezier(0.22, 0.9, 0.3, 1); }

@media (prefers-reduced-motion: reduce) {
  .batsman.swinging { animation: none; }
}
