/* Сцена боя на картинках (static/js/battle/sprite). Все правила живут под
   .stage-sprite или .battle-stage[data-mode="sprite"]: на остальной экран
   файл не влияет. Положение и размеры бойцов задает JS, движения - классы. */

.stage-sprite {
  position: absolute;
  inset: 0;
  overflow: hidden;
  /* visibility, а не display: скрытая сцена сохраняет размер для раскладки. */
  visibility: hidden;
  pointer-events: none;
  /* Слои сцены не перемешиваются с плашками и надписями поверх нее. */
  isolation: isolate;
}

.battle-stage[data-mode="sprite"] .stage-sprite {
  visibility: visible;
}

.battle-stage[data-mode="sprite"] .stage-fallback,
.battle-stage[data-mode="sprite"] .stage-canvas {
  display: none;
}

.stage-sprite .sprite-camera,
.stage-sprite .sprite-backdrop,
.stage-sprite .sprite-particles,
.stage-sprite .sprite-fighters,
.stage-sprite .sprite-fx {
  position: absolute;
  inset: 0;
}

.stage-sprite .sprite-camera {
  transform-origin: 50% 50%;
}

/* Canvas общего рендерера: на экран не выводится, из него копируются кадры. */
.stage-sprite .sprite-gl {
  position: absolute;
  top: 0;
  left: 0;
  width: 2px;
  height: 2px;
  overflow: hidden;
  visibility: hidden;
}

.stage-sprite.is-paused *,
.stage-sprite.is-paused *::before {
  animation-play-state: paused !important;
}

/* --- Локации: палитра фона и постамента, пока нет арта --- */

.stage-sprite {
  --sky-top: #0d1630;
  --sky-low: #27406b;
  --ground-far: #6b5a3c;
  --ground-near: #3d3021;
  --ground-glow: rgba(214, 182, 120, 0.45);
  --pedestal-top: #8d8a84;
  --pedestal-light: #b9b5ad;
  --pedestal-side: #55524d;
}

.stage-sprite.location-stadium {
  --sky-top: #0b1230;
  --sky-low: #2a3f78;
  --ground-far: #7a6544;
  --ground-near: #3f3122;
  --ground-glow: rgba(255, 236, 170, 0.4);
  --pedestal-top: #8d8a84;
  --pedestal-light: #bdb9b0;
  --pedestal-side: #55524d;
}

.stage-sprite.location-meadow {
  --sky-top: #7cc4ee;
  --sky-low: #d9f0e6;
  --ground-far: #7dbd55;
  --ground-near: #3f7f33;
  --ground-glow: rgba(240, 255, 170, 0.4);
  --pedestal-top: #8f9a78;
  --pedestal-light: #b7c49a;
  --pedestal-side: #57623f;
}

.stage-sprite.location-desert {
  --sky-top: #f08a3c;
  --sky-low: #ffd48a;
  --ground-far: #e2b063;
  --ground-near: #a9702f;
  --ground-glow: rgba(255, 230, 160, 0.45);
  --pedestal-top: #d9a860;
  --pedestal-light: #f0c987;
  --pedestal-side: #96652c;
}

.stage-sprite.location-canyon {
  --sky-top: #c9764a;
  --sky-low: #f0c39a;
  --ground-far: #b5573a;
  --ground-near: #6e2f20;
  --ground-glow: rgba(255, 190, 140, 0.35);
  --pedestal-top: #b4563a;
  --pedestal-light: #d98260;
  --pedestal-side: #6b2d1e;
}

.stage-sprite.location-sky {
  --sky-top: #2f8ef0;
  --sky-low: #bfe6ff;
  --ground-far: #e9f1f7;
  --ground-near: #9db4c9;
  --ground-glow: rgba(255, 255, 255, 0.55);
  --pedestal-top: #e8eef4;
  --pedestal-light: #ffffff;
  --pedestal-side: #9fb2c6;
}

.stage-sprite.location-frost {
  --sky-top: #07142e;
  --sky-low: #1f6b7a;
  --ground-far: #a9d8ee;
  --ground-near: #4f86b0;
  --ground-glow: rgba(190, 255, 240, 0.4);
  --pedestal-top: #9fd4f0;
  --pedestal-light: #dff5ff;
  --pedestal-side: #4c82ab;
}

