/* ============================================================================
 * 動物大逃亡 — DOM 渲染樣式
 *
 * 1:1 照搬視覺概念稿 mockup-escape-visual.html 的 <style>（只取遊戲畫面相關 class），
 * 把它從「文件頁 16:9 .vp 框」改成「整個遊戲視窗」。配色 / 漸層 / 比例 / 動畫曲線
 * 全部照搬。座標模型：#world 內以 960×540 邏輯像素 top-down 擺放（與引擎一致），
 * 相機平移只動 #world 的 transform；runner 只動自己的 transform；夜罩只改 CSS 變數。
 *
 * 順暢度鐵則：建一次 DOM，每幀只改 transform / CSS 變數，全程 translate3d 走 GPU 合成。
 * ========================================================================== */

.escape-root {
  /* CSS 變數（沿用 mockup :root 的調色盤） */
  --orange: #FF8C2A;
  --text: #eef1f8;
  --muted: #6a7a9a;
  --green: #34d399;
  --red: #fb7185;
  --purple: #a78bfa;
  --gold: #ffd700;

  position: absolute;
  inset: 0;
  overflow: hidden;
  font-family: 'Noto Sans TC', 'PingFang TC', 'Microsoft JhengHei', sans-serif;
  color: var(--text);
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;
}

/* ── .vp 視窗：mockup 是 16:9 的框；這裡是整個遊戲視窗 ───────────────────── */
.escape-vp {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #080c1a;
}

/* 960×540 邏輯舞台：等比縮放 + 置中（letterbox），所有子層在此座標系內定位。 */
.escape-stage {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 960px;
  height: 540px;
  transform-origin: center center;
  /* transform（scale + translate）由 JS 設定 */
}

