/* Combat battlefield VFX — episodic layer + unit presentation/ambient (see combat-effects pack) */

.combat-unit {
  --combat-unit-opacity: 1;
  opacity: var(--combat-unit-opacity);
}

/* Ambient / particle band: between feet-only and full-tile overshoot (sprite sits low in cell). */
.combat-cell__tile .combat-unit {
  --combat-fx-band-top: 24%;
  --combat-fx-band-bottom: 11%;
  --combat-fx-band-inset-x: 10%;
  /* Head anchor within portrait-fx band (idle sprite uses background-position 0 100%). */
  --combat-fx-head-y: 14%;
  /* ~upper chest at peak; scales with tile */
  --combat-fx-rise: calc(var(--combat-tile-size, 72px) * 0.48);
  --combat-unit-skill-icon-size: calc(var(--combat-tile-size, 72px) * 0.18);
  --combat-fx-orbit-scale: calc(var(--combat-tile-size, 72px) / 72px);
}

.combat-cell__tile .combat-unit__presentation,
.combat-cell__tile .combat-unit__ambient {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  overflow: visible;
}

/* Board unit ability/status icons (same WebP set as hand cards). */
.combat-unit__ability-icons,
.combat-unit__status-icons {
  pointer-events: none;
  z-index: 8;
}

/* Foot strip: ATK (left) + ability icons — below the portrait. */
.combat-unit__foot {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: 3px;
  max-width: 98%;
  z-index: 8;
  pointer-events: none;
}

.combat-unit__atk {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 2px;
  pointer-events: none;
  font-size: calc(var(--combat-unit-skill-icon-size) * 0.85);
  line-height: 1;
  color: var(--gs-text-stat, #d8c8a8);
}

.combat-unit__atk .combat-stat-damage-type-icon {
  width: var(--combat-unit-skill-icon-size);
  height: var(--combat-unit-skill-icon-size);
  object-fit: contain;
}

.combat-unit__atk-value {
  font-size: inherit;
}

/* Board skill/status icon bubbles — stage-anchored tooltips (skillIconCommon.attachSkillTooltip). */
.combat-unit__skill-icon[data-stage-skill-tooltip] {
  position: relative;
  overflow: visible;
}

/* Icon hosts receive pointer events; empty strip gaps pass clicks to the cell. */
.combat-unit__ability-icons .game-card__skill-icon.combat-unit__skill-icon,
.combat-unit__status-icons .game-card__skill-icon.combat-unit__skill-icon {
  pointer-events: auto;
  cursor: help;
}

.combat-unit__ability-icons {
  position: relative;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: 2px;
  max-width: 100%;
}

.combat-unit__status-icons {
  position: absolute;
  left: 50%;
  bottom: calc(var(--combat-unit-skill-icon-size) + 3px);
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  --combat-status-icon-size: var(--combat-unit-skill-icon-size);
}

/*
 * Anchor ability/status chrome at the bottom of the tile so grounded sprite strips
 * stay readable. Status sits above the ATK + ability foot row.
 */
.combat-unit:has(.combat-unit__foot:not(:empty)) .combat-unit__status-icons {
  bottom: calc(var(--combat-unit-skill-icon-size) + 3px);
}

.combat-unit__status-row {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: center;
  align-items: center;
  width: calc(5 * var(--combat-status-icon-size));
}

.combat-unit__status-row--overlap {
  margin-top: calc(var(--combat-status-icon-size) * -0.3);
  z-index: 1;
}

/* Board unit skill/status bubbles (shared chrome; borders set per strip below). */
.combat-unit .game-card__skill-icon.combat-unit__skill-icon {
  --skill-icon-master-size: 64;
  --skill-icon-art-inset: 3;
  --combat-skill-icon-size: var(--combat-unit-skill-icon-size);
  position: relative;
  box-sizing: border-box;
  width: var(--combat-skill-icon-size);
  height: var(--combat-skill-icon-size);
  filter: saturate(0.5);
  flex: 0 0 auto;
  aspect-ratio: 1;
  border-radius: 50%;
  border: none;
  background: rgba(10, 8, 18, 0.92);
  overflow: visible;
}

.combat-unit__ability-icons .game-card__skill-icon.combat-unit__skill-icon {
  border: 1px solid rgba(255, 210, 150, 0.55);
  box-shadow:
    inset 0 0 0 1px rgba(255, 240, 210, 0.12),
    0 0 0 1px rgba(0, 0, 0, 0.35),
    0 1px 3px rgba(0, 0, 0, 0.45);
}

.combat-unit__ability-icons .game-card__skill-icon--overflow .game-card__skill-icon-overflow {
  font-size: calc(var(--combat-skill-icon-size) * 0.5);
}

.combat-unit__status-icons .game-card__skill-icon.combat-unit__skill-icon {
  --combat-skill-icon-size: var(--combat-status-icon-size);
  border: 0.5px solid rgba(160, 160, 180, 0.55);
  box-shadow:
    inset 0 0 0 0.5px rgba(255, 240, 210, 0.12),
    0 0 0 0.5px rgba(0, 0, 0, 0.35),
    0 1px 2px rgba(0, 0, 0, 0.4);
}

.combat-unit__status-icons .game-card__skill-icon-clip {
  inset: 0.5px;
}

.combat-unit .game-card__skill-icon-clip {
  /* Inset so art does not paint over the outer status/ability ring. */
  position: absolute;
  inset: 1px;
  border-radius: 50%;
  overflow: hidden;
  pointer-events: none;
}

.combat-unit .game-card__skill-icon-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
  transform: scale(
    calc(
      var(--skill-icon-master-size) /
        (var(--skill-icon-master-size) - 2 * var(--skill-icon-art-inset))
    )
  );
  transform-origin: center center;
  pointer-events: none;
}

.combat-unit .game-card__skill-icon-badge {
  position: absolute;
  z-index: 1;
  right: calc(var(--combat-skill-icon-size) * -0.22);
  bottom: calc(var(--combat-skill-icon-size) * -0.22);
  box-sizing: border-box;
  width: calc(var(--combat-skill-icon-size) * 0.52);
  height: calc(var(--combat-skill-icon-size) * 0.52);
  min-width: 0;
  padding: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(var(--combat-skill-icon-size) * 0.34);
  line-height: 1;
  font-weight: 700;
  color: #f4e8c8;
  background: rgba(6, 6, 12, 0.92);
  border: 0.5px solid rgba(255, 220, 170, 0.35);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
  pointer-events: none;
}

/* Status row: green = buff, red = debuff (matches hand cards). */
.combat-unit__status-icons .game-card__skill-icon.combat-unit__skill-icon.game-card__skill-icon--status-positive {
  border: 0.5px solid rgba(72, 180, 90, 0.95);
  box-shadow:
    inset 0 0 0 0.5px rgba(120, 220, 130, 0.35),
    0 0 0 0.5px rgba(72, 180, 90, 0.9),
    0 1px 2px rgba(0, 0, 0, 0.4);
}

.combat-unit__status-icons .game-card__skill-icon.combat-unit__skill-icon.game-card__skill-icon--status-negative {
  border: 0.5px solid rgba(200, 58, 58, 0.95);
  box-shadow:
    inset 0 0 0 0.5px rgba(255, 120, 120, 0.3),
    0 0 0 0.5px rgba(200, 58, 58, 0.9),
    0 1px 2px rgba(0, 0, 0, 0.4);
}

.combat-fx-layer {
  overflow: visible;
}

/* #combat-fx-layer sits above the grid; episodic children use z-index 31 within the layer.
   Board units/icons may reach z-index 6 — the layer itself must stay above the grid (see combat.css z-index 30). */

/* Arena grid places gutters + board in columns; span the whole wrap so coords match cells. */
.combat-root--arena .combat-fx-layer {
  grid-column: 1 / -1;
  grid-row: 1;
  z-index: 40;
}

/* Control CC — portrait/sprite only (not whole tile chrome).
   Append --combat-unit-contact-shadow so surround blend survives FX filter replaces. */
.combat-unit--fx-petrified .combat-unit__icon,
.combat-unit--fx-petrified .combat-unit__sprite-idle {
  filter: grayscale(1) saturate(0) contrast(1.1) brightness(0.9)
    var(--combat-unit-contact-shadow, );
}

/* Frozen: blue-white crystal on portrait/sprite only (same scope as petrified — not tile chrome). */
.combat-unit--fx-frozen .combat-unit__icon,
.combat-unit--fx-frozen .combat-unit__sprite-idle {
  filter: brightness(1.14) saturate(1.45) hue-rotate(186deg) contrast(1.06)
    drop-shadow(0 0 2px rgba(220, 245, 255, 0.95))
    drop-shadow(0 0 7px rgba(130, 200, 255, 0.5))
    var(--combat-unit-contact-shadow, );
}

.combat-unit--fx-frozen .combat-unit__sprite-idle {
  position: relative;
}

:is(.combat-unit--fx-petrified, .combat-unit--fx-frozen, .combat-unit--fx-paralyzed)
  .combat-unit__sprite-idle {
  animation-play-state: paused !important;
}

/* Portrait-scoped overlays (creature band — not full tile / status-icon chrome). */
.combat-cell__tile .combat-unit__scale > .combat-unit__portrait-fx {
  position: absolute;
  left: var(--combat-fx-band-inset-x, 10%);
  right: var(--combat-fx-band-inset-x, 10%);
  top: var(--combat-fx-band-top, 24%);
  bottom: var(--combat-fx-band-bottom, 11%);
  display: block;
  z-index: 5;
  pointer-events: none;
  overflow: visible;
}

.combat-unit__portrait-fx .combat-unit__frozen-crystal {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(118deg, transparent 36%, rgba(255, 255, 255, 0.55) 44%, transparent 52%),
    linear-gradient(208deg, transparent 50%, rgba(195, 235, 255, 0.42) 56%, transparent 62%),
    linear-gradient(
      155deg,
      rgba(235, 250, 255, 0.32) 0%,
      rgba(160, 220, 255, 0.16) 42%,
      rgba(255, 255, 255, 0.28) 52%,
      rgba(120, 195, 255, 0.12) 100%
    );
  mix-blend-mode: screen;
  opacity: 0.88;
}

.combat-unit--fx-paralyzed .combat-unit__icon,
.combat-unit--fx-paralyzed .combat-unit__sprite-idle {
  filter: sepia(0.35) saturate(1.35) hue-rotate(248deg) brightness(0.88) contrast(1.02)
    var(--combat-unit-contact-shadow, );
}

