/*
 * The paper-doll rig and every animation state.
 *
 * Rule for this file: transforms and opacity only. Nothing here may animate a
 * property that triggers layout or paint, because eight characters x six parts
 * are running at once. The root's own transform belongs to the renderer, which
 * is why all whole-body motion lives on .ch__body instead.
 */

/* ===== Rig ===== */
.ch {
  position: absolute;
  left: 0; top: 0;
  width: 100px; height: 140px;   /* Assets.CHAR_W x Assets.CHAR_H */
  transform-origin: 50% 100%;    /* the feet - the point placed on the pitch */
  pointer-events: none;
  will-change: transform;
}
.ch__body { position: absolute; inset: 0; transform-origin: 50% 100%; }
.ch__part {
  position: absolute;
  display: block;
  object-fit: contain;
  image-rendering: auto;
}
.ch__shadow {
  position: absolute;
  left: 50%; bottom: -4px;
  width: 54px; height: 14px;
  margin-left: -27px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.28);
  filter: blur(2px);
}
.ch__badge {
  position: absolute;
  left: 33px; top: 50px;
  width: 13px; height: 9px;
  border-radius: 3px;
  background: var(--team-colour, #fff);
  border: 1.5px solid rgba(255, 255, 255, 0.9);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  z-index: 6;
}

/* ===== Faces =====
   All three are in the DOM; the class picks one. Swapping opacity rather than
   src means a goal never lands on an undecoded image. */
.ch__face { opacity: 0; }
.ch--face-idle .ch__face--idle,
.ch--face-goal .ch__face--goal,
.ch--face-sad  .ch__face--sad { opacity: 1; }

/* ===== idle - slow breath bob ===== */
.ch--idle .ch__body { animation: ch-breathe 2.6s ease-in-out infinite; }
@keyframes ch-breathe {
  0%, 100% { transform: translateY(0) scaleY(1); }
  50%      { transform: translateY(-2px) scaleY(1.012); }
}

/* ===== run =====
   Two things here are load bearing, and both were wrong first time.

   The arms are on the opposite delay to the legs. Right arm with right leg is
   how a tin soldier moves, not a person, and it was measurably what this did:
   arm-r sat at the same phase as leg-r for the whole cycle.

   The swing is biased forward rather than symmetrical. A limb that travels the
   same distance either side of vertical carries no information about which way
   the character is going, and on a front-on paper doll there is nothing else
   to say: it reads as a shuffle, or as running backwards. Forward reach larger
   than the trailing push, plus a real lean, is the whole cue. */
.ch--run .ch__leg-l { animation: ch-run-leg 0.42s linear infinite; }
.ch--run .ch__leg-r { animation: ch-run-leg 0.42s linear infinite -0.21s; }
.ch--run .ch__arm-l { animation: ch-run-arm 0.42s linear infinite; }
.ch--run .ch__arm-r { animation: ch-run-arm 0.42s linear infinite -0.21s; }
.ch--run .ch__body  { animation: ch-run-body 0.21s ease-in-out infinite alternate; }
/* Negative swings a hanging limb towards the front, positive towards the back. */
@keyframes ch-run-leg {
  0%   { transform: rotate(26deg); }    /* trailing, pushing off */
  50%  { transform: rotate(-40deg); }   /* reaching ahead */
  100% { transform: rotate(26deg); }
}
/* The arms lean the other way - a runner drives the elbow back - and the arc
   is deliberately small. These are front-on paper dolls, so a big horizontal
   swing does not read as forward and back; it just drags the arm across the
   torso, where one of them disappears behind it and the other sits on the
   belly. The legs and the lean carry the direction; the arms only have to
   counter them. */
@keyframes ch-run-arm {
  0%   { transform: rotate(-18deg); }   /* opposite to the leg on that side */
  50%  { transform: rotate(26deg); }
  100% { transform: rotate(-18deg); }
}
@keyframes ch-run-body {
  from { transform: translateY(0) rotate(8deg); }
  to   { transform: translateY(-3px) rotate(11deg); }
}

/* ===== kick - one leg swings through, the opposite arm counterbalances ===== */
.ch--kick .ch__leg-r { animation: ch-kick-leg 0.34s cubic-bezier(0.2, 0.9, 0.3, 1); }
.ch--kick .ch__leg-l { animation: ch-kick-plant 0.34s ease-out; }
.ch--kick .ch__arm-l { animation: ch-kick-arm 0.34s ease-out; }
.ch--kick .ch__body  { animation: ch-kick-body 0.34s ease-out; }
@keyframes ch-kick-leg {
  0%   { transform: rotate(38deg); }
  35%  { transform: rotate(-72deg); }
  100% { transform: rotate(0deg); }
}
@keyframes ch-kick-plant {
  0%, 100% { transform: rotate(0deg); }
  35%      { transform: rotate(16deg); }
}
@keyframes ch-kick-arm {
  0%, 100% { transform: rotate(0deg); }
  35%      { transform: rotate(58deg); }
}
@keyframes ch-kick-body {
  0%, 100% { transform: rotate(0deg); }
  35%      { transform: rotate(-11deg) translateY(-2px); }
}

/* ===== celebrate - jump, both arms up, hip wiggle ===== */
.ch--celebrate .ch__body  { animation: ch-dance-body 0.62s ease-in-out infinite; }
.ch--celebrate .ch__arm-l { animation: ch-dance-arm-l 0.31s ease-in-out infinite alternate; }
.ch--celebrate .ch__arm-r { animation: ch-dance-arm-r 0.31s ease-in-out infinite alternate; }
.ch--celebrate .ch__leg-l { animation: ch-dance-leg-l 0.62s ease-in-out infinite; }
.ch--celebrate .ch__leg-r { animation: ch-dance-leg-r 0.62s ease-in-out infinite; }
@keyframes ch-dance-body {
  0%   { transform: translateY(0) rotate(-7deg); }
  25%  { transform: translateY(-18px) rotate(0deg); }
  50%  { transform: translateY(0) rotate(7deg); }
  75%  { transform: translateY(-11px) rotate(0deg); }
  100% { transform: translateY(0) rotate(-7deg); }
}
/* Arms point down at rotate(0); ~160deg swings each one up and outward. */
@keyframes ch-dance-arm-l { from { transform: rotate(140deg); } to { transform: rotate(170deg); } }
@keyframes ch-dance-arm-r { from { transform: rotate(-140deg); } to { transform: rotate(-170deg); } }
@keyframes ch-dance-leg-l {
  0%, 100% { transform: rotate(0deg); }
  25%      { transform: rotate(26deg); }
  75%      { transform: rotate(-14deg); }
}
@keyframes ch-dance-leg-r {
  0%, 100% { transform: rotate(0deg); }
  25%      { transform: rotate(-26deg); }
  75%      { transform: rotate(14deg); }
}

/* ===== sad and sad-dance - arms folded, weight off one hip, head down =====
   Conceding used to play the celebration at half energy: the same jump, a bit
   smaller, arms still up. A smaller celebration does not read as
   disappointment - it reads as a smaller celebration. Folded arms, a slump and
   a dropped head are unmistakable even at this size, and they are the one
   gesture that cannot be confused with a happy one.

   Both states share the pose; only the tempo differs. Conceding sighs at
   roughly breathing pace, full time is slower and heavier. */
.ch--sad-dance .ch__body  { animation: ch-mope-body 1.9s ease-in-out infinite; }
.ch--sad-dance .ch__head  { animation: ch-mope-head 1.9s ease-in-out infinite; }
.ch--sad-dance .ch__arm-l { animation: ch-mope-arm-l 1.9s ease-in-out infinite; }
.ch--sad-dance .ch__arm-r { animation: ch-mope-arm-r 1.9s ease-in-out infinite; }

.ch--sad .ch__body  { animation: ch-mope-body 3.4s ease-in-out infinite; }
.ch--sad .ch__head  { animation: ch-mope-head 3.4s ease-in-out infinite; }
.ch--sad .ch__arm-l { animation: ch-mope-arm-l 3.4s ease-in-out infinite; }
.ch--sad .ch__arm-r { animation: ch-mope-arm-r 3.4s ease-in-out infinite; }

/* The left arm sits behind the torso in the rig, which is right everywhere
   else and wrong here: an arm folded across the chest has to be in front of
   it. The rig writes z-index inline, so the override has to shout. */
.ch--sad .ch__arm-l,
.ch--sad-dance .ch__arm-l { z-index: 4 !important; }

/* The legs stand off square too. Standing to attention under folded arms
   looks defiant; an uneven stance looks like the fight has gone out of it. */
.ch--sad .ch__leg-l,
.ch--sad-dance .ch__leg-l { transform: rotate(4deg); }
.ch--sad .ch__leg-r,
.ch--sad-dance .ch__leg-r { transform: rotate(-8deg); }

/* Leaning off one hip rather than standing square. Negative here is a lean
   towards the front, which reads as slumping rather than recoiling. */
@keyframes ch-mope-body {
  0%, 100% { transform: translateY(3px) rotate(-6deg); }
  50%      { transform: translateY(5px) rotate(-8deg); }
}
@keyframes ch-mope-head {
  0%, 100% { transform: rotate(-9deg) translateY(4px); }
  50%      { transform: rotate(-13deg) translateY(6px); }
}
/* The two arms cross at slightly different angles, so one folds over the
   other instead of the pair lying on top of each other. */
@keyframes ch-mope-arm-l {
  0%, 100% { transform: rotate(-54deg); }
  50%      { transform: rotate(-49deg); }
}
@keyframes ch-mope-arm-r {
  0%, 100% { transform: rotate(52deg); }
  50%      { transform: rotate(47deg); }
}

/* ===== dive - goalkeeper only, full-body tilt toward the ball side =====
   --dive-dir is +1 toward the far touchline, -1 toward the near one. */
.ch--dive .ch__body  { animation: ch-dive 0.42s cubic-bezier(0.25, 0.9, 0.3, 1) forwards; }
.ch--dive .ch__arm-l { transform: rotate(150deg); }
.ch--dive .ch__arm-r { transform: rotate(-150deg); }
@keyframes ch-dive {
  from { transform: rotate(0deg) translateY(0); }
  to   { transform: rotate(calc(var(--dive-dir, 1) * -72deg)) translateY(-14px); }
}

/* ===== tackle - the reach =====
   A lean in and a hand down for the ball, not a shoulder into the body. The
   knockback that follows is what makes possession move, but it does not have
   to look like a collision. */
.ch--tackle .ch__body  { animation: ch-reach-body 0.3s ease-out; }
.ch--tackle .ch__arm-r { animation: ch-reach-arm 0.3s ease-out; }
.ch--tackle .ch__arm-l { animation: ch-reach-arm-back 0.3s ease-out; }
.ch--tackle .ch__leg-r { animation: ch-reach-step 0.3s ease-out; }
@keyframes ch-reach-body {
  0%, 100% { transform: rotate(0deg) translateX(0); }
  40%      { transform: rotate(10deg) translateX(4px); }
}
@keyframes ch-reach-arm      { 0%, 100% { transform: rotate(0deg); } 40% { transform: rotate(-54deg); } }
@keyframes ch-reach-arm-back { 0%, 100% { transform: rotate(0deg); } 40% { transform: rotate(26deg); } }
@keyframes ch-reach-step     { 0%, 100% { transform: rotate(0deg); } 40% { transform: rotate(-20deg); } }

/* ===== stumble - spun round, not knocked down =====
   A flat paper doll cannot turn in 3D, but running scaleX through zero to -1
   and back reads exactly as a pirouette, and it keeps the player on their
   feet. Nobody gets floored. Runs for the length of the stun, so the pose and
   the rule agree about how long they cannot steer. */
.ch--stumble .ch__body  { animation: ch-spin-body 0.4s ease-in-out; }
.ch--stumble .ch__arm-l { animation: ch-spin-arm-l 0.4s ease-out; }
.ch--stumble .ch__arm-r { animation: ch-spin-arm-r 0.4s ease-out; }
.ch--stumble .ch__leg-l { animation: ch-spin-leg 0.4s ease-out; }
.ch--stumble .ch__leg-r { animation: ch-spin-leg 0.4s ease-out; }
@keyframes ch-spin-body {
  0%   { transform: scaleX(1) rotate(0deg) translateY(0); }
  25%  { transform: scaleX(-0.35) rotate(-6deg) translateY(-7px); }
  50%  { transform: scaleX(-1) rotate(0deg) translateY(-9px); }
  75%  { transform: scaleX(0.35) rotate(6deg) translateY(-4px); }
  100% { transform: scaleX(1) rotate(0deg) translateY(0); }
}
@keyframes ch-spin-arm-l { 0%, 100% { transform: rotate(0deg); } 30%, 70% { transform: rotate(88deg); } }
@keyframes ch-spin-arm-r { 0%, 100% { transform: rotate(0deg); } 30%, 70% { transform: rotate(-88deg); } }
@keyframes ch-spin-leg   { 0%, 100% { transform: rotate(0deg); } 40% { transform: rotate(15deg); } }

/* Motion sensitivity: keep the game readable, drop the perpetual motion. */
@media (prefers-reduced-motion: reduce) {
  .ch--idle .ch__body,
  .ch--sad .ch__body { animation: none; }
}

/* ===== "this one is you" =====
   A bobbing chevron over the player the human is driving. Auto-switch moves
   control around constantly, so without this the kid cannot tell who they are. */
.ch__marker {
  position: absolute;
  left: 50%; top: -14px;
  width: 0; height: 0;
  margin-left: -9px;
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-top: 12px solid var(--team-colour, #ffd166);
  filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.6));
  opacity: 0;
  z-index: 10;
}
.ch--mine .ch__marker { opacity: 1; animation: marker-bob 0.9s ease-in-out infinite; }
@keyframes marker-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}