.stage-sprite.location-lava {
  --sky-top: #1a0507;
  --sky-low: #7a1b12;
  --ground-far: #3a1a16;
  --ground-near: #140a0a;
  --ground-glow: rgba(255, 110, 40, 0.5);
  --pedestal-top: #2f2424;
  --pedestal-light: #5a4340;
  --pedestal-side: #120b0b;
}

.stage-sprite .sprite-backdrop {
  background:
    radial-gradient(ellipse 70% 38% at 50% 78%, var(--ground-glow), transparent 70%),
    linear-gradient(180deg, var(--sky-top) 0%, var(--sky-low) 45%, var(--ground-far) 46%, var(--ground-near) 100%);
  background-position: center;
  background-size: cover;
}

.stage-sprite .sprite-backdrop.has-art {
  background-position: center 62%;
  background-repeat: no-repeat;
  background-size: cover;
}

/* Северное сияние и жар лавы - вторым слоем поверх палитры. */
.stage-sprite.location-frost .sprite-backdrop:not(.has-art)::before,
.stage-sprite.location-lava .sprite-backdrop:not(.has-art)::before {
  position: absolute;
  inset: 0;
  content: "";
}

.stage-sprite.location-frost .sprite-backdrop:not(.has-art)::before {
  background: linear-gradient(100deg, transparent 8%, rgba(90, 255, 190, 0.28) 30%, transparent 46%, rgba(130, 170, 255, 0.24) 66%, transparent 84%);
  bottom: 56%;
}

.stage-sprite.location-lava .sprite-backdrop:not(.has-art)::before {
  background: radial-gradient(ellipse 60% 22% at 30% 100%, rgba(255, 120, 30, 0.6), transparent 70%),
    radial-gradient(ellipse 50% 18% at 82% 58%, rgba(255, 90, 20, 0.45), transparent 70%);
}

.stage-sprite .sprite-pedestal {
  position: absolute;
}

/* Боковина постамента: толщина глыбы под верхней площадкой. */
.stage-sprite .sprite-pedestal-side {
  position: absolute;
  top: 50%;
  right: 0;
  left: 0;
  height: 72%;
  border-radius: 0 0 50% 50% / 0 0 68% 68%;
  background: linear-gradient(180deg, var(--pedestal-side), rgba(0, 0, 0, 0.75));
}

.stage-sprite .sprite-pedestal-top {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(ellipse at 45% 30%, var(--pedestal-light), var(--pedestal-top) 68%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
}

.stage-sprite .sprite-pedestal.has-art {
  background: center / contain no-repeat;
}

.stage-sprite .sprite-pedestal.has-art > div {
  display: none;
}

/* --- Частицы локации --- */

.stage-sprite .sprite-particles {
  overflow: hidden;
}

.stage-sprite .sprite-particle {
  position: absolute;
  animation-delay: var(--delay, 0s);
  animation-duration: var(--span, 8s);
  animation-iteration-count: infinite;
  animation-timing-function: linear;
  transform: scale(var(--size, 1));
}

.stage-sprite .sprite-particle.leaf,
.stage-sprite .sprite-particle.snow {
  top: -6%;
  left: var(--start);
  animation-name: sprite-fall;
}

.stage-sprite .sprite-particle.leaf {
  width: 7px;
  height: 4px;
  border-radius: 70% 0;
  background: #9fd65c;
}

.stage-sprite .sprite-particle.snow {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.85);
}

.stage-sprite .sprite-particle.sand,
.stage-sprite .sprite-particle.dust,
.stage-sprite .sprite-particle.cloud {
  left: -14%;
  animation-name: sprite-drift;
}

.stage-sprite .sprite-particle.sand {
  top: calc(48% + var(--lane) * 0.5);
  width: 14px;
  height: 2px;
  border-radius: 2px;
  background: rgba(255, 236, 190, 0.55);
}

.stage-sprite .sprite-particle.dust {
  top: calc(40% + var(--lane) * 0.55);
  width: 18px;
  height: 8px;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(255, 210, 170, 0.4), transparent 70%);
}

.stage-sprite .sprite-particle.cloud {
  top: calc(var(--lane) * 0.36);
  width: 84px;
  height: 22px;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(255, 255, 255, 0.75), transparent 72%);
  animation-duration: calc(var(--span, 8s) * 3);
}