/* Head anchor (horizontal); stun orbits the crown, cursed sits above it. */
.combat-portrait-fx__cursed-sigil,
.combat-portrait-fx__stun-daze {
  position: absolute;
  left: 50%;
  top: var(--combat-fx-head-y, 14%);
  pointer-events: none;
}

.combat-portrait-fx__stun-daze {
  --stun-orbit-radius: 10px;
  width: 52%;
  max-width: 26px;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
}

.combat-portrait-fx__cursed-sigil {
  --combat-fx-cursed-gap: 4px;
  width: 44%;
  max-width: 26px;
  aspect-ratio: 1;
  z-index: 2;
  /* Bottom edge at crown — full sigil above the face (not centered on it). */
  transform: translate(-50%, calc(-100% - var(--combat-fx-cursed-gap, 4px)));
  transform-style: preserve-3d;
  perspective: 120px;
  opacity: 0.92;
}

.combat-portrait-fx__stun-daze-orbit {
  position: absolute;
  inset: 0;
  transform-origin: 50% 50%;
  animation: combat-portrait-fx-stun-spin 3.4s linear infinite;
}

.combat-portrait-fx__stun-star {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  background: rgba(255, 245, 160, 0.95);
  clip-path: polygon(
    50% 0%,
    61% 35%,
    98% 35%,
    68% 57%,
    79% 91%,
    50% 70%,
    21% 91%,
    32% 57%,
    2% 35%,
    39% 35%
  );
  transform: rotate(calc(var(--stun-star-i, 0) * 72deg)) translateY(calc(-1 * var(--stun-orbit-radius, 10px)));
  transform-origin: 50% 50%;
  opacity: 0.9;
  animation: combat-portrait-fx-stun-star-pulse 1.55s ease-in-out infinite;
  animation-delay: calc(var(--stun-star-i, 0) * 0.18s);
}

@keyframes combat-portrait-fx-stun-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@keyframes combat-portrait-fx-stun-star-pulse {
  0%,
  100% {
    opacity: 0.55;
    transform: rotate(calc(var(--stun-star-i, 0) * 72deg))
      translateY(calc(-1 * var(--stun-orbit-radius, 10px))) scale(0.88);
  }
  50% {
    opacity: 1;
    transform: rotate(calc(var(--stun-star-i, 0) * 72deg))
      translateY(calc(-1 * var(--stun-orbit-radius, 10px))) scale(1.05);
  }
}

.combat-unit--fx-control-feared .combat-unit__icon,
.combat-unit--fx-control-feared .combat-unit__sprite-idle {
  filter: brightness(0.88) saturate(0.75) var(--combat-unit-contact-shadow, );
}

/* Feared (and similar) may still use the presentation shell border — not debuff badges. */
.combat-unit__presentation--active::after {
  content: "";
  position: absolute;
  left: var(--combat-fx-band-inset-x, 10%);
  right: var(--combat-fx-band-inset-x, 10%);
  top: var(--combat-fx-band-top, 24%);
  bottom: var(--combat-fx-band-bottom, 11%);
  border-radius: 12%;
  border: 2px solid rgba(200, 220, 255, 0.35);
  box-shadow: inset 0 0 12px rgba(120, 180, 255, 0.25);
  pointer-events: none;
}

.combat-unit--fx-disease-tint .combat-unit__icon,
.combat-unit--fx-disease-tint .combat-unit__sprite-idle {
  filter: sepia(0.22) saturate(0.72) hue-rotate(64deg) brightness(0.9) contrast(1.02)
    var(--combat-unit-contact-shadow, );
}

.combat-portrait-fx__cursed-sigil-svg {
  display: block;
  width: 100%;
  height: 100%;
  animation: combat-portrait-fx-cursed-spin 3.4s linear infinite;
  transform-origin: 50% 52%;
}

@keyframes combat-portrait-fx-cursed-spin {
  from {
    transform: rotateX(58deg) rotateZ(0deg);
  }
  to {
    transform: rotateX(58deg) rotateZ(360deg);
  }
}

/* Soul hit: brief purple hue on portrait/icon only (not the whole cell). */
.combat-unit__icon,
.combat-unit__sprite-idle {
  position: relative;
}

.combat-portrait--fx-soul-hit {
  animation: combat-soul-portrait-hit 0.68s ease-out forwards;
}

@keyframes combat-soul-portrait-hit {
  0%,
  100% {
    filter: none;
  }
  18% {
    filter: hue-rotate(252deg) saturate(1.55) brightness(1.08) contrast(1.05);
  }
  42% {
    filter: hue-rotate(248deg) saturate(1.35) brightness(1.04) contrast(1.04);
  }
  68% {
    filter: hue-rotate(240deg) saturate(1.12) brightness(1.02);
  }
}

/* Poison hit: brief green tint on portrait/icon only. */
.combat-portrait--fx-poison-hit {
  animation: combat-poison-portrait-hit 0.58s ease-out forwards;
}

@keyframes combat-poison-portrait-hit {
  0%,
  100% {
    filter: none;
  }
  14% {
    filter: sepia(0.42) saturate(2.65) hue-rotate(74deg) brightness(1.12);
  }
  34% {
    filter: sepia(0.36) saturate(2.15) hue-rotate(66deg) brightness(1.06);
  }
  58% {
    filter: sepia(0.18) saturate(1.35) hue-rotate(58deg) brightness(1.02);
  }
}

.combat-unit--fx-snare-crippled::after,
.combat-unit--fx-snare-slowed::after {
  content: "";
  position: absolute;
  bottom: 4%;
  left: 50%;
  width: 42%;
  height: 3px;
  transform: translateX(-50%);
  border-radius: 2px;
  background: rgba(160, 120, 60, 0.85);
  z-index: 4;
}

/* Ambient particle loops (rich hosts mounted by ambient_mount.js) */
.combat-ambient {
  position: absolute;
  left: var(--combat-fx-band-inset-x, 10%);
  right: var(--combat-fx-band-inset-x, 10%);
  top: var(--combat-fx-band-top, 24%);
  bottom: var(--combat-fx-band-bottom, 11%);
  width: auto;
  height: auto;
  transform: none;
  pointer-events: none;
}

.combat-ambient--mounted {
  left: var(--combat-fx-band-inset-x, 10%);
  right: var(--combat-fx-band-inset-x, 10%);
  top: var(--combat-fx-band-top, 24%);
  bottom: var(--combat-fx-band-bottom, 11%);
  width: auto;
  height: auto;
  transform: none;
}

/* Burning: flames rise, dim, respawn (staggered cycle per tongue). */
.combat-ambient__fire-bed {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 0;
  height: 0;
}

.combat-ambient__fire-flame {
  --fire-flip: 1;
  position: absolute;
  left: var(--fire-x, 0);
  bottom: 0;
  width: calc(22px * var(--fire-scale, 1));
  height: calc(28px * var(--fire-scale, 1));
  margin-left: calc(-11px * var(--fire-scale, 1));
  transform-origin: 50% 100%;
  opacity: 0;
  pointer-events: none;
  /* Linear = no ease reversal; one shot: spawn → rise+grow → shrink+fade → loop. */
  animation: combat-ambient-fire-rise-cycle var(--fire-rise-cycle, 1.35s) linear
    var(--fire-spawn-delay, 0s) infinite;
}

.combat-ambient__fire-svg {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(var(--fire-flip, 1));
  transform-origin: 50% 100%;
  filter: drop-shadow(0 0 4px rgba(255, 100, 30, 0.55));
}

/* translateY only decreases (moves up); scale up then down; opacity out at end. */
@keyframes combat-ambient-fire-rise-cycle {
  0% {
    opacity: 0;
    transform: translateY(2px) scale(0.34);
  }
  6% {
    opacity: 0.92;
    transform: translateY(0) scale(0.48);
  }
  38% {
    opacity: 1;
    transform: translateY(calc(var(--combat-fx-rise) * -0.38)) scale(0.96);
  }
  62% {
    opacity: 0.78;
    transform: translateY(calc(var(--combat-fx-rise) * -0.68)) scale(0.84);
  }
  100% {
    opacity: 0;
    transform: translateY(calc(var(--combat-fx-rise) * -1)) scale(0.42);
  }
}

/* Poison: bubbles rise through the torso band and fade out. */
.combat-ambient__bubble-field {
  position: absolute;
  inset: 0;
  overflow: visible;
}

.combat-ambient__poison-bubble {
  position: absolute;
  bottom: 0;
  width: var(--bubble-size, 6px);
  height: var(--bubble-size, 6px);
  margin-left: calc(var(--bubble-size, 6px) * -0.5);
  border-radius: 50%;
  /* Hollow bubble: bright rim + highlight, transparent body (not a solid fill). */
  background: radial-gradient(
    circle at 30% 26%,
    rgba(255, 255, 255, 0.52) 0%,
    rgba(200, 255, 190, 0.2) 30%,
    rgba(70, 180, 85, 0.1) 52%,
    transparent 68%
  );
  border: 1px solid rgba(130, 235, 120, 0.48);
  box-shadow:
    inset 1px 1px 2px rgba(255, 255, 255, 0.42),
    inset -1px -2px 3px rgba(25, 90, 40, 0.1),
    0 0 4px rgba(70, 190, 80, 0.22);
  opacity: 0;
  animation: combat-ambient-poison-rise var(--bubble-dur, 2.2s) ease-out var(--bubble-delay, 0s) infinite;
}

/* Rise height matches burning (--combat-fx-rise on .combat-unit); % Y was ~px on the bubble only. */
@keyframes combat-ambient-poison-rise {
  0% {
    opacity: 0;
    transform: translateY(2px) scale(0.55);
  }
  12% {
    opacity: 0.85;
    transform: translateY(0) scale(0.65);
  }
  38% {
    opacity: 0.72;
    transform: translateY(calc(var(--combat-fx-rise) * -0.38)) scale(0.88);
  }
  72% {
    opacity: 0.5;
    transform: translateY(calc(var(--combat-fx-rise) * -0.72)) scale(1);
  }
  100% {
    opacity: 0;
    transform: translateY(calc(var(--combat-fx-rise) * -1)) scale(0.75);
  }
}

/* Regeneration: green–blue motes on asymmetric orbits around the torso. */
.combat-ambient__regen-field {
  position: absolute;
  inset: 0;
  --combat-fx-orbit-scale: calc(var(--combat-tile-size, 72px) / 72px);
}