/* ── 天空背景層（白天 / 夜晚兩張漸層疊放，靠 opacity 切換）──────────────── */
.escape-sky {
  position: absolute;
  inset: 0;
  z-index: 0;
  will-change: opacity;
}
.escape-sky.day {
  /* 照 mockup ① 白天天空漸層 */
  background: linear-gradient(180deg, #8fd0e6 0%, #bfe3c8 55%, #cfe8a8 100%);
}
.escape-sky.night {
  /* 照 mockup ② 夜空漸層 */
  background: linear-gradient(180deg, #0b1030 0%, #10204a 60%, #16352f 100%);
  opacity: 0; /* 由 JS 依 nightFactor 設 */
}

/* 天體（白天太陽 / 夜晚月亮，cross-fade）。照 mockup .sky-orb（74px；夜 56px）。 */
.escape-sun-orb {
  position: absolute;
  top: 9%;
  z-index: 1;
  image-rendering: auto;
  pointer-events: none;
  will-change: transform, opacity;
}
.escape-sun-orb img { display: block; width: 100%; height: 100%; }
.escape-sun { width: 74px; height: 74px; }
.escape-moon { width: 56px; height: 56px; }

/* 雲（照 mockup .clouds img：38px 高、opacity .85）。視差用 transform。 */
.escape-clouds {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  will-change: transform;
}
.escape-clouds img { position: absolute; height: 38px; opacity: .85; }

/* ── #world 捲動層（地形 + 樹 + 巨獸 + 記錄點 + runners）─────────────────── */
.escape-world {
  position: absolute;
  inset: 0;
  z-index: 2;
  will-change: transform;
}

/* 地形（照 mockup .terr：草頂 9px + 泥土漸層）。每塊一次性建立，不逐幀重建。 */
/* 像素風地形（kenney_pixel-platformer，PM 2026-06-08）：草頂磚鋪頂排(repeat-x) + 泥土磚填滿。
   --pp-tile = 顯示磚大小（18px 原圖 ×2 = 36，整數倍 → image-rendering:pixelated 最銳利）。 */
.escape-stage { --pp-tile: 36px; }
/* 地形磚由 JS 依生態域 inline 設定（biomeBg：每 500m 換森林/工業/雪地磚）；此處只留底色+像素縮放+陰影。 */
.escape-terr {
  position: absolute;
  background-color: #4a3526;
  image-rendering: pixelated;
}
.escape-plat {
  position: absolute;
  background-color: #4a3526;
  image-rendering: pixelated;
  box-shadow: 0 4px 8px rgba(0, 0, 0, .32);
}
/* §4.2 上層平台（普通跳可達）：偏亮提示「可選走的上層」。 */
.escape-plat.upper { filter: brightness(1.12); }
/* §4.2 彈跳獎勵平台（只有彈跳上得去）：金光提示「高獎勵」。 */
.escape-plat.reward {
  box-shadow: 0 0 14px rgba(255, 200, 90, .6), 0 4px 8px rgba(0, 0, 0, .32);
  filter: saturate(1.25) brightness(1.06);
}

/* 樹（照 mockup .tree：底部中心 origin，img 貼齊）。 */
.escape-tree {
  position: absolute;
  transform-origin: bottom center;
  pointer-events: none;
}
.escape-tree img { display: block; height: 100%; }

/* 梯子 / 藤蔓 / 吊橋 / 彈跳香菇（照 mockup，P3 視覺占位）。 */
.escape-ladder {
  position: absolute;
  width: 22px;
  background: repeating-linear-gradient(180deg, #a07a48 0 4px, transparent 4px 13px);
  border-left: 4px solid #6b4f2e;
  border-right: 4px solid #6b4f2e;
  border-radius: 3px;
}
.escape-climbvine {
  position: absolute;
  width: 8px;
  background: linear-gradient(180deg, #54b86a, #2c7a3e);
  border-radius: 5px;
}
.escape-climbvine span { position: absolute; font-size: 13px; left: -7px; }
.escape-bridge {
  position: absolute;
  height: 11px;
  background: repeating-linear-gradient(90deg, #a07a48 0 9px, #6b4f2e 9px 12px);
  border-radius: 3px;
  box-shadow: 0 3px 6px rgba(0, 0, 0, .3);
}
/* 彈跳點＝像素蘑菇（kenney_pixel-platformer，PM 2026-06-08）+ 文字說明。 */
.escape-bounce {
  position: absolute;
  width: 44px;
  height: 44px;
  background: var(--pp-mushroom) no-repeat center bottom / contain;
  image-rendering: pixelated;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .35));
}
/* 文字說明（PM 2026-06-08）：標示「跳!」讓玩家知道踩上去會彈高。 */
.escape-bounce:after {
  content: '⤒ 彈跳';
  position: absolute;
  left: 50%;
  top: -17px;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 11px;
  font-weight: 900;
  color: #fff;
  background: rgba(232, 77, 122, .92);
  padding: 1px 7px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, .35);
  animation: esc-bouncelbl 1s ease-in-out infinite;
}
@keyframes esc-bouncelbl { 0%,100% { transform: translate(-50%, 0); } 50% { transform: translate(-50%, -3px); } }

/* 生態域裝飾（松樹/草芽/管線/箱/齒輪/雪人…）：像素圖、貼地。放 terrainLayer（1:1 黏地板）；
   z-index 1 = 在地形磚(auto 0)之前、互動物(踏板/門/旗 z4~6)之後 → 站在地上、不擋機關。
   2026-06-09 清爽化：輕微調暗降彩 → 退為點綴，可互動物件才搶眼。 */
.escape-decor {
  position: absolute;
  z-index: 1;
  background-repeat: no-repeat;
  background-position: center bottom;
  background-size: contain;
  image-rendering: pixelated;
  pointer-events: none;
  filter: brightness(.92) saturate(.82);
  opacity: .92;
}

/* 標籤治理（PM 2026-06-09）：每種機關只有第一個顯示文字標籤（教學一次），之後 .nolabel 隱藏。 */
.escape-gate.nolabel .glabel,
.escape-lift.nolabel .lflabel,
.escape-keydoor.nolabel .kdlabel,
.escape-ckpt.nolabel .cklabel { display: none; }
.escape-bounce.nolabel:after { content: none; }

/* 升降台高牆（必經之路）：金屬磚柱（工業磚統一識別＝機械牆），牆頂只能搭台越過。 */
.escape-wall {
  position: absolute;
  width: 36px;
  z-index: 3;
  background:
    var(--g1) repeat-x left top / var(--pp-tile) var(--pp-tile),
    var(--d1) repeat left top / var(--pp-tile) var(--pp-tile);
  background-color: #4a4f60;
  image-rendering: pixelated;
  box-shadow: 0 0 10px rgba(0, 0, 0, .35);
  pointer-events: none;
}

/* 斷層提示（照 mockup .gapwarn）。 */
.escape-gapwarn {
  position: absolute;
  font-size: 11px;
  color: var(--red);
  font-weight: 800;
  z-index: 9;
  pointer-events: none;
}

/* actors 層：所有 runner 在此，位於夜罩之上（z-index 6 > .escape-lightmask 4）→
 * 夜晚「看得到夥伴、看不清地面」。與 #world 同相機平移（transform 每幀同步）。 */
.escape-actors {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  will-change: transform;
}

/* ── runner（生肖代幣，照 mockup .runner：54px 圓 + drop-shadow）─────────── */
.escape-runner {
  position: absolute;
  left: 0;
  top: 0;
  /* PM 2026-06-07：角色放大（54→78），更明顯。中心對齊 → margin = -size/2。 */
  width: 78px;
  height: 78px;
  margin-left: -39px;   /* 讓 transform 的 x/y 表示「中心點」 */
  margin-top: -39px;
  border-radius: 50%;
  /* 效能：移除 base drop-shadow（每幀隨相機平移會觸發重繪）；夜晚發光改用 .litglow。 */
  z-index: 6;
  will-change: transform;
}
/* 內層顯示頭像並承載動畫（roll/bob/jmp）；外層只負責定位 transform → 兩者互不干擾。 */
.escape-runner .anim {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background-size: cover;
  background-position: center;
}
.escape-runner .roll { animation: esc-roll .8s linear infinite; }
.escape-runner .bob { animation: esc-bob .5s ease-in-out infinite; }
.escape-runner .jumping { animation: esc-jmp 1.3s ease-in-out infinite; }
@keyframes esc-roll { to { transform: rotate(360deg); } }
@keyframes esc-bob {
  0%, 100% { transform: translateY(0) rotate(-5deg); }
  50% { transform: translateY(-7px) rotate(5deg); }
}
@keyframes esc-jmp {
  0%, 100% { transform: translateY(0) scaleY(1); }
  35% { transform: translateY(-58px) scaleY(1.15) rotate(180deg); }
}
/* 夜晚領路者/夥伴高光（照 mockup .litglow）。 */
.escape-runner.litglow { filter: drop-shadow(0 0 9px rgba(255, 235, 180, .7)) !important; }
/* 遠方夥伴在暗處（照 mockup brightness(.55)）。 */
.escape-runner.dim { filter: brightness(.55); }
/* §4.7 倒下虛影：半透明 + 去飽和 + 輕微閃爍，提示「等待救援」。opacity-only 動畫不影響定位 transform。 */
.escape-runner.ghost { filter: grayscale(.6) brightness(1.1); animation: esc-ghost 1.2s ease-in-out infinite; }
@keyframes esc-ghost { 0%, 100% { opacity: .30; } 50% { opacity: .58; } }
/* 死亡半透明虛影。 */
.escape-runner.dead { opacity: .4; }

/* ── 黑影巨獸（照 mockup .beast：radial-gradient 黑影 + 兩顆紅眼 + tag + loom）─ */
.escape-beast {
  position: absolute;
  left: 0;            /* 由 JS transform(世界座標) 定位；left:0 讓基準確定 */
  bottom: 6%;
  width: 26%;
  height: 80%;
  z-index: 5;
  background: radial-gradient(58% 52% at 62% 42%, #0c1f18 0%, #081512 58%, rgba(8, 15, 12, 0) 78%);
  /* 效能＋正確性：移除 filter:blur（移動層內每幀重繪）；移除 animation:esc-loom
   * （CSS 動畫會覆蓋 JS 的 transform 定位 → 巨獸就不會追玩家了）。漸層本身已柔邊。 */
  pointer-events: none;
  will-change: transform;
}
.escape-beast .eye {
  position: absolute;
  top: 32%;
  left: 52%;
  width: 15px;
  height: 21px;
  border-radius: 50%;
  background: var(--red);
  box-shadow: 0 0 16px 5px rgba(251, 113, 133, .85);
}
.escape-beast .eye.r { left: 64%; }
.escape-beast .tag {
  position: absolute;
  bottom: 4px;
  left: 18%;
  font-size: 10px;
  color: #fbb;
  background: rgba(0, 0, 0, .5);
  padding: 2px 7px;
  border-radius: 7px;
  white-space: nowrap;
}
@keyframes esc-loom {
  0%, 100% { transform: translateX(0) scale(1); }
  50% { transform: translateX(12px) scale(1.05); }
}

/* ── 記錄點（照 mockup .ckpt：發光火把 + 木桿 + 標籤 + pulse）────────────── */
.escape-ckpt {
  position: absolute;
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
}
/* 記錄點＝像素旗子（kenney_pixel-platformer，PM 2026-06-08）。旗圖已含旗桿 → 隱藏原木桿。 */
.escape-ckpt .glow {
  width: 40px;
  height: 48px;
  background-image: var(--pp-flag);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center bottom;
  image-rendering: pixelated;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .4));
  animation: none;
}
.escape-ckpt .pole { display: none; }
.escape-ckpt .cklabel {
  margin-top: 2px;
  font-size: 10px;
  font-weight: 800;
  color: var(--gold);
  background: rgba(0, 0, 0, .4);
  padding: 2px 7px;
  border-radius: 7px;
  white-space: nowrap;
}
@keyframes esc-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.18); }
}