.stage-sprite .sprite-particle.ember {
  top: 102%;
  left: var(--start);
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: #ffb04a;
  box-shadow: 0 0 5px #ff6a1a;
  animation-name: sprite-rise;
}

@keyframes sprite-fall {
  from { top: -6%; margin-left: 0; }
  to { top: 106%; margin-left: 46px; }
}

@keyframes sprite-drift {
  from { left: -14%; }
  to { left: 108%; }
}

@keyframes sprite-rise {
  0% { top: 102%; margin-left: 0; opacity: 0; }
  15% { opacity: 1; }
  100% { top: 28%; margin-left: -22px; opacity: 0; }
}

/* --- Боец: слои вложены, чтобы движения не мешали друг другу --- */

.stage-sprite .sprite-fighter {
  position: absolute;
  --ground-x: 50%;
  --ground-y: 96%;
  --dir-x: 1;
  --dir-y: -1;
}

.stage-sprite .sprite-act,
.stage-sprite .sprite-react,
.stage-sprite .sprite-breath,
.stage-sprite .sprite-marks,
.stage-sprite .sprite-shields,
.stage-sprite .sprite-aura {
  position: absolute;
  inset: 0;
}

.stage-sprite .sprite-act,
.stage-sprite .sprite-react,
.stage-sprite .sprite-breath {
  transform-origin: var(--ground-x) var(--ground-y);
  animation-duration: var(--fx-duration, 300ms);
  animation-timing-function: ease-out;
}

/* Canvas шире кадра на поле: деформация и хвост не обрезаются. */
.stage-sprite .sprite-frame {
  position: absolute;
  top: -6%;
  left: -6%;
  display: block;
  width: 112%;
  height: 112%;
}

.stage-sprite .sprite-shadow {
  position: absolute;
  top: calc(var(--ground-y) - 5%);
  left: calc(var(--ground-x) - 40%);
  width: 80%;
  height: 10%;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0) 70%);
}

/* Дыхание без деформации: картинка чуть растет от точки опоры. */
.stage-sprite .sprite-breath {
  animation: sprite-breath 3.4s ease-in-out infinite;
}

.stage-sprite .sprite-fighter.enemy .sprite-breath {
  animation-delay: -1.2s;
}

.stage-sprite .sprite-fighter.is-lowhp .sprite-breath {
  animation: sprite-pant 1.5s ease-in-out infinite;
}

/* С деформацией дышит сетка; от усталости остается только осанка. */
.stage-sprite .sprite-fighter.is-warp .sprite-breath,
.stage-sprite.is-calm .sprite-breath,
.stage-sprite .sprite-fighter.is-knocked .sprite-breath {
  animation: none;
}

.stage-sprite .sprite-fighter.is-warp.is-lowhp .sprite-breath,
.stage-sprite.is-calm .sprite-fighter.is-lowhp .sprite-breath {
  transform: translateY(1.5%) scale(1.01, 0.975);
}

@keyframes sprite-breath {
  0%, 100% { transform: scale(1, 1); }
  50% { transform: scale(0.994, 1.018); }
}

@keyframes sprite-pant {
  0%, 100% { transform: translateY(1.5%) scale(1.01, 0.975); }
  50% { transform: translateY(1.5%) scale(0.99, 1.01); }
}

/* --- Движения атакующего (.sprite-act) --- */

.stage-sprite .sprite-act.fx-windup {
  animation-name: sprite-windup;
  animation-fill-mode: forwards;
}

.stage-sprite .sprite-act.fx-lunge {
  animation-name: sprite-lunge;
  animation-fill-mode: forwards;
  animation-timing-function: cubic-bezier(0.2, 0.9, 0.3, 1);
}

.stage-sprite .sprite-act.fx-retreat {
  animation-name: sprite-retreat;
}

.stage-sprite .sprite-act.fx-growl {
  animation-name: sprite-growl;
}

.stage-sprite .sprite-act.fx-rest {
  animation-name: sprite-rest;
  animation-timing-function: ease-in-out;
}

.stage-sprite .sprite-act.fx-hop {
  animation-name: sprite-hop;
}

@keyframes sprite-windup {
  to { transform: translate(calc(var(--dir-x) * -5%), calc(var(--dir-y) * -3%)) scale(1.03, 0.96); }
}