.combat-ambient__regen-mote {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(200, 255, 240, 0.98) 0%, rgba(80, 200, 180, 0.55) 55%, transparent 72%);
  box-shadow: 0 0 5px rgba(100, 220, 200, 0.55);
  opacity: 0.75;
  animation: combat-ambient-regen-orbit var(--regen-dur, 2.5s) linear var(--regen-delay, 0s) infinite;
}

@keyframes combat-ambient-regen-orbit {
  0% {
    transform: rotate(0deg)
      translate(
        calc(var(--regen-rx, 18px) * var(--combat-fx-orbit-scale, 1)),
        calc(var(--regen-ry, 14px) * var(--combat-fx-orbit-scale, 1))
      )
      rotate(0deg) scale(0.9);
    filter: hue-rotate(var(--regen-hue, 0deg)) saturate(1.2);
  }
  50% {
    transform: rotate(180deg)
      translate(
        calc(var(--regen-rx, 18px) * var(--combat-fx-orbit-scale, 1) * 0.72),
        calc(var(--regen-ry, 14px) * var(--combat-fx-orbit-scale, 1) * -0.85)
      )
      rotate(-180deg) scale(1.12);
    filter: hue-rotate(calc(var(--regen-hue, 0deg) + 35deg)) saturate(1.45);
  }
  100% {
    transform: rotate(360deg)
      translate(
        calc(var(--regen-rx, 18px) * var(--combat-fx-orbit-scale, 1)),
        calc(var(--regen-ry, 14px) * var(--combat-fx-orbit-scale, 1))
      )
      rotate(-360deg) scale(0.9);
    filter: hue-rotate(calc(var(--regen-hue, 0deg) + 70deg)) saturate(1.25);
  }
}

/* Disease: buzzing flies around the torso. */
.combat-ambient__fly-field {
  position: absolute;
  inset: 0;
  --combat-fx-orbit-scale: calc(var(--combat-tile-size, 72px) / 72px);
}

.combat-ambient__fly {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 5px;
  height: 3px;
  margin: -1.5px 0 0 -2.5px;
  border-radius: 50%;
  background: rgba(28, 32, 24, 0.92);
  box-shadow:
    -3px -1px 0 0 rgba(40, 44, 36, 0.55),
    3px -1px 0 0 rgba(40, 44, 36, 0.55);
  opacity: 0.75;
  animation: combat-ambient-fly-buzz var(--fly-dur, 2.5s) ease-in-out var(--fly-delay, 0s) infinite;
}

.combat-ambient--kind-disease .combat-ambient__fly:nth-child(2) {
  animation-name: combat-ambient-fly-buzz-2;
}

.combat-ambient--kind-disease .combat-ambient__fly:nth-child(3) {
  animation-name: combat-ambient-fly-buzz-3;
}

.combat-ambient--kind-disease .combat-ambient__fly:nth-child(4) {
  animation-name: combat-ambient-fly-buzz-4;
}

