/*
  Combat bridge: latest combat/skill presentation only.
  The target worktree keeps its Claude-owned UI system; this file owns
  the imported combat actors, effects, health feedback, and authored skill timing.
*/
.boss-bar .bar{position:relative}
.boss-bar .bar>i{position:relative;z-index:1}
.boss-bar-damage{position:absolute;z-index:2;top:0;left:0;display:block;width:0;height:100%;border-radius:inherit;background:linear-gradient(90deg,#fff,#fffbed);box-shadow:0 0 9px rgba(255,255,255,.95);opacity:0;pointer-events:none}
.boss-bar-damage.active{animation:enemyHealthDamageFlash calc(520ms * var(--combat-speed-factor,1)) ease-out forwards}
.enemy{animation:none!important}
.enemy.boss{animation:none!important}
.enemy .enemy-health{position:absolute;left:7px;right:7px;top:-11px;height:4px;overflow:hidden;border-radius:5px;background:rgba(0,0,0,.45)}
.enemy .enemy-health>i{position:relative;z-index:1;display:block;width:100%;height:100%;background:var(--red);transition:width .18s ease-out}
.enemy .enemy-health-damage{position:absolute;z-index:2;top:0;left:0;display:block;width:0;height:100%;border-radius:inherit;background:linear-gradient(90deg,#fff,#fffbed);box-shadow:0 0 6px rgba(255,255,255,.9);opacity:0;pointer-events:none}
.enemy .enemy-health-damage.active{animation:enemyHealthDamageFlash calc(520ms * var(--combat-speed-factor,1)) ease-out forwards}
@keyframes enemyIdle{from,to{transform:none}}
@keyframes bossIdle{from,to{transform:none}}
@keyframes enemyHealthDamageFlash{0%{opacity:1;filter:brightness(1.65)}58%{opacity:.9;filter:brightness(1.12)}100%{opacity:0;filter:brightness(1)}}


/* Imported latest persistent combat CSS (source styles.css tail). */
 .heal-pillar{
  position:absolute;
  z-index:12;
  left:13%;
  bottom:64px;
  width:124px;
  height:190px;
  opacity:0;
  background:linear-gradient(90deg,transparent,rgba(121,244,174,.12) 16%,rgba(226,255,207,.5) 50%,rgba(121,244,174,.12) 84%,transparent);
  clip-path:polygon(36% 0,64% 0,88% 100%,12% 100%);
  animation:healPillarRise calc(820ms * var(--combat-speed-factor,1)) ease-out both;
}
.heal-pillar::after{
  content:"";
  position:absolute;
  left:16px;
  right:16px;
  bottom:4px;
  height:34px;
  border:3px solid rgba(187,255,190,.8);
  border-radius:50%;
  box-shadow:0 0 0 7px rgba(103,231,151,.16),0 0 24px rgba(91,238,149,.5);
}

@keyframes forestHazeBreathe{from{transform:translate3d(-.4%,0,0);opacity:.5}to{transform:translate3d(.5%,-2px,0);opacity:.68}}
@keyframes forestCanopyBreathe{from{transform:translate3d(-.3%,0,0)}to{transform:translate3d(.3%,2px,0)}}
@keyframes forestFarBreathe{from{transform:translate3d(-.22%,0,0);opacity:.46}to{transform:translate3d(.22%,-1px,0);opacity:.55}}
@keyframes forestLightBreathe{from{transform:translate3d(-.6%,0,0) skewX(-2deg);opacity:.19}to{transform:translate3d(.6%,0,0) skewX(1deg);opacity:.33}}
@keyframes forestMidBreathe{from{transform:translate3d(-.3%,0,0)}to{transform:translate3d(.3%,-2px,0)}}
@keyframes forestNearBreathe{from{transform:translate3d(-.22%,0,0)}to{transform:translate3d(.22%,1px,0)}}
@keyframes forestPathBreathe{from{transform:translate3d(0,0,0)}to{transform:translate3d(0,2px,0)}}
@keyframes forestFrontBreathe{from{transform:translate3d(-.22%,0,0)}to{transform:translate3d(.22%,-2px,0)}}
@keyframes forestTravelHaze{from{transform:translate3d(0,0,0);opacity:.58}50%{opacity:.68}to{transform:translate3d(-7%,0,0);opacity:.58}}
@keyframes forestTravelCanopy{to{transform:translate3d(-11%,0,0)}}
@keyframes forestTravelFar{to{transform:translate3d(-15%,0,0)}}
@keyframes forestTravelLight{from{transform:translate3d(0,0,0) skewX(-2deg);opacity:.21}50%{opacity:.32}to{transform:translate3d(-18%,0,0) skewX(-2deg);opacity:.21}}
@keyframes forestTravelMid{to{transform:translate3d(-22%,0,0)}}
@keyframes forestTravelNear{to{transform:translate3d(-28%,0,0)}}
@keyframes forestTravelPath{to{transform:translate3d(-5%,5px,0)}}
@keyframes forestTravelFront{to{transform:translate3d(-32%,0,0)}}
@keyframes encounterOmenIn{0%{transform:scale(.3);opacity:0}42%{opacity:.78}100%{transform:scale(1.18);opacity:0}}
@keyframes encounterForestClose{0%{transform:scale(1);opacity:.6}45%{transform:scale(1.035);opacity:.82}100%{transform:scale(1);opacity:.62}}
@keyframes enemyRevealIn{0%{transform:translate3d(54px,4px,0) scale(.72);opacity:0}65%{opacity:1}100%{transform:translate3d(0,0,0) scale(1);opacity:1}}
@keyframes encounterShadowWait{from{transform:translate3d(45px,0,0) scale(.78);opacity:.12}to{transform:translate3d(38px,0,0) scale(.84);opacity:.3}}
@keyframes enemyHitstop{0%,100%{transform:translate3d(0,0,0);opacity:1}50%{transform:translate3d(8px,-1px,0) scale(.96);opacity:1}}
@keyframes heroLunge{0%{transform:translate3d(0,0,0) scale(var(--actor-scale,1))}45%{transform:translate3d(43px,-2px,0) scale(calc(var(--actor-scale,1) * 1.02),calc(var(--actor-scale,1) * 0.98))}100%{transform:translate3d(0,0,0) scale(var(--actor-scale,1))}}
@keyframes heroSlashOne{0%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}48%{transform:translate3d(33px,-5px,0) rotate(-5deg) scale(calc(var(--actor-scale,1) * 1.04))}100%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}}
@keyframes heroSlashTwo{0%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}38%{transform:translate3d(57px,3px,0) rotate(4deg) scale(var(--actor-scale,1),calc(var(--actor-scale,1) * 0.97))}72%{transform:translate3d(57px,-7px,0) rotate(-3deg) scale(calc(var(--actor-scale,1) * 1.03))}100%{transform:translate3d(0,0,0) scale(var(--actor-scale,1))}}
@keyframes heroSlashThree{0%{transform:translate3d(0,0,0) scale(var(--actor-scale,1))}28%{transform:translate3d(0,3px,0) scale(calc(var(--actor-scale,1) * 0.99),calc(var(--actor-scale,1) * 0.98))}62%{transform:translate3d(72px,-14px,0) rotate(-7deg) scale(calc(var(--actor-scale,1) * 1.06))}78%{transform:translate3d(72px,-8px,0) scale(calc(var(--actor-scale,1) * 1.02))}100%{transform:translate3d(0,0,0) scale(var(--actor-scale,1))}}
@keyframes skillCinematicExposure{0%{opacity:0}24%{opacity:.92}100%{opacity:0}}
@keyframes starBladeRain{0%{transform:translate3d(38px,-110px,0) rotate(19deg);opacity:0}18%{opacity:1}82%{opacity:1}100%{transform:translate3d(-34px,235px,0) rotate(19deg);opacity:0}}
@keyframes parryBurst{0%{transform:scale(.18) rotate(-18deg);opacity:0}28%{opacity:1}100%{transform:scale(1.75) rotate(10deg);opacity:0}}
@keyframes healPillarRise{0%{transform:translate3d(0,36px,0) scaleX(.55);opacity:0}24%{opacity:.9}72%{opacity:.68}100%{transform:translate3d(0,-5px,0) scaleX(1.08);opacity:0}}

@media (max-width:649px) and (orientation:portrait){
  .enemy em{display:none}
  .forest-trunks.far{bottom:68px;height:59%}
  .forest-undergrowth.mid{bottom:58px;height:106px}
  .forest-trunks.near{bottom:44px;height:73%}
  .path-plane{bottom:-8px;height:138px}
  .forest-foreground{height:119px}
  .battlefield>#encounterOmen,.battlefield>.encounter-omen,.effect-layer>.encounter-omen{right:0;bottom:67px;width:52%;height:45%}
  .star-blade-rain{left:31%;top:18%;transform:translate3d(26px,-96px,0) rotate(19deg)}
  .parry-burst{left:13%;bottom:86px;width:62px;height:62px}
  .heal-pillar{left:8%;bottom:59px;width:112px;height:168px}
}

@media (max-width:649px) and (orientation:portrait) and (max-height:720px){
  .forest-trunks.far{bottom:64px}
  .forest-undergrowth.mid{bottom:54px;height:94px}
  .forest-trunks.near{bottom:41px}
  .path-plane{height:126px}
  .forest-foreground{height:106px}
}

/* ========================================================================== */
/* COMBAT CHOREOGRAPHY 2.0 ??actual combat.js contracts                       */
/* ========================================================================== */

.battlefield{--combat-speed-factor:1;--blade-light:#fff4c6;--blade-edge:#8de7ff;--blade-core:#fff}

/* Keep every melee step anchored at the target-side dash coordinate. */
/* ????대룞 ??쓣 CSS ?먯꽌 以꾩씠???덉쑝???섎룎?몃떎 ??qa-combat-v3 媛
   `0%,100%{transform:translate3d(var(--dash-x,56px)` 臾몄옄?댁쓣 洹몃?濡?寃?ы븳??
   QA ???덈? ?섏젙 湲덉? ?뚯씪?대씪 怨꾩빟??源⑥? ?딆븯?? ?대룞 ??쓣 諛붽씀?ㅻ㈃
   QA 怨꾩빟 蹂寃??ъ슜???뱀씤 + ?좉툑 ?ъ??????좏뻾?섏뼱???쒕떎. */
/*  [2026-07-31] **罹먮┃?곗뿉??will-change 瑜??덈? 嫄몄? 留덈씪.**
    ?쒕븣 "媛???щ챸?댁쭊??瑜??≪쑝?ㅺ퀬 `.hero.champion{will-change:transform}` ??
    ?곸떆濡?嫄몄뿀?붾뜲, 洹멸쾶 罹먮┃?곕? 萸됯컻???먯씤?댁뿀??
    will-change ???붿냼瑜?蹂꾨룄 ?⑹꽦 ?덉씠?대줈 ?밴꺽?쒗궓?? 洹몃윭硫?釉뚮씪?곗???
    ?덉씠?대? **?붿냼???먮옒 ?ш린(56x40)濡???踰??섏뒪?고솕????* GPU 媛 3諛곕줈
    ?섎┛?? ?대븣 ?곕뒗 寃껋? ?댁쨷?좏삎 蹂닿컙?닿퀬, `image-rendering:pixelated` ??
    諛곌꼍 ?대?吏 ?뺣??먮쭔 ?곸슜??肉?**?⑹꽦 ?덉씠???뺣??먮뒗 愿?ы븯吏 紐삵븳??**
    洹몃옒???쎌????쒖? ?딄퀬 臾쇨컧泥섎읆 踰덉죱?? 諛곗쑉??2 ??3 ?쇰줈 而ㅼ?硫????ы빐議뚮떎.
    ?밴꺽???놁븷硫?釉뚮씪?곗?媛 理쒖쥌 諛곗쑉?먯꽌 吏곸젒 洹몃젮 ?쎌????좊떎.
    (?먮옒 ?≪쑝?ㅻ뜕 '?쒓컙 ?щ챸'? will-change 瑜?**耳곕떎 猿먮떎** ?댁꽌 ?앷릿 寃껋씠??
     ?꾩삁 ???곕㈃ 洹?臾몄젣??媛숈씠 ?щ씪吏꾨떎.) */
.hero.champion.melee-action{z-index:12}
/* [2026-07-31] 洹쇱젒 怨듦꺽 以?罹먮┃?곌? 紐ъ뒪???ㅻ줈 ?ㅼ뼱媛 媛?ㅼ???臾몄젣.
   ??z-index:12 ???꾨Т ?④낵媛 ?놁뿀?? `.party`(z-index 7)?
   `.enemy-lane`(z-index 8) ????`isolation:isolate` ??媛곸옄 ?볦엫 留λ씫??
   留뚮뱺?? 洹몃옒???덉뼱濡쒖쓽 12 ??**?뚰떚 ?덉뿉?쒕쭔** ?좏슚?섍퀬, ?뚰떚 ?꾩껜??
   ?ъ쟾?????덉씤蹂대떎 ?꾨옒??源붾┛?? ??쒕줈 ?곸뿉寃?遺숇뒗 ?쒓컙 罹먮┃?곌?
   紐ъ뒪???ㅻ줈 ?щ씪議뚮떎.
   遺紐⑤? ?щ젮???닿껐?쒕떎. 洹쇱젒 ?곗텧 以묒뿉留??щ┛?????곸떆濡??щ━硫??곸씠
   ?щ젮?ㅼ뼱 ?뚮┫ ???곸씠 罹먮┃???ㅻ줈 ?ㅼ뼱媛꾨떎.
   10 ???댁쑀: ???덉씤(8)蹂대떎???? ?댄럺???덉씠??12)蹂대떎???꾨옒?ъ빞
   李멸꺽쨌?寃??댄럺?멸? 罹먮┃???꾩뿉 洹몃젮吏꾨떎. */
.party:has(.hero.champion.melee-action){ z-index:10; }
.hero.champion:is(.melee-action,.skill-star-dance,.skill-barrier-bash,.skill-heal-channel,.skill-comet-charge){transform-origin:50% 100%}
/* ?꾩? 媛숈? ?댁쑀濡??ㅽ봽?쇱씠?몄뿉??will-change 瑜?嫄몄? ?딅뒗?? */
.hero.champion.dash-approach{animation:combatDashApproach calc(90ms * var(--combat-speed-factor,1)) cubic-bezier(.34,.08,.24,1) both}
.hero.champion.dash-approach .arka-sprite{background-image:url("assets/knight-v1/walk.png?edge=1");background-size:1152px 128px;animation:knightWalk calc(190ms * var(--combat-speed-factor,1)) steps(1,end) infinite}
.hero.champion.attack-windup{animation:combatAttackWindup calc(60ms * var(--combat-speed-factor,1)) cubic-bezier(.35,0,.72,.28) both}
.hero.champion.attack-windup .arka-sprite{background-image:url("assets/knight-v1/attack.png?edge=1");background-size:280px 40px;background-position:-72px 0;animation:none}
.hero.champion.strike-horizontal{animation:combatStrikeHorizontal calc(240ms * var(--combat-speed-factor,1)) cubic-bezier(.12,.72,.2,1) both}
.hero.champion.strike-vertical{animation:combatStrikeVertical calc(240ms * var(--combat-speed-factor,1)) cubic-bezier(.12,.72,.2,1) both}
.hero.champion.strike-rising{animation:combatStrikeRising calc(240ms * var(--combat-speed-factor,1)) cubic-bezier(.12,.72,.2,1) both}
.hero.champion.strike-finisher{animation:combatStrikeFinisher calc(200ms * var(--combat-speed-factor,1)) cubic-bezier(.1,.68,.18,1) both}
.hero.champion:is(.strike-horizontal,.strike-vertical,.strike-rising,.strike-finisher) .arka-sprite{background-image:url("assets/knight-v1/attack.png?edge=1");background-size:280px 40px;animation:knightAttack calc(165ms * var(--combat-speed-factor,1)) steps(1,end) 1 both}
.hero.champion.dash-return{animation:combatDashReturn calc(170ms * var(--combat-speed-factor,1)) cubic-bezier(.3,.05,.55,1) both}
.hero.champion.dash-return .arka-sprite{background-image:url("assets/knight-v1/walk.png?edge=1");background-size:1152px 128px;animation:knightWalk calc(165ms * var(--combat-speed-factor,1)) steps(1,end) 1 both}

.hero.champion.combo-cross{--combo-tilt:-4deg;--slash-accent:#fff0b2}
.hero.champion.combo-breaker{--combo-tilt:3deg;--slash-accent:#9cecff}
.hero.champion.combo-rising{--combo-tilt:-7deg;--slash-accent:#d5c1ff}
/*  [2026-07-31] 媛뺥? 諛쒓킅??`filter` 瑜??곗? ?딅뒗?????ㅽ봽?쇱씠?멸? ?⑹꽦
    ?덉씠?대줈 ?밴꺽?섏뼱 3諛??뺣? ???댁쨷?좏삎 蹂닿컙?쇰줈 萸됯컿????will-change 二쇱꽍 李멸퀬).
    諛쒓킅? ?ㅼ뿉 源붾┛ `.aura` 濡??몃떎. */
.hero.champion.heavy-action .arka-sprite{filter:none}
.hero.champion.heavy-action .aura{
  opacity:.62;
  border-radius:50%;
  background:radial-gradient(circle,rgba(255,211,105,.7),rgba(255,211,105,.14) 55%,transparent 72%);
}
.hero.champion.combat-ready{animation:combatReadyStance calc(820ms * var(--combat-speed-factor,1)) ease-out both}
.hero.champion.combat-ready .sigil{animation:combatReadySigil calc(820ms * var(--combat-speed-factor,1)) ease-out both}

/* Hero skill body language is distinct from the basic three-hit combo. */
.hero.champion.skill-star-dance{z-index:12;animation:heroStarDance calc(var(--skill-duration,1180ms) * var(--combat-speed-factor,1)) cubic-bezier(.25,.65,.2,1) both}
.hero.champion.skill-star-dance .arka-sprite{background-image:url("assets/knight-v1/attack.png?edge=1");background-size:280px 40px;animation:knightAttack calc(280ms * var(--combat-speed-factor,1)) steps(1,end) 5}
.hero.champion.skill-barrier-bash{z-index:12;animation:heroBarrierBash calc(var(--skill-duration,700ms) * var(--combat-speed-factor,1)) cubic-bezier(.18,.78,.22,1) both}
.hero.champion.skill-barrier-bash .arka-sprite{background-image:url("assets/knight-v1/attack.png?edge=1");background-size:280px 40px;animation:knightAttack calc(310ms * var(--combat-speed-factor,1)) steps(1,end) 1 both}
.hero.champion.skill-heal-channel{z-index:12;animation:heroHealChannel calc(var(--skill-duration,840ms) * var(--combat-speed-factor,1)) ease-in-out both}
.hero.champion.skill-heal-channel .arka-sprite{background-position:0 0;animation:none}
.hero.champion.skill-comet-charge{z-index:12;animation:heroCometCharge calc(var(--skill-duration,800ms) * var(--combat-speed-factor,1)) cubic-bezier(.16,.76,.2,1) both}
.hero.champion.skill-comet-charge .arka-sprite{background-image:url("assets/knight-v2/thrust.png?v=skill-thrust-stab-v6-20260803");background-size:448px 40px;animation:knightV2ThrustCharge calc(800ms * var(--combat-speed-factor,1)) steps(1,end) 1 both}
/*  [2026-07-31] 移섏쑀 諛쒓킅??filter ???aura 濡??몃떎.
    filter ???ㅽ봽?쇱씠?몃? ?⑹꽦 ?덉씠?대줈 ?밴꺽?쒖폒 3諛??뺣? ??萸됯컿?? */
.hero.champion.skill-heal-channel .aura{
  opacity:.55;
  border-radius:50%;
  background:radial-gradient(circle,rgba(117,231,168,.66),rgba(117,231,168,.13) 55%,transparent 72%);
  animation:healAuraBreath calc(var(--skill-duration,840ms) * var(--combat-speed-factor,1)) ease-in-out both;
}

/* Blade arcs are local to enemy contact, not projectiles travelling across lane. */
.melee-slash{
  position:absolute;
  z-index:16;
  width:98px;
  height:78px;
  margin:-39px 0 0 -49px;
  opacity:0;
  border:0;
  border-radius:50%;
  background:conic-gradient(from 208deg,transparent 0 15%,rgba(255,255,255,.1) 16%,var(--blade-core) 18%,var(--slash-accent,var(--blade-light)) 21%,var(--blade-edge) 24%,transparent 27% 100%);
  filter:drop-shadow(0 0 5px rgba(255,244,198,.88));
  transform-origin:50% 50%;
  animation:meleeArcContact calc(300ms * var(--combat-speed-factor,1)) cubic-bezier(.08,.76,.2,1) both;
}
.melee-slash::after{
  content:"";
  position:absolute;
  inset:13px;
  border:2px solid transparent;
  border-top-color:rgba(255,255,255,.62);
  border-radius:50%;
}
.melee-slash.slash-horizontal{--arc-turn:-12deg;transform:rotate(-12deg) scaleX(1.16)}
.melee-slash.slash-vertical{--arc-turn:78deg;width:82px;height:112px;margin:-56px 0 0 -41px}
.melee-slash.slash-rising{--arc-turn:-48deg;width:105px;height:92px;margin:-46px 0 0 -52px}
.melee-slash.heavy{width:132px;height:108px;margin:-54px 0 0 -66px;filter:drop-shadow(0 0 7px rgba(255,221,124,.95)) drop-shadow(0 0 16px rgba(92,201,255,.5));animation-duration:calc(470ms * var(--combat-speed-factor,1))}
.melee-slash.critical{--slash-accent:#ffd16a}
.thrust-impact{
  position:absolute;
  z-index:19;
  width:86px;
  height:32px;
  margin:-16px 0 0 -43px;
  opacity:0;
  clip-path:polygon(0 42%,72% 42%,100% 50%,72% 58%,0 58%,24% 50%);
  background:linear-gradient(90deg,transparent 0 12%,#fff 28%,#fff5b6 58%,#6fdcff 83%,transparent 100%);
  filter:drop-shadow(0 0 7px rgba(138,228,255,.92));
  animation:thrustImpactBurst calc(360ms * var(--combat-speed-factor,1)) cubic-bezier(.12,.78,.2,1) both;
}
.thrust-impact::before,.thrust-impact::after{content:"";position:absolute;left:50%;top:50%;background:linear-gradient(90deg,transparent,#fff7cc,transparent);transform:translate(-50%,-50%)}
.thrust-impact::before{width:104px;height:3px}.thrust-impact::after{width:3px;height:50px;background:linear-gradient(180deg,transparent,#dff8ff,transparent)}

.star-blade-impact{
  position:absolute;
  z-index:17;
  width:54px;
  height:54px;
  margin:-27px 0 0 -27px;
  opacity:0;
  border:3px solid #b6efff;
  transform:rotate(45deg) scale(.2);
  box-shadow:inset 0 0 12px rgba(255,255,255,.9),0 0 18px #64cfff;
  animation:starBladeImpact calc(330ms * var(--combat-speed-factor,1)) ease-out both;
}
.star-blade-impact::before,.star-blade-impact::after{content:"";position:absolute;left:50%;top:50%;background:linear-gradient(90deg,transparent,#fff,transparent);transform:translate(-50%,-50%)}
.star-blade-impact::before{width:84px;height:3px}.star-blade-impact::after{width:3px;height:84px}

/* Screen stages darken only the fight lane; HUD remains fully readable. */
.skill-stage{position:absolute;z-index:13;inset:0;pointer-events:none;opacity:0}
.skill-stage.star-sword-rain{
  background:
    radial-gradient(circle at 70% 31%,rgba(198,242,255,.25) 0 2%,transparent 3%),
    radial-gradient(circle at 54% 24%,rgba(255,240,178,.18) 0 1%,transparent 2%),
    linear-gradient(112deg,rgba(5,8,22,.72),rgba(17,40,66,.16) 62%,rgba(5,8,22,.58));
  animation:starSwordStage calc(1180ms * var(--combat-speed-factor,1)) ease-in-out both;
}
.skill-stage.star-sword-rain::before{
  content:"";
  position:absolute;
  left:54%;
  top:30%;
  width:180px;
  height:180px;
  border:1px solid rgba(164,229,255,.4);
  border-radius:50%;
  box-shadow:inset 0 0 0 13px rgba(117,151,255,.04),0 0 28px rgba(76,198,255,.18);
  animation:starStageSigil calc(1180ms * var(--combat-speed-factor,1)) linear both;
}
.skill-stage.star-sword-rain::after{
  content:"";
  position:absolute;
  inset:18% 8% 24% 38%;
  opacity:.28;
  background:repeating-linear-gradient(112deg,transparent 0 43px,rgba(164,232,255,.19) 44px 46px,transparent 47px 78px);
}
.meteor-sword{
  position:absolute;
  z-index:18;
  width:11px;
  height:82px;
  margin:-58px 0 0 -6px;
  opacity:0;
  clip-path:polygon(50% 0,100% 18%,67% 79%,100% 84%,100% 90%,61% 88%,50% 100%,39% 88%,0 90%,0 84%,33% 79%,0 18%);
  background:linear-gradient(90deg,#6da8e8,#fff 44% 57%,#8de9ff 58% 72%,#476cb5);
  box-shadow:0 0 16px rgba(105,216,255,.8);
  animation:meteorSwordDrop calc(370ms * var(--combat-speed-factor,1)) cubic-bezier(.18,.72,.2,1) both;
}
.meteor-sword.meteor-1{--meteor-x:-34px;--meteor-turn:-13deg}
.meteor-sword.meteor-2{--meteor-x:25px;--meteor-turn:9deg}
.meteor-sword.meteor-3{--meteor-x:-16px;--meteor-turn:-5deg}
.meteor-sword.meteor-4{--meteor-x:38px;--meteor-turn:13deg}
.meteor-sword.meteor-5{--meteor-x:0px;--meteor-turn:0deg}
.meteor-sword.meteor-finisher{width:18px;height:112px;margin-top:-78px;filter:drop-shadow(0 0 8px #fff) drop-shadow(0 0 17px #68cfff);animation-duration:calc(520ms * var(--combat-speed-factor,1))}
.meteor-impact,.starburst-impact{
  position:absolute;
  z-index:18;
  width:42px;
  height:42px;
  margin:-21px 0 0 -21px;
  opacity:0;
  border:4px solid #d9f8ff;
  border-radius:50%;
  box-shadow:0 0 0 8px rgba(111,164,255,.2),0 0 30px rgba(98,211,255,.8);
  animation:meteorImpactBurst calc(520ms * var(--combat-speed-factor,1)) ease-out both;
}
.starburst-impact::before,.starburst-impact::after{content:"";position:absolute;left:50%;top:50%;width:112px;height:4px;background:linear-gradient(90deg,transparent,#fff0a8,transparent);transform:translate(-50%,-50%) rotate(25deg)}
.starburst-impact::after{transform:translate(-50%,-50%) rotate(-25deg)}

/* Guard/parry is a forward shock, visually separate from the passive barrier. */
.skill-stage.parry-flash{
  background:radial-gradient(circle at 29% 60%,rgba(224,252,255,.56) 0 2%,rgba(110,212,255,.16) 3% 18%,transparent 43%);
  animation:parryFlashStage calc(600ms * var(--combat-speed-factor,1)) ease-out both;
}
.skill-stage.thrust-charge-stage{
  background:
    radial-gradient(ellipse at 15% 73%,rgba(255,220,122,.22) 0 5%,rgba(94,216,255,.12) 6% 17%,transparent 34%),
    repeating-linear-gradient(112deg,transparent 0 38px,rgba(160,234,255,.18) 39px 41px,transparent 42px 78px);
  animation:thrustChargeStage calc(520ms * var(--combat-speed-factor,1)) ease-out both;
}
.parry-shockwave{
  z-index:17;
  left:9%;
  bottom:66px;
  width:46%;
  height:132px;
  opacity:0;
  border:3px solid rgba(180,239,255,.94);
  border-radius:50%;
  background:radial-gradient(ellipse,rgba(102,215,255,.18),transparent 68%);
  box-shadow:0 0 0 7px rgba(104,153,255,.16),0 0 28px rgba(89,215,255,.55);
  animation:parryShockwave calc(560ms * var(--combat-speed-factor,1)) cubic-bezier(.16,.72,.22,1) both;
}
.shield-impact{
  position:absolute;
  z-index:18;
  width:48px;
  height:62px;
  margin:-31px 0 0 -24px;
  opacity:0;
  border:3px solid #c4f2ff;
  border-radius:50% 50% 44% 44%;
  background:rgba(102,208,255,.14);
  box-shadow:0 0 17px rgba(100,215,255,.7);
  animation:shieldImpactContact calc(360ms * var(--combat-speed-factor,1)) ease-out both;
}
.parried-impact{animation:enemyParriedImpact calc(240ms * var(--combat-speed-factor,1)) steps(2,end) both!important}

/* Heal has ground sanctuary, vertical light, and five cheap transform-only motes. */
.skill-stage.heal-sanctuary{
  background:radial-gradient(ellipse at 28% 72%,rgba(139,255,174,.26) 0 10%,rgba(105,223,161,.08) 31%,transparent 54%);
  animation:healSanctuaryStage calc(820ms * var(--combat-speed-factor,1)) ease-in-out both;
}
.skill-stage.heal-sanctuary::before{
  content:"";
  position:absolute;
  left:7%;
  bottom:61px;
  width:43%;
  height:58px;
  border:3px solid rgba(164,255,190,.72);
  border-radius:50%;
  box-shadow:0 0 0 8px rgba(102,225,155,.1),0 0 28px rgba(90,234,151,.36);
  animation:healSanctuaryRing calc(820ms * var(--combat-speed-factor,1)) ease-out both;
}
.heal-light-column{
  position:absolute;
  z-index:15;
  left:8%;
  bottom:61px;
  width:42%;
  height:230px;
  opacity:0;
  clip-path:polygon(36% 0,64% 0,91% 100%,9% 100%);
  background:linear-gradient(90deg,transparent,rgba(133,244,171,.12) 22%,rgba(238,255,210,.58) 50%,rgba(133,244,171,.12) 78%,transparent);
  animation:healLightColumnFx calc(720ms * var(--combat-speed-factor,1)) ease-out both;
}
.heal-particle{
  position:absolute;
  z-index:16;
  left:var(--particle-x,30%);
  bottom:74px;
  width:7px;
  height:7px;
  opacity:0;
  border-radius:50%;
  background:#eaffc8;
  box-shadow:0 0 9px #6fea9f;
  animation:healMoteRise calc(480ms * var(--combat-speed-factor,1)) ease-out both;
}

/* Enemy attacks read as anticipation ??contact ??recovery. */
.enemy.sword-knockback{animation:enemySwordKnockback calc(200ms * var(--combat-speed-factor,1)) cubic-bezier(.12,.74,.2,1) both!important}
.enemy.enemy-windup{animation:enemyAttackWindup calc(220ms * var(--combat-speed-factor,1)) cubic-bezier(.28,0,.68,.32) both!important}
.enemy.boss-windup{animation:bossAttackWindup calc(220ms * var(--combat-speed-factor,1)) cubic-bezier(.28,0,.68,.32) both!important}
.enemy.enemy-lunge{z-index:11;animation:enemyAttackLunge calc(280ms * var(--combat-speed-factor,1)) cubic-bezier(.18,.68,.22,1) both!important}
.enemy.enemy-return{animation:enemyAttackReturn calc(230ms * var(--combat-speed-factor,1)) cubic-bezier(.3,.05,.55,1) both!important}
.enemy-strike-impact{
  position:absolute;
  z-index:18;
  width:58px;
  height:58px;
  margin:-29px 0 0 -29px;
  opacity:0;
  background:conic-gradient(from 45deg,transparent,#ffad78,transparent 14%,transparent 25%,#fff0c8 28%,transparent 37%,transparent 66%,#f2736b 70%,transparent 80%);
  animation:enemyStrikeContact calc(300ms * var(--combat-speed-factor,1)) ease-out both;
}

/* Hitstop pauses scene drift and actor idles for the exact gameplay-scaled beat. */
/*  [2026-07-31] 罹먮┃?곌? 源쒕묀?대뜕 ?먯씤 ??媛吏瑜??쒓굅?덈떎.
    ??`.arka-sprite` 瑜??덊듃?ㅽ넲 ?숈븞 硫덉텛??寃? ??踰?怨듦꺽???寃⑹씠 ??踰덉씠??
       48ms 吏쒕━ ?뺤?媛 珥덈떦 ??踰?嫄몃졇怨? 240ms 吏쒕━ 踰좉린 ?꾩쨷??嫄몃━硫?
       移쇱씠 ?쇱뼱遺숈뿀?ㅺ? ??덈떎. 踰좉린 紐⑥뀡 ?먯껜媛 蹂쇨굅由ш? ??吏湲덉? ?먰빐??
       諛곌꼍怨??곸? 洹몃?濡?硫덉떠 ?寃⑷컧???④릿??
    ??`.battlefield::after` ???붾㈃ ?꾩껜瑜???뒗 鍮꾨꽕?몃떎. 嫄곌린??opacity
       1 ??.55 ??1 ?뚮옒?쒕? 嫄몄뼱 ?먯뿀?쇰땲 怨듦꺽留덈떎 ?붾㈃ ?꾩껜媛 ??踰?踰덉찉???
       ?닿쾶 "?먭씀 源쒕묀?몃떎"???뺤껜?? ??젣?쒕떎. */
.battlefield.combat-hitstop .forest-scene>*,
.battlefield.combat-hitstop .enemy{animation-play-state:paused!important}

/* Ground-locked combat body: attack height belongs to the slash VFX, not the actor. */
@keyframes combatDashApproach{from{transform:translate3d(0,0,0) scale(var(--actor-scale,1))}to{transform:translate3d(var(--dash-x,56px),0,0) scale(var(--actor-scale,1))}}
@keyframes combatAttackWindup{from{transform:translate3d(var(--dash-x,56px),0,0) rotate(0) scale(var(--actor-scale,1))}to{transform:translate3d(var(--dash-x,56px),0,0) rotate(var(--combo-tilt,-2deg)) scale(calc(var(--actor-scale,1) * 0.99),calc(var(--actor-scale,1) * 1.01))}}
@keyframes combatStrikeHorizontal{0%,100%{transform:translate3d(var(--dash-x,56px),0,0) scale(var(--actor-scale,1))}52%{transform:translate3d(calc(var(--dash-x,56px) + 13px),0,0) rotate(4deg) scale(calc(var(--actor-scale,1) * 1.04),calc(var(--actor-scale,1) * 0.98))}}
@keyframes combatStrikeVertical{0%,100%{transform:translate3d(var(--dash-x,56px),0,0) scale(var(--actor-scale,1))}54%{transform:translate3d(calc(var(--dash-x,56px) + 9px),0,0) rotate(4deg) scale(calc(var(--actor-scale,1) * 1.03),calc(var(--actor-scale,1) * 0.98))}}
@keyframes combatStrikeRising{0%,100%{transform:translate3d(var(--dash-x,56px),0,0) scale(var(--actor-scale,1))}55%{transform:translate3d(calc(var(--dash-x,56px) + 12px),0,0) rotate(-6deg) scale(calc(var(--actor-scale,1) * 1.03))}}
@keyframes combatStrikeFinisher{0%,100%{transform:translate3d(var(--dash-x,56px),0,0) scale(var(--actor-scale,1))}42%{transform:translate3d(calc(var(--dash-x,56px) + 19px),0,0) rotate(-7deg) scale(calc(var(--actor-scale,1) * 1.06))}74%{transform:translate3d(calc(var(--dash-x,56px) + 8px),0,0) scale(calc(var(--actor-scale,1) * 1.02))}}
@keyframes combatDashReturn{from{transform:translate3d(var(--dash-x,56px),0,0) scale(var(--actor-scale,1))}to{transform:translate3d(0,0,0) scale(var(--actor-scale,1))}}
@keyframes combatReadyStance{0%{transform:scale(var(--actor-scale,1))}35%{transform:translate3d(5px,0,0) scale(calc(var(--actor-scale,1) * 1.02))}100%{transform:scale(var(--actor-scale,1))}}
@keyframes combatReadySigil{0%{transform:scale(.7);opacity:0}38%{transform:scale(1.3);opacity:1}100%{transform:scale(1);opacity:.72}}
/* Skill body language follows the shared motion contract:
   anticipation -> compression -> drive/contact -> brace -> recovery.
   The actor never translates vertically; the 50% 100% origin keeps the feet
   on the shared floor/root anchor while squash-and-stretch sells the impact. */
@keyframes heroStarDance{
  0%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}
  8%{transform:translate3d(-7px,0,0) rotate(-2deg) scale(calc(var(--actor-scale,1) * .98),calc(var(--actor-scale,1) * 1.02))}
  16%{transform:translate3d(-4px,0,0) rotate(-6deg) scale(calc(var(--actor-scale,1) * .94),calc(var(--actor-scale,1) * 1.06))}
  24%{transform:translate3d(26px,0,0) rotate(4deg) scale(calc(var(--actor-scale,1) * 1.02),calc(var(--actor-scale,1) * .98))}
  33%{transform:translate3d(56px,0,0) rotate(-4deg) scale(calc(var(--actor-scale,1) * 1.04),calc(var(--actor-scale,1) * .96))}
  47%{transform:translate3d(74px,0,0) rotate(5deg) scale(calc(var(--actor-scale,1) * 1.06),calc(var(--actor-scale,1) * .95))}
  62%{transform:translate3d(92px,0,0) rotate(-6deg) scale(calc(var(--actor-scale,1) * 1.07),calc(var(--actor-scale,1) * .94))}
  77%{transform:translate3d(104px,0,0) rotate(3deg) scale(calc(var(--actor-scale,1) * 1.05),calc(var(--actor-scale,1) * .96))}
  86%{transform:translate3d(72px,0,0) rotate(1deg) scale(calc(var(--actor-scale,1) * 1.02),calc(var(--actor-scale,1) * .99))}
  100%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}
}
@keyframes heroBarrierBash{
  0%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}
  12%{transform:translate3d(-6px,0,0) rotate(-2deg) scale(calc(var(--actor-scale,1) * .98),calc(var(--actor-scale,1) * 1.02))}
  23%{transform:translate3d(-3px,0,0) rotate(-7deg) scale(calc(var(--actor-scale,1) * .94),calc(var(--actor-scale,1) * 1.06))}
  32%{transform:translate3d(34px,0,0) rotate(5deg) scale(calc(var(--actor-scale,1) * 1.06),calc(var(--actor-scale,1) * .94))}
  40%{transform:translate3d(39px,0,0) rotate(3deg) scale(calc(var(--actor-scale,1) * 1.03),calc(var(--actor-scale,1) * .97))}
  53%{transform:translate3d(30px,0,0) rotate(-1deg) scale(calc(var(--actor-scale,1) * 1.01),calc(var(--actor-scale,1) * .99))}
  72%{transform:translate3d(12px,0,0) rotate(1deg) scale(calc(var(--actor-scale,1) * .99),calc(var(--actor-scale,1) * 1.01))}
  100%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}
}
@keyframes heroCometCharge{
  0%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}
  8%{transform:translate3d(-3px,0,0) rotate(0) scale(var(--actor-scale,1))}
  18%{transform:translate3d(-5px,0,0) rotate(0) scale(var(--actor-scale,1))}
  30%{transform:translate3d(-5px,0,0) rotate(0) scale(var(--actor-scale,1))}
  34%{transform:translate3d(-5px,0,0) rotate(0) scale(var(--actor-scale,1))}
  38%{transform:translate3d(calc(var(--thrust-dash,42px) * .25),0,0) rotate(1deg) scale(var(--actor-scale,1))}
  46%{transform:translate3d(calc(var(--thrust-dash,42px) * .72),0,0) rotate(1deg) scale(var(--actor-scale,1))}
  50%{transform:translate3d(var(--thrust-dash,42px),0,0) rotate(0) scale(var(--actor-scale,1))}
  56%{transform:translate3d(calc(var(--thrust-dash,42px) * .98),0,0) rotate(0) scale(var(--actor-scale,1))}
  66%{transform:translate3d(calc(var(--thrust-dash,42px) * .62),0,0) rotate(0) scale(var(--actor-scale,1))}
  82%{transform:translate3d(calc(var(--thrust-dash,42px) * .18),0,0) rotate(0) scale(var(--actor-scale,1))}
  100%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1))}
}
@keyframes heroHealChannel{
  0%,100%{transform:translate3d(0,0,0) rotate(0) scale(var(--actor-scale,1));opacity:1}
  14%{transform:translate3d(-2px,0,0) rotate(-1deg) scale(calc(var(--actor-scale,1) * .99),calc(var(--actor-scale,1) * 1.01));opacity:1}
  34%{transform:translate3d(-1px,0,0) rotate(-2deg) scale(calc(var(--actor-scale,1) * .96),calc(var(--actor-scale,1) * 1.05));opacity:1}
  54%{transform:translate3d(1px,0,0) rotate(1deg) scale(calc(var(--actor-scale,1) * 1.04),calc(var(--actor-scale,1) * .97));opacity:1}
  72%{transform:translate3d(0,0,0) rotate(-.5deg) scale(calc(var(--actor-scale,1) * 1.02),calc(var(--actor-scale,1) * .99));opacity:1}
}
@keyframes healAuraBreath{
  0%,100%{transform:scale(.82);opacity:.28}
  34%{transform:scale(.96);opacity:.48}
  54%{transform:scale(1.12);opacity:.68}
  72%{transform:scale(1.02);opacity:.5}
}
@keyframes meleeArcContact{0%{transform:rotate(calc(var(--arc-turn,0deg) - 16deg)) scale(.35);opacity:0}18%{opacity:1}58%{transform:rotate(calc(var(--arc-turn,0deg) + 9deg)) scale(1.08);opacity:1}100%{transform:rotate(calc(var(--arc-turn,0deg) + 19deg)) scale(1.26);opacity:0}}
@keyframes thrustChargeRing{
  0%,5%{opacity:0;transform:scale(.68)}
  12%{opacity:.52;transform:scale(.84)}
  22%,34%{opacity:.92;transform:scale(1)}
  40%{opacity:.72;transform:scale(1.08)}
  48%{opacity:.18;transform:scale(1.24)}
  54%,100%{opacity:0;transform:scale(1.34)}
}
@keyframes thrustChargeStage{
  0%,8%{opacity:0}
  18%,34%{opacity:.72}
  42%{opacity:.46}
  54%,100%{opacity:0}
}
@keyframes thrustImpactBurst{
  0%{transform:translateX(-18px) scaleX(.18);opacity:0}
  18%{transform:translateX(-7px) scaleX(.62);opacity:1}
  42%{transform:translateX(0) scaleX(1.08);opacity:1}
  100%{transform:translateX(18px) scaleX(1.45);opacity:0}
}
@keyframes starBladeImpact{0%{transform:rotate(18deg) scale(.15);opacity:0}34%{transform:rotate(45deg) scale(1);opacity:1}100%{transform:rotate(72deg) scale(1.5);opacity:0}}
@keyframes starSwordStage{0%{opacity:0}12%{opacity:1}76%{opacity:.92}100%{opacity:0}}
@keyframes starStageSigil{0%{transform:rotate(-25deg) scale(.65);opacity:0}22%{opacity:.72}100%{transform:rotate(55deg) scale(1.14);opacity:0}}
@keyframes meteorSwordDrop{0%{transform:translate3d(var(--meteor-x,0),-145px,0) rotate(var(--meteor-turn,0deg));opacity:0}18%{opacity:1}78%{opacity:1}100%{transform:translate3d(var(--meteor-x,0),23px,0) rotate(var(--meteor-turn,0deg));opacity:0}}
@keyframes meteorImpactBurst{0%{transform:scale(.18);opacity:0}24%{opacity:1}100%{transform:scale(2.05);opacity:0}}
@keyframes parryFlashStage{0%{opacity:0}16%{opacity:.92}42%{opacity:.5}100%{opacity:0}}
@keyframes parryShockwave{0%{transform:translate3d(-18px,0,0) scale(.24);opacity:0}24%{opacity:1}100%{transform:translate3d(124px,0,0) scale(1.65);opacity:0}}
@keyframes shieldImpactContact{0%{transform:scale(.25) rotate(-8deg);opacity:0}25%{opacity:1}100%{transform:scale(1.55) rotate(9deg);opacity:0}}
@keyframes enemyParriedImpact{0%,100%{transform:translate3d(0,0,0);opacity:1}38%{transform:translate3d(13px,-3px,0) rotate(3deg);opacity:1}}
@keyframes healSanctuaryStage{0%{opacity:0}18%{opacity:1}75%{opacity:.84}100%{opacity:0}}
@keyframes healSanctuaryRing{0%{transform:scale(.25);opacity:0}24%{opacity:1}100%{transform:scale(1.35);opacity:0}}
@keyframes healLightColumnFx{0%{transform:translate3d(0,24px,0) scaleX(.38);opacity:0}22%{opacity:.88}76%{opacity:.66}100%{transform:translate3d(0,-8px,0) scaleX(1.08);opacity:0}}
@keyframes healMoteRise{0%{transform:translate3d(0,18px,0) scale(.4);opacity:0}20%{opacity:1}100%{transform:translate3d(8px,-112px,0) scale(1.25);opacity:0}}
@keyframes enemySwordKnockback{0%{transform:translate3d(0,0,0);opacity:1}22%{transform:translate3d(var(--knockback-x,9px),-2px,0) scale(.95);opacity:1}100%{transform:translate3d(0,0,0);opacity:1}}
@keyframes enemyAttackWindup{from{transform:translate3d(0,0,0) scale(1)}to{transform:translate3d(9px,2px,0) rotate(3deg) scale(.94,1.06)}}
@keyframes bossAttackWindup{from{transform:translate3d(0,0,0) scale(1)}to{transform:translate3d(7px,4px,0) scale(1.08,.94)}}
@keyframes enemyAttackLunge{0%{transform:translate3d(8px,1px,0) scale(.96)}48%{transform:translate3d(-62px,-2px,0) scale(1.08,.95)}100%{transform:translate3d(-50px,0,0) scale(1)}}
@keyframes enemyAttackReturn{from{transform:translate3d(-50px,0,0)}to{transform:translate3d(0,0,0)}}
@keyframes enemyStrikeContact{0%{transform:scale(.25) rotate(-18deg);opacity:0}30%{transform:scale(1) rotate(0);opacity:1}100%{transform:scale(1.65) rotate(14deg);opacity:0}}

@media (max-width:649px) and (orientation:portrait){
  .melee-slash{width:82px;height:66px;margin:-33px 0 0 -41px}
  .melee-slash.slash-vertical{width:68px;height:94px;margin:-47px 0 0 -34px}
  .melee-slash.slash-rising{width:88px;height:76px;margin:-38px 0 0 -44px}
  .melee-slash.heavy{width:108px;height:90px;margin:-45px 0 0 -54px}
  .skill-stage.star-sword-rain::before{left:43%;top:29%;width:145px;height:145px}
  .meteor-sword{height:68px;margin-top:-50px}
  .meteor-sword.meteor-finisher{height:92px;margin-top:-67px}
  .parry-shockwave{left:5%;bottom:71px;width:43%;height:108px}
  .skill-stage.heal-sanctuary::before{left:4%;bottom:70px;width:39%;height:47px}
  .heal-light-column{left:4%;bottom:68px;width:39%;height:190px}
  .heal-particle{bottom:79px}
}

/* KNIGHT V2 ???꾨줈?앺듃 ?먯껜 3D 湲곗궗 (work/knight-lowpoly/anim_*.py).
   怨꾩빟: ?꾨젅??192x128 쨌 ?듭빱 (64,108) = 諛붾떏 遺덊닾紐???107 쨌 怨듭슜 ?붾젅??32??
   knight-v1 怨?媛숈? 怨꾩빟?대?濡?left/bottom/width/height ??洹몃?濡??대떎.
   3D 紐⑥뀡? idle / run / attack 3醫낅퓧?대떎. 湲곗〈 character-a ??怨듦꺽 3醫끒룸갑?는?
   ?쇨꺽 ?쒗듃媛 ?곕줈 ?덉뿀?쇰?濡? ?녿뒗 ?곹깭??媛숈? attack ?먮뒗 idle 濡?留ㅽ븨?섍퀬
   **?ъ깮 ?쒓컙? 湲곗〈 媛믪쓣 洹몃?濡??좎?**?쒕떎 ???곹깭 吏?띿떆媛꾧낵 ?닿툔?섎㈃ ?꾪닾
   ?곗텧(??쒋넂?덈뱶?끸넂3?고?)???꾨젅?꾩씠 ?섎┛??
   ?щ━湲?二쇨린 533ms ??STRIDE 瑜?諛곌꼍 ?ㅽ겕濡?40px/s ?먯꽌 ??궛??留욎텣 媛믪씠??
   ??二쇨린瑜?諛붽씀硫?諛쒖씠 誘몃걚?ъ쭊??(work/knight-lowpoly/HANDOFF.md 5-4). */
.arka-sprite{
  /* ?꾨젅??56x40 (?ㅻ（??20px). ?꾨옒 scale 濡??붾㈃??168x120 ?쇰줈 洹몃젮吏꾨떎.
     transform-origin ? ?ㅽ봽?쇱씠?몄쓽 ?듭빱(19,34) = (33.929%, 85%) ??
     ?듭빱瑜?怨좎젙?먯쑝濡??뺣??댁빞 諛쒖씠 諛붾떏?좎뿉???⑥? ?딅뒗??
     `transform` ???꾨땲??媛쒕퀎 `scale` ???대떎. 嫄룰린쨌怨듦꺽 ?쒗듃 ?좊땲硫붿씠?섏씠
     transform ???곗????딆?留? ??怨녹뿉?쒕쭔 諛곗쑉???뚯쑀?섍쾶 ?먮뒗 ?몄씠 ?덉쟾?섎떎. */
  scale:calc(3 / var(--party-scale, .72));
  transform-origin:33.929% 85%;
     left ??紐명넻 ?쇱そ ?앹씠 ?붾㈃ x 51.8px ???ㅻ룄濡??ㅼ륫??留욎톬??
     ??媛믪? ?덉뼱濡?諛뺤뒪 醫뚰몴(鍮꾪솗?)??party 諛곗쑉留?怨깊빐吏꾨떎. 諛섎㈃ ?듭빱?먯꽌
     ?⑥뼱吏?嫄곕━???ㅽ봽?쇱씠??諛곗쑉 2諛곌? 怨깊빐吏誘濡?怨꾩궛??吏곴?怨??ㅻⅤ??
     ?꾨젅???ш린??諛곗쑉??諛붽씀硫?諛섎뱶???ㅼ떆 ?ㅼ륫??寃?
     bottom ? ?듭빱 y(44)媛 而⑦뀒?대꼫 諛붾떏?좎뿉 ?뺥솗???용룄濡?-(52-44)/??濡??≪븯?? */
  left:-8px;
  bottom:-6px;
  width:56px;
  height:40px;
  /* ?댁쨷 drop-shadow ?쒓굅 (2026-07-30)
     `.unit` 怨?`.arka-sprite` ?묒そ??drop-shadow 媛 嫄몃젮 7px ?꾨옒濡??대몢??
     蹂듭궗蹂몄씠 ??寃??앷꼈????罹먮┃???ㅼ뿉 ???덈뒗 ?댁긽??洹몃┝?먯쓽 ?뺤껜??
     ?ㅽ봽?쇱씠???먯껜 洹몃┝?먮뒗 ?꾧퀬, 諛쒕컩 ?묒? 洹몃┝?먮? ::before 濡??곕줈 洹몃┛?? */
  filter:none;
  background-image:url("assets/knight-v2/idle.png?v=knightv2-px2");
  background-position:0 0;
  background-size:672px 40px;
  background-repeat:no-repeat;
  image-rendering:pixelated;
  animation:knightV2Idle 2s steps(12,end) infinite;
}
/* ?됯뎔留??щ━湲곕? ?대떎.
   ???dash-approach/dash-return)?먯꽌???щ━湲곕? ?곗? ?딅뒗??????쒕뒗 180ms ??
   56px ?대룞(311px/s)?몃뜲 ?щ━湲??ㅽ봽?쇱씠?몃뒗 56.4px/s 湲곗??대씪 5.5諛?誘몃걚?ъ?怨?
   (--dash-x 媛 360px 源뚯? 媛硫?35諛?, 420ms ?ъ씠?댁쓣 180ms 留??ъ깮?댁꽌 ?ㅻ━媛
   諛?諛뷀대룄 紐??덈떎. ???以묒뿉???꾨옒 洹쇱젒 ?곗텧 ?좊땲硫붿씠?섏쓽 ?덈퉬 ?먯꽭媛
   ?좎??섏뼱 "移쇱쓣 媛먯쑝硫??뚯쭊"?쇰줈 ?쏀엺?? */
.party.marching .arka-sprite{
  background-image:url("assets/knight-v2/run.png?v=knightv2-px2");
  background-size:448px 40px;
  animation:knightV2Run calc(420ms * var(--combat-speed-factor,1)) steps(8,end) infinite;
}
/* 洹쇱젒 ?곗텧 ?꾩껜(1000ms)??怨듦꺽 ?좊땲硫붿씠?섏쓣 **??踰덈쭔** ?ъ깮?쒕떎.
   ?댁쟾?먮뒗 windup(80ms)쨌1?(190)쨌2?(180)쨌留덈Т由?190)媛 媛곴컖 蹂꾧컻 ?곹깭?ъ꽌
   ?곹깭媛 諛붾??뚮쭏???좊땲硫붿씠?섏씠 泥섏쓬遺???ㅼ떆 ?쒖옉?덈떎. 洹몃옒???꾧껐??
   踰좉린媛 ?꾨땲??**?덈퉬?숈옉??1珥덉뿉 4踰?* ?섏솕????罹먮┃?곌? ?뺤떊?놁씠 蹂댁씠??
   媛?????먯씤?대떎.
   ?쒗?ㅼ? ?ㅽ봽?덉엫 諛곕텇???덈쵖?섍쾶 留욌뒗??
     ?덈퉬 0~26%   = 0~260ms  ??1?媛 260ms ???ㅼ뼱媛꾨떎
     ?ㅻ???42~49% = 420~490ms ??2? 450ms
     ?꾪뙥??49~76% = 490~760ms ??留덈Т由?630ms
     ?뚮났 76~100% = 760~1000ms ??蹂듦? 800ms
   利???踰덉쓽 踰좉린 ?덉뿉???덊듃留?3踰??쒕떎. */
/*  [2026-07-31] 湲곕낯 怨듦꺽??3?곌꺽?쇰줈 ?섎댋??
    寃뚯엫 濡쒖쭅(ui-combat.js)? ?먮옒遺??260 / 450 / 630ms ????踰??뚮━怨?
    `strike-horizontal` / `strike-vertical` / `strike-rising` / `strike-finisher`
    ?대옒?ㅻ? 媛덉븘 ?쇱슦怨??덉뿀?? 洹몃윴???ㅽ봽?쇱씠?몃뒗 attack.png ???λ퓧?대씪
    **??踰???媛숈? ?대젮踰좉린**濡?蹂댁??? 洹몃옒??媛濡쒕쿋湲?attack-h)?
    ?щ젮踰좉린(attack-r)瑜??덈줈 援ъ썙 媛??④퀎??臾쇰┛??
    ???쒗듃??媛숈? crop쨌諛곗쑉쨌?붾젅?몃줈 ?④퍡 援ъ썱??work/build-knight-anim-sprites.py).
    ?곕줈 援ъ슦硫?union crop ???щ씪???곌꺽 ?꾩쨷 罹먮┃?곌? 紐?px ???꾨떎.

    ??쑣룹쐢?쒖뾽 援ш컙? ?좊땲硫붿씠?섏쓣 ?뚮━吏 ?딄퀬 ?덈퉬 ?먯꽭(0踰??꾨젅??濡?硫덉텣??
    ?덉쟾?먮뒗 ?ш린??1000ms 吏쒕━ 怨듦꺽???뚮졇?붾뜲, ?④퀎媛 諛붾??뚮쭏??泥섏쓬遺??
    ?ㅼ떆 ?쒖옉???덈퉬?숈옉??1珥덉뿉 ??踰??섏솕?? ?댁젣 ?④퀎留덈떎 **?꾧껐??踰좉린
    ??踰?*??諛곗젙?섎?濡??ъ떆?묒씠 怨??ㅼ쓬 踰좉린???쒖옉???쒕떎. */
.unit.attacking .arka-sprite,
.hero.champion.melee-action .arka-sprite{
  background-image:url("assets/knight-v2/attack.png?v=knightv2-px2");
  background-position:0 0;
  background-size:280px 40px;
  animation:none;
}
/* ?ъ깮 ?쒓컙? ??.strike-* ??紐명넻 ?좊땲硫붿씠?섍낵 **媛숈? 媛?*?댁뼱???섍퀬,
   `ui-combat.js` ??`slashMs` ???媛숈븘???쒕떎. ??怨녹씠 ?닿툔?섎㈃ 移쇱씠
   ?용뒗 ?꾨젅?꾧낵 ?쇳빐媛 ?ㅼ뼱媛???쒓컙??踰뚯뼱吏꾨떎. */
.hero.champion.melee-action.strike-horizontal .arka-sprite{
  background-image:url("assets/knight-v2/attack-h.png?v=knightv2-px2");
  animation:knightV2SlashH calc(240ms * var(--combat-speed-factor,1)) steps(1,end) 1 both;
}
.hero.champion.melee-action.strike-vertical .arka-sprite{
  background-image:url("assets/knight-v2/attack.png?v=knightv2-px2");
  animation:knightV2SlashV calc(240ms * var(--combat-speed-factor,1)) steps(1,end) 1 both;
}
.hero.champion.melee-action.strike-rising .arka-sprite{
  background-image:url("assets/knight-v2/attack-r.png?v=knightv2-px2");
  animation:knightV2SlashR calc(240ms * var(--combat-speed-factor,1)) steps(1,end) 1 both;
}
/* 留덈Т由??????긽 ?대젮踰좉린?? ?????媛 媛濡쒕줈 ?묒뿀?쇰?濡?
   ?몃줈濡??대━李띾뒗 3?媛 ?뺤떎???ㅻⅨ ?숈옉?쇰줈 ?쏀엺?? */
.hero.champion.melee-action.strike-finisher .arka-sprite{
  background-image:url("assets/knight-v2/attack.png?v=knightv2-px2");
  animation:knightV2SlashF calc(200ms * var(--combat-speed-factor,1)) steps(1,end) 1 both;
}
/* 蹂듦? 以묒뿉??留덉?留??뚮났 ?꾨젅??4踰??쇰줈 硫덉텣?? 0踰??덈퉬)濡??뚯븘媛硫?
   ??踰좉퀬 ?섏꽌 ?ㅼ떆 移쇱쓣 媛먮뒗 寃껋쿂??蹂댁씤?? */
.hero.champion.melee-action.dash-return .arka-sprite{
  background-image:url("assets/knight-v2/attack.png?v=knightv2-px2");
  background-position:-224px 0;
  animation:none;
}
.hero.champion.skill-star-dance .arka-sprite{
  background-image:url("assets/knight-v2/attack.png?v=knightv2-px2");
  background-position:0 0;
  background-size:280px 40px;
  /* Five attack beats match the five meteor contacts; the final recovery
     frame is held while the body returns to its planted stance. */
  animation:knightV2Attack calc(260ms * var(--combat-speed-factor,1)) steps(1,end) 5 both;
}
/* 諛⑹뼱 紐⑥뀡???녿떎. 怨듦꺽???먮━寃??뚮젮 "諛?대텤?대뒗" ?숈옉?쇰줈 ?대떎. */
.hero.champion.skill-barrier-bash .arka-sprite{
  background-image:url("assets/knight-v2/attack.png?v=knightv2-px2");
  background-position:0 0;
  background-size:280px 40px;
  animation:knightV2Attack calc(720ms * var(--combat-speed-factor,1)) steps(1,end) 1 both;
}
.hero.champion.skill-heal-channel .arka-sprite{
  background-image:url("assets/knight-v2/idle.png?v=knightv2-px2");
  background-position:0 0;
  background-size:672px 40px;
  animation:knightV2Idle calc(2s * var(--combat-speed-factor,1)) steps(12,end) infinite;
}
/* ?쇨꺽 紐⑥뀡???녿떎. idle ??怨꾩냽 ?뚮━怨?諛앷린 ?뚮옒?쒕쭔 寃뱀튇?? */
.unit.hit .arka-sprite{
  background-image:url("assets/knight-v2/idle.png?v=knightv2-px2");
  background-position:0 0;
  background-size:672px 40px;
  animation:knightV2Idle 2s steps(12,end) infinite,
            knightHit calc(220ms * var(--combat-speed-factor,1)) linear 1;
}
/* ?밸━ ?뺤? ?ъ쫰 ??idle 泥??꾨젅??*/
.unit.celebrate .arka-sprite{
  background-image:url("assets/knight-v2/idle.png?v=knightv2-px2");
  background-size:672px 40px;
  background-position:0 0;
  animation:none;
}

/* 諛쒕컩 ?묒? 洹몃┝????罹먮┃????z-index -1)??????섎굹.
   ?ㅽ봽?쇱씠???듭빱媛 (64,108) ?대?濡?諛쒖꽑? ?꾨젅???곷떒?먯꽌 107px 吏?먯씠??
   background-position ?좊땲硫붿씠?섍낵 臾닿??섍쾶 ?쒖옄由ъ뿉 ?덉뼱???섎?濡??섏궗?붿냼濡??붾떎. */
.arka-sprite::before{
  content:"";
  position:absolute;
  z-index:-1;
  left:11px;
  top:27px;
  width:11px;
  height:4px;
  background:radial-gradient(50% 50% at 50% 50%,
    rgba(12,18,28,.50) 0%, rgba(12,18,28,.28) 55%, rgba(12,18,28,0) 100%);
  pointer-events:none;
}
/* `.unit` ??drop-shadow 媛 ?먯넀 ?꾩껜??嫄몃━誘濡?梨뷀뵾?몄뿉?쒕뒗 ?덈떎.
   梨뷀뵾?몄쓽 ?ㅻⅨ ?먯떇(.body/.head/.cape/.weapon)? display:none ?대씪 ?곹뼢 ?녿떎. */
.unit.champion{filter:none}
/*  [2026-07-31] 媛??踰꾪봽??諛쒓킅??`filter` 濡?二쇰㈃ ???쒕떎.
    filter ??will-change ? ?묎컳???붿냼瑜??⑹꽦 ?덉씠?대줈 ?밴꺽?쒗궓?? 洹몃윭硫?
    56x40 ?쇰줈 ?섏뒪?고솕????GPU 媛 3諛곕줈 ?섎━硫댁꽌 ?댁쨷?좏삎 蹂닿컙????
    罹먮┃?곌? 萸됯컿?? 踰꾪봽媛 嫄몃┫ ?뚮쭏???쎌????먮젮議뚮떎.
    諛쒓킅? 罹먮┃???ㅼ뿉 ?대? ?덈뒗 `.aura` ?붿냼濡???릿?? 洹몄そ留??덉씠?닿? ?섍퀬
    ?ㅽ봽?쇱씠?몃뒗 嫄대뱶由ъ? ?딅뒗?? */
.party.has-haste .unit.champion{filter:none}
.party.has-haste .unit.champion .aura{
  opacity:.5;
  border-radius:50%;
  background:radial-gradient(circle,rgba(243,200,106,.6),rgba(243,200,106,.12) 55%,transparent 72%);
}

@keyframes knightV2Idle{to{background-position-x:-672px}}
/*  [2026-07-31] -672px ??-448px. **?ш린媛 ?щ━湲곌? 留앷?吏?吏꾩쭨 ?먯씤?댁뿀??**
    ?щ━湲??쒗듃??448px(8?꾨젅??x 56)?몃뜲 -672px(idle ?쒗듃 ??源뚯? ?ㅽ겕濡ㅽ븯怨?
    ?덉뿀?? steps(8,end) 濡??섎늻硫?0, -84, -168, -252, -336, -420, -504, -588 ??
    硫덉텣?? 56 ??諛곗닔媛 ?꾨땲??**?꾨젅?꾩씠 諛섏뵫 ?섎젮 蹂댁씠怨?*, -504 遺?곕뒗 ?쒗듃
    諛뽰씠??鍮덉뭏???섏삩?? "?꾨젅?꾩씠 ?섎굹???곗냽?섏? ?딄퀬 ?댁긽???ㅽ봽?쇱씠?멸?
    諛섎났?쒕떎"媛 ?뺥솗???닿쾬?대떎.
    ?꾨젅??洹쒓꺽??48x34 ??56x40 ?쇰줈 諛붽? ??臾몄옄??移섑솚??idle 媛믩쭔 媛깆떊?섍퀬
    run ???볦낀?? ?쒗듃 ??낵 ?ㅽ봽?덉엫 ?앷컪? **??긽 媛숈븘???쒕떎**
    (猷⑦봽 紐⑥뀡? ?쒗듃 ??洹몃?濡? 1???ъ깮? ?쒗듃 ??- ?꾨젅????. */
@keyframes knightV2Run{to{background-position-x:-448px}}
/* 怨듦꺽? 洹좊벑 媛꾧꺽???꾨땲?? 李멸퀬??2D ?≪뀡 寃뚯엫 洹쒖튃: ?덈퉬瑜?踰꾪떚怨?
   媛??鍮좊Ⅸ 援ш컙(?ㅻ????????꾨젅?꾩쑝濡??ㅼ튂怨? ?꾪뙥?몃? 踰꾪뀲???寃⑷컧??
   留뚮뱺?? steps(5,end) 濡?洹좊벑 遺꾨같?섎㈃ ??由щ벉???щ씪吏꾨떎.
   ?꾨젅??0 ?덈퉬 26% 쨌 1 ?덈퉬?뺤젏 16% 쨌 2 ?ㅻ???7% 쨌 3 ?꾪뙥??27% 쨌 4 ?뚮났 24% */

/* 踰좉린 1??240ms)???꾨젅??諛곕텇.
   **?꾪뙥???꾨젅?꾩씠 ?쒖옉?섎뒗 44% 吏?먯씠 怨꾩빟?대떎.** `ui-combat.js` 媛
   `hitTimes = stepTimes + round(0.44 * slashMs)` 濡??쇳빐 ?쒓컖???≪쑝誘濡?
   ??媛믪쓣 諛붽씀硫?嫄곌린??媛숈씠 諛붽퓭??移쇰궇???용뒗 ?쒓컙怨??쇳빐媛 ?쇱튂?쒕떎.
   洹좊벑 5?깅텇(20%???쇰줈 ?섎늻硫??ㅻ??닿? ?ㅻ옒 癒몃Ъ???붿긽???뺤? ?붾㈃泥섎읆
   蹂댁씠怨??꾪뙥?멸? ??踰꾪떞?? ?덈퉬 ???ㅻ???12% ???꾪뙥??28% 踰꾪?. */
/*  [?댁슜??媛숈? ?ㅽ봽?덉엫????踰??먮뒗 ?댁쑀 ??以꾩씠吏 留덈씪]
    CSS ?좊땲硫붿씠?섏? `animation-name` 紐⑸줉??**?꾩튂**濡??숈씪?깆쓣 ?먮떒?쒕떎.
    ?대쫫??洹몃?濡쒕㈃ ?대옒?ㅻ? 諛붽퓭??釉뚮씪?곗???"媛숈? ?좊땲硫붿씠?섏씠 怨꾩냽?쒕떎"怨?
    蹂닿퀬 **?ъ떆?묓븯吏 ?딅뒗??**
    `setHeroMotionStep` ? ???쒖뒪???덉뿉???댁쟾 ?대옒?ㅻ? 吏?곌퀬 ???대옒?ㅻ?
    遺숈씤?? 以묎컙???ㅽ????뚮윭?쒓? ?놁쑝誘濡?釉뚮씪?곗???animation-name ??
    `none` ???섎뒗 ?쒓컙??蹂댁? 紐삵븳??
    洹몃옒?????④퀎媛 ?꾨? `knightV2Slash` ???대쫫???곕뜕 ?숈븞, 1?媛 ?앸궃 ??
    2쨌3???**?앸궃 ?좊땲硫붿씠?섏쓣 洹몃?濡??댁뼱諛쏆븘 ?뚮났 ?꾨젅?꾩뿉 硫덉떠 ?덉뿀??**
    ?쒗듃(background-image)留?諛붾뚯뼱??"移쇱씠 ???吏곸씤??濡?蹂댁???
    (紐명넻 ?좊땲硫붿씠?섏? combatStrikeHorizontal/Rising/Finisher 濡??대쫫???щ씪
     ?뺤긽 ?ъ떆?묐릱?? 洹몃옒??紐몃쭔 ?吏곸씠怨?移쇱? 硫덉떠 ?덉뿀??)
    ??肄ㅻ낫 ?덉뿉?????④퀎???대쫫????긽 ?쒕줈 ?щ씪???쒕떎. */
@keyframes knightV2SlashH{
  0%,17.9%{background-position:0 0}
  18%,31.9%{background-position:-56px 0}
  32%,43.9%{background-position:-112px 0}
  44%,71.9%{background-position:-168px 0}
  72%,100%{background-position:-224px 0}
}
@keyframes knightV2SlashV{
  0%,17.9%{background-position:0 0}
  18%,31.9%{background-position:-56px 0}
  32%,43.9%{background-position:-112px 0}
  44%,71.9%{background-position:-168px 0}
  72%,100%{background-position:-224px 0}
}
@keyframes knightV2SlashR{
  0%,17.9%{background-position:0 0}
  18%,31.9%{background-position:-56px 0}
  32%,43.9%{background-position:-112px 0}
  44%,71.9%{background-position:-168px 0}
  72%,100%{background-position:-224px 0}
}
@keyframes knightV2SlashF{
  0%,17.9%{background-position:0 0}
  18%,31.9%{background-position:-56px 0}
  32%,43.9%{background-position:-112px 0}
  44%,71.9%{background-position:-168px 0}
  72%,100%{background-position:-224px 0}
}
@keyframes knightV2Attack{
  0%,25.9%{background-position:0 0}
  26%,41.9%{background-position:-56px 0}
  42%,48.9%{background-position:-112px 0}
  49%,75.9%{background-position:-168px 0}
  76%,100%{background-position:-224px 0}
}
@keyframes knightV2Thrust{
  0%,12.4%{background-position:0 0}
  12.5%,24.9%{background-position:-56px 0}
  25%,37.4%{background-position:-112px 0}
  37.5%,49.9%{background-position:-168px 0}
  50%,62.4%{background-position:-224px 0}
  62.5%,74.9%{background-position:-280px 0}
  75%,87.4%{background-position:-336px 0}
  87.5%,100%{background-position:-392px 0}
}
/* The special is held in a readable guard; only the dash window cycles the
   thrust poses.  Basic attacks keep their separate slash sheet/timeline. */
@keyframes knightV2ThrustCharge{
  0%,34.9%{background-position:0 0}
  35%,39.9%{background-position:-112px 0}
  40%,49.9%{background-position:-168px 0}
  50%,57.9%{background-position:-224px 0}
  58%,65.9%{background-position:-280px 0}
  66%,73.9%{background-position:-336px 0}
  74%,89.9%{background-position:-392px 0}
  90%,100%{background-position:0 0}
}

.enemy[class*="sprite-"]{
  width:76px;
  height:72px;
  border:0;
  border-radius:0;
  background:none;
  box-shadow:none;
  /* Global enemy scale is applied to the whole actor, including its contact
     shadow. This keeps the feet and shadow on the same floor anchor while
     preserving the parent motion transforms. */
  --enemy-visual-scale:.7;
  scale:var(--enemy-visual-scale,1);
  transform-origin:50% 100%;
  bottom:calc(3px + var(--enemy-depth-rise,0px));
}
.enemy[class*="sprite-"]::before,
.enemy[class*="sprite-"]::after{display:none!important;content:none!important}
.enemy[class*="sprite-"].boss{width:96px;height:88px;bottom:-2px;border:0;background:none;box-shadow:none}
.enemy .enemy-sprite{
  position:absolute;
  z-index:2;
  left:50%;
  bottom:-12px;
  width:150px;
  height:var(--enemy-frame-height);
  margin-left:-75px;
  background-image:var(--enemy-idle-sheet);
  background-position:0 0;
  background-size:var(--enemy-idle-width) var(--enemy-frame-height);
  background-repeat:no-repeat;
  image-rendering:pixelated;
  filter:brightness(1.08) drop-shadow(0 4px 0 rgba(0,0,0,.62)) drop-shadow(0 0 3px rgba(225,246,218,.42));
  transform:scaleX(-1);
  transform-origin:50% 100%;
  animation:enemySpriteIdle var(--enemy-idle-duration,.8s) steps(var(--enemy-idle-steps),end) infinite;
  pointer-events:none;
}
.enemy.sprite-mushroom{
  --enemy-frame-height:101px;
  --enemy-idle-sheet:url("assets/monster-a/mushroom-idle.png");
  --enemy-idle-width:600px;
  --enemy-idle-end:-450px;
  --enemy-idle-steps:3;
  --enemy-attack-sheet:url("assets/monster-a/mushroom-attack.png");
  --enemy-attack-width:1200px;
  --enemy-attack-end:-1050px;
  --enemy-attack-steps:7;
  --enemy-hurt-sheet:url("assets/monster-a/mushroom-hurt.png");
  --enemy-death-sheet:url("assets/monster-a/mushroom-death.png");
}
.enemy.sprite-goblin{
  --enemy-frame-height:102px;
  --enemy-idle-sheet:url("assets/monster-a/goblin-idle.png");
  --enemy-idle-width:600px;
  --enemy-idle-end:-450px;
  --enemy-idle-steps:3;
  --enemy-attack-sheet:url("assets/monster-a/goblin-attack.png");
  --enemy-attack-width:1200px;
  --enemy-attack-end:-1050px;
  --enemy-attack-steps:7;
  --enemy-hurt-sheet:url("assets/monster-a/goblin-hurt.png");
  --enemy-death-sheet:url("assets/monster-a/goblin-death.png");
}
.enemy.sprite-flying-eye{
  --enemy-frame-height:71px;
  --enemy-idle-sheet:url("assets/monster-a/flying-eye-idle.png");
  --enemy-idle-width:1200px;
  --enemy-idle-end:-1050px;
  --enemy-idle-steps:7;
  --enemy-idle-duration:.68s;
  --enemy-attack-sheet:url("assets/monster-a/flying-eye-attack.png");
  --enemy-attack-width:1200px;
  --enemy-attack-end:-1050px;
  --enemy-attack-steps:7;
  --enemy-hurt-sheet:url("assets/monster-a/flying-eye-hurt.png");
  --enemy-death-sheet:url("assets/monster-a/flying-eye-death.png");
}
.enemy.sprite-skeleton{
  --enemy-frame-height:71px;
  --enemy-idle-sheet:url("assets/monster-a/skeleton-idle.png");
  --enemy-idle-width:600px;
  --enemy-idle-end:-450px;
  --enemy-idle-steps:3;
  --enemy-attack-sheet:url("assets/monster-a/skeleton-attack.png");
  --enemy-attack-width:1200px;
  --enemy-attack-end:-1050px;
  --enemy-attack-steps:7;
  --enemy-hurt-sheet:url("assets/monster-a/skeleton-hurt.png");
  --enemy-death-sheet:url("assets/monster-a/skeleton-death.png");
}
.enemy.sprite-cogheim-droid-a,
.enemy.sprite-cogheim-droid-b,
.enemy.sprite-cogheim-droid-c{
  --droid-scale:1;
  --enemy-frame-height:96px;
  --enemy-idle-width:96px;
  --enemy-idle-end:0px;
  --enemy-idle-steps:1;
  --enemy-attack-width:96px;
  --enemy-attack-end:0px;
  --enemy-attack-steps:1;
  --enemy-idle-duration:1.05s;
}
.enemy.sprite-cogheim-droid-a{
  --enemy-idle-sheet:url("assets/cogheim-droids/droid-a.png");
  --enemy-attack-sheet:url("assets/cogheim-droids/droid-a.png");
  --enemy-hurt-sheet:url("assets/cogheim-droids/droid-a.png");
  --enemy-death-sheet:url("assets/cogheim-droids/droid-a.png");
}
.enemy.sprite-cogheim-droid-b{
  --enemy-idle-sheet:url("assets/cogheim-droids/droid-b.png");
  --enemy-attack-sheet:url("assets/cogheim-droids/droid-b.png");
  --enemy-hurt-sheet:url("assets/cogheim-droids/droid-b.png");
  --enemy-death-sheet:url("assets/cogheim-droids/droid-b.png");
}
.enemy.sprite-cogheim-droid-c{
  --enemy-idle-sheet:url("assets/cogheim-droids/droid-c.png");
  --enemy-attack-sheet:url("assets/cogheim-droids/droid-c.png");
  --enemy-hurt-sheet:url("assets/cogheim-droids/droid-c.png");
  --enemy-death-sheet:url("assets/cogheim-droids/droid-c.png");
}
.enemy.sprite-cogheim-droid-a .enemy-sprite,
.enemy.sprite-cogheim-droid-b .enemy-sprite,
.enemy.sprite-cogheim-droid-c .enemy-sprite{
  bottom:-8px;
  width:96px;
  height:96px;
  margin-left:-48px;
  background-size:96px 96px!important;
  animation:cogheimDroidIdle calc(1.05s * var(--combat-speed-factor,1)) steps(2,end) infinite;
}
.enemy.sprite-cogheim-droid-a .enemy-sprite,
.enemy.sprite-cogheim-droid-b .enemy-sprite,
.enemy.sprite-cogheim-droid-c .enemy-sprite{bottom:-5px}
.enemy.sprite-cogheim-droid-a.boss .enemy-sprite,
.enemy.sprite-cogheim-droid-b.boss .enemy-sprite,
.enemy.sprite-cogheim-droid-c.boss .enemy-sprite{
  --droid-scale:1.36;
  bottom:-2px;
  filter:brightness(1.12) drop-shadow(0 4px 0 rgba(0,0,0,.66)) drop-shadow(0 0 9px var(--enemy-glow,rgba(235,168,102,.62)));
  transform:scaleX(-1) scale(var(--droid-scale));
}
.enemy.sprite-mushroom .enemy-sprite{bottom:-3px}
.enemy.sprite-goblin .enemy-sprite{bottom:-4px}
.enemy.sprite-flying-eye .enemy-sprite{bottom:6px}
.enemy.sprite-skeleton .enemy-sprite{bottom:-5px}
.enemy.sprite-skeleton.boss .enemy-sprite{
  bottom:0;
  filter:brightness(1.12) drop-shadow(0 4px 0 rgba(0,0,0,.66)) drop-shadow(0 0 9px rgba(235,102,122,.62));
  transform:scaleX(-1) scale(1.42);
}
.enemy.enemy-windup .enemy-sprite,
.enemy.enemy-lunge .enemy-sprite,
.enemy.boss-windup .enemy-sprite{
  background-image:var(--enemy-attack-sheet);
  background-size:var(--enemy-attack-width) var(--enemy-frame-height);
  animation:enemySpriteAttack calc(500ms * var(--combat-speed-factor,1)) steps(var(--enemy-attack-steps),end) 1 both;
}
.enemy.enemy-return .enemy-sprite{
  background-image:var(--enemy-attack-sheet);
  background-size:var(--enemy-attack-width) var(--enemy-frame-height);
  animation:enemySpriteAttack calc(230ms * var(--combat-speed-factor,1)) steps(var(--enemy-attack-steps),end) 1 reverse both;
}
.enemy.hit .enemy-sprite{
  background-image:var(--enemy-hurt-sheet);
  background-size:600px var(--enemy-frame-height);
  animation:enemySpriteHurt calc(180ms * var(--combat-speed-factor,1)) steps(3,end) 1 both;
}
.enemy[class*="sprite-"].dead{
  opacity:1;
  transform:none;
  transition:none;
  animation:enemySpriteDeathFade calc(330ms * var(--combat-speed-factor,1)) ease-out both!important;
}
.enemy.dead .enemy-sprite{
  background-image:var(--enemy-death-sheet);
  background-size:600px var(--enemy-frame-height);
  animation:enemySpriteDeath calc(330ms * var(--combat-speed-factor,1)) steps(3,end) 1 both;
}
.enemy.sprite-cogheim-droid-a.enemy-windup .enemy-sprite,
.enemy.sprite-cogheim-droid-b.enemy-windup .enemy-sprite,
.enemy.sprite-cogheim-droid-c.enemy-windup .enemy-sprite,
.enemy.sprite-cogheim-droid-a.enemy-lunge .enemy-sprite,
.enemy.sprite-cogheim-droid-b.enemy-lunge .enemy-sprite,
.enemy.sprite-cogheim-droid-c.enemy-lunge .enemy-sprite,
.enemy.sprite-cogheim-droid-a.boss-windup .enemy-sprite,
.enemy.sprite-cogheim-droid-b.boss-windup .enemy-sprite,
.enemy.sprite-cogheim-droid-c.boss-windup .enemy-sprite{
  background-size:96px 96px;
  animation:cogheimDroidAttack calc(500ms * var(--combat-speed-factor,1)) ease-out 1 both;
}
.enemy.sprite-cogheim-droid-a.enemy-return .enemy-sprite,
.enemy.sprite-cogheim-droid-b.enemy-return .enemy-sprite,
.enemy.sprite-cogheim-droid-c.enemy-return .enemy-sprite{
  background-size:96px 96px;
  animation:cogheimDroidReturn calc(230ms * var(--combat-speed-factor,1)) ease-in 1 both;
}
.enemy.sprite-cogheim-droid-a.hit .enemy-sprite,
.enemy.sprite-cogheim-droid-b.hit .enemy-sprite,
.enemy.sprite-cogheim-droid-c.hit .enemy-sprite{
  background-size:96px 96px;
  animation:cogheimDroidHit calc(180ms * var(--combat-speed-factor,1)) ease-out 1 both;
}
.enemy.sprite-cogheim-droid-a.dead .enemy-sprite,
.enemy.sprite-cogheim-droid-b.dead .enemy-sprite,
.enemy.sprite-cogheim-droid-c.dead .enemy-sprite{
  background-size:96px 96px;
  animation:cogheimDroidDeath calc(330ms * var(--combat-speed-factor,1)) ease-in 1 both;
}

@keyframes cogheimDroidIdle{from,to{transform:scaleX(-1) scale(var(--droid-scale))}}
@keyframes cogheimDroidAttack{45%{transform:scaleX(-1) scale(var(--droid-scale)) translateX(-5px) rotate(-3deg)}100%{transform:scaleX(-1) scale(var(--droid-scale))}}
@keyframes cogheimDroidReturn{from{transform:scaleX(-1) scale(var(--droid-scale)) translateX(-4px)}to{transform:scaleX(-1) scale(var(--droid-scale))}}
@keyframes cogheimDroidHit{45%{filter:brightness(1.7) drop-shadow(0 0 6px #fff);transform:scaleX(-1) scale(var(--droid-scale)) translateX(4px)}100%{transform:scaleX(-1) scale(var(--droid-scale))}}
@keyframes cogheimDroidDeath{to{opacity:0;transform:scaleX(-1) scale(var(--droid-scale)) translateY(7px) rotate(-8deg)}}

@keyframes knightAIdle{to{background-position-x:-672px}}
@keyframes knightARun{to{background-position-x:-1344px}}
@keyframes knightAAttackOne{to{background-position-x:-360px}}
@keyframes knightAAttackTwo{to{background-position-x:-672px}}
@keyframes knightAAttackThree{to{background-position-x:-360px}}
@keyframes knightAHurt{to{background-position-x:-672px}}
@keyframes knightADefend{to{background-position-x:-360px}}
@keyframes enemySpriteIdle{to{background-position-x:var(--enemy-idle-end)}}
@keyframes enemySpriteAttack{to{background-position-x:var(--enemy-attack-end)}}
@keyframes enemySpriteHurt{to{background-position-x:var(--enemy-hurt-end,-450px)}}
@keyframes enemySpriteDeath{to{background-position-x:var(--enemy-death-end,-450px)}}
@keyframes enemySpriteDeathFade{0%,68%{opacity:1;transform:translateY(0)}100%{opacity:0;transform:translateY(-7px)}}

@media (max-width:649px) and (orientation:portrait){
  /* knight-v2 ??128px ?꾨젅????諛쒖꽑??bottom:-20px. character-a(168px, -40px)
     湲곗???洹몃?濡??먮㈃ 罹먮┃?곌? 20px ?대젮媛꾨떎. 醫뚯륫 2px 蹂댁젙留??좎??쒕떎. */
  .arka-sprite{left:-9px;bottom:-6px}
  .enemy[class*="sprite-"]{width:72px}
  .enemy[class*="sprite-"].boss{width:92px}
}

/* [2026-07-31] ?댄럺???ш린瑜?罹먮┃?곗뿉 留욎텣?? --fx-zoom ? 0.85.
   ???댄럺?몃뱾? 罹먮┃?곌? ?붾㈃ 68px ?대뜕 ?쒖젅 湲곗??쇰줈 洹몃젮議뚮떎. 吏湲덉?
   58px ?대?濡?58/68 = 0.85 瑜?怨깊빐???먮옒 ?섎룄??鍮꾩쑉???쒕떎.
   (?쒕븣 1.5 濡??щ젮??붾뜲, 洹몃븣 罹먮┃?곕? ?ㅼ슦??以??뚭퀬 留욎텣 媛믪씠?덈떎.
    罹먮┃?곌? ?ㅼ떆 ?묒븘議뚯쑝誘濡??섎룎由곕떎.)
   `transform` ???꾨땲??媛쒕퀎 `scale` ?띿꽦???대떎. ???댄럺?몃뱾? JS 媛 ?붾㈃
   醫뚰몴瑜?怨꾩궛??left/top ??px 濡?諛뺢퀬, ?먭린 ?먯떊? ?뚯닔 margin ?쇰줈 以묒떖??
   留욎텣?? `scale` ? ?덉씠?꾩썐 ?꾩튂瑜?嫄대뱶由ъ? ?딄퀬 ?먭린 以묒떖 湲곗??쇰줈留?
   而ㅼ?誘濡??듭빱媛 ?닿툔?섏? ?딅뒗?? 諛섎?濡?transform ??scale ???뷀븯硫?
   湲곗〈 rotate/scale ?좊땲硫붿씠?섏쓣 ??뼱???댄럺?멸? ?щ씪吏꾨떎.
   .projectile ? ?쒖쇅?쒕떎. transform ?쇰줈 ?대룞?섎뒗 ?덉씠??諛곗쑉???뱀쑝硫?
   鍮꾪뻾 嫄곕━源뚯? 1.5諛곌? ?쒕떎. */
.star-blade-impact,
.starburst-impact,
.shield-impact,
.enemy-strike-impact,
.parry-burst{ scale:var(--fx-zoom, .85); }

/*  [2026-07-31] 紐ъ뒪???덊듃諛뺤뒪.
    --hit-left / --hit-right ???ㅽ봽?쇱씠???꾨젅???쇱そ?먯꽌 ?덊듃諛뺤뒪 醫뚯슦 ?앷퉴吏??
    鍮꾩쑉?대떎. 罹먮┃?곌? ?대뵒源뚯? ?뚭퀬?ㅼ? 怨꾩궛?섎뒗 ???대떎(ui-combat.js heroAttack).

    [???덊듃諛뺤뒪媛 ?곕줈 ?꾩슂?쒓?]
    ?꾨젅?꾩뿉???щ챸 ?щ갚??留롮븘 `.enemy` 諛뺤뒪???꾨젅???앹쓣 湲곗??쇰줈 ?≪쑝硫?
    罹먮┃?곌? 紐ъ뒪???덉쑝濡??ㅼ뼱媛꾨떎. 洹몃젃?ㅺ퀬 遺덊닾紐??쎌? ?앹뿉 ??留욎텛硫?
    移쇱씠 ???용뒗 寃껋쿂??蹂댁씤?? 洹몃옒??寃됰낫??**??堉??덉そ**???덊듃諛뺤뒪瑜??먭퀬
    嫄곌린瑜?移섍쾶 ?쒕떎. 移쇰걹???덊듃諛뺤뒪 ?앹뿉 ?우쑝硫??붾㈃?먯꽌???ㅻ（?ｊ낵 ?댁쭩
    寃뱀퀜 蹂댁씤????洹멸쾶 ?寃⑷컧?대떎.

    [媛믪쓣 ?먯뀑 ?쎌?濡??≪? ?댁쑀]
    ?덉そ?쇰줈 ?ㅼ씠???묒쓣 鍮꾩쑉(?? 紐명넻 ??쓽 15%)濡??≪쑝硫???紐ъ뒪?곕뒗 留롮씠,
    ?묒? 紐ъ뒪?곕뒗 議곌툑 ?ㅼ뼱媛 **紐ъ뒪?곕쭏???먯젙???щ씪吏꾨떎.** 紐⑤뱺 紐ъ뒪?곌?
    媛숈? 諛곗쑉濡?洹몃젮吏誘濡? 遺덊닾紐??곸뿭?먯꽌 **醫뚯슦 5 ?먯뀑 ?쎌???* 洹좎씪?섍쾶
    ?ㅼ뿬 ?붾㈃??寃뱀묠???대뒓 紐ъ뒪?곗뿉?쒕굹 媛숆쾶 留뚮뱾?덈떎.

    ?ㅽ봽?쇱씠?몃뒗 `transform:scaleX(-1)` 濡?諛섏쟾??洹몃젮吏誘濡??붾㈃ ?쇱そ =
    ?먯뀑???ㅻⅨ履??앹씠?? 洹몃옒???붾㈃ 湲곗? 寃?踰붿쐞??[??x1, ??x0] ?대떎.
    ?ㅽ봽?쇱씠?몃? 異붽??섎㈃ ??媛믪쓣 諛섎뱶???ㅼ떆 ?ъ꽌 ?ｌ쓣 寃? 鍮좊쑉由щ㈃ 0 ?쇰줈
    ?⑥뼱??罹먮┃?곌? ?꾨젅???쇱そ ?앷퉴吏 ?뚭퀬?좊떎.
        寃?踰붿쐞(?붾㈃)     ?덊듃諛뺤뒪 ??
      ?쒕줈?대뱶   2~94 /96      82px
      ?닿낏      45~90 /150     35px
      ?덉븣      52~93 /150     31px
      怨좊툝由?   59~92 /150     23px
      踰꾩꽢      63~86 /150     13px  */
.enemy.sprite-flying-eye .enemy-sprite{--hit-left:0.3800;--hit-right:0.5867}
.enemy.sprite-goblin .enemy-sprite{--hit-left:0.4267;--hit-right:0.5800}
.enemy.sprite-mushroom .enemy-sprite{--hit-left:0.4533;--hit-right:0.5400}
.enemy.sprite-skeleton .enemy-sprite{--hit-left:0.3333;--hit-right:0.5667}
.enemy.sprite-cogheim-droid-a .enemy-sprite{--hit-left:0.0729;--hit-right:0.9271}
.enemy.sprite-cogheim-droid-b .enemy-sprite{--hit-left:0.0729;--hit-right:0.9271}
.enemy.sprite-cogheim-droid-c .enemy-sprite{--hit-left:0.0729;--hit-right:0.9271}

/*  [2026-07-31] ?곕윭吏? `loseEncounter()` 媛 `hero-down` ??遺숈씤??
    寃뚯엫 ?먮쫫???꾩쟾??二쎌쓬???꾨땲????2.2珥????뚮났???됯뎔???ш컻?쒕떎.
    洹몃옒??臾대쫷 轅뉕퀬 寃???볦? ?딆? ?먯꽭??
    `both` 濡?留덉?留??꾨젅??f5)???좎??쒕떎. ?뚮났 ???대옒?ㅻ? ?쇰㈃ idle 濡??뚯븘媛꾨떎.
    ?ㅽ봽?덉엫 ?대쫫???ㅻⅨ 紐⑥뀡怨?寃뱀튂吏 ?딆븘???ъ떆?묐맂??knightV2Slash ?ш굔 李멸퀬). */
.hero.champion.hero-down .arka-sprite{
  background-image:url("assets/knight-v2/down.png?v=knightv2-px2");
  background-position:0 0;
  background-size:336px 40px;
  animation:knightV2Down calc(600ms * var(--combat-speed-factor,1)) steps(1,end) 1 both;
}
/*  寃쎌쭅? ?먮━寃? 臾대꼫吏???쒓컙? ?ㅼ튂?? 留덉?留??먯꽭瑜??ㅻ옒 踰꾪떞??
    洹좊벑 6?깅텇?대㈃ 二쇱??됰뒗 寃??꾨땲??痢듦퀎瑜??대젮媛??寃껋쿂??蹂댁씤?? */
@keyframes knightV2Down{
  0%,12.9%{background-position:0 0}
  13%,23.9%{background-position:-56px 0}
  24%,32.9%{background-position:-112px 0}
  33%,41.9%{background-position:-168px 0}
  42%,54.9%{background-position:-224px 0}
  55%,100%{background-position:-280px 0}
}

/* Enemy contact shadows mirror the hero's pixel-scene shadow. This is placed
   after the generic sprite reset so the shadow is actually visible, then the
   actor-level scale above shrinks it together with the feet. */
.enemy[class*="sprite-"]::after{
  content:""!important;
  position:absolute;
  z-index:1;
  left:50%;
  bottom:-3px;
  display:block!important;
  width:72px;
  height:15px;
  border:0;
  border-radius:50%;
  background:radial-gradient(ellipse,rgba(5,9,10,.48),rgba(5,9,10,.2) 48%,transparent 72%);
  box-shadow:none;
  transform:translateX(-50%) scaleX(1.18);
  pointer-events:none;
}
.enemy[class*="sprite-"].boss::after{width:88px;height:18px}

/* Stage 1 gear beetle replacement.
   The source sprite is a 56x40 fixed-pitch sheet, upscaled 3x with nearest
   filtering so the game keeps the approved pixel block size. */
.enemy.sprite-stage1-gear-burrower{
  width:96px;
  height:100px;
  border:0;
  border-radius:0;
  background:none;
  box-shadow:none;
}
.enemy.sprite-stage1-gear-burrower .enemy-sprite{
  --enemy-frame-height:120px;
  --enemy-idle-sheet:url("assets/stage1-gear-burrower/idle.png");
  --enemy-idle-width:2184px;
  --enemy-idle-end:-2016px;
  --enemy-idle-steps:12;
  --enemy-idle-duration:1.3s;
  --enemy-attack-sheet:url("assets/stage1-gear-burrower/attack.png");
  --enemy-attack-width:2184px;
  --enemy-attack-end:-2016px;
  --enemy-attack-steps:12;
  --enemy-hurt-sheet:url("assets/stage1-gear-burrower/idle.png");
  --enemy-hurt-end:0px;
  --enemy-death-sheet:url("assets/stage1-gear-burrower/death.png");
  --enemy-death-end:-2016px;
  width:168px;
  height:120px;
  margin-left:-84px;
  bottom:-24px;
  background-size:2184px 120px;
  transform:none;
  filter:brightness(1.04) drop-shadow(0 5px 0 rgba(0,0,0,.66));
  --hit-left:.1786;
  --hit-right:.8214;
}
.enemy.sprite-stage1-gear-burrower.enemy-windup .enemy-sprite,
.enemy.sprite-stage1-gear-burrower.enemy-lunge .enemy-sprite,
.enemy.sprite-stage1-gear-burrower.boss-windup .enemy-sprite{
  background-image:var(--enemy-attack-sheet);
  background-size:2184px 120px;
  animation:enemySpriteAttack calc(500ms * var(--combat-speed-factor,1)) steps(12,end) 1 both;
}
.enemy.sprite-stage1-gear-burrower.enemy-return .enemy-sprite{
  background-image:var(--enemy-attack-sheet);
  background-size:2184px 120px;
  animation:enemySpriteAttack calc(230ms * var(--combat-speed-factor,1)) steps(12,end) 1 reverse both;
}
.enemy.sprite-stage1-gear-burrower.hit .enemy-sprite{
  background-image:var(--enemy-hurt-sheet);
  background-size:2184px 120px;
  /* No hurt sheet was supplied for this enemy. Hold the aligned idle frame
     during the outer hit reaction instead of applying the old 600px/3-step
     rule, which would sample between the 168px sprite cells. */
  background-position:0 0;
  animation:none;
}
.enemy.sprite-stage1-gear-burrower.dead{
  opacity:1;
  transform:none;
  animation:none!important;
}
.enemy.sprite-stage1-gear-burrower.dead .enemy-sprite{
  background-image:var(--enemy-death-sheet);
  background-size:2184px 120px;
  animation:enemySpriteDeath calc(500ms * var(--combat-speed-factor,1)) steps(12,end) 1 both;
}

/* Stage 1 Ash Hound. The three strips are fixed 133x101 transparent cells;
   the 13-cell pitch keeps the ground/root anchor locked while the dog attacks
   and falls sideways. The source already faces left, so do not mirror it. */
.enemy.sprite-stage1-ash-hound{
  width:96px;
  height:100px;
  border:0;
  border-radius:0;
  background:none;
  box-shadow:none;
}
.enemy.sprite-stage1-ash-hound .enemy-sprite{
  --enemy-frame-height:101px;
  --enemy-idle-sheet:url("assets/stage1-ash-hound/idle.png?v=ash80-detail-20260802");
  --enemy-idle-width:1729px;
  --enemy-idle-end:-1596px;
  --enemy-idle-steps:12;
  --enemy-idle-duration:1.3s;
  --enemy-attack-sheet:url("assets/stage1-ash-hound/attack.png?v=ash80-detail-20260802");
  --enemy-attack-width:1729px;
  --enemy-attack-end:-1596px;
  --enemy-attack-steps:12;
  --enemy-hurt-sheet:url("assets/stage1-ash-hound/idle.png?v=ash80-detail-20260802");
  --enemy-hurt-end:0px;
  --enemy-death-sheet:url("assets/stage1-ash-hound/death.png?v=ash80-detail-20260802");
  --enemy-death-end:-1596px;
  width:133px;
  height:101px;
  margin-left:-66.5px;
  bottom:-30px;
  background-size:1729px 101px;
  transform:none;
  filter:brightness(1.04) drop-shadow(0 4px 0 rgba(0,0,0,.66));
  --hit-left:.13;
  --hit-right:.87;
}
.enemy.sprite-stage1-ash-hound.enemy-windup .enemy-sprite,
.enemy.sprite-stage1-ash-hound.enemy-lunge .enemy-sprite,
.enemy.sprite-stage1-ash-hound.boss-windup .enemy-sprite{
  background-image:var(--enemy-attack-sheet);
  background-size:1729px 101px;
  animation:enemySpriteAttack calc(500ms * var(--combat-speed-factor,1)) steps(12,end) 1 both;
}
.enemy.sprite-stage1-ash-hound.enemy-return .enemy-sprite{
  background-image:var(--enemy-attack-sheet);
  background-size:1729px 101px;
  animation:enemySpriteAttack calc(230ms * var(--combat-speed-factor,1)) steps(12,end) 1 reverse both;
}
.enemy.sprite-stage1-ash-hound.hit .enemy-sprite{
  background-image:var(--enemy-hurt-sheet);
  background-size:1729px 101px;
  background-position:0 0;
  animation:none;
}
.enemy.sprite-stage1-ash-hound.dead{
  opacity:1;
  transform:none;
  animation:none!important;
}
.enemy.sprite-stage1-ash-hound.dead .enemy-sprite{
  background-image:var(--enemy-death-sheet);
  background-size:1729px 101px;
  animation:enemySpriteDeath calc(780ms * var(--combat-speed-factor,1)) steps(12,end) 1 both;
}

/* Normal-enemy contact shadows stay in the same soft pixel-scene family as
   the hero shadow. Bosses keep their stronger presentation treatment. */
.enemy[class*="sprite-"]:not(.boss)::after{
  background:radial-gradient(ellipse,rgba(12,18,28,.30),rgba(12,18,28,.14) 48%,transparent 72%);
}
.enemy.sprite-stage1-gear-burrower:not(.boss) .enemy-sprite,
.enemy.sprite-stage1-ash-hound:not(.boss) .enemy-sprite{
  filter:brightness(1.04) drop-shadow(0 3px 0 rgba(12,18,28,.30));
}

/* Stage 1 boss: Rivet Crusher. Runtime frames are separate transparent PNGs
   with a shared floor anchor, so the image may overflow this actor box during
   the plate strike or the sideways collapse. Nothing is clipped here. */
.enemy.sprite-stage1-rivet-crusher{
  width:150px;
  height:130px;
  bottom:-2px;
  border:0;
  border-radius:0;
  background:none;
  box-shadow:none;
  --hit-left:.08;
  --hit-right:.92;
}
.enemy.sprite-stage1-rivet-crusher .enemy-sprite.variable-sprite{
  position:absolute;
  z-index:2;
  inset:0;
  width:100%;
  height:100%;
  margin:0;
  overflow:visible;
  background:none!important;
  filter:none;
  transform:none!important;
  animation:none!important;
  pointer-events:none;
}
.enemy.sprite-stage1-rivet-crusher .rivet-crusher-frame{
  position:absolute;
  display:block;
  max-width:none;
  max-height:none;
  image-rendering:pixelated;
  image-rendering:crisp-edges;
  /* The approved 16-colour bake already contains the top-centre light.
     Keep only a soft contact lift here; do not brighten or glow the palette
     again after quantization. */
  filter:drop-shadow(0 3px 0 rgba(12,18,28,.30));
  pointer-events:none;
}
.enemy.sprite-stage1-rivet-crusher::after{
  width:114px;
  height:18px;
  transform:translateX(-50%) scaleX(1.16);
}
.enemy.sprite-stage1-rivet-crusher.dead{
  opacity:1;
  transform:none;
  transition:none;
  animation:rivetCrusherDeathFade calc(1350ms * var(--combat-speed-factor,1)) ease-out both!important;
}
@keyframes rivetCrusherDeathFade{
  0%,82%{opacity:1}
  100%{opacity:0}
}
@media (max-width:649px){
  .enemy.sprite-stage1-rivet-crusher{width:140px;height:122px}
}

/* Impact flash is deliberately last: stage-specific sprite filters above
   must not be able to tint the white hit silhouette back to its base color. */
.enemy .enemy-sprite,
.enemy .rivet-crusher-frame{transition:filter 45ms ease-out}
.enemy.hit .enemy-sprite,
.enemy.hit .enemy-sprite img,
.enemy.hit .rivet-crusher-frame{filter:brightness(0) invert(1) drop-shadow(0 0 5px rgba(255,255,255,.92)) !important}

/* -------------------------------------------------------------------------
   Dedicated skill asset split (2026-08-02)
   The body and the effect are separate pixel deliverables.  Character skill
   sheets keep the approved 56x40 logical frame, anchor (19,34), 3x nearest
   presentation, and shared 16-colour palette.  Effect sheets are transparent
   local sprites; their anchors are positioned at the hit point or floor and
   never move the actor root.  CSS cinematic tints above remain only as a
   screen-level atmosphere layer, not as the readable skill shape.
   ------------------------------------------------------------------------- */
.hero.champion.skill-star-dance .arka-sprite{
  background-image:url("assets/skill-assets/character/star-dance.png?v=skill-assets-v1-20260802");
  background-position:0 0;
  background-size:280px 40px;
  animation:skillCharacterStar calc(var(--skill-duration,1180ms) * var(--combat-speed-factor,1)) steps(5,end) 1 both;
}
.hero.champion.skill-barrier-bash .arka-sprite{
  background-image:url("assets/skill-assets/character/barrier-bash.png?v=skill-assets-v1-20260802");
  background-position:0 0;
  background-size:280px 40px;
  animation:skillCharacterBarrier calc(var(--skill-duration,700ms) * var(--combat-speed-factor,1)) steps(5,end) 1 both;
}
.hero.champion.skill-heal-channel .arka-sprite{
  background-image:url("assets/skill-assets/character/heal-channel.png?v=skill-assets-v1-20260802");
  background-position:0 0;
  background-size:448px 40px;
  animation:skillCharacterHeal calc(var(--skill-duration,840ms) * var(--combat-speed-factor,1)) steps(8,end) 1 both;
}
@keyframes skillCharacterStar{to{background-position-x:-224px}}
@keyframes skillCharacterBarrier{to{background-position-x:-224px}}
@keyframes skillCharacterHeal{to{background-position-x:-392px}}

/* Falling star / meteor at an enemy contact point. */
.meteor-sword{
  width:60px;height:96px;margin:-84px 0 0 -30px;
  clip-path:none;border:0;border-radius:0;box-shadow:none;
  background-image:url("assets/skill-assets/effects/meteor.png?v=skill-assets-v1-20260802");
  background-repeat:no-repeat;background-size:300px 96px;background-position:0 0;
  image-rendering:pixelated;
  animation:skillMeteorMotion calc(300ms * var(--combat-speed-factor,1)) cubic-bezier(.18,.72,.2,1) both,
            skillMeteorFrames calc(300ms * var(--combat-speed-factor,1)) steps(5,end) both;
}
.meteor-sword.meteor-finisher{width:60px;height:96px;margin-top:-84px;filter:none;animation-duration:calc(420ms * var(--combat-speed-factor,1))}
@keyframes skillMeteorMotion{
  0%{transform:translate3d(var(--meteor-x,0),-52px,0) rotate(var(--meteor-turn,0deg));opacity:0}
  18%{opacity:1}
  78%{opacity:1}
  100%{transform:translate3d(var(--meteor-x,0),9px,0) rotate(var(--meteor-turn,0deg));opacity:0}
}
@keyframes skillMeteorFrames{to{background-position-x:-240px}}

/* Per-target star impact is a six-frame transparent sprite, not a gradient. */
.meteor-impact.starburst-impact{
  width:72px;height:72px;margin:-36px 0 0 -36px;
  border:0;border-radius:0;box-shadow:none;background-color:transparent;
  background-image:url("assets/skill-assets/effects/star-impact.png?v=skill-assets-v1-20260802");
  background-repeat:no-repeat;background-size:432px 72px;background-position:0 0;
  image-rendering:pixelated;
  animation:skillImpactMotion calc(420ms * var(--combat-speed-factor,1)) ease-out both,
            skillImpactFrames calc(420ms * var(--combat-speed-factor,1)) steps(6,end) both;
}
@keyframes skillImpactMotion{0%{transform:scale(.32);opacity:0}20%{opacity:1}100%{transform:scale(1.2);opacity:0}}
@keyframes skillImpactFrames{to{background-position-x:-360px}}

/* Barrier shock and per-enemy shield contact use separate sheets. */
.parry-shockwave{
  left:9%;bottom:66px;width:192px;height:96px;
  border:0;border-radius:0;box-shadow:none;background-color:transparent;
  background-image:url("assets/skill-assets/effects/parry-wave.png?v=skill-assets-v1-20260802");
  background-repeat:no-repeat;background-size:1152px 96px;background-position:0 0;
  image-rendering:pixelated;
  animation:skillWaveMotion calc(560ms * var(--combat-speed-factor,1)) cubic-bezier(.16,.72,.22,1) both,
            skillWaveFrames calc(560ms * var(--combat-speed-factor,1)) steps(6,end) both;
}
@keyframes skillWaveMotion{0%{transform:translate3d(-18px,0,0) scale(.45);opacity:0}24%{opacity:1}100%{transform:translate3d(124px,0,0) scale(1.05);opacity:0}}
@keyframes skillWaveFrames{to{background-position-x:-960px}}
.shield-impact{
  width:72px;height:96px;margin:-48px 0 0 -36px;
  border:0;border-radius:0;box-shadow:none;background-color:transparent;
  background-image:url("assets/skill-assets/effects/shield-impact.png?v=skill-assets-v1-20260802");
  background-repeat:no-repeat;background-size:432px 96px;background-position:0 0;
  image-rendering:pixelated;
  animation:skillShieldMotion calc(360ms * var(--combat-speed-factor,1)) ease-out both,
            skillShieldFrames calc(360ms * var(--combat-speed-factor,1)) steps(6,end) both;
}
@keyframes skillShieldMotion{0%{transform:scale(.35) rotate(-8deg);opacity:0}25%{opacity:1}100%{transform:scale(1.12) rotate(9deg);opacity:0}}
@keyframes skillShieldFrames{to{background-position-x:-360px}}

/* Healing ring, column, and motes are independent transparent sprites. */
.skill-stage.heal-sanctuary::before{
  left:7%;bottom:61px;width:192px;height:60px;
  border:0;border-radius:0;box-shadow:none;background-color:transparent;
  background-image:url("assets/skill-assets/effects/heal-ring.png?v=skill-assets-v1-20260802");
  background-repeat:no-repeat;background-size:1536px 60px;background-position:0 0;
  image-rendering:pixelated;
  animation:skillHealRingMotion calc(820ms * var(--combat-speed-factor,1)) ease-out both,
            skillHealRingFrames calc(820ms * var(--combat-speed-factor,1)) steps(8,end) both;
}
@keyframes skillHealRingMotion{0%{transform:scale(.45);opacity:0}24%{opacity:1}100%{transform:scale(1.12);opacity:0}}
@keyframes skillHealRingFrames{to{background-position-x:-1344px}}
.heal-light-column{
  left:8%;bottom:61px;width:72px;height:192px;
  clip-path:none;background-color:transparent;
  background-image:url("assets/skill-assets/effects/heal-column.png?v=skill-assets-v1-20260802");
  background-repeat:no-repeat;background-size:576px 192px;background-position:0 0;
  image-rendering:pixelated;
  animation:skillHealColumnMotion calc(720ms * var(--combat-speed-factor,1)) ease-out both,
            skillHealColumnFrames calc(720ms * var(--combat-speed-factor,1)) steps(8,end) both;
}
@keyframes skillHealColumnMotion{0%{transform:translate3d(0,24px,0) scaleX(.6);opacity:0}22%{opacity:.88}76%{opacity:.66}100%{transform:translate3d(0,-8px,0) scaleX(1);opacity:0}}
@keyframes skillHealColumnFrames{to{background-position-x:-504px}}
.heal-particle{
  width:30px;height:30px;bottom:74px;border-radius:0;background-color:transparent;box-shadow:none;
  background-image:url("assets/skill-assets/effects/heal-particle.png?v=skill-assets-v1-20260802");
  background-repeat:no-repeat;background-size:240px 30px;background-position:0 0;
  image-rendering:pixelated;
  animation:skillHealParticleMotion calc(480ms * var(--combat-speed-factor,1)) ease-out both,
            skillHealParticleFrames calc(480ms * var(--combat-speed-factor,1)) steps(8,end) both;
}
@keyframes skillHealParticleMotion{0%{transform:translate3d(0,18px,0) scale(.55);opacity:0}20%{opacity:1}100%{transform:translate3d(8px,-112px,0) scale(1);opacity:0}}
@keyframes skillHealParticleFrames{to{background-position-x:-210px}}

/* Imported Stage 1 tile mapping (source ui-system.css stage block). */
/* [2026-08-02] Stage 1 uses the two supplied 130x102 loop tiles at an exact
   3x pixel scale. Keep the town tile in the full background layer and place
   the opaque floor tile only in the perspective plane below the horizon. */
.battlefield.stage-theme-1{
  --stage-pixel-far:url("assets/backgrounds-pixel/stage-01-background-loop-130x102.png?v=3x-loop-20260802");
  --stage-pixel-ground:url("assets/backgrounds-pixel/stage-01-floor-loop-130x102.png?v=3x-loop-20260802");
}
.battlefield.stage-theme-1 .painted-battle-scene{
  --battle-horizon:68%;
  --battle-floor-surface:var(--stage-pixel-ground);
  --battle-floor-size:390px 306px;
  /* The supplied floor tile is a horizontal loop. It is not vertically
     tileable, so do not repeat it on the Y axis inside the perspective plane. */
  --battle-floor-repeat:repeat-x;
  --battle-floor-position-end:-390px 0;
  --battle-floor-color:#596733;
}
.battlefield.stage-theme-1 .painted-battle-scene>.pixel-far{
  top:0;
  height:100%;
}
.battlefield.stage-theme-1 .pixel-far .painted-track::before,
.battlefield.stage-theme-1 .pixel-far .painted-track::after{
  background-size:390px 306px;
  background-position:0 0;
  background-repeat:repeat-x;
}
.battlefield.stage-theme-1 .painted-battle-scene::before{
  background-size:390px 306px;
  background-position:0 0;
  background-repeat:repeat-x;
}

/* A 390px tile is the exact 3x size, but a narrow phone viewport can be
   375px or smaller. Fit one complete loop in that case so the clock/windmill
   artwork is not clipped at the viewport edges; image-rendering keeps it
   pixelated rather than anti-aliased. */
@media (max-width:389px){
  .battlefield.stage-theme-1 .pixel-far .painted-track::before,
  .battlefield.stage-theme-1 .pixel-far .painted-track::after{
    background-size:100vw auto;
    background-position:50% 0;
  }
  .battlefield.stage-theme-1 .painted-battle-scene{
    --battle-floor-size:100vw auto;
    --battle-floor-position-end:-100vw 0;
  }
  .battlefield.stage-theme-1 .painted-battle-scene::before{
    background-size:100vw auto;
    background-position:50% 0;
  }
}

/* [2026-08-02] Stage 1 tile layout fix.
   The shared parallax track intentionally extends by 8% and starts at -32px,
   which is useful for the old multi-layer art but crops a single 130x102 tile
   on a phone. Stage 1 gets two exact scene-width copies instead: one copy is
   visible, the second follows it, and the existing -50% travel is seamless. */
.battlefield.stage-theme-1 .painted-battle-scene>.pixel-far{
  left:0;
  right:0;
  overflow:hidden;
}
.battlefield.stage-theme-1 .pixel-far .painted-track{
  left:0;
  width:200%;
}
.battlefield.stage-theme-1 .pixel-far .painted-track::before,
.battlefield.stage-theme-1 .pixel-far .painted-track::after{
  width:50%;
  flex:0 0 50%;
  background-size:100% 100%;
  background-position:0 0;
  background-repeat:no-repeat;
}
.battlefield.stage-theme-1 .painted-battle-scene::before{
  /* Keep the horizontal 3x tile width, but fit its full height to the
     projected floor plane so the texture is not vertically clipped. */
  background-size:390px 100%;
  background-repeat:repeat-x;
}
@media (max-width:649px){
  .battlefield.stage-theme-1 .painted-battle-scene{
    --battle-floor-size:390px 100%;
    --battle-floor-position-end:-390px 0;
  }
}

/* [2026-08-02] Preserve the supplied tile aspect ratio on responsive screens.
   The previous geometry fix used 100% 100% for the far tile, which removed the
   source 130:102 ratio.  Keep one complete contain-sized tile per scene copy;
   the horizontal repeat remains seamless while the tile itself is never
   stretched.  The background asset contains its own lower town/ground band,
   so the opaque floor plane starts below that band instead of covering it at
   the old 68% boundary.  The longer pre-perspective plane gives the 3x floor
   tile its full 390:306 image ratio without clipping its lower rows. */
.battlefield.stage-theme-1 .pixel-far .painted-track::before,
.battlefield.stage-theme-1 .pixel-far .painted-track::after{
  background-size:contain;
  background-position:0 0;
  background-repeat:repeat-x;
}
.battlefield.stage-theme-1 .painted-battle-scene{
  --battle-horizon:74%;
  --battle-floor-size:390px 306px;
  --battle-floor-position-end:-390px 0;
}
.battlefield.stage-theme-1 .painted-battle-scene::before{
  bottom:-306px;
  background-size:390px 306px;
  background-repeat:repeat-x;
  background-position:0 0;
}