@keyframes sprite-lunge {
  from { transform: translate(calc(var(--dir-x) * -5%), calc(var(--dir-y) * -3%)) scale(1.03, 0.96); }
  to { transform: translate(calc(var(--dir-x) * 10%), calc(var(--dir-y) * 6%)) scale(0.98, 1.03); }
}

@keyframes sprite-retreat {
  from { transform: translate(calc(var(--dir-x) * 10%), calc(var(--dir-y) * 6%)) scale(0.98, 1.03); }
  to { transform: none; }
}

@keyframes sprite-growl {
  0%, 100% { transform: none; }
  18%, 52% { transform: translate(calc(var(--dir-x) * 5%), calc(var(--dir-y) * 3%)) scale(1.05); }
  35%, 70% { transform: translate(calc(var(--dir-x) * 2%), calc(var(--dir-y) * 1%)) scale(1.01); }
}

@keyframes sprite-rest {
  0%, 100% { transform: none; }
  45% { transform: scale(1.03, 0.94); }
}

@keyframes sprite-hop {
  0%, 100% { transform: none; }
  28% { transform: translateY(-12%); }
  52% { transform: scale(1.04, 0.95); }
  74% { transform: translateY(-6%); }
}

/* --- Реакции защищающегося (.sprite-react) --- */

.stage-sprite .sprite-react.fx-hurt {
  animation-name: sprite-hurt;
}

.stage-sprite .sprite-react.fx-hurt-crit {
  animation-name: sprite-hurt-crit;
}

.stage-sprite .sprite-react.fx-brace {
  animation-name: sprite-brace;
}

.stage-sprite .sprite-react.fx-flinch {
  animation-name: sprite-flinch;
}

.stage-sprite .sprite-react.fx-dodge {
  animation-name: sprite-dodge;
  animation-timing-function: ease-in-out;
}

.stage-sprite .sprite-react.fx-stagger {
  animation-name: sprite-stagger;
  animation-timing-function: ease-in-out;
}

.stage-sprite .sprite-react.fx-hurt .sprite-frame,
.stage-sprite .sprite-react.fx-hurt-crit .sprite-frame {
  animation: sprite-sting var(--fx-duration, 450ms) ease-out;
}

@keyframes sprite-hurt {
  0%, 100% { transform: none; }
  14% { transform: translate(calc(var(--dir-x) * -6%), calc(var(--dir-y) * -4%)) rotate(calc(var(--dir-x) * -3deg)); }
  38% { transform: translate(calc(var(--dir-x) * 2%), calc(var(--dir-y) * 1%)); }
  62% { transform: translate(calc(var(--dir-x) * -2%), calc(var(--dir-y) * -1%)); }
}

@keyframes sprite-hurt-crit {
  0%, 100% { transform: none; }
  12% { transform: translate(calc(var(--dir-x) * -10%), calc(var(--dir-y) * -6%)) rotate(calc(var(--dir-x) * -6deg)); }
  30% { transform: translate(calc(var(--dir-x) * 4%), calc(var(--dir-y) * 2%)); }
  48% { transform: translate(calc(var(--dir-x) * -5%), calc(var(--dir-y) * -3%)); }
  70% { transform: translate(calc(var(--dir-x) * 2%), calc(var(--dir-y) * 1%)); }
}

@keyframes sprite-brace {
  0%, 100% { transform: none; }
  25% { transform: translate(calc(var(--dir-x) * -2.5%), calc(var(--dir-y) * -1.5%)) scale(1.02, 0.98); }
}

@keyframes sprite-flinch {
  0%, 100% { transform: none; }
  30% { transform: translate(calc(var(--dir-x) * -1.5%), 0) scale(0.99, 1.01); }
}

/* Шаг в сторону от линии атаки. */
@keyframes sprite-dodge {
  0%, 100% { transform: none; }
  30%, 62% { transform: translate(calc(var(--dir-x) * -15%), calc(var(--dir-y) * 8%)); }
}

@keyframes sprite-stagger {
  0%, 100% { transform: none; }
  25% { transform: rotate(4deg); }
  55% { transform: rotate(-5deg) translateY(1%); }
  80% { transform: rotate(3deg); }
}

@keyframes sprite-sting {
  0% { filter: brightness(1.9) saturate(1.4); }
  100% { filter: none; }
}

/* --- Состояния: нокаут и позы итога остаются после отмены показа --- */