@keyframes combat-ambient-fly-buzz {
  0%,
  100% {
    transform: translate(
        calc(20px * var(--combat-fx-orbit-scale, 1)),
        calc(10px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(12deg);
    opacity: 0.55;
  }
  25% {
    transform: translate(
        calc(-16px * var(--combat-fx-orbit-scale, 1)),
        calc(-14px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(-18deg);
    opacity: 0.95;
  }
  50% {
    transform: translate(
        calc(-22px * var(--combat-fx-orbit-scale, 1)),
        calc(8px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(8deg);
    opacity: 0.7;
  }
  75% {
    transform: translate(
        calc(12px * var(--combat-fx-orbit-scale, 1)),
        calc(16px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(-6deg);
    opacity: 1;
  }
}

@keyframes combat-ambient-fly-buzz-2 {
  0%,
  100% {
    transform: translate(
        calc(-18px * var(--combat-fx-orbit-scale, 1)),
        calc(-8px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(-10deg);
    opacity: 0.6;
  }
  30% {
    transform: translate(
        calc(22px * var(--combat-fx-orbit-scale, 1)),
        calc(12px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(14deg);
    opacity: 1;
  }
  60% {
    transform: translate(
        calc(10px * var(--combat-fx-orbit-scale, 1)),
        calc(-18px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(-4deg);
    opacity: 0.65;
  }
}

@keyframes combat-ambient-fly-buzz-3 {
  0%,
  100% {
    transform: translate(
        calc(8px * var(--combat-fx-orbit-scale, 1)),
        calc(-20px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(6deg);
    opacity: 0.65;
  }
  35% {
    transform: translate(
        calc(-20px * var(--combat-fx-orbit-scale, 1)),
        calc(10px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(-16deg);
    opacity: 0.95;
  }
  70% {
    transform: translate(
        calc(18px * var(--combat-fx-orbit-scale, 1)),
        calc(18px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(10deg);
    opacity: 0.7;
  }
}

@keyframes combat-ambient-fly-buzz-4 {
  0%,
  100% {
    transform: translate(
        calc(-10px * var(--combat-fx-orbit-scale, 1)),
        calc(18px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(-8deg);
    opacity: 0.7;
  }
  40% {
    transform: translate(
        calc(16px * var(--combat-fx-orbit-scale, 1)),
        calc(-10px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(12deg);
    opacity: 1;
  }
  80% {
    transform: translate(
        calc(-18px * var(--combat-fx-orbit-scale, 1)),
        calc(-14px * var(--combat-fx-orbit-scale, 1))
      )
      rotate(-14deg);
    opacity: 0.6;
  }
}

/* Episodic one-shots on #combat-fx-layer */
.combat-episodic {
  position: absolute;
  pointer-events: none;
  z-index: 31;
}

/* Source → target attack ray (origin at attacker cell center). */
.combat-episodic--ray {
  width: var(--combat-ray-len, 48px);
  height: 0;
  transform: translate(0, -50%) rotate(var(--combat-ray-angle, 0deg));
  transform-origin: 0 50%;
  animation: combat-episodic-ray 0.62s ease-out forwards;
}

.combat-episodic__ray-glow,
.combat-episodic__ray-core,
.combat-episodic__ray-head {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
}

.combat-episodic__ray-glow {
  width: 100%;
  height: 14px;
  border-radius: 999px;
  opacity: 0.55;
  filter: blur(3px);
}

.combat-episodic__ray-core {
  width: 100%;
  height: 5px;
  border-radius: 999px;
  opacity: 0.95;
}

.combat-episodic__ray-head {
  right: 0;
  left: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  transform: translate(50%, -50%);
  opacity: 0;
  animation: combat-episodic-ray-head 0.62s ease-out forwards;
}

.combat-episodic--ray-light .combat-episodic__ray-glow {
  background: linear-gradient(90deg, rgba(255, 250, 210, 0.75), rgba(255, 230, 140, 0.05));
}
.combat-episodic--ray-light .combat-episodic__ray-core {
  background: linear-gradient(90deg, rgba(255, 255, 245, 1), rgba(255, 235, 160, 0.35));
  box-shadow: 0 0 10px rgba(255, 245, 200, 0.95);
}
.combat-episodic--ray-light .combat-episodic__ray-head {
  background: radial-gradient(circle, rgba(255, 255, 240, 1) 0%, rgba(255, 220, 120, 0) 72%);
  box-shadow: 0 0 14px rgba(255, 245, 200, 0.9);
}

.combat-episodic--ray-ranged .combat-episodic__ray-glow {
  background: linear-gradient(90deg, rgba(220, 190, 120, 0.65), rgba(160, 130, 70, 0.05));
}
.combat-episodic--ray-ranged .combat-episodic__ray-core {
  background: linear-gradient(90deg, rgba(240, 210, 140, 1), rgba(180, 140, 70, 0.25));
}

.combat-episodic--ray-fire .combat-episodic__ray-glow {
  background: linear-gradient(90deg, rgba(255, 140, 40, 0.7), rgba(255, 60, 0, 0.05));
}
.combat-episodic--ray-fire .combat-episodic__ray-core {
  background: linear-gradient(90deg, rgba(255, 220, 120, 1), rgba(255, 90, 20, 0.3));
  box-shadow: 0 0 8px rgba(255, 120, 40, 0.85);
}

.combat-episodic--ray-cold .combat-episodic__ray-glow {
  background: linear-gradient(90deg, rgba(140, 210, 255, 0.65), rgba(60, 140, 220, 0.05));
}
.combat-episodic--ray-cold .combat-episodic__ray-core {
  background: linear-gradient(90deg, rgba(210, 245, 255, 1), rgba(100, 180, 255, 0.25));
}

.combat-episodic--ray-poison .combat-episodic__ray-glow {
  background: linear-gradient(90deg, rgba(120, 230, 90, 0.6), rgba(40, 120, 40, 0.05));
}
.combat-episodic--ray-poison .combat-episodic__ray-core {
  background: linear-gradient(90deg, rgba(170, 255, 140, 1), rgba(60, 160, 50, 0.25));
}

.combat-episodic--ray-magic .combat-episodic__ray-glow {
  background: linear-gradient(
    90deg,
    rgba(110, 240, 255, 0.9),
    rgba(200, 120, 255, 0.55),
    rgba(80, 30, 160, 0.08)
  );
  opacity: 0.78;
  filter: blur(4px);
  animation: combat-magic-ray-pulse 0.44s ease-in-out infinite alternate;
}
.combat-episodic--ray-magic .combat-episodic__ray-core {
  background: linear-gradient(90deg, rgba(210, 255, 252, 1), rgba(190, 130, 255, 0.55));
  box-shadow:
    0 0 10px rgba(90, 230, 255, 0.95),
    0 0 20px rgba(170, 110, 255, 0.85);
  animation: combat-magic-ray-core-pulse 0.36s ease-in-out infinite alternate;
}
.combat-episodic--ray-magic .combat-episodic__ray-head {
  background: radial-gradient(circle, rgba(235, 255, 255, 1) 0%, rgba(170, 120, 255, 0) 72%);
  box-shadow:
    0 0 14px rgba(120, 235, 255, 0.95),
    0 0 22px rgba(190, 130, 255, 0.9);
}

.combat-episodic--ray-soul .combat-episodic__ray-glow {
  background: linear-gradient(90deg, rgba(160, 110, 255, 0.65), rgba(60, 20, 120, 0.05));
}
.combat-episodic--ray-soul .combat-episodic__ray-core {
  background: linear-gradient(90deg, rgba(200, 160, 255, 1), rgba(100, 50, 180, 0.25));
  box-shadow: 0 0 8px rgba(160, 100, 255, 0.75);
}

.combat-episodic--ray-melee .combat-episodic__ray-glow {
  background: linear-gradient(90deg, rgba(220, 80, 80, 0.55), rgba(120, 20, 20, 0.05));
}
.combat-episodic--ray-melee .combat-episodic__ray-core {
  width: 72%;
  height: 6px;
  background: linear-gradient(90deg, rgba(255, 200, 200, 0.95), rgba(180, 40, 40, 0.15));
  animation: combat-episodic-slash 0.42s ease-out forwards;
}

/* Impact flash at target (paired with ray; splashes remain separate). */
.combat-episodic--impact {
  transform: translate(-50%, -50%);
}

.combat-episodic--impact-light {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  animation: combat-episodic-pop 0.48s ease-out forwards;
}

/* Ranged: arrow projectile (no beam). */
.combat-episodic--ranged-arrow {
  width: 0;
  height: 0;
  transform: translate(0, -50%);
}

.combat-episodic__arrow-bolt {
  /* Asset is 126×15; keep on-board length under ~half a tile so commander snipes stay readable. */
  --combat-ranged-arrow-height: clamp(6px, calc(var(--combat-tile-size, 72px) * 0.1), 11px);
  --arrow-bolt-mirror-x: 1;
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  width: auto;
  height: var(--combat-ranged-arrow-height);
  max-width: calc(var(--combat-tile-size, 72px) * 0.55);
  margin-top: calc(var(--combat-ranged-arrow-height) / -2);
  object-fit: contain;
  object-position: left center;
  transform: rotate(var(--arrow-angle, 0deg)) scaleX(var(--arrow-bolt-mirror-x, 1));
  transform-origin: 0 50%;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
  opacity: 0.25;
  pointer-events: none;
}

/* Firebolt projectile (CSS ember — no bitmap). */
.combat-episodic--firebolt {
  width: 0;
  height: 0;
  transform: translate(0, -50%);
}

.combat-episodic__firebolt {
  --firebolt-size: clamp(10px, calc(var(--combat-tile-size, 72px) * 0.18), 16px);
  position: absolute;
  left: 0;
  top: 0;
  width: calc(var(--firebolt-size) * 2.1);
  height: var(--firebolt-size);
  margin-top: calc(var(--firebolt-size) / -2);
  margin-left: calc(var(--firebolt-size) * -0.35);
  border-radius: 50% / 45%;
  background:
    radial-gradient(ellipse at 72% 50%, rgba(255, 255, 220, 0.98) 0%, rgba(255, 200, 60, 0.95) 28%, rgba(255, 110, 20, 0.85) 58%, rgba(220, 40, 0, 0.15) 100%);
  box-shadow:
    0 0 6px 2px rgba(255, 160, 40, 0.85),
    0 0 14px 4px rgba(255, 80, 0, 0.45),
    -4px 0 8px 1px rgba(255, 120, 0, 0.35);
  transform: rotate(var(--firebolt-angle, 0deg));
  transform-origin: 35% 50%;
  opacity: 0.3;
  pointer-events: none;
}

.combat-episodic__firebolt::after {
  content: "";
  position: absolute;
  inset: 18% 8% 18% 42%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 0%, rgba(255, 230, 140, 0.4) 55%, transparent 75%);
}

/* Mirror asymmetric hit FX when travel is right → left on screen. */
.combat-episodic--facing-left.combat-episodic--blood {
  transform: translate(-50%, -45%) scaleX(-1);
}

.combat-episodic--facing-left.combat-episodic--cold-burst {
  transform: translate(-50%, -50%) scaleX(-1);
}

.combat-episodic--facing-left.combat-episodic--fire-eruption {
  transform: translate(-50%, 0) scaleX(-1);
}

.combat-episodic--facing-left.combat-episodic--icon-fly-in {
  transform: translate(-50%, -58%) scaleX(-1);
}

.combat-episodic--facing-left.combat-episodic--soul-wisp {
  transform: translate(-50%, -50%) scaleX(-1);
}

.combat-episodic--ray.combat-episodic--facing-left .combat-episodic__ray-glow,
.combat-episodic--ray.combat-episodic--facing-left .combat-episodic__ray-core {
  transform: translateY(-50%) scaleX(-1);
}

.combat-episodic--ray.combat-episodic--facing-left .combat-episodic__ray-head {
  transform: translate(-50%, -50%) scaleX(-1);
}

.combat-episodic--ray-melee.combat-episodic--facing-left .combat-episodic__ray-core {
  animation-name: combat-episodic-slash-left;
}

/* Physical hits: blood droplets splatter outward from the injured unit. */
.combat-episodic--blood {
  transform: translate(-50%, -45%);
  width: 0;
  height: 0;
  pointer-events: none;
}

.combat-episodic__blood-drop {
  position: absolute;
  left: 0;
  top: 0;
  width: 5px;
  height: 7px;
  border-radius: 45% 55% 50% 50%;
  background: radial-gradient(ellipse at 35% 30%, #c42a2a 0%, #6b0f0f 55%, #3a0808 100%);
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.55);
  opacity: 0;
  animation: combat-episodic-blood-drop 0.52s ease-out forwards;
}

.combat-episodic__blood-drop:nth-child(odd) {
  width: 4px;
  height: 6px;
  background: radial-gradient(ellipse at 40% 35%, #a81818 0%, #5c0c0c 60%, #2a0606 100%);
}

@keyframes combat-episodic-blood-drop {
  0% {
    opacity: 0;
    transform: translate(0, 0) rotate(var(--blood-rot, 0deg)) scale(0.4);
  }
  22% {
    opacity: 0.95;
    transform: translate(calc(var(--blood-dx, 0px) * 0.35), calc(var(--blood-dy, 0px) * 0.35))
      rotate(var(--blood-rot, 0deg)) scale(0.9);
  }
  100% {
    opacity: 0;
    transform: translate(var(--blood-dx, 0px), var(--blood-dy, 0px)) rotate(var(--blood-rot, 0deg))
      scale(0.3);
  }
}

.combat-episodic--impact-light {
  background: radial-gradient(circle, rgba(255, 252, 230, 1) 0%, rgba(255, 220, 120, 0) 72%);
  box-shadow: 0 0 16px rgba(255, 245, 200, 0.95);
}

/* Fire: SVG flame tongues rise from portrait base with shimmer. */
.combat-episodic--fire-eruption {
  transform: translate(-50%, 0);
  width: 0;
  height: 0;
}

.combat-episodic__fire-flame {
  --fire-flip: 1;
  position: absolute;
  left: var(--fire-x, 0);
  bottom: 0;
  width: calc(26px * var(--fire-scale, 1));
  height: calc(34px * var(--fire-scale, 1));
  margin-left: calc(-13px * var(--fire-scale, 1));
  transform-origin: 50% 100%;
  opacity: 0;
  pointer-events: none;
  animation: combat-episodic-fire-rise var(--fire-rise-ms, 0.85s) ease-out var(--fire-delay, 0s) forwards;
}

.combat-episodic__fire-svg {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(var(--fire-flip, 1));
  transform-origin: 50% 100%;
  filter: drop-shadow(0 0 4px rgba(255, 100, 30, 0.55));
  animation: combat-episodic-fire-shimmer var(--fire-shimmer-ms, 0.3s) ease-in-out var(--fire-delay, 0s) infinite
    alternate;
}

/* Cold: snowflake shatters outward. */
.combat-episodic--cold-burst {
  transform: translate(-50%, -50%);
  width: 0;
  height: 0;
}

.combat-episodic__cold-flake {
  position: absolute;
  left: 0;
  top: 0;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  opacity: 0;
  background:
    conic-gradient(
      from 0deg,
      transparent 0deg 25deg,
      rgba(220, 245, 255, 0.95) 25deg 35deg,
      transparent 35deg 55deg,
      rgba(200, 235, 255, 1) 55deg 65deg,
      transparent 65deg 85deg,
      rgba(220, 245, 255, 0.95) 85deg 95deg,
      transparent 95deg 115deg,
      rgba(200, 235, 255, 1) 115deg 125deg,
      transparent 125deg 145deg,
      rgba(220, 245, 255, 0.95) 145deg 155deg,
      transparent 155deg 175deg,
      rgba(200, 235, 255, 1) 175deg 185deg,
      transparent 185deg 360deg
    ),
    radial-gradient(circle, rgba(235, 250, 255, 1) 0%, rgba(140, 210, 255, 0.35) 42%, transparent 68%);
  border-radius: 50%;
  box-shadow: 0 0 10px rgba(180, 230, 255, 0.85);
  animation: combat-episodic-cold-flake 0.62s ease-out forwards;
}

.combat-episodic__cold-shard {
  position: absolute;
  left: 0;
  top: 0;
  width: 5px;
  height: 5px;
  margin: -2px 0 0 -2px;
  border-radius: 1px;
  background: linear-gradient(135deg, rgba(245, 252, 255, 1) 0%, rgba(150, 210, 255, 0.9) 100%);
  box-shadow: 0 0 4px rgba(200, 235, 255, 0.9);
  opacity: 0;
  animation: combat-episodic-cold-shard 0.55s ease-out forwards;
}

/* Soul: ethereal wisps pull inward on the icon center (with portrait hue). */
.combat-episodic--soul-wisp {
  transform: translate(-50%, -50%);
  width: 0;
  height: 0;
}

.combat-episodic__soul-ring {
  position: absolute;
  left: 0;
  top: 0;
  width: 40px;
  height: 40px;
  margin: -20px 0 0 -20px;
  border-radius: 50%;
  border: 2px solid rgba(180, 130, 255, 0.75);
  box-shadow:
    0 0 16px rgba(140, 90, 255, 0.55),
    0 0 28px rgba(100, 60, 220, 0.35);
  opacity: 0;
  animation: combat-episodic-soul-ring 0.62s ease-in forwards;
}

.combat-episodic__soul-wisp-mote {
  position: absolute;
  left: 0;
  top: 0;
  width: calc(10px * var(--soul-wisp-scale, 1));
  height: calc(14px * var(--soul-wisp-scale, 1));
  margin: calc(-7px * var(--soul-wisp-scale, 1)) 0 0 calc(-5px * var(--soul-wisp-scale, 1));
  border-radius: 50% 50% 45% 45%;
  background: radial-gradient(
    ellipse at 50% 30%,
    rgba(245, 230, 255, 0.95) 0%,
    rgba(180, 120, 255, 0.85) 42%,
    rgba(90, 40, 180, 0) 78%
  );
  box-shadow: 0 0 8px rgba(160, 110, 255, 0.75);
  opacity: 0;
  mix-blend-mode: screen;
  pointer-events: none;
  animation: combat-episodic-soul-wisp 0.62s ease-in forwards;
}

/* Grenade throw: ballistic sprite + fuse trail sparks. */
.combat-episodic--grenade-throw {
  position: absolute;
  width: 0;
  height: 0;
  pointer-events: none;
}

.combat-episodic__grenade-bolt {
  --grenade-bolt-mirror-x: 1;
  position: absolute;
  display: block;
  width: clamp(14px, calc(var(--combat-tile-size, 72px) * 0.22), 22px);
  height: auto;
  object-fit: contain;
  transform: translate(-50%, -50%) rotate(var(--grenade-bolt-rotation, 0deg))
    scaleX(var(--grenade-bolt-mirror-x, 1));
  transform-origin: center center;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.55));
  pointer-events: none;
}

.combat-episodic__grenade-fuse-trail {
  position: absolute;
  width: 7px;
  height: 7px;
  margin: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  mix-blend-mode: screen;
  background: radial-gradient(
    circle,
    rgba(255, 248, 210, 0.98) 0%,
    rgba(255, 175, 70, 0.82) 42%,
    rgba(255, 90, 20, 0.35) 68%,
    transparent 78%
  );
  box-shadow:
    0 0 5px rgba(255, 210, 120, 0.9),
    0 0 9px rgba(255, 120, 30, 0.55);
  opacity: calc(0.35 + var(--fuse-intensity, 0.55) * 0.65);
  animation: combat-grenade-fuse-trail 0.32s ease-out forwards;
}

/* Grenade impact: central flash + rAF sparks (white → yellow → red → ember). */
.combat-episodic--grenade-flash-aoe {
  width: calc(var(--combat-tile-size, 72px) * 2.35);
  height: calc(var(--combat-tile-size, 72px) * 2.35);
}

.combat-episodic--grenade-flash {
  position: absolute;
  width: 34px;
  height: 34px;
  margin: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  mix-blend-mode: screen;
  background: radial-gradient(
    circle,
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 235, 140, 0.92) 34%,
    rgba(255, 90, 30, 0.55) 58%,
    transparent 76%
  );
  box-shadow:
    0 0 18px rgba(255, 240, 200, 0.95),
    0 0 28px rgba(255, 120, 40, 0.75);
  animation: combat-grenade-flash 0.58s ease-out forwards;
}

.combat-episodic__grenade-spark {
  position: absolute;
  margin: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  mix-blend-mode: screen;
  will-change: left, top, width, height, opacity, background, box-shadow;
}

/* AoE detonation: smoke clears after the flash (explode / detonate / grenade splash). */
.combat-episodic--grenade-smoke {
  position: absolute;
  width: calc(var(--combat-tile-size, 72px) * 2.6);
  height: calc(var(--combat-tile-size, 72px) * 2.6);
  margin: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgba(70, 62, 55, 0.55) 0%,
    rgba(48, 44, 40, 0.38) 38%,
    rgba(30, 28, 26, 0.12) 62%,
    transparent 78%
  );
  filter: blur(6px);
  opacity: 0;
  animation: combat-grenade-smoke 0.78s ease-out forwards;
}

@keyframes combat-grenade-smoke {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.45);
  }
  22% {
    opacity: 0.72;
    transform: translate(-50%, -50%) scale(0.95);
  }
  55% {
    opacity: 0.45;
    transform: translate(-50%, -52%) scale(1.35);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -56%) scale(1.75);
  }
}

/* Magic twinkles (positions + teal/purple hues driven by episodic.js rAF). */
.combat-episodic__magic-particle {
  position: absolute;
  width: 7px;
  height: 7px;
  margin: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  mix-blend-mode: screen;
  animation: combat-magic-sparkle-twinkle var(--magic-sparkle-period, 0.52s) ease-in-out infinite;
  animation-delay: var(--magic-sparkle-delay, 0s);
  will-change: left, top, opacity, transform, filter, background, box-shadow;
}

/* Status-apply stamp: icon rises into place above the portrait, then fades. */
.combat-episodic--icon-fly-in {
  transform: translate(-50%, -58%);
  width: 44px;
  height: 44px;
  pointer-events: none;
}

.combat-effect-icon-fly-in {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.55));
  transform-origin: 50% 88%;
  animation: combat-effect-icon-fly-in 0.58s ease-out forwards;
}

.combat-episodic--impact-dust {
  width: 22px;
  height: 12px;
  background: radial-gradient(ellipse, rgba(160, 140, 110, 0.55) 0%, transparent 70%);
  animation: combat-episodic-dust 0.45s ease-out forwards;
}

.combat-episodic--impact-block {
  width: 24px;
  height: 24px;
  border: 2px solid rgba(200, 200, 210, 0.8);
  border-radius: 50%;
  animation: combat-episodic-block 0.4s ease-out forwards;
}

/* Floating green ``+`` glyphs on restored units (paired with healing hit-splash). */
.combat-episodic--heal-burst {
  width: 0;
  height: 0;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.combat-episodic--heal-burst::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(180, 255, 190, 0.55) 0%,
    rgba(80, 220, 120, 0.22) 48%,
    rgba(40, 160, 80, 0) 72%
  );
  animation: combat-episodic-heal-pop 0.42s ease-out forwards;
}

.combat-episodic__heal-plus {
  --heal-plus-i: 0;
  --heal-plus-x: 0px;
  position: absolute;
  left: 0;
  top: 0;
  margin: 0;
  transform: translate(calc(-50% + var(--heal-plus-x)), -40%);
  color: var(--combat-color-heal, #6be872);
  font-weight: 900;
  font-size: calc(var(--combat-splash-icon, 14px) * 0.95);
  line-height: 1;
  text-shadow:
    0 0 4px rgba(40, 120, 60, 0.85),
    0 1px 2px rgba(0, 0, 0, 0.65);
  opacity: 0;
  pointer-events: none;
  animation: combat-episodic-heal-plus 0.7s ease-out forwards;
  animation-delay: calc(var(--heal-plus-i) * 55ms);
}

@keyframes combat-episodic-heal-pop {
  0% {
    opacity: 0;
    transform: scale(0.4);
  }
  35% {
    opacity: 0.85;
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(1.35);
  }
}

@keyframes combat-episodic-heal-plus {
  0% {
    opacity: 0;
    transform: translate(calc(-50% + var(--heal-plus-x)), 4px) scale(0.55);
  }
  18% {
    opacity: 1;
    transform: translate(calc(-50% + var(--heal-plus-x)), -6px) scale(1.05);
  }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--heal-plus-x)), -42px) scale(0.85);
  }
}

@keyframes combat-grenade-fuse-trail {
  0% {
    transform: translate(-50%, -50%) scale(1.15);
    filter: brightness(1.35) saturate(1.2);
  }
  100% {
    transform: translate(-50%, -72%) scale(0.3);
    filter: brightness(0.75) saturate(0.85);
    opacity: 0;
  }
}

@keyframes combat-grenade-flash {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.35);
    filter: brightness(1.2) saturate(1.1);
  }
  18% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.15);
    filter: brightness(2.2) saturate(1.4);
  }
  42% {
    opacity: 0.92;
    transform: translate(-50%, -50%) scale(1.45);
    filter: brightness(1.6) saturate(1.65);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.85);
    filter: brightness(0.55) saturate(0.7);
  }
}

@keyframes combat-magic-ray-pulse {
  0% {
    opacity: 0.62;
    filter: blur(3px) brightness(0.9) saturate(1.15);
  }
  100% {
    opacity: 0.92;
    filter: blur(5px) brightness(1.4) saturate(1.75);
  }
}

@keyframes combat-magic-ray-core-pulse {
  0% {
    filter: brightness(0.95) saturate(1.2);
    box-shadow:
      0 0 8px rgba(90, 230, 255, 0.75),
      0 0 14px rgba(170, 110, 255, 0.65);
  }
  100% {
    filter: brightness(1.45) saturate(1.85);
    box-shadow:
      0 0 14px rgba(120, 250, 255, 1),
      0 0 26px rgba(200, 140, 255, 1);
  }
}

@keyframes combat-magic-sparkle-twinkle {
  0%,
  100% {
    transform: translate(-50%, -50%) scale(0.7);
    filter: brightness(0.88) saturate(1.15);
  }
  45% {
    transform: translate(-50%, -50%) scale(1.45);
    filter: brightness(1.75) saturate(2);
  }
}

@keyframes combat-episodic-pop {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.4);
  }
  30% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.35);
  }
}

@keyframes combat-episodic-dust {
  0% {
    opacity: 0;
    transform: translate(-50%, -40%) scale(0.6);
  }
  40% {
    opacity: 0.8;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -60%) scale(1.2);
  }
}

@keyframes combat-episodic-block {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.7);
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.15);
  }
}

