.map-replay{position:fixed;inset:0;width:min(980px,calc(100vw - 24px));max-width:980px;height:min(860px,calc(100dvh - 24px));max-height:calc(100dvh - 24px);margin:auto;padding:0;border:2px solid #b99a58;border-radius:12px;background:#14243a;color:#f5dfab;overflow:hidden;box-shadow:0 18px 80px #0009}
.map-replay[open]{display:flex;flex-direction:column}.map-replay::backdrop{background:#081410de}
.map-replay-forces{flex:none;padding:7px 16px;background:#101a27;border-bottom:1px solid #b99a5844}.map-replay-forces .battle-force-receipt{margin:0}.map-replay-forces .battle-force-side{min-height:77px}
.room-experience.is-room-result:has(.room-action-report>.battle-force-receipt) :is(.palace-card-deck,.palace-result-body)>.map-replay-open{min-height:30px;height:30px;margin:0;padding:4px 10px;flex:none;align-self:flex-end;font-size:11px}
.is-map-replay{overflow:hidden}
.map-replay header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 16px;flex:none;border-bottom:1px solid #b99a5866}.map-replay header small{font-size:11px;letter-spacing:3px}.map-replay h2{font:700 21px/1.4 var(--serif);margin:3px 0;color:#ffe6ad}
.map-replay button,.map-replay-open{min-height:44px;padding:8px 14px;border:1px solid #b99a58;border-radius:6px;background:#263a52;color:#ffe8b5;font:600 12px/1.4 var(--sans)}
.map-replay-open{margin:8px 0}.map-replay header button{white-space:nowrap}
.map-replay-stage{flex:1;min-height:0;background:#214f52;overflow:hidden}.map-replay svg{display:block;width:100%;height:100%;object-fit:contain}
.map-replay [data-replay-cell]{opacity:.58;stroke:#f4dbab55;stroke-width:1;transition:fill .65s ease,opacity .65s ease}
.map-replay .replay-flag path{stroke:#e9cc85;stroke-width:1;transition:fill .4s}.map-replay .replay-flag text{fill:#fff4d4;font:bold 12px serif}
.map-replay .replay-flag text.is-compound{font-size:9px;letter-spacing:0}
.map-replay .replay-city-name{fill:#fff3cc;font:11px var(--sans);paint-order:stroke;stroke:#153a32;stroke-width:3px}
.map-replay .is-captured .replay-flag{animation:replay-claim .65s ease-out}
.map-replay .replay-impact>circle{fill:#ffe4a066;stroke:#ffe0a1;stroke-width:3}.map-replay .replay-impact text{font:bold 27px serif;fill:#fff7ce;stroke:#923d28;stroke-width:.5}
.map-replay .is-fighting>circle{animation:replay-clash .65s ease-out 2}
.map-replay footer{display:grid;grid-template-columns:1fr auto;gap:8px;padding:12px 16px;flex:none;border-top:1px solid #b99a5866}.map-replay footer strong{font-size:14px;line-height:1.5}.map-replay footer span{font-size:12px}.map-replay footer button{grid-column:1/-1}.map-replay [hidden]{display:none!important}
@keyframes replay-clash{from{r:6;opacity:1}to{r:44;opacity:0}}
@keyframes replay-claim{from{opacity:0;translate:0 -16px}to{opacity:1;translate:0 0}}
@media(max-width:700px){.map-replay{width:calc(100vw - 12px);height:calc(100dvh - 16px);max-height:calc(100dvh - 16px)}.map-replay h2{font-size:17px}.map-replay header,.map-replay footer{padding:10px}.map-replay footer strong{font-size:12px}.yr-controls>.map-replay-open{font-size:11px;padding:5px 8px;min-height:36px}}
@media(prefers-reduced-motion:reduce){.map-replay *{animation:none!important;transition:none!important}}
.map-replay .is-fighting .replay-force-left{animation:replay-left .6s ease-in-out 2 alternate}.map-replay .is-fighting .replay-force-right{animation:replay-right .6s ease-in-out 2 alternate}
@keyframes replay-left{from{translate:-28px 0}to{translate:28px 0}}@keyframes replay-right{from{translate:28px 0}to{translate:-28px 0}}
@media(max-width:700px){.map-replay{height:min(560px,calc(100dvh - 16px))}}
@media(max-width:700px){.map-replay-forces{padding:5px 8px}.map-replay-forces .battle-force-side{min-height:66px}}
@media(max-width:700px) and (max-height:620px){.map-replay header{padding:7px 9px}.map-replay h2{font-size:15px}.map-replay header small{font-size:9px}.map-replay footer{gap:5px;padding:7px 9px}.map-replay-commander{padding:0;gap:7px}.map-replay-commander .historical-face.is-small{width:31px;height:35px}.map-replay-commander small{font-size:9px}.map-replay-commander strong{font-size:11px}.map-replay .replay-matchup{padding:5px 8px}.map-replay .replay-person .historical-face.is-small,.map-replay .replay-person .replay-player-face{width:35px;height:40px}}
.map-replay .is-retreating .replay-force-left{animation:replay-retreat 1.2s ease-out both}
.map-replay .is-retreating>text{fill:#ffad87}
.map-replay .is-retreating>circle{stroke:#ce7057}
@keyframes replay-retreat{from{translate:18px 0;opacity:1}to{translate:-110px 30px;opacity:0}}
.map-replay-commander{display:flex;align-items:center;gap:10px;grid-column:1/-1;padding:5px 0}
.map-replay-commander[hidden]{display:none}
.map-replay-commander .historical-face.is-small{width:42px;height:52px;flex-shrink:0}
.map-replay-commander small,.map-replay-commander strong{display:block}
.map-replay-commander small{font-size:10px;color:#dac89c}
.map-replay-commander strong{font-size:14px;color:#ffe6ad}
/* Recorded expedition roster: compact portraits remain visible above the map. */
.replay-matchup{display:grid;grid-template-columns:minmax(0,1fr) 32px minmax(0,1fr);align-items:center;gap:8px;flex:none;padding:10px 16px;background:linear-gradient(90deg,#1f3853,#152237,#482e2a);border-bottom:1px solid #b99a5866}
.replay-team{min-width:0;text-align:center}.replay-team>small{font-size:11px;letter-spacing:1px;color:#ead3a1}.replay-roster{display:flex;justify-content:center;align-items:center;gap:18px;margin-top:6px;min-width:0}.replay-person{display:grid;justify-items:center;gap:3px;min-width:0;flex:0 1 110px}.replay-person .historical-face.is-small,.replay-person .replay-player-face{display:block;width:64px;height:72px;object-fit:cover;border:1px solid #c4a562;border-radius:5px;margin:0;filter:none}.replay-person strong{font-size:13px;color:#fff0cf;white-space:nowrap}.replay-person small{font-size:10px;color:#dcccae;white-space:nowrap}.replay-versus{text-align:center;font:700 27px/1 var(--serif);color:#e4bb69}.replay-solo{font-size:11px;color:#dcccae}
@media(max-width:700px){.replay-matchup{padding:7px 8px;gap:3px;grid-template-columns:minmax(0,1fr) 23px minmax(0,1fr)}.replay-roster{gap:6px;margin-top:4px}.replay-person .historical-face.is-small,.replay-person .replay-player-face{width:44px;height:50px}.replay-person strong{font-size:11px}.replay-person small{font-size:9px}.replay-team>small{font-size:10px}.replay-versus{font-size:21px}}
.replay-battle-units{pointer-events:none}.battle-dust{fill:#d2bd91;opacity:.25}.battle-arrow{fill:none;stroke:#ffe4ad;stroke-width:1.6;opacity:0}.battle-sparks{stroke:#ffe7a3;stroke-width:2;opacity:0}.replay-impact>circle{opacity:.3}.replay-impact>text{paint-order:stroke;stroke:#3d261b;stroke-width:2}
.is-fighting .replay-army-left{animation:army-charge-left 2s ease-out both}.is-fighting .replay-army-right{animation:army-charge-right 2s ease-out both}.is-fighting .battle-soldier{animation:soldier-stride .25s ease-in-out infinite alternate;animation-delay:calc(var(--unit)*-.04s)}.is-fighting .battle-horse{animation:soldier-stride .18s ease-in-out infinite alternate}.is-fighting .battle-spear{animation:spear-thrust .4s ease-in-out infinite alternate}.is-fighting .battle-arrow{animation:arrow-volley 1.1s ease-in infinite;animation-delay:calc(var(--unit)*.13s)}.is-fighting .battle-sparks{animation:steel-clash .45s 1s ease-out infinite}.is-fighting .battle-dust{animation:battle-dust 1s ease-in-out infinite alternate}
.is-retreating .replay-army-left{animation:army-flee-left 1s ease-in both}.is-retreating .replay-army-right{translate:-40px 0}.is-victorious .replay-army-left{translate:40px 0}.is-victorious .replay-army-right{animation:army-flee-right 1s ease-in both}.is-victorious .replay-force-right{animation:army-flee-right 1s ease-in both}
@keyframes army-charge-left{from{translate:-28px 0}to{translate:40px 0}}@keyframes army-charge-right{from{translate:28px 0}to{translate:-40px 0}}@keyframes soldier-stride{to{translate:1px -2px}}@keyframes spear-thrust{to{rotate:14deg}}@keyframes arrow-volley{0%{translate:0 0;opacity:0}15%{opacity:1}100%{translate:150px 22px;opacity:0}}@keyframes steel-clash{from{opacity:1;scale:.5}to{opacity:0;scale:1.6}}@keyframes battle-dust{to{opacity:.5;scale:1.15}}@keyframes army-flee-left{from{translate:40px 0;opacity:1}to{translate:-90px 20px;opacity:.1}}@keyframes army-flee-right{from{translate:-40px 0;opacity:1}to{translate:90px 20px;opacity:.1}}
@media(prefers-reduced-motion:reduce){.map-replay .replay-battle-units *,.map-replay .replay-army-left,.map-replay .replay-army-right{animation:none!important;transition:none!important}}
/* Painted troops occupy the battle foreground; rear infantry and mounted vanguard move separately. */
.replay-battle-units image{filter:drop-shadow(0 3px 2px #091b2299)}
.replay-impact:has(.replay-battle-units:not([style*="none"]))>.replay-force{display:none!important}
.replay-impact>text{transform:translateY(-78px);font-size:32px}
.replay-impact>circle{transform:translateY(-87px)}
.battle-rank-rear{opacity:.88}
.is-fighting .battle-rank-rear{animation:rank-march .48s ease-in-out infinite alternate}
.is-fighting .battle-rank-front{animation:vanguard-charge .34s ease-in-out infinite alternate}
.is-fighting .battle-standard{animation:rank-march .48s ease-in-out infinite alternate}
@keyframes rank-march{to{translate:2px -1px}}
@keyframes vanguard-charge{to{translate:3px -3px;rotate:-.5deg}}
@keyframes army-charge-left{from{translate:-32px 0}to{translate:8px 0}}
@keyframes army-charge-right{from{translate:32px 0}to{translate:-8px 0}}
.is-retreating .replay-army-right{translate:-8px 0}.is-victorious .replay-army-left{translate:8px 0}
@keyframes army-flee-left{from{translate:8px 0;opacity:1}to{translate:-100px 20px;opacity:0}}
@keyframes army-flee-right{from{translate:-8px 0;opacity:1}to{translate:100px 20px;opacity:0}}
/* Frame animation changes the weapon/arm pose, rather than sliding a static formation. */
.battle-pose{opacity:0}.battle-pose.pose-0{opacity:1}
.map-replay .battle-warrior svg{width:104px;height:104px;overflow:hidden}
.is-fighting .battle-pose{animation:weapon-strike 1.12s steps(1,end) infinite;animation-delay:var(--phase)}
.is-fighting .battle-pose.pose-1{animation-delay:calc(var(--phase) - .84s)}
.is-fighting .battle-pose.pose-2{animation-delay:calc(var(--phase) - .56s)}
.is-fighting .battle-pose.pose-3{animation-delay:calc(var(--phase) - .28s)}
@keyframes weapon-strike{0%,24.99%{opacity:1}25%,100%{opacity:0}}
@media(prefers-reduced-motion:reduce){.map-replay .battle-pose{animation:none!important;opacity:0}.map-replay .battle-pose.pose-0{opacity:1}}
/* Close to weapon reach before exchanging blows. */
.is-fighting .replay-army-left,.is-fighting .replay-army-right{animation-duration:.65s}
@keyframes army-charge-left{from{translate:-30px 0}to{translate:30px 0}}
@keyframes army-charge-right{from{translate:30px 0}to{translate:-30px 0}}
.is-retreating .replay-army-right{translate:-30px 0}.is-victorious .replay-army-left{translate:30px 0}
@keyframes army-flee-left{from{translate:30px 0;opacity:1}to{translate:-100px 20px;opacity:0}}
@keyframes army-flee-right{from{translate:-30px 0;opacity:1}to{translate:100px 20px;opacity:0}}
/* City ownership seals are anchored to the city, not the combat title offset. */
.map-replay .replay-impact:not(.is-combat)>text,
.map-replay .replay-impact:not(.is-combat)>circle{transform:none}
/* Contact distance: close first, then alternate attacks and physical recoil. */
@keyframes army-charge-left{from{translate:-18px 0}to{translate:50px 0}}
@keyframes army-charge-right{from{translate:18px 0}to{translate:-50px 0}}
.is-fighting .replay-army-left,.is-fighting .replay-army-right{animation-duration:.5s}
.is-fighting .replay-army-left .battle-contact{animation:contact-left 1.12s .5s infinite}
.is-fighting .replay-army-right .battle-contact{animation:contact-right 1.12s .5s infinite}
.is-fighting .battle-pose{animation-delay:calc(.5s + var(--phase))}
.is-fighting .battle-pose.pose-1{animation-delay:calc(.5s + var(--phase) - .84s)}
.is-fighting .battle-pose.pose-2{animation-delay:calc(.5s + var(--phase) - .56s)}
.is-fighting .battle-pose.pose-3{animation-delay:calc(.5s + var(--phase) - .28s)}
@keyframes contact-left{0%,18%,50%,100%{translate:0 0}28%,38%{translate:7px 0}76%{translate:-9px 2px}88%{translate:-4px 1px}}
/* The right army is mirrored: negative local x moves it away from the clash. */
@keyframes contact-right{0%,18%,50%,100%{translate:0 0}28%,38%{translate:-9px 2px}76%{translate:7px 0}88%{translate:3px 0}}
.is-fighting .battle-sparks{animation:contact-spark .56s .78s infinite}
@keyframes contact-spark{0%{opacity:1;scale:.6}25%{opacity:0;scale:1.5}100%{opacity:0;scale:1.5}}
.is-retreating .replay-army-right{translate:-50px 0}.is-victorious .replay-army-left{translate:50px 0}
@keyframes army-flee-left{from{translate:50px 0;opacity:1}to{translate:-100px 20px;opacity:0}}
@keyframes army-flee-right{from{translate:-50px 0;opacity:1}to{translate:100px 20px;opacity:0}}
/* Battle foreground: layered ranks, low smoke, volleys and a broken retreat. */
.battlefield-ground{display:none;pointer-events:none}.replay-impact.is-combat .battlefield-ground{display:block}
.battlefield-ground image{filter:saturate(.65) contrast(1.15)}
.is-combat .replay-battle-units{filter:drop-shadow(0 6px 4px #000a)}
.is-combat>text,.is-combat>circle{display:none}
.battle-haze ellipse{fill:#c2ac86;opacity:.13;transform-box:fill-box;transform-origin:center}
.is-fighting .battle-haze ellipse{animation:combat-smoke 2.6s ease-in-out infinite alternate;animation-delay:var(--smoke-delay)}
.battle-crossfire path{stroke:#b8ab8e;stroke-width:1.2;opacity:0}
.is-fighting .battle-crossfire path{animation:combat-volley 1.6s linear infinite;animation-delay:var(--volley-delay)}
.is-fighting .battle-warrior{filter:drop-shadow(1px 4px 2px #0009)}
.is-fighting .battle-warrior:nth-child(2n){animation:combat-stagger 1.68s .8s ease-in-out infinite}
.is-fighting .battle-warrior:nth-child(3n){animation:combat-stagger 2.1s 1.2s ease-in-out infinite}
.replay-impact[data-loser=left].is-retreating .replay-army-left .battle-warrior:nth-child(3n),.replay-impact[data-loser=right].is-victorious .replay-army-right .battle-warrior:nth-child(3n){animation:combat-fall .85s ease-in forwards;transform-box:fill-box;transform-origin:center bottom}
.replay-impact[data-loser=left].is-retreating .replay-army-left{animation:army-flee-left 1.7s .35s ease-in both}
.replay-impact[data-loser=right].is-victorious .replay-army-right{animation:army-flee-right 1.7s .35s ease-in both}
@keyframes combat-smoke{from{translate:-16px 4px;scale:.85;opacity:.12}to{translate:22px -14px;scale:1.35;opacity:.4}}
@keyframes combat-volley{0%,12%{opacity:0;translate:0 var(--volley-y)}18%{opacity:.9}70%{opacity:.8}100%{translate:310px calc(var(--volley-y) + 115px);opacity:0}}
@keyframes combat-stagger{0%,36%,100%{translate:0 0;rotate:0deg}45%{translate:-5px 3px;rotate:-4deg}63%{translate:-3px 2px;rotate:-2deg}}
@keyframes combat-fall{to{rotate:-75deg;translate:-8px 24px;opacity:.2}}
@media(prefers-reduced-motion:reduce){.map-replay .battle-haze ellipse,.map-replay .battle-crossfire path,.map-replay .battle-warrior{animation:none!important}}
.map-replay-stage.is-battlefield .battlefield-ground{opacity:0}
.map-replay-stage.is-battlefield .battle-haze,.map-replay-stage.is-battlefield .battle-dust{filter:blur(7px)}
.map-replay-stage.is-battlefield .battle-dust{opacity:.13!important}
@media(prefers-reduced-motion:reduce){.map-replay-stage.is-battlefield>svg{transform:none}}

.map-replay-stage.is-battlefield{overflow:hidden}
.map-replay-stage.is-battlefield .replay-battle-units{transform:translateY(-40px) scale(.68);transform-origin:0 0}
.map-replay-stage.is-battlefield .battlefield-ground{display:none!important}
.map-replay [data-replay-city].is-battle-target>circle{r:9;fill:#ffe3a1;stroke:#9e3424;stroke-width:3}
.map-replay [data-replay-city].is-battle-target>.replay-city-name{font-size:20px;font-weight:700;paint-order:stroke;stroke:#102030;stroke-width:4px;fill:#fff5da}