/* ── 發光球（照 mockup .orb：黃球 + 暖光 box-shadow + pulse / floaty）────────
   PM 2026-06-07：光球太小看不到 → 放大（36→64）、強化發光（三層光暈）；改用 box-shadow 脈動
   (esc-orb-glow)，不用 transform 脈動 — 因 orb 透過 JS 寫 inline transform translate3d 跟玩家走，
   esc-pulse 用 transform: scale 會覆蓋掉 translate3d，orb 卡在舞台左上角不跟玩家（PM 回報根因）。 */
.escape-orb {
  position: absolute;
  left: 0;
  top: 0;
  width: 64px;
  height: 64px;
  margin-left: -32px;
  margin-top: -32px;
  border-radius: 50%;
  z-index: 7;
  background-size: cover;
  background-position: center;
  /* 三層光暈：內亮黃 + 中暖橘 + 外擴散，在黑夜中極為醒目 */
  box-shadow:
    0 0 22px 10px rgba(255, 240, 180, .95),
    0 0 48px 22px rgba(255, 200, 90, .75),
    0 0 80px 36px rgba(255, 160, 60, .45);
  animation: esc-orb-glow 1.4s ease-in-out infinite;
  pointer-events: none;
  will-change: transform;
}
/* 光暈靜態 — 不動畫（PM 2026-06-07 perf 修：之前 box-shadow keyframes 每幀 paint ~300x300px 區域，
   手機累積卡頓；改 opacity keyframes 會覆蓋 JS 設的 day/night opacity → 重複『inline vs keyframe』
   覆蓋陷阱）。三層靜態光暈已經很醒目，不需要脈動。 */
@keyframes esc-orb-glow { 0%, 100% { } }
/* .free 也只動 box-shadow + margin-top（不動 transform），避免覆蓋 translate3d 定位。 */
.escape-orb.free { animation: esc-orb-glow 1.4s ease-in-out infinite, esc-floaty 2.4s ease-in-out infinite; }
@keyframes esc-floaty { 0%, 100% { margin-top: -32px; } 50% { margin-top: -42px; } }

/* ── 夜罩光圈（照 mockup .lightmask radial-gradient；圓心跟領路者，靠 CSS 變數）─
 *  關鍵：暖光中心 → 近黑邊緣。夜晚＝整片暗、只有光圈內看得到地板。
 *  每幀只更新 --lx / --ly / opacity，不重建漸層字串結構。 */
.escape-lightmask {
  /* 效能：2×stage 大的固定漸層，圓心跟玩家靠 transform 平移（GPU 合成、零重繪），
   * 取代每幀改 radial-gradient 位置（全螢幕重繪→卡頓，PM 2026-06-05 回報）。 */
  position: absolute;
  left: 0;
  top: 0;
  width: 200%;
  height: 200%;
  z-index: 4;
  pointer-events: none;
  background: radial-gradient(circle 150px at center,
    rgba(255, 231, 160, .16) 0 60px,
    rgba(10, 9, 18, .4) 118px,
    rgba(3, 4, 9, .9) 165px,
    rgba(3, 4, 9, .95) 100%);
  opacity: 0; /* 由 JS 依 nightFactor 設（白天 0 → 夜晚 1） */
  will-change: transform, opacity;
}
/* 日光泡泡：用『高 alpha 暖白色直接覆蓋』夜色（PM 2026-06-07 v2）。
   先前用 mix-blend-mode: screen 在多層夜罩下效果被稀釋（PM 截圖回報『還是黑了』），
   改用直接 alpha 覆蓋 — 中央 0.95 alpha 的暖白色直接蓋掉 95% 的夜，視覺上強烈像白天。
   z-index 介於 lightmask(4) 和 actors(6) → 玩家仍在亮泡泡內。 */
.escape-sunbubble {
  position: absolute;
  left: 0;
  top: 0;
  width: 200%;
  height: 200%;
  z-index: 5;
  pointer-events: none;
  background: radial-gradient(circle 400px at center,
    rgba(255, 250, 220, 0.95) 0 180px,    /* 中央：95% 不透明暖白 → 幾乎完全蓋掉夜色 */
    rgba(255, 240, 190, 0.78) 260px,      /* 第二圈：78% 仍很亮 */
    rgba(255, 220, 150, 0.45) 330px,      /* 第三圈：45% 過渡 */
    rgba(255, 200, 120, 0.18) 380px,      /* 邊緣：18% 輕微暖光 */
    rgba(0, 0, 0, 0) 400px,               /* 400px 外完全透明 → 保留夜色 */
    rgba(0, 0, 0, 0) 100%);
  /* 不用 mix-blend-mode — 直接覆蓋更可控、跨瀏覽器一致。 */
  opacity: 0;
  will-change: transform, opacity;
}

/* ── 隊伍前緣邊界簾幕（PM 2026-06-08）────────────────────────────────────────
   領先者被繫繩擋住、還到不了的前方 → 用簾幕蓋住那段地形，讓「目前能走到的邊界」明確。
   left 由 JS 動態設（穩態約 800px）、蓋到右緣 right:0；z-index 5 介於地形/夜罩(≤4) 與
   actors(6) 之間 → 被擋的領先者本人仍清楚可見、只蓋住到不了的前方。screen-space（不隨相機平移）。 */
.escape-team-boundary {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 800px;            /* JS 動態覆寫 */
  z-index: 5;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.22s ease;
  background: linear-gradient(to right,
    rgba(12, 9, 22, 0) 0,
    rgba(11, 8, 20, 0.72) 16px,
    rgba(6, 4, 14, 0.97) 80px,
    rgba(5, 3, 12, 0.98) 100%);   /* 邊界往右快速轉近全不透明 → 確實蓋住到不了的地形（PM:不要出現）*/
  will-change: opacity, left;
}
.escape-team-boundary.show { opacity: 1; }
/* 邊界線：左緣一道暖色發光豎條（明確標示「這裡是目前邊界」）。 */
.escape-team-boundary::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: linear-gradient(to bottom, rgba(255, 214, 130, 0.95), rgba(255, 150, 86, 0.7));
  box-shadow: 0 0 14px 4px rgba(255, 184, 96, 0.5);
}
/* 提示文字「⏳ 等待隊友」直書、置中於簾幕。 */
.escape-team-boundary-label {
  position: absolute;
  left: 16px;
  top: 50%;
  transform: translateY(-50%);
  writing-mode: vertical-rl;
  letter-spacing: 5px;
  font-weight: 800;
  font-size: 16px;
  color: #ffe7b0;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.65);
  white-space: nowrap;
}

/* ── 協力門（里程碑關卡，PM 2026-06-08）：生態域邊界的能量牆 + 踏板 ────────────────
   能量牆鎖住前進，全隊踩 required(=活著玩家數) 塊踏板才開。在 terrainLayer 內 → 隨相機平移。 */