@keyframes combat-episodic-ray {
  0% {
    opacity: 0;
    clip-path: inset(0 100% 0 0);
  }
  18% {
    opacity: 1;
    clip-path: inset(0 0 0 0);
  }
  72% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

@keyframes combat-episodic-ray-head {
  0% {
    opacity: 0;
    transform: translate(30%, -50%) scale(0.4);
  }
  35% {
    opacity: 1;
    transform: translate(50%, -50%) scale(1.1);
  }
  100% {
    opacity: 0;
    transform: translate(55%, -50%) scale(1.35);
  }
}

@keyframes combat-episodic-slash {
  0% {
    opacity: 0;
    clip-path: inset(0 100% 0 0);
  }
  40% {
    opacity: 1;
    clip-path: inset(0 0 0 0);
  }
  100% {
    opacity: 0;
  }
}

@keyframes combat-episodic-slash-left {
  0% {
    opacity: 0;
    clip-path: inset(0 0 0 100%);
  }
  40% {
    opacity: 1;
    clip-path: inset(0 0 0 0);
  }
  100% {
    opacity: 0;
  }
}

@keyframes combat-episodic-fire-rise {
  0% {
    opacity: 0;
    transform: translate(0, 2px) scale(calc(var(--fire-scale, 1) * 0.38), calc(var(--fire-scale, 1) * 0.16));
  }
  10% {
    opacity: 0.72;
    transform: translate(calc(var(--fire-drift, 0px) * 0.15), -5px)
      scale(calc(var(--fire-scale, 1) * 0.68), calc(var(--fire-scale, 1) * 0.58));
  }
  22% {
    opacity: 0.82;
    transform: translate(calc(var(--fire-drift, 0px) * 0.35), -16px)
      scale(calc(var(--fire-scale, 1) * 1.06), calc(var(--fire-scale, 1) * 1.04));
  }
  35% {
    opacity: 0.62;
    transform: translate(calc(var(--fire-drift, 0px) * 0.5), -26px)
      scale(calc(var(--fire-scale, 1) * 0.94), calc(var(--fire-scale, 1) * 0.88));
  }
  50% {
    opacity: 0.4;
    transform: translate(calc(var(--fire-drift, 0px) * 0.68), -36px)
      scale(calc(var(--fire-scale, 1) * 0.78), calc(var(--fire-scale, 1) * 0.72));
  }
  68% {
    opacity: 0.22;
    transform: translate(calc(var(--fire-drift, 0px) * 0.85), -46px)
      scale(calc(var(--fire-scale, 1) * 0.58), calc(var(--fire-scale, 1) * 0.52));
  }
  85% {
    opacity: 0.08;
    transform: translate(calc(var(--fire-drift, 0px) * 0.98), -54px)
      scale(calc(var(--fire-scale, 1) * 0.38), calc(var(--fire-scale, 1) * 0.34));
  }
  100% {
    opacity: 0;
    transform: translate(var(--fire-drift, 0px), -62px)
      scale(calc(var(--fire-scale, 1) * 0.2), calc(var(--fire-scale, 1) * 0.18));
  }
}

@keyframes combat-episodic-fire-shimmer {
  0% {
    opacity: 0.5;
  }
  100% {
    opacity: 0.82;
  }
}

@keyframes combat-episodic-cold-flake {
  0% {
    opacity: 0;
    transform: scale(0.1) rotate(0deg);
  }
  28% {
    opacity: 1;
    transform: scale(1.12) rotate(-14deg);
  }
  100% {
    opacity: 0;
    transform: scale(1.35) rotate(-28deg);
  }
}

@keyframes combat-episodic-cold-shard {
  0% {
    opacity: 0;
    transform: translate(0, 0) scale(0.2) rotate(0deg);
  }
  22% {
    opacity: 1;
    transform: translate(calc(var(--cold-dx, 0px) * 0.25), calc(var(--cold-dy, 0px) * 0.25)) scale(0.9);
  }
  100% {
    opacity: 0;
    transform: translate(var(--cold-dx, 0px), var(--cold-dy, 0px)) scale(0.3) rotate(-48deg);
  }
}

@keyframes combat-episodic-soul-ring {
  0% {
    opacity: 0.2;
    transform: scale(1.55);
  }
  40% {
    opacity: 0.9;
    transform: scale(0.9);
  }
  100% {
    opacity: 0;
    transform: scale(0.2);
  }
}

@keyframes combat-episodic-soul-wisp {
  0% {
    opacity: 0;
    transform: translate(var(--soul-dx, 0px), var(--soul-dy, 0px)) scale(0.55);
  }
  22% {
    opacity: 0.95;
    transform: translate(calc(var(--soul-dx, 0px) * 0.72), calc(var(--soul-dy, 0px) * 0.72))
      scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(0, 0) scale(0.25);
  }
}

/* Legacy ranged arrow keyframes removed — flight is driven by rAF in episodic.js (see grenade throw). */

@keyframes combat-effect-icon-fly-in {
  0% {
    opacity: 0;
    transform: scale(0.48) translateY(28px);
  }
  18% {
    opacity: 0.5;
    transform: scale(0.58) translateY(18px);
  }
  62% {
    opacity: 1;
    transform: scale(0.78) translateY(2px);
  }
  68% {
    opacity: 1;
    transform: scale(0.76) translateY(0);
  }
  100% {
    opacity: 0;
    transform: scale(0.74) translateY(-2px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .combat-ambient__fire-flame,
  .combat-ambient__fire-svg,
  .combat-ambient__poison-bubble,
  .combat-ambient__regen-mote,
  .combat-ambient__fly,
  .combat-portrait-fx__cursed-sigil-svg {
    animation: none !important;
  }

  .combat-ambient__fire-flame {
    opacity: 0.75;
  }

  .combat-portrait-fx__cursed-sigil {
    opacity: 0.85;
  }

  .combat-episodic__fire-svg {
    animation: none !important;
    opacity: 0.72;
  }

  .combat-episodic__fire-flame {
    animation-duration: 0.45s !important;
  }

  .combat-portrait--fx-soul-hit {
    animation: none !important;
    filter: hue-rotate(248deg) saturate(1.25) brightness(1.04);
  }

  .combat-portrait--fx-poison-hit {
    animation: none !important;
    filter: sepia(0.28) saturate(1.6) hue-rotate(66deg) brightness(1.04);
  }

  .combat-effect-icon-fly-in {
    animation-duration: 0.38s !important;
  }

  .combat-portrait-fx__stun-daze-orbit,
  .combat-portrait-fx__stun-star,
  .combat-portrait-fx__cursed-sigil-svg {
    animation: none !important;
  }

  :is(.combat-unit--fx-petrified, .combat-unit--fx-frozen, .combat-unit--fx-paralyzed)
    .combat-unit__sprite-idle {
    animation-play-state: paused !important;
  }

  .combat-episodic--soul-wisp {
    animation: none !important;
    opacity: 0 !important;
  }

  .combat-episodic--teleport,
  .combat-episodic--teleport-streak {
    animation: none !important;
    opacity: 0 !important;
  }

  .combat-unit--teleport-hidden .combat-unit__presentation {
    opacity: 0 !important;
  }

  .combat-unit--teleport-arrive .combat-unit__presentation {
    animation: none !important;
    filter: none !important;
  }
}

/* Teleport: purple swirl vanish / arrive + rapid streak between cells. */
.combat-unit--teleport-hidden .combat-unit__presentation {
  opacity: 0;
  transition: opacity 0.1s ease-out;
  pointer-events: none;
}

.combat-unit--teleport-arrive .combat-unit__icon,
.combat-unit--teleport-arrive .combat-unit__sprite-idle {
  animation: combat-teleport-unit-arrive 0.13s ease-out forwards;
}

@keyframes combat-teleport-unit-arrive {
  0% {
    opacity: 0;
    filter: brightness(3.2) saturate(1.35) drop-shadow(0 0 10px rgba(186, 120, 255, 0.95));
  }
  42% {
    opacity: 1;
    filter: brightness(2.4) saturate(1.5) drop-shadow(0 0 14px rgba(210, 150, 255, 1));
  }
  100% {
    opacity: 1;
    filter: none;
  }
}

.combat-episodic--teleport {
  position: absolute;
  width: 0;
  height: 0;
  pointer-events: none;
  transform: translate(-50%, -55%);
}

.combat-episodic__teleport-core {
  position: absolute;
  left: 0;
  top: 0;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(244, 220, 255, 0.98) 0%, rgba(148, 72, 220, 0.55) 45%, transparent 72%);
  box-shadow: 0 0 10px rgba(170, 90, 255, 0.75);
}

.combat-episodic__teleport-flash {
  position: absolute;
  left: 0;
  top: 0;
  width: 44px;
  height: 44px;
  margin: -22px 0 0 -22px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(
    circle,
    rgba(255, 248, 255, 0.98) 0%,
    rgba(210, 150, 255, 0.72) 32%,
    rgba(120, 50, 200, 0.2) 58%,
    transparent 72%
  );
  mix-blend-mode: screen;
  opacity: 0;
}

.combat-episodic__teleport-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(248, 230, 255, 1) 0%, rgba(176, 98, 255, 0.92) 55%, rgba(90, 28, 160, 0.4) 100%);
  box-shadow:
    0 0 6px rgba(200, 130, 255, 0.95),
    0 0 12px rgba(130, 60, 220, 0.55);
  transform: rotate(var(--tp-orbit-deg, 0deg)) translateX(22px) rotate(calc(-1 * var(--tp-orbit-deg, 0deg)));
  opacity: 0;
}