/* Падают к центру сцены: так лежащая собака не уходит за край. */
.stage-sprite .sprite-fighter.is-knocked .sprite-react {
  transform: rotate(calc(var(--dir-x) * 74deg)) scale(0.9);
  transition: transform var(--state-duration, 0ms) cubic-bezier(0.5, 0, 0.9, 0.6);
}

.stage-sprite .sprite-fighter.is-knocked .sprite-frame {
  filter: brightness(0.55) saturate(0.6);
  transition: filter var(--state-duration, 0ms) ease-in;
}

.stage-sprite .sprite-fighter.is-knocked .sprite-shadow {
  opacity: 0.5;
}

.stage-sprite .sprite-fighter.is-victory .sprite-act {
  transform: scale(1.05);
  transition: transform 300ms ease-out;
}

.stage-sprite .sprite-fighter.is-defeat .sprite-act {
  transform: translateY(1.5%) scale(1.02, 0.93);
  transition: transform var(--state-duration, 0ms) ease-in;
}

.stage-sprite .sprite-fighter.is-defeat .sprite-frame {
  filter: brightness(0.8) saturate(0.8);
}

/* --- Щиты, следы, аура --- */

.stage-sprite .sprite-shield {
  position: absolute;
  opacity: 0;
  transition: opacity 180ms ease-out;
}

.stage-sprite .sprite-shield.is-flared {
  animation: sprite-shield-pulse 900ms ease-in-out infinite;
}

.stage-sprite .sprite-shield.is-struck {
  animation: sprite-shield-struck 380ms ease-out;
}

@keyframes sprite-shield-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.04); }
}

@keyframes sprite-shield-struck {
  0% { filter: brightness(2.2); transform: scale(1.12); }
  100% { filter: none; transform: scale(1); }
}

.stage-sprite .sprite-mark {
  position: absolute;
  width: 15%;
  min-width: 12px;
  aspect-ratio: 40 / 28;
  transform: translate(-50%, -50%) rotate(var(--turn, 0deg));
}

.stage-sprite .sprite-mark.deep {
  width: 19%;
  min-width: 15px;
}

.stage-sprite .sprite-mark svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.stage-sprite .sprite-mark ellipse {
  fill: rgba(72, 22, 18, 0.74);
  stroke: rgba(255, 214, 196, 0.4);
  stroke-width: 0.8;
}

.stage-sprite .sprite-mark.is-new {
  animation: sprite-mark-pop 320ms ease-out;
}

@keyframes sprite-mark-pop {
  from { opacity: 0; transform: translate(-50%, -50%) rotate(var(--turn, 0deg)) scale(1.8); }
  to { opacity: 1; transform: translate(-50%, -50%) rotate(var(--turn, 0deg)) scale(1); }
}

.stage-sprite .sprite-aura {
  background: radial-gradient(ellipse 60% 54% at var(--ground-x) 56%, rgba(225, 255, 245, 0.8), rgba(170, 245, 225, 0.35) 45%, rgba(170, 245, 225, 0) 72%);
  opacity: 0;
  transition: opacity 260ms ease-out;
}

.stage-sprite .sprite-aura.is-on {
  opacity: 1;
  animation: sprite-aura 1100ms ease-in-out infinite;
}

@keyframes sprite-aura {
  0%, 100% { transform: scale(0.96); }
  50% { transform: scale(1.06); }
}

/* --- Молния --- */

.stage-sprite .sprite-bolts {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.stage-sprite .sprite-bolt {
  opacity: 1;
  transition: opacity 200ms ease-out;
}

.stage-sprite .sprite-bolt.is-fading {
  opacity: 0;
}

.stage-sprite .sprite-bolt.is-charge circle {
  transform-box: fill-box;
  transform-origin: center;
  animation: sprite-charge var(--fx-duration, 400ms) ease-in forwards;
}

@keyframes sprite-charge {
  from { opacity: 0.2; transform: scale(0.3); }
  to { opacity: 1; transform: scale(1.5); }
}

/* --- Эффекты контакта --- */

.stage-sprite .sprite-spark {
  position: absolute;
  width: 4px;
  height: 4px;
  margin: -2px;
  border-radius: 50%;
  background: var(--spark, #ffffff);
  box-shadow: 0 0 6px var(--spark, #ffffff);
  animation: sprite-spark 500ms ease-out forwards;
}

@keyframes sprite-spark {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translate(var(--dx), var(--dy)) scale(0.3); }
}

.stage-sprite .sprite-flash {
  position: absolute;
  width: 46px;
  height: 46px;
  margin: -23px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.95), rgba(255, 240, 170, 0.6) 40%, rgba(255, 200, 80, 0) 70%);
  animation: sprite-flash 400ms ease-out forwards;
}