.escape-gate {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 14px;
  margin-left: -7px;            /* 以 gate.x 為中心 */
  z-index: 5;
  pointer-events: none;
}
.escape-gate .gbar {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background: repeating-linear-gradient(-20deg,
    rgba(120, 200, 255, 0.5) 0 8px,
    rgba(70, 150, 230, 0.72) 8px 16px);
  box-shadow: 0 0 16px 4px rgba(110, 190, 255, 0.6), inset 0 0 8px rgba(255, 255, 255, 0.5);
  transition: opacity 0.4s ease;
}
.escape-gate.open .gbar { opacity: 0; }
.escape-gate .glabel {
  position: absolute;
  left: 50%;
  top: 20px;
  transform: translateX(-50%);
  white-space: nowrap;
  font-size: 12px;
  font-weight: 800;
  color: #ffe7b0;
  background: rgba(20, 20, 36, 0.72);
  padding: 2px 8px;
  border-radius: 8px;
  text-shadow: 0 1px 3px #000;
}
/* 踏板：地面上的圓盤，踩到變亮綠。 */
.escape-plate {
  position: absolute;
  width: 48px;
  height: 14px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 35%, #cfd6e2, #8a93a6);
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.35);
  z-index: 4;
  pointer-events: none;
  transition: background 0.12s, box-shadow 0.12s;
}
.escape-plate.on {
  background: radial-gradient(circle at 50% 35%, #b6ffce, #43d17a);
  box-shadow: 0 0 12px 3px rgba(80, 230, 130, 0.7), inset 0 -3px 0 rgba(0, 80, 30, 0.3);
}
/* 協力升降台（移動平台，PM 2026-06-08）：木+金屬台面 + 標籤；top 由 JS 每幀設（確定性 tSec）。 */
.escape-lift {
  position: absolute;
  height: 18px;
  z-index: 4;
  /* PM 2026-06-09：平滑木紋 → 像素橫樑磚（pp-ind-girder，與整體像素語言統一；鏈式吊台）。 */
  background: var(--pp-girder) repeat-x left top / 18px 18px;
  image-rendering: pixelated;
  box-shadow: 0 0 10px 2px rgba(150, 210, 255, .45), 0 3px 6px rgba(0, 0, 0, .4);
  pointer-events: none;
}
.escape-lift .lflabel {
  position: absolute;
  left: 50%; top: -16px; transform: translateX(-50%);
  white-space: nowrap; font-size: 10px; font-weight: 800; color: #fff;
  background: rgba(40, 90, 150, .85); padding: 1px 6px; border-radius: 7px; text-shadow: 0 1px 2px #000;
}
/* 鑰匙門（PM 2026-06-08）：金色鎖牆擋路 + 飄浮鑰匙；撿到鑰匙 → 門開。terrainLayer 內隨相機平移。 */
.escape-keydoor {
  position: absolute; top: 0; bottom: 0; width: 18px; margin-left: -9px; z-index: 5; pointer-events: none;
}
.escape-keydoor .kdbar {
  position: absolute; inset: 0; border-radius: 4px;
  background: repeating-linear-gradient(20deg, rgba(255,210,90,.55) 0 8px, rgba(220,160,40,.78) 8px 16px);
  box-shadow: 0 0 16px 4px rgba(255,200,90,.55), inset 0 0 8px rgba(255,255,255,.5);
  image-rendering: pixelated; transition: opacity .4s ease;
}
.escape-keydoor.open .kdbar { opacity: 0; }
.escape-keydoor .kdlabel {
  position: absolute; left: 50%; top: 22px; transform: translateX(-50%); white-space: nowrap;
  font-size: 12px; font-weight: 800; color: #ffe7b0; background: rgba(40,30,12,.74); padding: 2px 8px;
  border-radius: 8px; text-shadow: 0 1px 3px #000;
}
.escape-key {
  position: absolute; width: 36px; height: 36px; z-index: 4; pointer-events: none;
  background: no-repeat center / contain; image-rendering: pixelated;
  filter: drop-shadow(0 0 8px rgba(255,210,90,.8)); animation: esc-keyfloat 1.2s ease-in-out infinite;
}
.escape-key.got { display: none; }
@keyframes esc-keyfloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }

/* ── §4.9 裂地分割 + 圖騰填色 ─────────────────────────────────────────────── */
/* 圖騰層：與 #world 同相機平移（transform 由 JS 每幀設）。z 在地形之上、夜罩之上。 */
.escape-cleave-layer { position: absolute; inset: 0; z-index: 6; pointer-events: none; will-change: transform; }
/* 單一圖騰：立在世界 x 的地面附近（top 固定、left=世界 x）。 */
.escape-totem { position: absolute; top: 330px; width: 64px; margin-left: -32px; display: flex; flex-direction: column; align-items: center; gap: 4px; }
.escape-totem .ta { width: 30px; height: 30px; border-radius: 50%; background-size: cover; background-position: center; border: 2px solid rgba(255,255,255,.6); box-shadow: 0 0 8px rgba(0,0,0,.5); }
/* 圖騰柱：半透明石柱，內含由下往上的填色。 */
.escape-totem .tg { position: relative; width: 40px; height: 92px; border-radius: 8px; overflow: hidden;
  background: rgba(20,16,30,.55); border: 2px solid rgba(255,210,120,.85); box-shadow: 0 0 14px rgba(255,180,80,.5); }
