/* 锚点不参与缩放和位移，避免按下、聚焦时图标跑偏。 */
.scenario-map .scenario-echo, .sound-marker, .sound-marker:hover, .sound-marker:active {transform:translate(-50%,-50%);animation:none;transition:none}
.echo-revealing :is(.marker-core,.scenario-core) {animation:voice-surface 2.6s cubic-bezier(.2,.65,.3,1) both}
.echo-revealing .marker-caption {animation:voice-caption 2.8s ease both}
.echo-revealing .marker-ripple {animation:voice-ring 3.6s ease-out infinite}
.echo-revealing .marker-ripple.second {animation-delay:.65s}
/* 演示隐声抵达：先出现两道音纹，再让声音核心从波纹中心凝聚出来。 */
.echo-revealing.scenario-echo .marker-ripple {animation:voice-arrival-ring 1.65s cubic-bezier(.16,.7,.28,1) both}
.echo-revealing.scenario-echo .marker-ripple.second {animation-delay:.22s}
.echo-revealing.scenario-echo .scenario-core {animation:voice-arrival-core 1.8s .25s cubic-bezier(.16,.8,.3,1) both}
.echo-dust {position:absolute;inset:0;pointer-events:none;color:var(--marker,#8da395)}
.echo-dust i {position:absolute;left:50%;top:50%;width:3px;height:3px;border-radius:50%;background:currentColor;opacity:0}
.echo-dissolving {pointer-events:none!important}
.echo-dissolving :is(.marker-core,.scenario-core) {animation:voice-wind 1.9s ease-in both}
.echo-dissolving :is(.marker-caption,.marker-ripple) {animation:voice-fade .8s ease both}
.echo-dissolving .echo-dust i {animation:voice-grain 1.95s var(--delay) cubic-bezier(.25,.1,.55,1) both}
.scenario-boundary.wind-away {animation:voice-fade 1.8s ease both}
@keyframes voice-surface {0%,12%{opacity:0;filter:blur(7px);transform:translateY(10px) scale(.55)}65%{opacity:.8;filter:blur(.5px)}100%{opacity:1;filter:blur(0);transform:none}}
@keyframes voice-caption {0%,50%{opacity:0}100%{opacity:1}}
@keyframes voice-ring {0%{opacity:0;transform:scale(.4)}20%{opacity:.65}100%{opacity:0;transform:scale(2.3)}}
@keyframes voice-arrival-ring {0%{opacity:0;transform:scale(.22)}18%{opacity:.72}70%{opacity:.36}100%{opacity:0;transform:scale(2.55)}}
@keyframes voice-arrival-core {0%,8%{opacity:0;filter:blur(8px);transform:scale(.35)}55%{opacity:.72;filter:blur(1px);transform:scale(.9)}82%{transform:scale(1.08)}100%{opacity:1;filter:blur(0);transform:scale(1)}}
@keyframes voice-wind {0%{opacity:1;filter:blur(0);transform:none}40%{opacity:.7;filter:blur(1px);transform:translate(6px,-2px)}100%{opacity:0;filter:blur(9px);transform:translate(48px,-20px) scale(.7,.3)}}
@keyframes voice-grain {0%{opacity:0;transform:translate(var(--x),var(--y)) scale(.4)}20%{opacity:.8}100%{opacity:0;transform:translate(var(--drift),var(--rise)) scale(.1);filter:blur(2px)}}
@keyframes voice-fade {to{opacity:0}}
@media(prefers-reduced-motion:reduce){
 .echo-revealing :is(.marker-core,.scenario-core,.marker-caption),.echo-revealing .marker-ripple{animation:none;filter:none}
 .echo-dissolving .echo-dust{display:none}
 .echo-dissolving :is(.marker-core,.scenario-core){animation:voice-fade .1s both}
}