.combat-episodic--teleport-vanish .combat-episodic__teleport-dot {
  animation: combat-teleport-dot-vanish 0.15s ease-in forwards;
  animation-delay: var(--tp-orbit-delay, 0s);
}

.combat-episodic--teleport-vanish {
  animation: combat-teleport-wrap-condense 0.15s ease-in 0.1s forwards;
}

.combat-episodic--teleport-vanish .combat-episodic__teleport-flash {
  animation: combat-teleport-origin-flash 0.12s ease-out 0.1s forwards;
}

.combat-episodic--teleport-arrive {
  animation: combat-teleport-wrap-expand 0.13s ease-out forwards;
}

.combat-episodic--teleport-arrive .combat-episodic__teleport-dot {
  animation: combat-teleport-dot-arrive 0.13s ease-out forwards;
  animation-delay: var(--tp-orbit-delay, 0s);
}

.combat-episodic--teleport-arrive .combat-episodic__teleport-flash {
  animation: combat-teleport-origin-flash 0.11s ease-out 0.02s forwards;
}

.combat-episodic--teleport-streak {
  position: absolute;
  width: 0;
  height: 0;
  pointer-events: none;
  transform: translate(0, -50%) rotate(var(--tp-streak-angle, 0deg));
  transform-origin: 0 50%;
}

.combat-episodic--teleport-streak::before {
  content: "";
  position: absolute;
  left: 0;
  top: -3px;
  width: var(--tp-streak-len, 40px);
  height: 6px;
  border-radius: 999px;
  background: linear-gradient(
    90deg,
    rgba(255, 245, 255, 0.95) 0%,
    rgba(200, 130, 255, 0.88) 28%,
    rgba(130, 55, 210, 0.55) 72%,
    transparent 100%
  );
  box-shadow:
    0 0 8px rgba(210, 140, 255, 0.85),
    0 0 16px rgba(140, 60, 220, 0.45);
  opacity: 0;
  animation: combat-teleport-streak 0.05s ease-out forwards;
}

@keyframes combat-teleport-dot-vanish {
  0% {
    opacity: 0;
    transform: rotate(var(--tp-orbit-deg, 0deg)) translateX(8px) rotate(calc(-1 * var(--tp-orbit-deg, 0deg))) scale(0.5);
  }
  35% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: rotate(calc(var(--tp-orbit-deg, 0deg) + 200deg)) translateX(2px) rotate(calc(-1 * var(--tp-orbit-deg, 0deg) - 200deg)) scale(0);
  }
}