.escape-totem .tf { position: absolute; left: 0; bottom: 0; width: 100%; height: 100%; transform: scaleY(0); transform-origin: bottom;
  background: linear-gradient(0deg, #ffcd5f, #ff8a3d); will-change: transform; }
.escape-totem .tk { font-size: 10px; font-weight: 800; color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,.8); white-space: nowrap; }
.escape-totem.mine .tg { border-color: #7fffd4; box-shadow: 0 0 18px rgba(127,255,212,.7); animation: esc-totem-pulse 1s ease-in-out infinite; }
.escape-totem.mine .tk { color: #aaffe6; }
.escape-totem.filled .tg { border-color: #6fe0b8; box-shadow: 0 0 20px rgba(111,224,184,.9); }
.escape-totem.filled .ta { border-color: #6fe0b8; }
@keyframes esc-totem-pulse { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
/* 切割線：整條垂直紅裂縫（站線上 → 倒數結束掉落）。寬 = 2×CLEAVE_LINE_HALF_W(28)=56，置中。 */
/* 預警：全螢幕垂直柱（天空到地板），預告即將切斷的位置（紅橘閃爍）。 */
.escape-cleaveline {
  position: absolute; top: 0; bottom: 0; width: 54px; margin-left: -27px; pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(255,85,45,.26) 50%, transparent);
  border-left: 2px dashed rgba(255,95,60,.72);
  border-right: 2px dashed rgba(255,95,60,.72);
  animation: esc-cleave-flash .9s ease-in-out infinite;
}
/* 致命：警示柱收回，真正的「切斷地板」由地形層 mask（setGroundCut）挖空露出背景完成；
   這裡只在地板留一道紅色破口邊緣標示危險帶（非疊黑塊）。 */
.escape-cleaveline.open {
  top: auto; bottom: 0; height: 96px;
  background: none;
  border-left: 3px solid rgba(255,110,60,.95);
  border-right: 3px solid rgba(255,110,60,.95);
  box-shadow: 0 0 14px 2px rgba(255,90,50,.5);
  animation: none;
  overflow: visible;
}
/* 致命裂縫底部：地底冒出火焰（PM 2026-06-07）。橘紅核心 + 黃色高溫 + 上方煙霧；GPU 合成、不影響佈局。 */
.escape-cleaveline.open::before,
.escape-cleaveline.open::after {
  content: ''; position: absolute; left: 50%; bottom: -2px;
  width: 80%; height: 56px; margin-left: -40%;
  pointer-events: none; will-change: transform, opacity;
  border-radius: 50% 50% 35% 35% / 60% 60% 40% 40%;
  filter: blur(2px);
}
.escape-cleaveline.open::before {
  background: radial-gradient(ellipse at 50% 100%,
    #fff3a8 0%, #ffd24a 22%, #ff8a2a 48%, #ff3b18 72%, rgba(180,30,10,0) 100%);
  mix-blend-mode: screen;
  animation: esc-flame-flicker .42s ease-in-out infinite alternate;
}
.escape-cleaveline.open::after {
  height: 48px; width: 60%; margin-left: -30%;
  background: radial-gradient(ellipse at 50% 100%,
    #ffe07a 0%, #ff9b3a 38%, rgba(255,80,40,0) 90%);
  mix-blend-mode: screen;
  animation: esc-flame-flicker .31s ease-in-out infinite alternate-reverse;
}
@keyframes esc-flame-flicker {
  0%   { transform: translateY(0) scaleY(.92) scaleX(.96); opacity: .85; }
  50%  { transform: translateY(-6px) scaleY(1.12) scaleX(1.04); opacity: 1; }
  100% { transform: translateY(-2px) scaleY(1.0) scaleX(1.0); opacity: .9; }
}

/* ╔══ 五種裂地變體（PM 2026-06-07）═══════════════════════════════════════════╗
   父類別 .cle-1..5 套在 .escape-cleave-layer。預警柱（.escape-cleaveline）+ 致命視覺
   (.open) 各變體重新著色/動畫；DOM 結構不變、不重建元素。預設 = cle-1（既有斷裂+火焰）。 */

/* ── 2: 火焰柱（Fire Pillar）─ 橘紅警示，致命=高聳火柱直衝向上 ──────────────── */
.escape-cleave-layer.cle-2 .escape-cleaveline {
  background: linear-gradient(90deg, transparent, rgba(255,140,40,.22) 50%, transparent);
  border-color: rgba(255,170,60,.78);
}
.escape-cleave-layer.cle-2 .escape-cleaveline.open {
  top: auto; bottom: 0; height: 240px;
  border-left: 3px solid rgba(255,160,60,.95);
  border-right: 3px solid rgba(255,160,60,.95);
  box-shadow: 0 0 22px 4px rgba(255,120,40,.55);
  background: none;
}
.escape-cleave-layer.cle-2 .escape-cleaveline.open::before {
  height: 220px; width: 100%; margin-left: -50%;
  background: radial-gradient(ellipse at 50% 100%,
    #fff8b8 0%, #ffd24a 18%, #ff8a2a 42%, #ff3b18 70%, rgba(180,30,10,0) 100%);
  animation: esc-fire-pillar .25s ease-in-out infinite alternate;
}
.escape-cleave-layer.cle-2 .escape-cleaveline.open::after {
  height: 160px; width: 70%; margin-left: -35%;
  background: radial-gradient(ellipse at 50% 100%,
    #ffeaa0 0%, #ffaf45 35%, rgba(255,80,40,0) 95%);
  animation: esc-fire-pillar .19s ease-in-out infinite alternate-reverse;
}
@keyframes esc-fire-pillar {
  0%   { transform: translateY(0) scaleY(.95) scaleX(.92); opacity: .9; }
  100% { transform: translateY(-12px) scaleY(1.08) scaleX(1.08); opacity: 1; }
}

/* ── 3: 落雷（Lightning）─ 黃白警示，致命=從天而降的閃電 ─────────────────── */
.escape-cleave-layer.cle-3 .escape-cleaveline {
  background: linear-gradient(90deg, transparent, rgba(255,250,140,.18) 50%, transparent);
  border-color: rgba(255,235,120,.75);
  border-style: dotted;
  animation: esc-bolt-tele .55s ease-in-out infinite;
}
.escape-cleave-layer.cle-3 .escape-cleaveline.open {
  top: 0; bottom: 0; height: auto;
  width: 10px; margin-left: -5px;
  background: linear-gradient(180deg, #ffffff 0%, #fffac0 18%, #ffec70 42%, #b0c0ff 80%, rgba(180,200,255,0) 100%);
  border: none;
  box-shadow: 0 0 18px 6px rgba(255,240,140,.7), 0 0 36px 12px rgba(180,200,255,.4);
  filter: drop-shadow(0 0 8px #fff);
  animation: esc-bolt-strike .12s steps(2, end) infinite;
}
.escape-cleave-layer.cle-3 .escape-cleaveline.open::before,
.escape-cleave-layer.cle-3 .escape-cleaveline.open::after {
  display: none;
}
@keyframes esc-bolt-tele { 0%,100% { opacity: 1; } 50% { opacity: .3; } }
@keyframes esc-bolt-strike { 0% { opacity: 1; transform: translateX(0); } 50% { opacity: .55; transform: translateX(2px); } 100% { opacity: 1; transform: translateX(-2px); } }

/* ── 4: 隕石雨（Meteors）─ 紅色俯衝警示，致命=隕石落地爆破 ────────────────── */
.escape-cleave-layer.cle-4 .escape-cleaveline {
  background: linear-gradient(180deg, rgba(255,80,40,.4) 0%, rgba(255,140,60,.18) 50%, transparent 100%);
  border-color: rgba(255,90,40,.7);
  animation: esc-meteor-tele .8s ease-in-out infinite;
}
.escape-cleave-layer.cle-4 .escape-cleaveline.open {
  top: auto; bottom: 0; height: 110px; width: 80px; margin-left: -40px;
  background: radial-gradient(circle at 50% 70%,
    #ff5a18 0%, #ff8a2a 25%, #ffd24a 45%, rgba(255,80,40,.3) 70%, rgba(60,20,10,0) 100%);
  border: 2px solid rgba(255,120,60,.9);
  border-radius: 50%;
  box-shadow: 0 0 32px 8px rgba(255,90,40,.6);
  animation: esc-meteor-impact 1.1s ease-in-out infinite;
}
.escape-cleave-layer.cle-4 .escape-cleaveline.open::before {
  content: ''; position: absolute; left: 50%; top: -80px;
  width: 24px; height: 80px; margin-left: -12px;
  background: linear-gradient(180deg, rgba(255,255,255,0), #ffd24a 60%, #ff5a18 100%);
  filter: blur(3px);
  animation: esc-meteor-trail .4s ease-out infinite;
}
.escape-cleave-layer.cle-4 .escape-cleaveline.open::after { display: none; }
@keyframes esc-meteor-tele {
  0%,100% { transform: scaleY(1); opacity: .8; }
  50% { transform: scaleY(1.05); opacity: 1; }
}
@keyframes esc-meteor-impact {
  0% { transform: scale(.7); opacity: .6; }
  20% { transform: scale(1.08); opacity: 1; }
  100% { transform: scale(.95); opacity: .85; }
}
@keyframes esc-meteor-trail {
  0%   { opacity: .9; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(20px); }
}

/* ── 5: 毒霧（Toxic Gas）─ 綠色擴散警示，致命=湧出的毒煙雲 ──────────────── */
.escape-cleave-layer.cle-5 .escape-cleaveline {
  background: linear-gradient(180deg, rgba(120,220,90,.05) 0%, rgba(130,230,90,.32) 100%);
  border-color: rgba(140,230,100,.7);
  border-style: dashed;
  animation: esc-gas-tele 1.2s ease-in-out infinite;
}
.escape-cleave-layer.cle-5 .escape-cleaveline.open {
  top: auto; bottom: 0; height: 140px; width: 130px; margin-left: -65px;
  background: radial-gradient(ellipse at 50% 85%,
    #c8ff90 0%, #82e660 30%, #4cc04a 55%, rgba(40,140,60,.25) 80%, rgba(20,80,30,0) 100%);
  border: none;
  border-radius: 50% 50% 35% 35%;
  box-shadow: 0 0 22px 6px rgba(120,220,90,.45);
  filter: blur(2px);
  animation: esc-gas-puff 1.5s ease-in-out infinite alternate;
  mix-blend-mode: normal;
}
.escape-cleave-layer.cle-5 .escape-cleaveline.open::before {
  content: ''; position: absolute; left: 50%; bottom: 30px;
  width: 110%; height: 80%; margin-left: -55%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 60%,
    rgba(200,255,140,.7) 0%, rgba(100,200,80,.4) 50%, rgba(60,140,60,0) 90%);
  filter: blur(3px);
  animation: esc-gas-puff 1.1s ease-in-out infinite alternate-reverse;
}
.escape-cleave-layer.cle-5 .escape-cleaveline.open::after { display: none; }
@keyframes esc-gas-tele {
  0%,100% { opacity: .55; transform: translateY(2px) scaleY(.95); }
  50% { opacity: .95; transform: translateY(-4px) scaleY(1.05); }
}
@keyframes esc-gas-puff {
  0%   { transform: translateY(0) scale(.92); opacity: .8; }
  100% { transform: translateY(-10px) scale(1.05); opacity: 1; }
}

/* 解除方塊：踩到即解除；指定玩家頭像 + 標記（我的＝金框脈動、已解除＝綠框打勾）。
   PM 2026-06-07：方塊必須在『地板上』，否則玩家無法踩到。bottom:54px 改 bottom:0 + 略大尺寸。 */
.escape-block {
  position: absolute; bottom: 0; width: 64px; height: 64px; margin-left: -32px; z-index: 6; pointer-events: none;
  border-radius: 12px; background: rgba(20,27,46,.72); border: 2px solid rgba(255,255,255,.4);
  box-shadow: 0 4px 14px rgba(0,0,0,.5); display: flex; align-items: center; justify-content: center;
}
.escape-block .ba { width: 48px; height: 48px; border-radius: 8px; background-size: cover; background-position: center; }
/* 停留進度：由下往上填的綠色覆蓋（站滿 2s → 解除）。 */
.escape-block .bp { position: absolute; inset: 0; border-radius: 10px; background: rgba(52,211,153,.45); transform: scaleY(0); transform-origin: bottom; transition: transform .08s linear; pointer-events: none; }
.escape-block .bk { position: absolute; top: -12px; right: -8px; font-size: 14px; font-weight: 900; color: #ffd700; text-shadow: 0 1px 3px #000; z-index: 1; }
.escape-block.mine { border-color: #ffd700; box-shadow: 0 0 16px 3px rgba(255,215,0,.6); animation: esc-cleave-flash 1s ease-in-out infinite; }
.escape-block.done { border-color: #34d399; box-shadow: 0 0 16px 3px rgba(52,211,153,.7); animation: none; }
.escape-block.done .bk { color: #34d399; }
/* 倒數橫幅：頂部置中、紅橘警示。 */
.escape-cleave-banner { position: absolute; top: 96px; left: 50%; transform: translateX(-50%); z-index: 11;
  display: flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: 12px; pointer-events: none;
  background: rgba(180,40,30,.86); color: #fff; font-size: 14px; font-weight: 800; white-space: nowrap;
  border: 1px solid rgba(255,180,120,.6); box-shadow: 0 6px 24px rgba(180,40,30,.5); animation: esc-cleave-flash .9s ease-in-out infinite; }
@keyframes esc-cleave-flash { 0%,100% { opacity: 1; } 50% { opacity: .72; } }

/* 狀態提示橫幅（死亡/重生/倒下/救援）——置中、醒目，玩家死亡/凍結時知道發生什麼事。 */
.escape-status {
  position: absolute; top: 42%; left: 50%; transform: translate(-50%, -50%); z-index: 12; pointer-events: none;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 12px 22px; border-radius: 14px; max-width: 86%; text-align: center; line-height: 1.4;
  background: rgba(8, 12, 26, .88); color: #fff; font-size: 18px; font-weight: 800;
  border: 1px solid rgba(255, 255, 255, .22); box-shadow: 0 8px 28px rgba(0, 0, 0, .55);
  animation: esc-status-pop .22s ease-out;
}
@keyframes esc-status-pop { from { opacity: 0; transform: translate(-50%, -50%) scale(.7); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } }

/* §4.6 跌落選擇彈窗（PM 2026-06-07）：跳出『回記錄點 / 結算離開』選擇。掛在 root 不被縮放。 */
.escape-respawn-choice {
  position: absolute; top: 38%; left: 50%; transform: translate(-50%, -50%);
  z-index: 35; pointer-events: auto;
  display: none; flex-direction: column; gap: 14px; align-items: center;
  padding: 22px 26px; border-radius: 18px; max-width: 86%; text-align: center;
  background: rgba(8, 12, 26, .96);
  border: 1px solid rgba(255, 255, 255, .22);
  box-shadow: 0 14px 36px rgba(0, 0, 0, .65);
  animation: esc-status-pop .22s ease-out;
}
.escape-respawn-choice .title {
  font-size: 22px; font-weight: 900; color: #fff;
  text-shadow: 0 2px 6px rgba(0,0,0,.5);
}
.escape-respawn-choice .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-width: 230px; padding: 14px 22px;
  border-radius: 14px; border: 1px solid rgba(255,255,255,.28);
  font-size: 16px; font-weight: 800; color: #fff;
  cursor: pointer; user-select: none; -webkit-user-select: none;
  background: rgba(28, 36, 56, .92);
  transition: transform .08s, filter .08s, box-shadow .08s;
  box-shadow: 0 4px 12px rgba(0, 0, 0, .35);
}
.escape-respawn-choice .btn:active { transform: scale(.96); filter: brightness(1.15); }
.escape-respawn-choice .btn.primary {
  background: linear-gradient(180deg, #34d399 0%, #10b981 100%);
  border-color: rgba(255,255,255,.46);
  text-shadow: 0 1px 2px rgba(0, 60, 30, .7);
}
.escape-respawn-choice .btn.primary[disabled],
.escape-respawn-choice .btn.primary.disabled {
  background: linear-gradient(180deg, #6b7280 0%, #4b5563 100%);
  opacity: .55; cursor: not-allowed; box-shadow: none;
}
.escape-respawn-choice .btn.secondary {
  background: linear-gradient(180deg, #fb7185 0%, #ef4444 100%);
  border-color: rgba(255,255,255,.46);
  text-shadow: 0 1px 2px rgba(60, 0, 0, .7);
}
.escape-respawn-choice .badge {
  font-size: 12px; font-weight: 700; padding: 3px 9px; border-radius: 9px;
  background: rgba(0, 0, 0, .28); margin-left: 4px;
}

/* §4.9 裂地能量條（HUD top-center）：走一定距離填滿 → 觸發裂地封鎖。 */
.escape-charge {
  position: absolute; top: 14px; left: 50%; transform: translateX(-50%); z-index: 11; pointer-events: none;
  width: 230px; height: 20px; border-radius: 11px; overflow: hidden;
  background: rgba(8, 12, 26, .62); border: 1px solid rgba(255, 255, 255, .18);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .35);
}
.escape-charge-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0%;
  background: linear-gradient(90deg, #ffb14a, #ff7a2a); transition: width .12s linear;
}
.escape-charge.full .escape-charge-fill {
  background: linear-gradient(90deg, #fb7185, #ef4444); animation: esc-cleave-flash .6s ease-in-out infinite;
}
.escape-charge-label {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 800; color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); letter-spacing: .5px;
}
/* 封鎖邊界：只在地面帶顯示（≈ 地板高度，不貫穿畫面、不衝進天空），與「只切地板」一致。 */
.escape-cleavewall {
  position: absolute; bottom: 0; height: 78px; width: 8px; margin-left: -4px; z-index: 5; pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(255, 80, 80, .5));
  box-shadow: 0 0 14px 3px rgba(255, 70, 70, .45);
}

/* ── HUD（照 mockup 重排）──────────────────────────────────────────────── */
.escape-hud { position: absolute; inset: 0; z-index: 10; pointer-events: none; }

.escape-btn-back {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  left: calc(env(safe-area-inset-left, 0px) + 14px);
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-weight: 800;
  color: #fff;
  background: rgba(8, 12, 26, .66);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  padding: 7px 12px;
  border-radius: 11px;
  border: 1px solid rgba(255, 255, 255, .18);
  cursor: pointer;
  pointer-events: auto;
}
.escape-btn-vol {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  right: calc(env(safe-area-inset-right, 0px) + 14px);
  z-index: 30;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 12, 26, .66);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  border: 1px solid rgba(255, 255, 255, .18);
  cursor: pointer;
  pointer-events: auto;
}
.escape-btn-vol img { width: 21px; height: 21px; object-fit: contain; }
/* PM 2026-06-07：音量 emoji（🔊/🔇），按下切換明確顯示靜音狀態。 */
.escape-btn-vol .vico { font-size: 20px; line-height: 1; user-select: none; -webkit-user-select: none; }

.escape-hud-team {
  position: absolute;
  top: 54px;
  left: 14px;
  display: flex;
  gap: 5px;
}
.escape-chip {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background-size: cover;
  background-position: center;
  border: 2px solid var(--green);
  position: relative;
}
.escape-chip.dead { border-color: #556; filter: grayscale(1) opacity(.5); }
.escape-chip.lit:after {
  content: '⭐';
  position: absolute;
  top: -11px;
  right: -7px;
  font-size: 13px;
}

.escape-hud-dist {
  position: absolute;
  top: 60px;
  right: 14px;
  text-align: right;
  background: rgba(8, 12, 26, .62);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  padding: 6px 12px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, .14);
}
.escape-hud-dist .n { font-size: 28px; font-weight: 900; color: #fff; line-height: 1; }
.escape-hud-dist .u { font-size: 11px; color: var(--gold); font-weight: 700; }

.escape-respawn {
  position: absolute;
  top: 118px;
  right: 14px;
  font-size: 13px;
  font-weight: 800;
  color: #fff;
  background: rgba(8, 12, 26, .66);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  border: 1px solid #1a2040;
  padding: 5px 11px;
  border-radius: 999px;
}

/* ── 觸控控制（PM 2026-06-08 改版）：左半浮動搖桿 + 右半點擊跳躍（取代 ◀▶跳 按鈕）──────── */
.escape-touchzone {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 50%;
  z-index: 24;            /* 低於 返回/音量(z30) → 角落鈕可按；高於遊戲畫面 */
  touch-action: none;
  pointer-events: auto;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}
.escape-touchzone.left { left: 0; }
.escape-touchzone.right { right: 0; }
.tz-hint {
  position: absolute;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 22px);
  left: 50%;
  transform: translateX(-50%);
  font-size: 13px;
  font-weight: 800;
  color: rgba(255, 255, 255, .55);
  background: rgba(20, 24, 40, .3);
  padding: 4px 13px;
  border-radius: 13px;
  white-space: nowrap;
  pointer-events: none;
  /* 2026-06-09 清爽化：提示顯示 10s 後自動淡出（教學完就收）。 */
  animation: esc-hintfade .6s ease 10s forwards;
}
@keyframes esc-hintfade { to { opacity: 0; } }
/* 浮動搖桿（出現在左半手指落點；left/top = 中心，由 JS 設）。 */
.escape-joystick {
  position: absolute;
  width: 112px;
  height: 112px;
  margin-left: -56px;
  margin-top: -56px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, .14), rgba(30, 40, 70, .34));
  border: 2px solid rgba(255, 255, 255, .38);
  box-shadow: 0 4px 16px rgba(0, 0, 0, .35);
  z-index: 26;
  pointer-events: none;
}
.escape-joystick .knob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 35%, #ffd98a, #ff9c3c);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .4);
  transform: translate(-50%, -50%);   /* JS 覆寫位移 */
}

/* ── 舊控制鈕樣式（已停用，保留不影響）──────────────────────────────────────── */
.escape-controls {
  position: absolute;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
  left: 0;
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  padding: 0 calc(env(safe-area-inset-right, 0px) + 16px) 0 calc(env(safe-area-inset-left, 0px) + 16px);
  z-index: 30;
}
.escape-controls .lr { display: flex; gap: 16px; }
/* PM 2026-06-07：控制鈕放大（58→78px，跳：74→108px），更容易按到。 */
.escape-btn {
  width: 78px;
  height: 78px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  font-weight: 900;
  background: rgba(20, 27, 46, .82);
  border: 2px solid rgba(255, 255, 255, .28);
  color: #fff;
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, .4);
  position: relative;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  pointer-events: auto;
  opacity: .9;                 /* PM 2026-06-08：三顆動作鈕透明度 90% */
  transition: transform .07s, filter .07s, box-shadow .07s;
}
.escape-btn:active,
.escape-btn.pressed { transform: scale(.84); filter: brightness(1.5); }
.escape-btn.jump {
  width: 108px;
  height: 108px;
  margin-right: 30px;          /* PM 2026-06-08：跳躍鈕再往左一點（離右緣多 30px） */
  flex-direction: column;
  gap: 0;
  color: #06281c;
  background: radial-gradient(circle at 50% 32%, #6ff0b0, #27c07a 72%);
  border-color: #bff7da;
  border-width: 3px;
  box-shadow: 0 6px 0 #0e8a52, 0 10px 18px rgba(0, 0, 0, .45);
}
.escape-btn.jump:active,
.escape-btn.jump.pressed {
  transform: translateY(4px) scale(.96);
  box-shadow: 0 1px 0 #0e8a52, 0 3px 8px rgba(0, 0, 0, .5);
}
.escape-btn.jump .jg { width: 38px; height: 38px; object-fit: contain; }
.escape-btn.jump .jl { font-size: 16px; letter-spacing: 1px; font-weight: 900; }

/* ── 工業域地標結構（PM 2026-06-09 工業地圖加料）────────────────────────────── */
/* 天頂吊鏈（鏈磚 repeat-y）+ 吊鉤（龍門吊感）。背景元素：調暗、不擋機關。 */
.escape-chain {
  position: absolute;
  top: 0;
  width: 22px;
  z-index: 1;
  background-repeat: repeat-y;
  background-position: top center;
  background-size: 22px 22px;
  image-rendering: pixelated;
  pointer-events: none;
  filter: brightness(.9);
  opacity: .9;
}
.escape-chain .hookend {
  position: absolute;
  bottom: -20px;
  left: 50%;
  transform: translateX(-50%);
  width: 22px;
  height: 22px;
  background: no-repeat center / contain;
  image-rendering: pixelated;
}
/* 煙囪管（直管 repeat-y + 彎頭頂）＝工廠地標剪影。 */
.escape-bigpipe {
  position: absolute;
  width: 34px;
  z-index: 1;
  background-repeat: repeat-y;
  background-position: top center;
  background-size: 34px 34px;
  image-rendering: pixelated;
  pointer-events: none;
  filter: brightness(.9);
  opacity: .92;
}
.escape-bigpipe .elbowtop {
  position: absolute;
  top: -30px;
  left: 0;
  width: 34px;
  height: 34px;
  background: no-repeat center / contain;
  image-rendering: pixelated;
}
/* 黑暗甬道進出口警報燈：閃爍紅光（覆寫裝飾調暗；小元素動畫、重繪面積極小）。 */
.escape-decor.alarm {
  filter: none;
  opacity: 1;
  animation: esc-alarm 1.6s steps(2, end) infinite;
}
@keyframes esc-alarm {
  50% { filter: brightness(1.7) drop-shadow(0 0 7px rgba(255, 80, 60, .85)); }
}

/* ── 天氣特效（PM 2026-06-09：風吹/打雷/下雨/大太陽 隨機場景變化）─────────────────
   全部 transform/opacity-only（GPU 合成、零 layout/paint 抖動）；由 .escape-stage 的
   w-<weather> class 控制顯隱（opacity transition 緩變 → 天氣切換有過場感）。 */
.escape-weather { position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity 1.6s ease; }
/* 大太陽：暖色柔光罩（z3：蓋地形、不蓋夜罩/角色）。 */
.escape-weather.warm {
  z-index: 3;
  background: radial-gradient(circle at 36% 22%, rgba(255, 214, 120, .34), rgba(255, 190, 90, .12) 46%, rgba(0, 0, 0, 0) 70%);
}
.escape-stage.w-sunny .escape-weather.warm { opacity: 1; }
/* 下雨：斜紋雨幕（200% 高、transform 循環下移 → 無重繪）。 */
.escape-weather.rain {
  z-index: 7;
  top: -100%;
  height: 200%;
  background: repeating-linear-gradient(105deg, transparent 0 22px, rgba(195, 218, 255, .38) 22px 24px);
  will-change: transform;
}
.escape-stage.w-rain .escape-weather.rain,
.escape-stage.w-storm .escape-weather.rain { opacity: .55; animation: esc-rainfall .6s linear infinite; }
.escape-stage.w-storm .escape-weather.rain { opacity: .85; animation-duration: .38s; }
@keyframes esc-rainfall { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-80px, 298px, 0); } }
/* 暴風雨暗罩 + 閃電白閃（間歇 keyframes，只在 storm 時跑）。 */
.escape-weather.dim { z-index: 8; background: rgba(10, 14, 30, 1); }
.escape-stage.w-storm .escape-weather.dim { opacity: .22; }
.escape-weather.flash { z-index: 9; background: #fff; transition: none; }
.escape-stage.w-storm .escape-weather.flash { animation: esc-lightning 6.4s linear infinite; }
@keyframes esc-lightning {
  0%, 88.9% { opacity: 0; }
  89.4% { opacity: .8; }
  89.9% { opacity: 0; }
  91.2% { opacity: .5; }
  91.9%, 100% { opacity: 0; }
}
/* 風吹：葉子橫飄（每片自己的時長/延遲，transform-only）。 */
.escape-weather.wind { z-index: 7; }
.escape-stage.w-windy .escape-weather.wind { opacity: .9; }
.escape-weather.wind span {
  position: absolute;
  left: 100%;
  animation-name: esc-leafdrift;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-play-state: paused;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .25));
}
.escape-stage.w-windy .escape-weather.wind span { animation-play-state: running; }
@keyframes esc-leafdrift {
  0% { transform: translate3d(0, 0, 0) rotate(0deg); }
  25% { transform: translate3d(-280px, 26px, 0) rotate(150deg); }
  50% { transform: translate3d(-560px, -12px, 0) rotate(290deg); }
  75% { transform: translate3d(-840px, 22px, 0) rotate(430deg); }
  100% { transform: translate3d(-1120px, -6px, 0) rotate(580deg); }
}