.stage-sprite .sprite-flash.crit {
  width: 64px;
  height: 64px;
  margin: -32px;
  background: radial-gradient(circle, #ffffff, rgba(255, 214, 90, 0.85) 38%, rgba(255, 120, 40, 0) 72%);
}

.stage-sprite .sprite-flash.block {
  background: radial-gradient(circle, rgba(255, 255, 255, 0) 38%, rgba(235, 248, 255, 0.95) 50%, rgba(150, 215, 255, 0) 70%);
}

.stage-sprite .sprite-flash.armor {
  background: radial-gradient(circle, rgba(255, 245, 220, 0.95), rgba(255, 170, 60, 0.7) 42%, rgba(255, 130, 30, 0) 70%);
}

.stage-sprite .sprite-flash.fizzle {
  width: 34px;
  height: 34px;
  margin: -17px;
  background: radial-gradient(circle, rgba(230, 235, 245, 0.85), rgba(160, 170, 190, 0.4) 45%, rgba(120, 130, 150, 0) 70%);
}

@keyframes sprite-flash {
  from { opacity: 1; transform: scale(calc(var(--flash-scale, 1) * 0.35)); }
  to { opacity: 0; transform: scale(calc(var(--flash-scale, 1) * 1.5)); }
}

.stage-sprite .sprite-dust {
  position: absolute;
  width: 22px;
  height: 14px;
  margin: -7px -11px;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(240, 225, 200, 0.75), rgba(240, 225, 200, 0) 70%);
  animation: sprite-dust 760ms ease-out forwards;
}

@keyframes sprite-dust {
  from { opacity: 0.9; transform: scale(0.5); }
  to { opacity: 0; transform: translate(var(--dx), var(--dy)) scale(1.7); }
}

/* Кольца рыка идут от пасти к сопернику. */
.stage-sprite .sprite-ring {
  position: absolute;
  width: 26px;
  height: 26px;
  margin: -13px;
  border: 3px solid rgba(120, 205, 255, 0.85);
  border-radius: 50%;
  animation: sprite-ring 880ms ease-out forwards;
}

.stage-sprite .sprite-ring.enemy {
  border-color: rgba(255, 140, 120, 0.85);
}

@keyframes sprite-ring {
  from { opacity: 0.95; transform: scale(0.3); }
  to { opacity: 0; transform: translate(var(--dx), var(--dy)) scale(2.4); }
}

.stage-sprite .sprite-puff {
  position: absolute;
  width: 20px;
  height: 14px;
  margin: -7px -10px;
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0) 70%);
  animation: sprite-puff 880ms ease-out forwards;
}

@keyframes sprite-puff {
  from { opacity: 0.9; transform: scale(0.4); }
  to { opacity: 0; transform: translate(var(--dx), -22px) scale(1.8); }
}

/* --- Камера --- */

.stage-sprite .sprite-camera.fx-focus {
  animation: sprite-focus var(--fx-duration, 2400ms) ease-in-out;
}

.stage-sprite .sprite-camera.fx-shake {
  animation: sprite-shake 340ms linear;
}

.stage-sprite .sprite-camera.fx-thud {
  animation: sprite-thud 280ms ease-out;
}

@keyframes sprite-focus {
  0%, 100% { transform: none; }
  30%, 75% { transform: scale(1.12); }
}

@keyframes sprite-shake {
  0%, 100% { transform: none; }
  20% { transform: translate(-3px, 2px); }
  40% { transform: translate(3px, -2px); }
  60% { transform: translate(-2px, -1px); }
  80% { transform: translate(2px, 1px); }
}

@keyframes sprite-thud {
  0%, 100% { transform: none; }
  30% { transform: translateY(3px); }
}

/* Системное "уменьшение движения": тот же режим, что is-calm из JS. */
@media (prefers-reduced-motion: reduce) {
  .stage-sprite .sprite-breath,
  .stage-sprite .sprite-particle,
  .stage-sprite .sprite-shield.is-flared,
  .stage-sprite .sprite-camera {
    animation: none;
  }
}