@keyframes combat-teleport-dot-arrive {
  0% {
    opacity: 0;
    transform: rotate(var(--tp-orbit-deg, 0deg)) translateX(2px) rotate(calc(-1 * var(--tp-orbit-deg, 0deg))) scale(0);
  }
  40% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: rotate(calc(var(--tp-orbit-deg, 0deg) + 160deg)) translateX(24px) rotate(calc(-1 * var(--tp-orbit-deg, 0deg) - 160deg)) scale(0.85);
  }
}

@keyframes combat-teleport-wrap-condense {
  0% {
    transform: translate(-50%, -55%) scale(1);
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -55%) scale(0);
    opacity: 0;
  }
}

@keyframes combat-teleport-wrap-expand {
  0% {
    transform: translate(-50%, -55%) scale(0);
    opacity: 0;
  }
  45% {
    opacity: 1;
    transform: translate(-50%, -55%) scale(1.08);
  }
  100% {
    transform: translate(-50%, -55%) scale(1);
    opacity: 0;
  }
}

@keyframes combat-teleport-origin-flash {
  0% {
    opacity: 0;
    transform: scale(0.2);
  }
  35% {
    opacity: 1;
    transform: scale(1.15);
  }
  100% {
    opacity: 0;
    transform: scale(1.45);
  }
}

@keyframes combat-teleport-streak {
  0% {
    opacity: 0;
    transform: scaleX(0.05);
  }
  25% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: scaleX(1);
  }
}

/* ---------------------------------------------------------------------------
   combat_display.style — portrait FX modifiers on `.combat-unit__scale`.
   Each `--style-<name>` class is gated by the server-side allow-list in
   `combat_engine._COMBAT_DISPLAY_STYLES` and the client allow-list in
   `combat/context.js#COMBAT_DISPLAY_STYLES`. Effects are confined to the
   scaled portrait subtree so they track sprite scaling/offset.
   --------------------------------------------------------------------------- */

/* `radiant` — a soft luminous breathing on the portrait paired with two
   offset expanding light waves that radiate outward from the sprite center.
   Used for sun/spirit creatures (e.g. Yling). Pseudo-elements sit behind the
   portrait via z-index: -1 inside the scale shell's stacking context (the
   scale shell already establishes one via `transform`). */
.combat-cell__tile .combat-unit__scale--style-radiant {
  --combat-radiant-core: rgba(255, 244, 198, 0.62);
  --combat-radiant-mid: rgba(255, 220, 140, 0.32);
  animation: combat-unit-style-radiant-pulse 2.8s ease-in-out infinite;
}

.combat-cell__tile .combat-unit__scale--style-radiant::before,
.combat-cell__tile .combat-unit__scale--style-radiant::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 52%;
  width: 78%;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(
    circle at center,
    var(--combat-radiant-core) 0%,
    var(--combat-radiant-mid) 38%,
    rgba(255, 200, 100, 0) 62%
  );
  mix-blend-mode: screen;
  z-index: -1;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.5);
  animation: combat-unit-style-radiant-wave 3.4s ease-out infinite;
  will-change: transform, opacity;
}

.combat-cell__tile .combat-unit__scale--style-radiant::after {
  animation-delay: 1.7s;
}

@keyframes combat-unit-style-radiant-pulse {
  0%,
  100% {
    filter: brightness(1) drop-shadow(0 0 4px rgba(255, 230, 170, 0));
  }
  50% {
    filter: brightness(1.18) drop-shadow(0 0 12px rgba(255, 230, 170, 0.6));
  }
}

@keyframes combat-unit-style-radiant-wave {
  0% {
    transform: translate(-50%, -50%) scale(0.5);
    opacity: 0.7;
  }
  60% {
    opacity: 0.32;
  }
  100% {
    transform: translate(-50%, -50%) scale(1.7);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .combat-cell__tile .combat-unit__scale--style-radiant {
    animation: none;
    filter: brightness(1.08) drop-shadow(0 0 6px rgba(255, 230, 170, 0.45));
  }
  .combat-cell__tile .combat-unit__scale--style-radiant::before,
  .combat-cell__tile .combat-unit__scale--style-radiant::after {
    animation: none;
    opacity: 0.18;
    transform: translate(-50%, -50%) scale(1);
  }
}

/* `ethereal` — bright / white pixels fade toward transparent; color stays on darker pixels.
   Filter targets portrait only (not radiant glow pseudo-elements on the scale shell).
   Requires `ensureCombatSvgFilters()` (#combat-filter-ethereal-alpha in document). */
.combat-cell__tile .combat-unit__scale--style-ethereal .combat-unit__sprite-idle,
.combat-cell__tile .combat-unit__scale--style-ethereal .combat-unit__icon {
  filter: url(#combat-filter-ethereal-alpha);
}

/* Defeat: portrait dissolves to ash; flecks scatter on the wind before grid removal. */
.combat-unit.combat-unit--ash-death {
  pointer-events: none;
  z-index: 9;
}

.combat-unit.combat-unit--ash-death .combat-unit__scale {
  animation: combat-unit-ash-death 900ms cubic-bezier(0.33, 0, 0.55, 1) forwards;
  transform-origin: center bottom;
}

.combat-unit.combat-unit--ash-death .combat-unit__ability-icons,
.combat-unit.combat-unit--ash-death .combat-unit__status-icons {
  animation: combat-unit-ash-death-icons 480ms ease-out forwards;
}

.combat-unit__death-ash {
  position: absolute;
  inset: -20% -10% -10% -10%;
  z-index: 6;
  pointer-events: none;
  overflow: visible;
}

/* Extra room downwind so flecks are not clipped at the art-frame edge. */
.combat-unit__death-ash--a {
  inset: -20% -10% -10% -40%;
}

.combat-unit__death-ash--b {
  inset: -20% -40% -10% -10%;
}

.combat-unit__death-ash-fleck {
  position: absolute;
  width: var(--ash-size, 3px);
  height: calc(var(--ash-size, 3px) * 0.72);
  margin: 0;
  border-radius: 40% 60% 55% 45%;
  background: radial-gradient(
    ellipse at 40% 35%,
    rgba(196, 184, 168, 0.95) 0%,
    rgba(120, 110, 98, 0.85) 55%,
    rgba(72, 66, 58, 0.55) 100%
  );
  box-shadow: 0 0 1px rgba(40, 36, 32, 0.35);
  animation: combat-unit-death-ash 860ms cubic-bezier(0.2, 0.55, 0.35, 1) forwards;
  animation-delay: var(--ash-delay, 0ms);
  transform-origin: center center;
  opacity: 0;
}

@keyframes combat-unit-death-ash {
  0% {
    opacity: 0;
    transform: translate(0, 0) rotate(0deg) scale(0.55);
  }
  12% {
    opacity: 0.9;
    transform: translate(calc(var(--ash-x, 48px) * 0.08), calc(var(--ash-y, -12px) * 0.1))
      rotate(calc(var(--ash-rot, 12deg) * 0.15)) scale(1);
  }
  55% {
    opacity: 0.65;
    transform: translate(
        calc(var(--ash-x, 48px) * 0.55),
        calc(var(--ash-y, -12px) * 0.55 + var(--ash-drift, 0px))
      )
      rotate(calc(var(--ash-rot, 12deg) * 0.6)) scale(0.85);
  }
  100% {
    opacity: 0;
    transform: translate(var(--ash-x, 48px), calc(var(--ash-y, -12px) + var(--ash-drift, 0px)))
      rotate(var(--ash-rot, 12deg)) scale(0.25);
  }
}

@keyframes combat-unit-ash-death {
  0% {
    opacity: 1;
    filter: grayscale(0) sepia(0) brightness(1) blur(0);
    transform: translate(var(--combat-display-x, 0%), var(--combat-display-y, 0%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1)))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1)));
  }
  28% {
    opacity: 0.88;
    filter: grayscale(0.45) sepia(0.25) brightness(0.92) contrast(1.05) blur(0.3px);
    transform: translate(var(--combat-display-x, 0%), calc(var(--combat-display-y, 0%) - 3%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1)))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1)));
  }
  62% {
    opacity: 0.4;
    filter: grayscale(0.85) sepia(0.35) brightness(0.7) contrast(0.95) blur(1px);
    transform: translate(
        calc(var(--combat-display-x, 0%) + (3% * var(--ash-wind-sign, 1))),
        calc(var(--combat-display-y, 0%) - 6%)
      )
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1) * 0.97))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1) * 0.97));
  }
  100% {
    opacity: 0;
    filter: grayscale(1) sepia(0.4) brightness(0.55) blur(2px);
    transform: translate(
        calc(var(--combat-display-x, 0%) + (6% * var(--ash-wind-sign, 1))),
        calc(var(--combat-display-y, 0%) - 8%)
      )
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1) * 0.94))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1) * 0.94));
  }
}

@keyframes combat-unit-ash-death-icons {
  to {
    opacity: 0;
    transform: translateY(4px) scale(0.6);
  }
}

/* Rise Decayed: ghoul rises from flat ground (scaleY from bottom). */
.combat-unit.combat-unit--rise-spawn {
  pointer-events: none;
  z-index: 8;
}

.combat-unit.combat-unit--rise-spawn .combat-unit__scale {
  animation: combat-unit-rise-spawn 650ms cubic-bezier(0.22, 0.82, 0.28, 1) both;
  transform-origin: center bottom;
}

.combat-unit.combat-unit--rise-spawn .combat-unit__ability-icons,
.combat-unit.combat-unit--rise-spawn .combat-unit__status-icons {
  animation: combat-unit-spawn-chrome-in 420ms ease-out 220ms both;
}

@keyframes combat-unit-rise-spawn {
  0% {
    opacity: 0.15;
    filter: brightness(0.55) saturate(0.7) blur(0.6px);
    transform: translate(var(--combat-display-x, 0%), var(--combat-display-y, 0%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1)))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1) * 0.04));
  }
  55% {
    opacity: 0.92;
    filter: brightness(0.92) saturate(0.95) blur(0);
    transform: translate(var(--combat-display-x, 0%), var(--combat-display-y, 0%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1)))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1) * 0.78));
  }
  100% {
    opacity: 1;
    filter: none;
    transform: translate(var(--combat-display-x, 0%), var(--combat-display-y, 0%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1)))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1)));
  }
}

