/* เถ้าแห่งคำสาป v3.1 — fx31.css · เอฟเฟคพิเศษ (ใช้เฉพาะ transform/opacity · โหนดถูกลบหลังจบ)
   ปิดได้: localStorage ashes_fx31 = "0" หรือ ⋯ ตั้งค่า → เอฟเฟคพิเศษ */

#fx31-canvas { position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: 55; pointer-events: none; }

/* fx31 on → hide the v1/v2 effects it replaces (small square bursts, plain slash, grey souls, plain -N numbers) */
body.fx31-on .fx-burst, body.fx31-on .fx-slash, body.fx31-on .fx-soul-grey,
body.fx31-on .float-dmg.dmg, body.fx31-on .float-dmg.heal { display: none !important; }
/* corpses fade after the shards have taken over */
body.fx31-on .enemy.dead { opacity: 0; transition: opacity .05s linear .26s; }

/* ── floating numbers ── */
.fx31-num {
  position: fixed; z-index: 70; pointer-events: none; font-weight: 900; line-height: 1;
  font-size: calc(1.45rem * var(--s, 1)); color: #fff; white-space: nowrap;
  -webkit-text-stroke: 3px #14060a; paint-order: stroke fill;
  text-shadow: 0 0 10px var(--nc, #ff3d63), 0 3px 0 #0008;
  transform: translate(-50%, -50%); will-change: transform, opacity;
  animation: fx31Num .95s cubic-bezier(.2, .8, .3, 1) forwards;
}
.fx31-num.dmg { --nc: #ff3d63; color: #fff1ee; }
.fx31-num.heal { --nc: #4fe08a; color: #eaffee; }
.fx31-num.block { --nc: #6ab8ff; color: #e6f3ff; font-size: calc(1.15rem * var(--s, 1)); }
@keyframes fx31Num {
  0% { transform: translate(-50%, -30%) scale(.3); opacity: 0; }
  14% { transform: translate(-50%, -60%) scale(1.35); opacity: 1; }
  30% { transform: translate(-50%, -66%) scale(1); opacity: 1; }
  72% { opacity: 1; }
  100% { transform: translate(calc(-50% + var(--dx, 0px)), -170%) scale(.9); opacity: 0; }
}

/* ── shatter / hurt ── */
.fx31-shatter { position: fixed; z-index: 54; pointer-events: none; }
.fx31-hurt {
  position: fixed; inset: 0; z-index: 52; pointer-events: none;
  background: radial-gradient(ellipse at 50% 50%, transparent 55%, rgba(200, 20, 50, .5) 100%);
  animation: fx31Hurt .5s ease-out forwards;
}
@keyframes fx31Hurt { 0% { opacity: 0; } 22% { opacity: 1; } 100% { opacity: 0; } }
.fx31-lostgray { filter: grayscale(.75) brightness(.8); transition: filter 1s ease; }

/* ── 4. traps ── */
.fx31-glyph { position: fixed; z-index: 62; pointer-events: none; width: 84px; height: 84px; margin: -42px 0 0 -42px; color: #e0a45a; filter: drop-shadow(0 0 8px #d9964f); animation: fx31Glyph 1.2s ease-out forwards; }
.fx31-glyph svg { width: 100%; height: 100%; }
@keyframes fx31Glyph {
  0% { transform: scale(.4) rotate(-40deg); opacity: 0; }
  25% { transform: scale(1.15) rotate(0); opacity: 1; }
  45% { transform: scale(.95); opacity: .75; } 62% { transform: scale(1.1); opacity: 1; }
  100% { transform: scale(.7) translateY(-20px); opacity: 0; }
}
.fx31-reveal { position: fixed; left: 50%; top: 40%; z-index: 66; pointer-events: none; text-align: center; transform: translate(-50%, -50%); animation: fx31Reveal 1.2s cubic-bezier(.2, .9, .3, 1) forwards; }
.fx31-reveal small { display: block; color: #ffd9a0; font-weight: 800; letter-spacing: .16em; font-size: .72rem; text-shadow: 0 0 8px #d9964f; }
.fx31-reveal b { display: block; margin-top: 2px; padding: 8px 22px; font-size: 1.35rem; font-weight: 900; color: #fff1d2; background: linear-gradient(180deg, #4a3212f2, #1c1208f2); border: 1px solid #d9964f; border-radius: 12px; box-shadow: 0 0 26px #d9964f66, 0 6px 18px #000a; white-space: nowrap; }
@keyframes fx31Reveal {
  0% { transform: translate(-50%, -50%) scale(1.8); opacity: 0; filter: brightness(2.2); }
  16% { transform: translate(-50%, -50%) scale(.96); opacity: 1; filter: brightness(1.4); }
  26% { transform: translate(-50%, -50%) scale(1); filter: none; }
  78% { opacity: 1; } 100% { transform: translate(-50%, -64%) scale(1); opacity: 0; }
}
.fx31-reveal.rm { animation: fx31Fade 1.2s ease forwards; transform: translate(-50%, -50%); }
.fx31-trapmark { position: absolute; left: 4px; top: -13px; display: flex; gap: 5px; pointer-events: none; z-index: 3; }
.hero-panel { position: relative; }
.fx31-trapmark i { width: 12px; height: 12px; transform: rotate(45deg); border: 1.5px solid #e0a45a; background: radial-gradient(circle, #ffd9a0 0 2px, #4a3212 3px); box-shadow: 0 0 8px #d9964f; animation: fx31TrapPulse 1.6s ease-in-out infinite; animation-delay: calc(var(--i) * .25s); }
@keyframes fx31TrapPulse { 0%, 100% { opacity: .55; transform: rotate(45deg) scale(.9); } 50% { opacity: 1; transform: rotate(45deg) scale(1.15); } }

/* ── 4. persistent buff glow ── */
.unit.fx-buffed { border-color: #ffd27a; box-shadow: 0 4px 14px #000a, 0 0 14px #ffc45a88, inset 0 0 14px #ffc45a33; }
.unit.fx-buffed::before { /* orbiting motes */
  content: ""; position: absolute; left: 50%; top: 50%; width: 118%; aspect-ratio: 1; margin: 0; pointer-events: none; z-index: 4;
  transform: translate(-50%, -50%) rotate(0deg); border-radius: 50%;
  background:
    radial-gradient(circle at 50% 3%, #fff7d0 0 1.6px, #ffc45a 2px, transparent 4px),
    radial-gradient(circle at 97% 50%, #fff7d0 0 1.3px, #ffc45a 1.7px, transparent 3.4px),
    radial-gradient(circle at 24% 92%, #fff7d0 0 1.4px, #ffc45a 1.8px, transparent 3.6px);
  animation: fx31Orbit 4.4s linear infinite;
}
.unit.fx-buffed::after { content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 3; background: radial-gradient(ellipse at 50% 100%, #ffc45a55, transparent 65%); animation: fx31Breathe 2.6s ease-in-out infinite; }
@keyframes fx31Orbit { to { transform: translate(-50%, -50%) rotate(360deg); } }
@keyframes fx31Breathe { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }
.player-portrait.fx31-buffed { position: relative; border-color: var(--fxc, #ffd27a); box-shadow: 0 4px 16px #0008, 0 0 14px var(--fxc, #ffd27a), inset 0 0 8px color-mix(in srgb, var(--fxc, #ffd27a) 50%, transparent); }
.player-portrait.fx31-buffed::before { content: ""; position: absolute; left: 50%; top: 50%; width: 132%; aspect-ratio: 1; pointer-events: none; transform: translate(-50%, -50%); border-radius: 50%;
  background: radial-gradient(circle at 50% 2%, #fff 0 1.5px, var(--fxc) 2px, transparent 4px), radial-gradient(circle at 98% 50%, #fff 0 1.3px, var(--fxc) 1.7px, transparent 3.4px), radial-gradient(circle at 20% 90%, #fff 0 1.3px, var(--fxc) 1.7px, transparent 3.4px);
  animation: fx31Orbit 5s linear infinite; }
.fx31-kw { animation: fx31Kw .7s cubic-bezier(.2, .9, .3, 1); }
@keyframes fx31Kw { 0% { transform: scale(1); filter: brightness(1); } 25% { transform: scale(1.35); filter: brightness(1.9) drop-shadow(0 0 7px #ffd27a); } 60% { transform: scale(.96); } 100% { transform: scale(1); filter: none; } }

/* ═════ 3. rarity entrance — shared frame ═════ */
@keyframes fx31Fade { 0% { opacity: 0; } 15% { opacity: 1; } 80% { opacity: 1; } 100% { opacity: 0; } }
.fx31-cine { position: fixed; inset: 0; z-index: 60; pointer-events: none; overflow: hidden; --tc: #e0b560; --tc2: #fff1c0; --d: 1.2s; }
.fx31-cine > * { position: absolute; }
.fx31-cine .dim { inset: 0; background: radial-gradient(ellipse at 50% 44%, rgba(0, 0, 0, .45), rgba(0, 0, 0, .88)); animation: fx31Dim var(--d) ease-out both; }
@keyframes fx31Dim { 0% { opacity: 0; } 14% { opacity: 1; } 80% { opacity: 1; } 100% { opacity: 0; } }
.fx31-cine .rays { left: 50%; top: 44%; width: 120vmax; height: 120vmax; margin: -60vmax 0 0 -60vmax; border-radius: 50%; will-change: transform, opacity;
  background: repeating-conic-gradient(from 0deg, color-mix(in srgb, var(--tc) 60%, transparent) 0 4deg, transparent 4deg 15deg);
  -webkit-mask: radial-gradient(circle, #000 0, transparent 34%); mask: radial-gradient(circle, #000 0, transparent 34%); animation: fx31Rays var(--d) ease-out both; }
@keyframes fx31Rays { 0% { transform: rotate(0) scale(.3); opacity: 0; } 22% { opacity: .85; } 100% { transform: rotate(36deg) scale(1); opacity: 0; } }
.fx31-cine .beam { left: 50%; top: 0; bottom: 56%; width: min(30vw, 120px); margin-left: calc(min(30vw, 120px) / -2); transform-origin: 50% 0; will-change: transform, opacity; -webkit-mask: linear-gradient(90deg, transparent, #000 35%, #000 65%, transparent); mask: linear-gradient(90deg, transparent, #000 35%, #000 65%, transparent);
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--tc) 55%, transparent) 55%, var(--tc2) 100%); animation: fx31Beam var(--d) ease-out both; }
@keyframes fx31Beam { 0% { transform: scaleY(0); opacity: 0; } 16% { transform: scaleY(1); opacity: .95; } 100% { transform: scaleY(1); opacity: 0; } }
.fx31-cine .flare { left: 50%; top: 44%; width: 40vmin; height: 40vmin; margin: -20vmin; border-radius: 50%; background: radial-gradient(circle, #fff, var(--tc2) 20%, transparent 62%); animation: fx31Flare var(--d) ease-out both; }
@keyframes fx31Flare { 0% { opacity: 0; transform: scale(.3); } 24% { opacity: .5; transform: scale(1.1); } 60% { opacity: 0; transform: scale(1.6); } 100% { opacity: 0; } }
.fx31-cine .frame { left: 50%; top: 44%; width: min(70vw, 290px); aspect-ratio: 16 / 10; border: 2px solid var(--tc); border-radius: 10px; overflow: hidden; background: #0a0806;
  box-shadow: 0 0 34px color-mix(in srgb, var(--tc) 75%, transparent), 0 10px 26px #000c; transform: translate(-50%, -50%); animation: fx31Frame var(--d) cubic-bezier(.2, .9, .3, 1) both; will-change: transform, opacity; }
@keyframes fx31Frame { 0% { opacity: 0; transform: translate(-50%, -50%) scale(.7); } 18% { opacity: 1; transform: translate(-50%, -50%) scale(1.06); } 28% { transform: translate(-50%, -50%) scale(1); } 80% { opacity: 1; } 100% { opacity: 0; transform: translate(-50%, -54%) scale(1.04); } }
.fx31-cine .frame .art { position: absolute; inset: 0; background: center / cover no-repeat; }
.fx31-cine .frame .art.crest { display: flex; align-items: center; justify-content: center; font-size: 3.4rem; color: var(--tc2); background: radial-gradient(circle, color-mix(in srgb, var(--tc) 35%, #0a0806), #0a0806); }
.fx31-cine .frame .shine { position: absolute; inset: 0; background: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, .5) 50%, transparent 62%); transform: translateX(-120%); animation: fx31Shine var(--d) ease-in-out both; }
@keyframes fx31Shine { 0%, 25% { transform: translateX(-120%); } 55% { transform: translateX(120%); } 100% { transform: translateX(120%); } }
.fx31-cine .cap { left: 0; right: 0; top: calc(44% + min(70vw, 290px) * .3125 + 14px); text-align: center; animation: fx31Cap var(--d) ease-out both; }
.fx31-cine .cap small { display: block; font-weight: 800; letter-spacing: .2em; font-size: .72rem; color: var(--tc2); text-shadow: 0 0 10px var(--tc); }
.fx31-cine .cap b { display: block; margin-top: 3px; font-size: clamp(1.4rem, 7vw, 1.9rem); font-weight: 900; color: #fff; letter-spacing: .04em; text-shadow: 0 0 18px var(--tc), 0 0 2px var(--tc), 0 3px 0 #000a; }
@keyframes fx31Cap { 0% { opacity: 0; transform: translateY(14px) scale(.9); letter-spacing: .4em; } 22% { opacity: 1; transform: none; } 80% { opacity: 1; } 100% { opacity: 0; } }
/* rare = quick, soft; curse = dark */
.fx31-cine.rare .dim { background: radial-gradient(ellipse at 50% 44%, transparent 30%, rgba(0, 0, 0, .5)); }
.fx31-cine.rare .frame { width: min(54vw, 220px); }
.fx31-cine.rare .cap { top: calc(44% + min(54vw, 220px) * .3125 + 12px); }
.fx31-cine.rare .cap b { font-size: 1.25rem; }
.fx31-cine.curse .dim { background: radial-gradient(ellipse at 50% 44%, rgba(40, 0, 16, .55), rgba(6, 0, 4, .92)); }
/* custom entrance: the frame lands after the character's motif plays */
.fx31-cine.custom .frame { animation-name: fx31FrameC; }
.fx31-cine.custom .cap { animation-name: fx31CapC; }
.fx31-cine.custom .shine { animation-name: fx31ShineC; }
@keyframes fx31FrameC { 0%, 30% { opacity: 0; transform: translate(-50%, -50%) scale(.78); } 42% { opacity: 1; transform: translate(-50%, -50%) scale(1.05); } 50% { transform: translate(-50%, -50%) scale(1); } 86% { opacity: 1; } 100% { opacity: 0; transform: translate(-50%, -53%) scale(1.03); } }
@keyframes fx31CapC { 0%, 38% { opacity: 0; transform: translateY(12px) scale(.9); letter-spacing: .4em; } 54% { opacity: 1; transform: none; } 86% { opacity: 1; } 100% { opacity: 0; } }
@keyframes fx31ShineC { 0%, 46% { transform: translateX(-120%); } 76% { transform: translateX(120%); } 100% { transform: translateX(120%); } }
.fx31-cine.custom .beam, .fx31-cine.custom .rays { display: none; }
.fx31-cine.rm > :not(.dim):not(.frame):not(.cap) { display: none; }
.fx31-cine.rm .dim, .fx31-cine.rm .frame, .fx31-cine.rm .cap { animation-name: fx31Fade; }
.fx31-cine.rm .frame { transform: translate(-50%, -50%); }

/* ═════ 3b. per-character entrances ═════ */
/* — vs_keeper · lighthouse beam sweeps in — */
.v-vs_keeper .k-sea { left: -10%; right: -10%; bottom: -6%; height: 38%; background: radial-gradient(ellipse at 50% 100%, rgba(90, 47, 208, .6), transparent 70%); animation: fx31Fade var(--d) ease-out both; }
.v-vs_keeper .k-beam { left: 9%; top: 34%; width: 150vmax; height: 22vmax; margin-top: -11vmax; transform-origin: 0 50%; will-change: transform, opacity;
  background: linear-gradient(90deg, #fff 0, var(--tc2) 5%, color-mix(in srgb, var(--tc) 72%, transparent) 30%, transparent 100%); clip-path: polygon(0 47%, 100% 0, 100% 100%, 0 53%); animation: kBeam 1.4s cubic-bezier(.35, .1, .3, 1) both; }
@keyframes kBeam { 0% { transform: rotate(-62deg); opacity: 0; } 10% { opacity: 1; } 64% { opacity: 1; } 100% { transform: rotate(34deg); opacity: 0; } }
.v-vs_keeper .k-lamp { left: 9%; top: 34%; width: 44px; height: 44px; margin: -22px 0 0 -22px; border-radius: 50%; background: radial-gradient(#fff, var(--tc2) 25%, var(--tc) 48%, transparent 72%); animation: kLamp 1.4s ease-out both; }
@keyframes kLamp { 0% { opacity: 0; transform: scale(.3); } 12% { opacity: 1; transform: scale(1.6); } 70% { opacity: .9; transform: scale(1.1); } 100% { opacity: 0; } }
/* — vs_revenant · black-sun eclipse rises, corona flares — */
.v-vs_revenant .r-corona { left: 50%; top: 44%; width: 92vmin; height: 92vmin; margin: -46vmin 0 0 -46vmin; border-radius: 50%; will-change: transform, opacity;
  background: repeating-conic-gradient(from 0deg, color-mix(in srgb, var(--tc) 85%, transparent) 0 2deg, transparent 2deg 8deg);
  -webkit-mask: radial-gradient(circle, transparent 19%, #000 21%, transparent 60%); mask: radial-gradient(circle, transparent 19%, #000 21%, transparent 60%); animation: rCor 1.45s ease-out both; }
@keyframes rCor { 0%, 26% { opacity: 0; transform: scale(.4) rotate(0); } 46% { opacity: 1; transform: scale(1) rotate(10deg); } 100% { opacity: 0; transform: scale(1.3) rotate(28deg); } }
.v-vs_revenant .r-sun { left: 50%; top: 44%; width: 36vmin; height: 36vmin; margin: -18vmin 0 0 -18vmin; border-radius: 50%; background: #000; will-change: transform, opacity;
  box-shadow: 0 0 0 2px var(--tc2), 0 0 36px 8px var(--tc), 0 0 110px 34px color-mix(in srgb, var(--tc) 55%, transparent); animation: rSun 1.45s cubic-bezier(.2, .8, .3, 1) both; }
@keyframes rSun { 0% { transform: translateY(62vh) scale(.6); opacity: 0; } 22% { opacity: 1; } 38% { transform: none; } 60% { transform: translateY(-2vh) scale(1.05); opacity: 1; } 100% { transform: translateY(-5vh) scale(1.3); opacity: 0; } }
/* — vs_herald · crown of stars ignites · eyes open — */
.v-vs_herald .h-star { left: var(--x); top: var(--y); margin: -16px 0 0 -16px; width: 32px; text-align: center; font-size: 30px; line-height: 32px; color: var(--tc2); text-shadow: 0 0 12px var(--tc), 0 0 26px var(--tc); animation: hStar 1.5s ease-out both; animation-delay: calc(var(--i) * .08s + .1s); }
@keyframes hStar { 0% { opacity: 0; transform: scale(0) rotate(-90deg); } 10% { opacity: 1; transform: scale(2.1) rotate(0); filter: brightness(2.2); } 22% { transform: scale(1.1); filter: none; } 74% { opacity: 1; } 100% { opacity: 0; transform: scale(1); } }
.v-vs_herald .h-eye { left: var(--x); top: var(--y); width: var(--w, 44px); height: calc(var(--w, 44px) * .5); margin: calc(var(--w, 44px) * -.25) 0 0 calc(var(--w, 44px) * -.5); transform-origin: 50% 50%; animation: hEye 1.5s ease-in-out both; animation-delay: calc(var(--i) * .07s + .5s); filter: drop-shadow(0 0 6px var(--tc)); }
.v-vs_herald .h-eye svg { width: 100%; height: 100%; }
@keyframes hEye { 0% { opacity: 0; transform: scaleY(0); } 10% { opacity: 1; transform: scaleY(1.1); } 18% { transform: scaleY(1); } 50% { opacity: 1; transform: scaleY(1); } 56% { transform: scaleY(.08); } 62% { transform: scaleY(1); } 100% { opacity: 0; transform: scaleY(1); } }
.v-vs_herald .h-veil { inset: 0; background: radial-gradient(ellipse at 50% 20%, rgba(138, 92, 255, .38), transparent 60%); animation: fx31Fade var(--d) ease-out both; }
/* — hx_countess · crimson mist + bats — */
.v-hx_countess .c-mist { inset: -10%; background: radial-gradient(ellipse 50% 30% at 20% 82%, rgba(190, 20, 56, .6), transparent 70%), radial-gradient(ellipse 55% 35% at 82% 72%, rgba(150, 10, 44, .55), transparent 70%), radial-gradient(ellipse 60% 40% at 50% 100%, rgba(210, 30, 66, .55), transparent 70%); animation: cMist 1.45s ease-out both; }
@keyframes cMist { 0% { opacity: 0; transform: translateY(14%) scale(1); } 30% { opacity: 1; } 100% { opacity: 0; transform: translateY(-9%) scale(1.15); } }
.v-hx_countess .c-bat { left: 0; top: var(--y); width: 70px; height: 36px; margin-left: -74px; will-change: transform; animation: cBat 1.15s cubic-bezier(.4, 0, .5, 1) both; animation-delay: var(--dl); }
.v-hx_countess .c-bat svg { width: 100%; height: 100%; animation: cFlap .13s ease-in-out infinite alternate; transform-origin: 50% 60%; }
@keyframes cBat { 0% { transform: translate3d(0, 0, 0) scale(var(--s, 1)); } 100% { transform: translate3d(calc(100vw + 120px), var(--dy), 0) scale(var(--s, 1)); } }
@keyframes cFlap { from { transform: scaleY(1); } to { transform: scaleY(.35); } }
/* — gw_golem · ground slam, dust, cracks — */
.v-gw_golem .g-crack { inset: 0; width: 100%; height: 100%; }
.v-gw_golem .g-crack path { fill: none; stroke: var(--tc2); stroke-width: .7; stroke-linecap: round; stroke-dasharray: 1; stroke-dashoffset: 1; filter: drop-shadow(0 0 2px var(--tc)); animation: gCrack 1.3s ease-out both; animation-delay: .34s; }
@keyframes gCrack { 0% { stroke-dashoffset: 1; opacity: 1; } 28% { stroke-dashoffset: 0; opacity: 1; } 80% { opacity: 1; } 100% { stroke-dashoffset: 0; opacity: 0; } }
.v-gw_golem .g-flash { left: 50%; top: 78%; width: 70vmin; height: 24vmin; margin: -12vmin 0 0 -35vmin; border-radius: 50%; background: radial-gradient(ellipse, #fff, var(--tc2) 25%, transparent 65%); animation: gFlash 1.3s ease-out both; }
@keyframes gFlash { 0%, 24% { opacity: 0; transform: scale(.3); } 30% { opacity: 1; transform: scale(1); } 52% { opacity: 0; transform: scale(1.6); } 100% { opacity: 0; } }
.v-gw_golem .frame { animation-name: fx31FrameSlam; }
@keyframes fx31FrameSlam { 0% { opacity: 0; transform: translate(-50%, -190%) scale(1.15); } 12% { opacity: 1; } 27% { transform: translate(-50%, -50%) scale(1.04, .9); } 33% { transform: translate(-50%, -50%) scale(.98, 1.04); } 40% { transform: translate(-50%, -50%) scale(1); } 84% { opacity: 1; } 100% { opacity: 0; transform: translate(-50%, -50%) scale(1.02); } }
.v-gw_golem .cap { animation-name: fx31CapC; }
/* — gw_colossus · weeping giant: tear-rain + two stomps — */
.v-gw_colossus .w-rain { inset: -30% 0 0; will-change: transform, opacity; background: repeating-linear-gradient(101deg, transparent 0 16px, rgba(160, 235, 215, .5) 16px 17px, transparent 17px 38px); animation: wRain .42s linear infinite, fx31Fade var(--d) ease-out both; }
@keyframes wRain { from { transform: translateY(-14%); } to { transform: translateY(0); } }
.v-gw_colossus .w-rip { left: 50%; top: 80%; width: 30vmin; height: 9vmin; margin: -4.5vmin 0 0 -15vmin; border: 2px solid var(--tc2); border-radius: 50%; opacity: 0; animation: wRip .8s ease-out both; animation-delay: calc(.28s + var(--i) * .17s); }
@keyframes wRip { 0% { opacity: .95; transform: scale(.2); } 100% { opacity: 0; transform: scale(3.6); } }
.v-gw_colossus .frame { animation-name: fx31FrameStomp; }
@keyframes fx31FrameStomp { 0% { opacity: 0; transform: translate(-50%, -50%) scale(1.7); } 18% { opacity: 1; transform: translate(-50%, -50%) scale(1.06); } 24% { transform: translate(-50%, -50%) scale(.97); } 30% { transform: translate(-50%, -50%) scale(1.02); } 44% { transform: translate(-50%, -50%) scale(1.07); } 50% { transform: translate(-50%, -50%) scale(.97); } 58% { transform: translate(-50%, -50%) scale(1); } 86% { opacity: 1; } 100% { opacity: 0; } }
.v-gw_colossus .cap { animation-name: fx31CapC; }
/* — ob_templar · holy cross + shield — */
.v-ob_templar .t-bar { left: 50%; top: 44%; background: linear-gradient(90deg, transparent, var(--tc2), transparent); will-change: transform, opacity; animation: tBar 1.3s ease-out both; }
.v-ob_templar .t-bar.h { width: 130vw; height: 8px; margin: -4px 0 0 -65vw; }
.v-ob_templar .t-bar.v { width: 8px; height: 130vh; margin: -65vh 0 0 -4px; background: linear-gradient(180deg, transparent, var(--tc2), transparent); animation-delay: .06s; }
@keyframes tBar { 0% { opacity: 0; transform: scale(0); } 16% { opacity: 1; transform: scale(1); } 60% { opacity: .6; } 100% { opacity: 0; transform: scale(1.1); } }
.v-ob_templar .t-shield { left: 50%; top: 44%; width: 60vmin; height: 60vmin; margin: -30vmin 0 0 -30vmin; animation: tShield 1.3s cubic-bezier(.2, .9, .3, 1) both; filter: drop-shadow(0 0 14px var(--tc)); }
@keyframes tShield { 0%, 8% { opacity: 0; transform: scale(.3); } 28% { opacity: .9; transform: scale(1.06); } 40% { transform: scale(1); } 72% { opacity: .55; } 100% { opacity: 0; transform: scale(1.1); } }
/* — gw_lord · ghost-fire pillars + crown — */
.v-gw_lord .l-pil { bottom: 0; left: var(--x); width: 9vmin; margin-left: -4.5vmin; height: var(--h); transform-origin: 50% 100%; background: linear-gradient(0deg, color-mix(in srgb, var(--tc) 70%, transparent), color-mix(in srgb, var(--tc) 20%, transparent) 60%, transparent); animation: lPil 1.4s ease-out both; animation-delay: calc(var(--i) * .07s); will-change: transform, opacity; }
@keyframes lPil { 0% { opacity: 0; transform: scaleY(0); } 22% { opacity: .95; transform: scaleY(1); } 100% { opacity: 0; transform: scaleY(1.1); } }
.v-gw_lord .l-crown { left: 50%; top: 4%; width: 60px; margin-left: -30px; text-align: center; font-size: 52px; line-height: 1; color: var(--tc2); text-shadow: 0 0 20px var(--tc), 0 0 40px var(--tc); animation: lCrown 1.4s cubic-bezier(.3, .8, .3, 1) both; }
@keyframes lCrown { 0%, 18% { opacity: 0; transform: translateY(-30px) scale(1.6); } 40% { opacity: 1; transform: translateY(0) scale(1); } 82% { opacity: 1; } 100% { opacity: 0; } }
/* — ob_martyr · descending halo + light column — */
.v-ob_martyr .m-col { left: 50%; top: 0; bottom: 0; width: 36vmin; margin-left: -18vmin; background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--tc2) 65%, transparent), transparent); animation: mCol 1.3s ease-out both; }
@keyframes mCol { 0% { opacity: 0; transform: scaleX(.2); } 25% { opacity: .85; transform: scaleX(1); } 100% { opacity: 0; transform: scaleX(1.2); } }
.v-ob_martyr .m-halo { left: 50%; top: 14%; width: 44vmin; height: 12vmin; margin-left: -22vmin; border: 4px solid var(--tc2); border-radius: 50%; box-shadow: 0 0 22px var(--tc), inset 0 0 18px var(--tc); animation: mHalo 1.3s cubic-bezier(.3, .9, .3, 1) both; }
@keyframes mHalo { 0% { opacity: 0; transform: translateY(-34vh) scale(.5); } 40% { opacity: 1; transform: translateY(0) scale(1); } 80% { opacity: 1; transform: translateY(4px) scale(1.02); } 100% { opacity: 0; transform: translateY(10px) scale(1.1); } }
/* — vs_awaken · the void opens an eye — */
.v-vs_awaken .a-veil { inset: 0; background: radial-gradient(circle at 50% 44%, #000 20%, rgba(40, 10, 90, .6) 60%, transparent); animation: fx31Fade var(--d) ease-out both; }
.v-vs_awaken .a-eye { left: 50%; top: 44%; width: min(94vw, 420px); height: calc(min(94vw, 420px) * .5); margin: calc(min(94vw, 420px) * -.25) 0 0 calc(min(94vw, 420px) * -.5); filter: drop-shadow(0 0 18px var(--tc)); animation: aEye 1.4s cubic-bezier(.3, .8, .3, 1) both; }
.v-vs_awaken .a-eye svg { width: 100%; height: 100%; }
@keyframes aEye { 0%, 6% { opacity: 0; transform: scaleY(0); } 30% { opacity: 1; transform: scaleY(1.08); } 40% { transform: scaleY(1); } 70% { opacity: .9; } 100% { opacity: 0; transform: scale(1.25); } }
/* — vs_nova · star collapses, then goes supernova — */
.v-vs_nova .n-core { left: 50%; top: 44%; width: 34px; height: 34px; margin: -17px 0 0 -17px; border-radius: 50%; background: #fff; box-shadow: 0 0 30px 12px var(--tc), 0 0 80px 30px var(--tc); animation: nCore 1.3s ease-in-out both; will-change: transform, opacity; }
@keyframes nCore { 0% { opacity: 0; transform: scale(7); } 28% { opacity: 1; transform: scale(.35); } 36% { transform: scale(.2); } 42% { opacity: 1; transform: scale(2); } 54% { opacity: .75; transform: scale(7); } 66% { opacity: 0; transform: scale(11); } 100% { opacity: 0; transform: scale(11); } }
/* — ob_crusade · burning lances fall — */
.v-ob_crusade .cr-lance { top: 0; left: var(--x); width: 7px; height: 46vh; margin-left: -3px; transform: rotate(var(--r)); background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--tc) 70%, transparent) 55%, #fff 100%); box-shadow: 0 0 14px var(--tc); animation: crLance .5s cubic-bezier(.5, 0, 1, .6) both; animation-delay: calc(var(--i) * .17s + .05s); will-change: transform, opacity; }
@keyframes crLance { 0% { opacity: 0; transform: translateY(-60vh) rotate(var(--r)); } 20% { opacity: 1; } 85% { opacity: 1; transform: translateY(46vh) rotate(var(--r)); } 100% { opacity: 0; transform: translateY(56vh) rotate(var(--r)); } }
/* — hx_frenzy · heartbeat — */
.v-hx_frenzy .f-beat { inset: 0; box-shadow: inset 0 0 120px 40px rgba(220, 0, 50, .85); animation: fBeat .5s ease-out both; animation-delay: calc(var(--i) * .42s + .05s); }
@keyframes fBeat { 0% { opacity: 0; transform: scale(1.1); } 30% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(.96); } }
.v-hx_frenzy .f-heart { left: 50%; top: 44%; width: 22vmin; height: 22vmin; margin: -11vmin 0 0 -11vmin; border-radius: 50%; background: radial-gradient(circle, var(--tc2), var(--tc) 40%, transparent 70%); animation: fHeart .5s ease-out both; animation-delay: calc(var(--i) * .42s + .05s); }
@keyframes fHeart { 0% { opacity: 0; transform: scale(.4); } 25% { opacity: .95; transform: scale(1.4); } 100% { opacity: 0; transform: scale(2.4); } }
/* — hx_sovereign · blood moon rises — */
.v-hx_sovereign .s-moon { left: 50%; top: 44%; width: 46vmin; height: 46vmin; margin: -23vmin 0 0 -23vmin; border-radius: 50%; background: radial-gradient(circle at 35% 35%, #ffb3c0, var(--tc) 45%, var(--tc3) 100%);
  -webkit-mask: radial-gradient(circle at 72% 36%, transparent 0 40%, #000 41%); mask: radial-gradient(circle at 72% 36%, transparent 0 40%, #000 41%); box-shadow: 0 0 60px 14px var(--tc); animation: sMoon 1.4s cubic-bezier(.2, .8, .3, 1) both; will-change: transform, opacity; }
@keyframes sMoon { 0% { opacity: 0; transform: translateY(55vh) scale(.7); } 40% { opacity: 1; transform: translateY(0) scale(1); } 80% { opacity: .9; } 100% { opacity: 0; transform: translateY(-4vh) scale(1.06); } }
.v-hx_sovereign .s-glow { inset: 0; background: radial-gradient(circle at 50% 44%, rgba(255, 40, 80, .4), transparent 62%); animation: fx31Fade var(--d) ease-out both; }
/* — n_crow_mother · crows sweep — */
.v-n_crow_mother .cm-crow { left: 0; top: var(--y); width: 54px; height: 26px; margin-left: -60px; will-change: transform; animation: cmCrow 1.25s cubic-bezier(.35, .1, .5, 1) both; animation-delay: var(--dl); }
.v-n_crow_mother .cm-crow.r { left: auto; right: 0; margin: 0 -60px 0 0; animation-name: cmCrowR; }
.v-n_crow_mother .cm-crow svg { width: 100%; height: 100%; animation: cFlap .15s ease-in-out infinite alternate; transform-origin: 50% 60%; }
@keyframes cmCrow { 0% { transform: translate3d(0, 0, 0) scale(var(--s, 1)); } 100% { transform: translate3d(calc(100vw + 130px), var(--dy), 0) scale(var(--s, 1)); } }
@keyframes cmCrowR { 0% { transform: translate3d(0, 0, 0) scale(var(--s, 1)) scaleX(-1); } 100% { transform: translate3d(calc(-100vw - 130px), var(--dy), 0) scale(var(--s, 1)) scaleX(-1); } }
/* — ob_chain_warden · chains slam from both sides — */
.v-ob_chain_warden .cw-ch { top: 44%; width: 52%; height: 10px; margin-top: -5px; background: repeating-linear-gradient(90deg, var(--tc) 0 15px, transparent 15px 21px); border-radius: 5px; box-shadow: 0 0 12px var(--tc); will-change: transform, opacity; }
.v-ob_chain_warden .cw-ch.l { left: 0; animation: cwL 1.3s cubic-bezier(.6, 0, .3, 1) both; }
.v-ob_chain_warden .cw-ch.r { right: 0; animation: cwR 1.3s cubic-bezier(.6, 0, .3, 1) both; }
@keyframes cwL { 0% { transform: translateX(-100%); } 26% { transform: translateX(0); opacity: 1; } 34% { transform: translateX(-3%) scaleY(1.5); } 70% { opacity: 1; } 100% { transform: translateX(-30%); opacity: 0; } }
@keyframes cwR { 0% { transform: translateX(100%); } 26% { transform: translateX(0); opacity: 1; } 34% { transform: translateX(3%) scaleY(1.5); } 70% { opacity: 1; } 100% { transform: translateX(30%); opacity: 0; } }
/* — ob_fallen_saint · broken halo + wings — */
.v-ob_fallen_saint .fs-wing { left: 50%; top: 44%; width: 46vmin; height: 20vmin; margin-top: -10vmin; background: radial-gradient(ellipse at 0 50%, #fff, color-mix(in srgb, var(--tc2) 70%, transparent) 40%, transparent 72%); border-radius: 100% 0 0 100% / 50%; will-change: transform, opacity; }
.v-ob_fallen_saint .fs-wing.l { margin-left: -46vmin; transform-origin: 100% 50%; transform: scaleX(-1); animation: fsWL 1.2s ease-out both; }
.v-ob_fallen_saint .fs-wing.r { margin-left: 0; transform-origin: 0 50%; animation: fsWR 1.2s ease-out both; }
.v-ob_fallen_saint .fs-wing.l { background: radial-gradient(ellipse at 100% 50%, #fff, color-mix(in srgb, var(--tc2) 70%, transparent) 40%, transparent 72%); border-radius: 0 100% 100% 0 / 50%; }
@keyframes fsWL { 0% { opacity: 0; transform: scaleX(0); } 28% { opacity: .9; transform: scaleX(1); } 100% { opacity: 0; transform: scaleX(1.12); } }
@keyframes fsWR { 0% { opacity: 0; transform: scaleX(0); } 28% { opacity: .9; transform: scaleX(1); } 100% { opacity: 0; transform: scaleX(1.12); } }
.v-ob_fallen_saint .fs-halo { left: 50%; top: 14%; width: 40vmin; height: 10vmin; margin-left: -20vmin; border: 3px solid var(--tc2); border-bottom-color: transparent; border-left-color: transparent; border-radius: 50%; box-shadow: 0 0 16px var(--tc); animation: fsHalo 1.2s ease-in both; }
@keyframes fsHalo { 0% { opacity: 0; transform: translateY(-4vh) rotate(0); } 24% { opacity: 1; } 100% { opacity: 0; transform: translateY(46vh) rotate(38deg); } }

/* ═════ 4b. boss · victory · defeat ═════ */
.fx31-boss { position: fixed; inset: 0; z-index: 58; pointer-events: none; overflow: hidden; animation: fx31BossLife 1.9s linear forwards; }
@keyframes fx31BossLife { 0%, 82% { opacity: 1; } 100% { opacity: 0; } }
.fx31-boss .pulse { position: absolute; left: 50%; top: 28%; width: 30vmax; height: 30vmax; margin: -15vmax 0 0 -15vmax; border-radius: 50%; background: radial-gradient(circle, transparent 38%, rgba(20, 0, 10, .85) 56%, transparent 72%); animation: fx31BossPulse 1.1s ease-out both; will-change: transform, opacity; }
.fx31-boss .pulse.p2 { background: radial-gradient(circle, transparent 42%, rgba(138, 30, 90, .55) 54%, transparent 66%); animation-delay: .16s; }
@keyframes fx31BossPulse { 0% { opacity: 0; transform: scale(.2); } 18% { opacity: 1; } 100% { opacity: 0; transform: scale(4.2); } }
.fx31-boss .band { position: absolute; left: 0; right: 0; top: 9%; padding: 8px 8px 8px; text-align: center; background: linear-gradient(90deg, transparent, rgba(12, 2, 8, .92) 14%, rgba(34, 6, 22, .95) 50%, rgba(12, 2, 8, .92) 86%, transparent);
  border-top: 1px solid #ff3d6388; border-bottom: 1px solid #b18cff88; animation: fx31BossBand 1.9s cubic-bezier(.2, .9, .3, 1) both; will-change: transform, opacity; }
.fx31-boss .band small { display: block; color: #ff8aa0; letter-spacing: .5em; font-size: .72rem; font-weight: 800; }
.fx31-boss .band b { display: block; font-size: clamp(1.5rem, 7.4vw, 2.3rem); font-weight: 900; letter-spacing: .06em; color: #fff; line-height: 1.15; text-shadow: 0 0 22px #ff3d63, 0 0 3px #ff3d63, 0 3px 0 #000; }
.fx31-boss .band em { display: block; font-style: normal; color: #cdb8ff; font-size: .78rem; letter-spacing: .18em; margin-top: 2px; }
.fx31-boss.final .band b { text-shadow: 0 0 22px #b18cff, 0 0 3px #b18cff, 0 3px 0 #000; }
@keyframes fx31BossBand { 0% { opacity: 0; transform: translateX(-40%) scaleX(.6); } 16% { opacity: 1; transform: none; } 80% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateX(14%); } }
.fx31-boss.rm .pulse { display: none; }
.fx31-boss.rm .band { animation: fx31Fade 1.9s ease forwards; }

.fx31-winflash { position: fixed; inset: 0; z-index: 56; pointer-events: none; overflow: hidden; animation: fx31WinLife 1.2s linear forwards; }
.fx31-winflash.boss { animation-duration: 1.7s; }
@keyframes fx31WinLife { 0% { opacity: 0; } 14% { opacity: 1; } 70% { opacity: 1; } 100% { opacity: 0; } }
.fx31-winflash .rays, .fx31-endfx .rays { position: absolute; left: 50%; top: 34%; width: 120vmax; height: 120vmax; margin: -60vmax 0 0 -60vmax; border-radius: 50%; will-change: transform;
  background: repeating-conic-gradient(from 0deg, rgba(255, 210, 122, .3) 0 3deg, transparent 3deg 14deg); -webkit-mask: radial-gradient(circle, #000 0, transparent 30%); mask: radial-gradient(circle, #000 0, transparent 30%); animation: fx31Spin 14s linear infinite; }
.fx31-winflash.rm .rays { animation: none; opacity: .5; }
@keyframes fx31Spin { to { transform: rotate(360deg); } }
.fx31-endfx { position: absolute; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; }
.fx31-endfx .rays { top: 40%; opacity: .8; }
.fx31-endfx .glow { position: absolute; left: 50%; top: 40%; width: 90vmin; height: 90vmin; margin: -45vmin 0 0 -45vmin; border-radius: 50%; background: radial-gradient(circle, rgba(255, 190, 90, .42), transparent 65%); animation: fx31Breathe 3.2s ease-in-out infinite; }
.fx31-endfx.loss .vig { position: absolute; inset: 0; background: radial-gradient(ellipse at 50% 46%, rgba(20, 24, 30, 0) 18%, rgba(10, 8, 12, .92) 100%); animation: fx31VigIn 1.4s ease-out both; }
@keyframes fx31VigIn { from { opacity: 0; } to { opacity: 1; } }
.overlay.show[data-end="loss"], .overlay.show[data-end="end"] { backdrop-filter: blur(6px) saturate(.35); }
.fx31-lossvig { position: fixed; inset: 0; z-index: 52; pointer-events: none; background: radial-gradient(ellipse at 50% 50%, transparent 40%, rgba(10, 6, 14, .85) 100%); animation: fx31Fade 2.2s ease forwards; }
.fx31-lossvig.rm { animation-duration: 1.2s; }

/* ── reduced motion: nothing moves; only quiet fades remain (JS also checks the in-game motion pref) ── */
@media (prefers-reduced-motion: reduce) {
  .fx31-cine > :not(.dim):not(.frame):not(.cap), .fx31-boss .pulse, .fx31-glyph { display: none; }
  .fx31-num { animation: fx31Fade .9s ease forwards; transform: translate(-50%, -50%); }
  .unit.fx-buffed::before, .player-portrait.fx31-buffed::before, .unit.fx-buffed::after, .fx31-trapmark i, .fx31-kw { animation: none; }
}
body.reduce-motion .fx31-num { animation: fx31Fade .9s ease forwards; transform: translate(-50%, -50%); }
body.reduce-motion .unit.fx-buffed::before, body.reduce-motion .player-portrait.fx31-buffed::before, body.reduce-motion .unit.fx-buffed::after, body.reduce-motion .fx31-trapmark i, body.reduce-motion .fx31-kw { animation: none; }
