/* ============================================================================
   舞台效果（stage-fx）樣式
   ----------------------------------------------------------------------------
   class 命名：ge-fx-m-<id>（motion）／ge-fx-i-<id>（impulse）／ge-fx-v-<id>（visibility）
   參數全走 CSS 變數（apply.js 的 VARS 表設定），keyframes 裡讀變數。
   全部用 transform／opacity／filter，不用 margin（舊版 .ge-anim-* 用 margin 會觸發 layout）。
   ============================================================================ */

.ge-fx { display: block; }
.ge-fx-motion, .ge-fx-impulse, .ge-fx-vis { transform-origin: bottom center; }
.ge-fx-paused { animation-play-state: paused !important; }
.ge-fx-hidden { opacity: 0 !important; }

/* ─── motion 持續動作 ─── */
.ge-fx-m-idle  { animation: geFxIdle  var(--fx-period, 3000ms) ease-in-out infinite; }
.ge-fx-m-float { animation: geFxFloat var(--fx-period, 2500ms) ease-in-out infinite; }
.ge-fx-m-sway  { animation: geFxSway  var(--fx-period, 2000ms) ease-in-out infinite; }
.ge-fx-m-pulse { animation: geFxPulse var(--fx-period, 1200ms) ease-in-out infinite; }
.ge-fx-m-spin  { animation: geFxSpin  var(--fx-period, 4000ms) linear infinite; transform-origin: center; }
.ge-fx-m-blink { animation: geFxBlink var(--fx-period, 800ms) ease-in-out infinite; }

@keyframes geFxIdle {
    0%, 100% { transform: translateY(0); }
    25%      { transform: translateY(calc(var(--fx-amp, 2px) * -1)); }
    75%      { transform: translateY(calc(var(--fx-amp, 2px) * 0.5)); }
}
@keyframes geFxFloat {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(calc(var(--fx-amp, 10px) * -1)); }
}
@keyframes geFxSway {
    0%, 100% { transform: rotate(calc(var(--fx-deg, 3deg) * -1)); }
    50%      { transform: rotate(var(--fx-deg, 3deg)); }
}
@keyframes geFxPulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(var(--fx-scale, 1.03)); }
}
@keyframes geFxSpin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}
@keyframes geFxBlink {
    0%, 100% { opacity: 1; }
    50%      { opacity: var(--fx-min, 0.3); }
}

/* ─── impulse 一次性動作 ─── */
.ge-fx-i-shake     { animation: geFxShake     var(--fx-dur, 400ms) ease-in-out var(--fx-times, 3); }
.ge-fx-i-hop       { animation: geFxHop       var(--fx-dur, 400ms) ease-out 1; }
.ge-fx-i-recoil    { animation: geFxRecoil    var(--fx-dur, 500ms) ease-out 1; }
.ge-fx-i-emphasize { animation: geFxEmphasize var(--fx-dur, 400ms) ease-out 1; }
.ge-fx-i-startle   { animation: geFxStartle   var(--fx-dur, 500ms) ease-out 1; }
.ge-fx-i-heartbeat { animation: geFxHeartbeat var(--fx-dur, 600ms) ease-in-out var(--fx-times, 2); transform-origin: center; }

@keyframes geFxShake {
    0%, 100% { transform: translateX(0); }
    20%      { transform: translateX(var(--fx-amp, 6px)); }
    40%      { transform: translateX(calc(var(--fx-amp, 6px) * -1)); }
    60%      { transform: translateX(calc(var(--fx-amp, 6px) * 0.66)); }
    80%      { transform: translateX(calc(var(--fx-amp, 6px) * -0.66)); }
}
@keyframes geFxHop {
    0%, 100% { transform: translateY(0); }
    40%      { transform: translateY(calc(var(--fx-amp, 20px) * -1)); }
}
@keyframes geFxRecoil {
    0%   { transform: translateX(0); }
    25%  { transform: translateX(var(--fx-dist, -30px)); }
    100% { transform: translateX(0); }
}
@keyframes geFxEmphasize {
    0%   { transform: scale(1); }
    40%  { transform: scale(var(--fx-scale, 1.1)); }
    100% { transform: scale(1); }
}
@keyframes geFxStartle {
    0%, 100% { transform: translateY(0); }
    25%      { transform: translateY(calc(var(--fx-amp, 12px) * -1)); }
    50%      { transform: translateY(0); }
    75%      { transform: translateY(calc(var(--fx-amp, 12px) * -0.6)); }
}
@keyframes geFxHeartbeat {
    0%, 100% { transform: scale(1); }
    30%      { transform: scale(var(--fx-scale, 1.02)); }
    60%      { transform: scale(1); }
}