/* Summoner / clone / mischief: portal swirl consolidates while the unit fades in. */
.combat-unit.combat-unit--portal-summon {
  pointer-events: none;
  z-index: 8;
}

.combat-unit.combat-unit--portal-summon .combat-unit__scale {
  animation: combat-unit-portal-summon 820ms cubic-bezier(0.2, 0.75, 0.25, 1) both;
  transform-origin: center center;
}

.combat-unit.combat-unit--portal-summon .combat-unit__ability-icons,
.combat-unit.combat-unit--portal-summon .combat-unit__status-icons {
  animation: combat-unit-spawn-chrome-in 380ms ease-out 360ms both;
}

@keyframes combat-unit-portal-summon {
  0% {
    opacity: 0;
    filter: brightness(2.4) saturate(1.35) blur(1.2px);
    transform: translate(var(--combat-display-x, 0%), var(--combat-display-y, 0%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1) * 0.55))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1) * 0.55));
  }
  38% {
    opacity: 0.35;
    filter: brightness(1.85) saturate(1.25) blur(0.4px);
    transform: translate(var(--combat-display-x, 0%), var(--combat-display-y, 0%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1) * 0.82))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1) * 0.82));
  }
  72% {
    opacity: 1;
    filter: brightness(1.15) saturate(1.08) blur(0);
    transform: translate(var(--combat-display-x, 0%), var(--combat-display-y, 0%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1) * 1.03))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1) * 1.03));
  }
  100% {
    opacity: 1;
    filter: none;
    transform: translate(var(--combat-display-x, 0%), var(--combat-display-y, 0%))
      scaleX(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-x, 1)))
      scaleY(calc(var(--combat-display-scale, 1) * var(--combat-display-scale-y, 1)));
  }
}

@keyframes combat-unit-spawn-chrome-in {
  0% {
    opacity: 0;
    transform: translateY(6px) scale(0.7);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.combat-unit__summon-portal {
  position: absolute;
  inset: -28% -18% -12% -18%;
  z-index: 7;
  pointer-events: none;
  overflow: visible;
}

.combat-unit__summon-portal-core {
  position: absolute;
  left: 50%;
  top: 58%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(255, 248, 220, 0.95) 0%,
    rgba(120, 210, 120, 0.65) 42%,
    rgba(40, 90, 50, 0.15) 70%,
    transparent 78%
  );
  box-shadow:
    0 0 12px rgba(140, 230, 140, 0.75),
    0 0 28px rgba(70, 160, 90, 0.45);
  animation: combat-unit-summon-portal-core 820ms ease-out both;
}

.combat-unit__summon-portal-ring {
  position: absolute;
  left: 50%;
  top: 58%;
  width: 52px;
  height: 52px;
  margin: -26px 0 0 -26px;
  border-radius: 50%;
  border: 2px solid rgba(160, 240, 160, 0.55);
  box-shadow:
    0 0 10px rgba(100, 200, 110, 0.45),
    inset 0 0 12px rgba(180, 255, 180, 0.25);
  animation: combat-unit-summon-portal-ring 820ms cubic-bezier(0.2, 0.7, 0.25, 1) both;
}

.combat-unit__summon-portal-dot {
  position: absolute;
  left: 50%;
  top: 58%;
  width: var(--sp-size, 3px);
  height: var(--sp-size, 3px);
  margin: calc(var(--sp-size, 3px) / -2) 0 0 calc(var(--sp-size, 3px) / -2);
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(240, 255, 220, 1) 0%,
    rgba(110, 200, 100, 0.9) 55%,
    rgba(40, 100, 50, 0.35) 100%
  );
  box-shadow: 0 0 5px rgba(140, 230, 120, 0.85);
  opacity: 0;
  animation: combat-unit-summon-portal-dot 780ms cubic-bezier(0.25, 0.7, 0.2, 1) both;
  animation-delay: var(--sp-delay, 0ms);
}

@keyframes combat-unit-summon-portal-core {
  0% {
    opacity: 0;
    transform: scale(0.2);
  }
  28% {
    opacity: 1;
    transform: scale(1.35);
  }
  70% {
    opacity: 0.75;
    transform: scale(0.85);
  }
  100% {
    opacity: 0;
    transform: scale(0.15);
  }
}

@keyframes combat-unit-summon-portal-ring {
  0% {
    opacity: 0;
    transform: scale(1.55) rotate(0deg);
  }
  22% {
    opacity: 0.95;
    transform: scale(1.15) rotate(40deg);
  }
  100% {
    opacity: 0;
    transform: scale(0.15) rotate(220deg);
  }
}

@keyframes combat-unit-summon-portal-dot {
  0% {
    opacity: 0;
    transform: rotate(var(--sp-orbit-deg, 0deg)) translateX(calc(var(--sp-orbit-r, 28px) * 1.35))
      rotate(calc(-1 * var(--sp-orbit-deg, 0deg))) scale(0.4);
  }
  18% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: rotate(calc(var(--sp-orbit-deg, 0deg) + var(--sp-spin, 720deg))) translateX(2px)
      rotate(calc(-1 * (var(--sp-orbit-deg, 0deg) + var(--sp-spin, 720deg)))) scale(0.2);
  }
}

@media (prefers-reduced-motion: reduce) {
  .combat-unit.combat-unit--rise-spawn .combat-unit__scale,
  .combat-unit.combat-unit--portal-summon .combat-unit__scale,
  .combat-unit.combat-unit--rise-spawn .combat-unit__ability-icons,
  .combat-unit.combat-unit--rise-spawn .combat-unit__status-icons,
  .combat-unit.combat-unit--portal-summon .combat-unit__ability-icons,
  .combat-unit.combat-unit--portal-summon .combat-unit__status-icons,
  .combat-unit__summon-portal-core,
  .combat-unit__summon-portal-ring,
  .combat-unit__summon-portal-dot {
    animation: none !important;
  }
}

/* Soft idle motion for static combat portraits (custom_visuals.classes idle tags). */
.combat-unit--idle-flying .combat-unit__art-frame {
  /* Default hover clearance; bob keyframes keep this floor so reduced-motion still floats. */
  transform: translateY(-10%);
  animation: combat-unit-idle-bob 2.55s ease-in-out infinite;
  animation-delay: var(--combat-idle-delay, 0ms);
}

.combat-unit--idle-humanoid .combat-unit__art-frame {
  animation: combat-unit-idle-breathe-x 2.9s ease-in-out infinite;
  animation-delay: var(--combat-idle-delay, 0ms);
  transform-origin: center bottom;
}

.combat-unit--idle-monstrous .combat-unit__art-frame {
  /* Frame stays put; breathe the portrait art so scaleY doesn't lift a centered icon. */
  animation: none;
}

.combat-unit--idle-monstrous .combat-unit__icon {
  animation: combat-unit-idle-breathe-y 3.4s ease-in-out infinite;
  animation-delay: var(--combat-idle-delay, 0ms);
  transform-origin: center bottom;
}

@keyframes combat-unit-idle-bob {
  0%,
  100% {
    transform: translateY(-10%);
  }
  50% {
    transform: translateY(-14%);
  }
}

@keyframes combat-unit-idle-breathe-x {
  0%,
  100% {
    transform: scaleX(1);
  }
  50% {
    transform: scaleX(1.045);
  }
}

@keyframes combat-unit-idle-breathe-y {
  /* Preserve team facing (`--combat-facing-scale-x` from `.combat-unit__icon--flipped`). */
  0%,
  100% {
    transform: scaleX(var(--combat-facing-scale-x, 1)) scaleY(1);
  }
  50% {
    transform: scaleX(var(--combat-facing-scale-x, 1)) scaleY(1.022);
  }
}

/* Melee strike lean (presentation substitute while sprites are frozen). */
.combat-unit--melee-lean .combat-unit__art-frame {
  animation: combat-unit-melee-lean-a 320ms ease-in-out;
  transform-origin: center bottom;
}

.combat-unit--melee-lean .combat-unit__art-frame--b {
  animation-name: combat-unit-melee-lean-b;
}

@keyframes combat-unit-melee-lean-a {
  0%,
  100% {
    transform: rotate(0deg) translateX(0);
  }
  40% {
    transform: rotate(11deg) translateX(5%);
  }
}

@keyframes combat-unit-melee-lean-b {
  0%,
  100% {
    transform: rotate(0deg) translateX(0);
  }
  40% {
    transform: rotate(-11deg) translateX(-5%);
  }
}

.combat-unit--melee-lean.combat-unit--idle-flying .combat-unit__art-frame,
.combat-unit--melee-lean.combat-unit--idle-humanoid .combat-unit__art-frame,
.combat-unit--melee-lean.combat-unit--idle-monstrous .combat-unit__art-frame {
  animation: combat-unit-melee-lean-a 320ms ease-in-out;
}

.combat-unit--melee-lean.combat-unit--idle-monstrous .combat-unit__icon {
  animation: none;
}

.combat-unit--melee-lean.combat-unit--idle-flying .combat-unit__art-frame--b,
.combat-unit--melee-lean.combat-unit--idle-humanoid .combat-unit__art-frame--b,
.combat-unit--melee-lean.combat-unit--idle-monstrous .combat-unit__art-frame--b {
  animation-name: combat-unit-melee-lean-b;
}

.combat-unit.combat-unit--ash-death.combat-unit--idle-flying .combat-unit__art-frame,
.combat-unit.combat-unit--ash-death.combat-unit--idle-humanoid .combat-unit__art-frame,
.combat-unit.combat-unit--ash-death.combat-unit--idle-monstrous .combat-unit__art-frame,
.combat-unit.combat-unit--ash-death.combat-unit--idle-monstrous .combat-unit__icon {
  animation: none;
}

@media (prefers-reduced-motion: reduce) {
  .combat-unit.combat-unit--ash-death .combat-unit__scale,
  .combat-unit.combat-unit--ash-death .combat-unit__ability-icons,
  .combat-unit.combat-unit--ash-death .combat-unit__status-icons,
  .combat-unit__death-ash-fleck {
    animation: none;
    opacity: 0;
  }

  .combat-unit--idle-flying .combat-unit__art-frame,
  .combat-unit--idle-humanoid .combat-unit__art-frame,
  .combat-unit--idle-monstrous .combat-unit__art-frame,
  .combat-unit--idle-monstrous .combat-unit__icon,
  .combat-unit--melee-lean .combat-unit__art-frame {
    animation: none;
  }
}