/* ─── visibility 進退場（forwards：播完停在終態，引擎收到 animationend 後換成靜態 class） ─── */
.ge-fx-v-fadeIn   { animation: geFxFadeIn   var(--fx-dur, 500ms) ease-out forwards; }
.ge-fx-v-fadeOut  { animation: geFxFadeOut  var(--fx-dur, 500ms) ease-in  forwards; }
.ge-fx-v-slideIn  { animation: geFxSlideIn  var(--fx-dur, 500ms) ease-out forwards; }
.ge-fx-v-slideOut { animation: geFxSlideOut var(--fx-dur, 500ms) ease-in  forwards; }

@keyframes geFxFadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes geFxFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes geFxSlideIn {
    from { opacity: 0; transform: translate(var(--fx-dx, -200px), var(--fx-dy, 0)); }
    to   { opacity: 1; transform: translate(0, 0); }
}
@keyframes geFxSlideOut {
    from { opacity: 1; transform: translate(0, 0); }
    to   { opacity: 0; transform: translate(var(--fx-dx, -200px), var(--fx-dy, 0)); }
}

/* 玩家端「減少動態」：持續動作全停（impulse／visibility／filter 保留，見改造計畫 §七） */
@media (prefers-reduced-motion: reduce) {
    .ge-fx-motion[class*="ge-fx-m-"] { animation: none !important; }
}

/* ─── 圖層特效（槽位內：canvas 粒子／霧／打雷） ─── */
.ge-fx-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.ge-fx-fog { position: absolute; inset: 0; overflow: hidden; opacity: var(--fog-opacity, 0.4); }
.ge-fx-fog-a, .ge-fx-fog-b {
    position: absolute; top: -20%; height: 140%; width: 200%; left: 0;
    background: radial-gradient(ellipse 30% 40% at 20% 50%, rgba(255,255,255,0.55), transparent 70%),
                radial-gradient(ellipse 35% 45% at 60% 40%, rgba(255,255,255,0.45), transparent 70%),
                radial-gradient(ellipse 25% 35% at 85% 65%, rgba(255,255,255,0.5), transparent 70%);
    animation: geFxFogDrift var(--fog-period, 60000ms) linear infinite;
}
.ge-fx-fog-b { animation-duration: calc(var(--fog-period, 60000ms) * 1.6); animation-direction: reverse; opacity: 0.7; }
@keyframes geFxFogDrift { from { transform: translateX(-50%); } to { transform: translateX(0); } }

.ge-fx-lightning { position: absolute; inset: 0; background: #fff; opacity: 0; pointer-events: none; }
.ge-fx-lightning-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; }
.ge-fx-lightning.ge-fx-flash { animation: geFxFlash 420ms linear 1; }
.ge-fx-lightning.ge-fx-flash .ge-fx-lightning-img { animation: geFxFlashImg 420ms linear 1; }
@keyframes geFxFlash {
    0% { opacity: 0; } 8% { opacity: 0.9; } 20% { opacity: 0.1; } 30% { opacity: 0.8; } 60% { opacity: 0.25; } 100% { opacity: 0; }
}
@keyframes geFxFlashImg { 0% { opacity: 0; } 8% { opacity: 1; } 30% { opacity: 1; } 100% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
    .ge-fx-fog-a, .ge-fx-fog-b { animation: none !important; }
}
