/* 语音输入与游戏页 HUD 布局层，与 demo 的 style.css 分开放（style.css 保持和 demo 一致好对 diff）。
   本文件在 style.css 之后加载：等特异性下后到者胜，游戏页新 HUD 的覆盖全部收在这里。 */

#wallet-badge {
  flex: 0 0 auto;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  color: #e8eaf0;
  font-size: 12px;
  white-space: nowrap;
}
#wallet-badge.low { background: rgba(240, 82, 82, 0.25); color: #ffb4b4; }

#input-mode {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border: none;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.08);
  color: #e8eaf0;
  font-size: 16px;
  cursor: pointer;
}
#input-mode:hover { background: rgba(255, 255, 255, 0.14); }

/* display:flex 会压过 hidden 属性的 UA 样式，必须显式补一条 */
#voice-hold[hidden] { display: none; }

#voice-hold {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 38px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.08);
  color: #e8eaf0;
  font-size: 14px;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: none; /* 按住期间不触发滚动/下拉刷新 */
}
#voice-hold:hover { background: rgba(255, 255, 255, 0.12); }
#voice-hold.recording {
  background: rgba(240, 82, 82, 0.28);
  box-shadow: 0 0 0 2px rgba(240, 82, 82, 0.45) inset;
  animation: voice-pulse 1.2s ease-in-out infinite;
}
@keyframes voice-pulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(240, 82, 82, 0.45) inset; }
  50% { box-shadow: 0 0 0 4px rgba(240, 82, 82, 0.7) inset; }
}

/* ═══════════ 游戏页新 HUD（Figma 155:16197 默认 / 170:16741 开面板，2026-08-07 稿）═══════════
   画面上只留四件事：左上回首页、右上小地图、底部居中语音药丸、右下对话气泡（红点）。
   编辑模式常开（开关下线）、天气/时间全自动（药丸隐藏）、性能条/撤销板/地图切换入口都不再露出。 */

/* —— 左上：回首页 —— */
#home-entry {
  position: fixed;
  top: calc(16px + env(safe-area-inset-top, 0px));
  left: max(16px, env(safe-area-inset-left, 0px));
  z-index: 21;
  display: flex; align-items: center; gap: 10px;
}
#go-home {
  position: static;            /* 盖掉旧的 fixed 定位（挪进 #home-entry 排版） */
  width: 36px; height: 36px; min-width: 0; min-height: 0;
  display: grid; place-items: center;
  border: 0.8px solid rgba(255, 255, 255, 0.4);
  border-radius: 8px;
  background: rgba(38, 46, 62, 0.7);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.25), inset 0 0 4px rgba(255, 255, 255, 0.25);
  cursor: pointer; padding: 0;
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
#go-home img { width: 18px; height: 18px; display: block; }
#go-home:active { transform: scale(0.94); }
.home-label { font: 600 15px/1 "PingFang SC", system-ui, sans-serif; color: #fff; }

/* —— 右上：小地图（120×120，圆角 18；canvas 内部仍 336 保 retina）—— */
#minimap {
  left: auto !important;
  bottom: auto !important;
  right: var(--map-right) !important;
  top: var(--map-top) !important;
  width: var(--map-size) !important; height: var(--map-size) !important;
  border-radius: 18px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
}
#sheet-handle { display: none !important; } /* 旧的移动端浮标被面板按钮取代 */

/* —— 下线的旧 HUD：编辑开关/性能条已从 DOM 移除；这里兜住 JS 还会创建的 —— */
#undo-bar { display: none !important; }                    /* 桌面 ⌘Z 仍可用，浮板不再露出 */
#weather-time-toggle, #weather-toggle,
#weather-time-menu, #weather-menu { display: none !important; }  /* 天气/时间全自动（2026-08-07 拍板） */

/* —— 底部居中：语音药丸（170:16582）—— */
#mic-pill {
  position: fixed;
  left: 50%;
  bottom: var(--dock-bottom);
  transform: translateX(-50%);
  z-index: 30;
  height: 46px;
  display: flex; align-items: center; gap: 24px;
  padding: 4px 3px 4px 16px;
  border-radius: 100px;
  background: rgba(32, 36, 34, 0.85);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); /* Figma BACKGROUND_BLUR=10（生成代码给的 5 是半倍缩略值） */
  border: 0.8px solid rgba(255, 255, 255, 0.05);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.4), inset 0 0 4px rgba(255, 255, 255, 0.25);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
/* 状态文案的换字：上一条向上滚出、下一条从下方滚入，同时容器宽度做过渡
   （2026-08-18 用户报障「状态生硬切换、宽度也生硬变化」）。
   动效参数直接复用对话面板那套 token（--dur-3 / --ease-expo），两处观感一致。

   【为什么要显式宽度】药丸宽度是内容撑出来的，auto → auto 无法过渡；所以
   setLive 每次量出新文案的自然宽度写到这里，由下面这条 transition 接管。
   配合 flex: none —— 否则 flex 会把这个显式宽度再压缩掉，过渡就走样。 */
#mic-live {
  position: relative;
  flex: none;
  color: rgba(255, 255, 255, 0.5);   /* 兜底：万一有人往这层塞裸文本，也别继承成页面深色 ink（"已取消"黑字那次） */
  height: 20px;                 /* 字号 14 + 上下余量：emoji 墨迹盒 ~1.3em，14px 高会裁"✅"顶脚（2026-08-19 报障） */
  max-width: 300px;
  overflow: hidden;
  transition: width var(--dur-3) var(--ease-expo);
}
#mic-live .live-line {
  position: absolute; left: 0; top: 0;
  width: max-content; max-width: 300px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 800 14px/20px "PingFang SC", system-ui, sans-serif;   /* 行高=容器高，emoji 不再被裁 */
  color: rgba(255, 255, 255, 0.5);
  /* 换字过渡不用 ease-expo（2026-08-19 报障"退场瞬间不可见、跳动感强"）：
     视窗只有一行高，expo 前段太快，旧行一动就冲出裁剪窗=肉眼看是瞬间消失。
     缓起缓收曲线 + 行程缩到 80%，进出都能看清滑动与淡入淡出。 */
  transition: transform var(--dur-3) cubic-bezier(0.4, 0.1, 0.25, 1),
              opacity   var(--dur-3) cubic-bezier(0.4, 0.1, 0.25, 1);
}
#mic-live .live-line.enter { transform: translateY(80%); opacity: 0; }    /* 待入场：在下方 */
#mic-live .live-line.leave { transform: translateY(-80%); opacity: 0; }   /* 出场：向上滑走 */
/* 角色配色（2026-08-19 二修：只染「设计师/建筑工/质检员」几个字，其余文案保持原灰白）
   ——角色名由 rollLive 包进 <b>，颜色按行上的 role-* 类给 */
#mic-live .live-line b { font: inherit; }
#mic-live .live-line.role-designer b { color: #ffb15e; }
#mic-live .live-line.role-builder  b { color: #8fd672; }
#mic-live .live-line.role-qc       b { color: #7ec8ff; }
/* 两级滚动（2026-08-19 拍板）：同角色只换右半段——前缀「设计师 · 」钉住，
   文案在行内自己的小视窗（.lv-rest-wrap）里上下滚；角色切换才整行滚。
   vertical-align:top 必须给：inline-block+overflow:hidden 的基线在盒底，不校正会整体下坠。 */
#mic-live .lv-rest-wrap {
  position: relative; display: inline-block; vertical-align: top;
  height: 20px; overflow: hidden;
  transition: width var(--dur-3) var(--ease-expo);
}
#mic-live .lv-rest {
  position: absolute; left: 0; top: 0; width: max-content; white-space: nowrap;
  transition: transform var(--dur-3) cubic-bezier(0.4, 0.1, 0.25, 1),
              opacity   var(--dur-3) cubic-bezier(0.4, 0.1, 0.25, 1);
}
#mic-live .lv-rest.enter { transform: translateY(80%); opacity: 0; }
#mic-live .lv-rest.leave { transform: translateY(-80%); opacity: 0; }
#big-mic {
  position: relative;   /* 涟漪 ::before/::after 的锚（否则会锚到整条药丸变大椭圆） */
  width: 40px; height: 40px; flex: none;
  display: grid; place-items: center; padding: 0;
  border: 0.8px solid rgba(255, 229, 133, 0.7);
  border-radius: 20px;
  background: #f25b17;
  box-shadow: 0 0 4px rgba(255, 81, 0, 0.9), inset 0 0 4px rgba(255, 171, 92, 0.7);
  cursor: pointer;
  user-select: none; -webkit-user-select: none; touch-action: none;
}
#big-mic img { width: 24px; height: 24px; display: block; position: relative; z-index: 1; }
#big-mic.recording {
  animation: mic-glow 1.2s ease-in-out infinite;
}
@keyframes mic-glow {
  0%, 100% { box-shadow: 0 0 4px rgba(255, 81, 0, 0.9), inset 0 0 4px rgba(255, 171, 92, 0.7); }
  50% { box-shadow: 0 0 14px rgba(255, 81, 0, 1), inset 0 0 6px rgba(255, 199, 130, 0.9); }
}
/* 录音中：两圈光波从按钮散开（错峰 0.9s 循环） */
#big-mic.recording::before, #big-mic.recording::after {
  content: ""; position: absolute; inset: -1px; border-radius: 50%;
  border: 2px solid rgba(255, 130, 50, 0.65);
  animation: mic-ripple 1.8s ease-out infinite;
  pointer-events: none;
}
#big-mic.recording::after { animation-delay: 0.9s; }
@keyframes mic-ripple {
  0% { transform: scale(1); opacity: 0.85; }
  100% { transform: scale(2.1); opacity: 0; }
}
#big-mic:disabled { opacity: 0.55; }

/* ── 录音态布局（2026-08-07 效果稿）：✕ | 波形 | 在听… | 实时转写 | 🎤 ──
   默认态只显示引导文案；.rec 时换成录音件套，容器宽度随内容自然过渡 */
#mic-cancel, #mic-wave, #mic-listen, #mic-partial, #mic-tips { display: none; }
#mic-pill.rec #mic-live { display: none; }
#mic-pill.rec { gap: 12px; padding-left: 5px; }
#mic-pill.rec #mic-cancel {
  display: grid; place-items: center;
  width: 36px; height: 36px; flex: none; padding: 0;
  border: none; border-radius: 50%;
  background: rgba(255, 255, 255, 0.16);
  color: rgba(255, 255, 255, 0.8);
  font-size: 15px; line-height: 1;
  cursor: pointer;
}
#mic-pill.rec #mic-cancel:active { transform: scale(0.92); }
#mic-pill.rec #mic-wave {
  display: flex; align-items: center; gap: 2.5px; height: 24px; flex: none;
}
#mic-wave i {
  width: 3px; height: 4px; border-radius: 2px;
  background: linear-gradient(180deg, #ffb066, #ff5f1f);
  box-shadow: 0 0 5px rgba(255, 105, 30, 0.35);
  opacity: 0.75;                    /* 光谱弱一档，别抢文字 */
  transition: height 0.09s ease;
}
#mic-pill.rec #mic-listen {
  display: block; flex: none;
  font: 800 15px/1 "PingFang SC", system-ui, sans-serif; color: #fff;
}
/* 识别到内容：真实文本顶掉「在听…」 */
#mic-pill.rec.has-partial #mic-listen { display: none; }
#mic-pill.rec #mic-partial {
  display: block; max-width: 200px;
  font: 500 14px/1.3 "PingFang SC", system-ui, sans-serif;
  color: rgba(255, 255, 255, 0.55);
  /* 超宽不截省略号：overflow 藏 + JS 每次更新 scrollLeft=scrollWidth，始终看到最新的尾部 */
  white-space: nowrap; overflow: hidden;
}
#mic-pill.rec #mic-partial:empty { display: none; }
/* tips 挂在药丸正下方（说完自动发送） */
/* tips 挂在药丸【上方】（2026-08-19 拍板：点录音不许挪动药丸整体位置——
   原来 tips 放下方、录音态整个药丸抬高 30px 腾位，视觉上就是"按钮跳了一下"）。
   上方是开阔场景区，浮着不占位，药丸的 bottom 全程不变。 */
#mic-pill.rec #mic-tips {
  display: block; position: absolute; bottom: calc(100% + 8px); left: 50%;
  transform: translateX(-50%);
  padding: 5px 12px; border-radius: 100px;
  background: rgba(32, 36, 34, 0.85);
  border: 0.8px solid rgba(255, 255, 255, 0.05);
  font: 500 12px/1 "PingFang SC", system-ui, sans-serif; font-style: normal;
  color: rgba(255, 255, 255, 0.75);
  white-space: nowrap;
}
/* 打开面板 = 打字模式：语音药丸过渡淡出（不瞬移），关面板回来 */
body:not(.chat-hidden) #mic-pill { opacity: 0; transform: translateX(-50%) translateY(10px); pointer-events: none; }

/* —— 右下：对话气泡（170:16536 玻璃圆钮 + 红点）—— */
#panel-toggle {
  position: fixed;
  left: auto; bottom: var(--dock-bottom);
  right: max(30px, env(safe-area-inset-right, 0px));
  z-index: 30;
  width: 46px; height: 46px; padding: 0;
  display: grid; place-items: center;
  border: none; border-radius: 50%;
  /* 与首页顶部功能钮同一块底（home_toolButton_bg 46×46 切图 + 同参投影） */
  background: url("assets/homeui/home_toolButton_bg.png") center / 46px 46px no-repeat;
  filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.28));
  box-shadow: none;
  cursor: pointer;
  transition: opacity 0.25s ease;
  font-size: 0;                /* 兜底：按钮里只有切图 */
}
#panel-toggle > img { width: 26px; height: 25px; display: block; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35)); }
#panel-toggle:active { transform: scale(0.94); }
#panel-toggle .dot {
  position: absolute; right: 4px; top: 2px;   /* 与首页红点同位同环 */
  width: 8px; height: 8px; border-radius: 4px;
  background: #ff3232; box-shadow: 0 0 0 1.5px rgba(0, 0, 0, 0.25);
  display: none;
}
#panel-toggle.unread .dot { display: block; }
body:not(.chat-hidden) #panel-toggle { opacity: 0; pointer-events: none; } /* 面板盖住它，顺手淡掉 */

/* —— 对话面板（170:16779 浮窗化：301 宽、顶到 16 刚好盖住小地图顶）—— */
/* 面板几何只写一处：收起把手挂在面板【外侧】，得按同样的边距和宽度对齐；
   两边各抄一份数字的话，改一个忘一个就会错位。 */
:root {
  --chat-top: calc(16px + env(safe-area-inset-top, 0px));
  --chat-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  --chat-right: max(24px, env(safe-area-inset-right, 0px));
  --chat-w: 301px;
}
#chat {
  top: var(--chat-top);
  right: var(--chat-right);
  bottom: var(--chat-bottom);
  width: var(--chat-w);
  padding: 12px 0;
  gap: 10px;
  background: rgba(31, 36, 34, 0.85);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); /* Figma BACKGROUND_BLUR=10（生成代码给的 5 是半倍缩略值） */
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 20px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.4), inset 0 0 4px rgba(255, 255, 255, 0.25);
  color: #fff;
}
/* 开/收都走过渡：右滑出屏 + 淡出（visibility 延迟到动画完，收起后不挡点击） */
body #chat {
  display: flex;
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0.35, 1), opacity 0.28s ease, visibility 0s linear 0s;
}
body.chat-hidden #chat {
  display: flex;               /* 盖掉旧的 display:none，改用位移隐藏才有过渡 */
  transform: translateX(calc(100% + 40px));
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0.35, 1), opacity 0.28s ease, visibility 0s linear 0.28s;
}

/* 头行 */
#chat-head {
  padding: 0 12px; margin: 0;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  background: none; border: none; box-shadow: none; min-height: 40px;
}
.ph-user { display: flex; align-items: center; gap: 8px; min-width: 0; }
.ph-ava { position: relative; width: 40px; height: 40px; flex: none; }
.ph-ava .ring, .ph-ava .glow { position: absolute; inset: 0; width: 40px; height: 40px; }
.ph-ava .face {
  position: absolute; left: 2px; top: 2px;
  width: 36px; height: 36px; border-radius: 50%;
  object-fit: cover; object-position: top center;
}
#ph-name { font: 800 15px/1 "PingFang SC", system-ui, sans-serif; color: #fff; letter-spacing: -0.13px; }
.ph-right { display: flex; align-items: center; gap: 10px; }
#quota-pill {
  height: 32px; padding: 0 12px;
  display: inline-flex; align-items: center; gap: 8px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.05);
  box-shadow: none;
}
#quota-pill img { width: 18px; height: 18px; object-fit: contain; }
#quota-pill b { font: 800 12px/1 "PingFang SC", system-ui, sans-serif; color: #fff; letter-spacing: -0.13px; font-variant-numeric: tabular-nums; }
/* 收起把手：面板【左外侧】的一片竖标签，箭头朝右＝往右推走。
   竖直居中不用 translateY —— 上下都钉到面板的同一对边距、再给 margin: auto 0，
   由过度约束自动居中；这样 transform 只留给"收起时跟着面板一起右滑"，两者不打架。 */
#sheet-collapse {
  position: fixed;
  /* 往面板里【压 1px】并抬到面板之上：面板自己有一圈 box-shadow，把手若在它下面，
     那圈阴影会正好糊在接缝上，看起来像被切成两块（2026-08-19 用户报障）。
     压 1px 是为了盖掉面板的左边框，这样两块看着是连成一体的。 */
  right: calc(var(--chat-right) + var(--chat-w) - 1px);
  top: var(--chat-top); bottom: var(--chat-bottom);
  height: 84px; margin: auto 0;
  width: 26px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.07); border-right: none;
  border-radius: 12px 0 0 12px;
  background: rgba(31, 36, 34, 0.85);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  box-shadow: -2px 0 8px rgba(0, 0, 0, 0.35);
  cursor: pointer; color: #fff;
  z-index: calc(var(--z-panel) + 1);
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0.35, 1), opacity 0.28s ease, visibility 0s linear 0s;
}
#sheet-collapse img { width: 16px; height: 16px; display: block; }   /* 切图本身就朝右，不转 */
#sheet-collapse:hover { background: rgba(46, 53, 50, 0.9); }
#sheet-collapse:active { background: rgba(58, 66, 62, 0.95); }
/* 收起时跟面板同一条曲线一起右滑出屏，不能留在原地 */
body.chat-hidden #sheet-collapse {
  transform: translateX(calc(var(--chat-w) + 40px));
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: transform 0.28s cubic-bezier(0.32, 0.72, 0.35, 1), opacity 0.28s ease, visibility 0s linear 0.28s;
}
.ph-divider { height: 1px; background: rgba(255, 255, 255, 0.1); flex: none; }

/* 消息流 */
#messages { padding: 0 12px; background: none; }
#messages::-webkit-scrollbar { width: 0; }
#chat .msg.bot {
  max-width: 100%;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  color: #fff;
  font: 500 13px/1.5 "PingFang SC", system-ui, sans-serif;
  letter-spacing: -0.13px;
  padding: 8px;
}
/* 用户消息不动：沿用 style.css 原版橙色渐变气泡（2026-08-07 用户拍板） */

/* ── 选中工具条（Figma 170:16949）：居中玻璃条 = 4 圆钮 + 竖分割 + 红糖果「完成」 ──
   注意特异性：style.css 有 #toolbar button / #toolbar #delete / #toolbar #deselect 的旧皮，
   这里全部用同级或更高选择器压平；旧 msg-in 动画的 transform 关键帧会顶掉 translateX
   （fill:both 的坑）→ animation:none。 */
#toolbar {
  position: fixed;
  left: 50%; transform: translateX(-50%);
  /* 与语音药丸同一离底距离（见 style.css :root --dock-bottom）。
     工具条和药丸是【互斥出现】的——选中物体时药丸淡出、工具条露面——两者离底
     不一致的话，选中/取消选中会看到底部跳一下（原来 30px vs 13px，差 17px）。 */
  bottom: var(--dock-bottom);
  top: auto; right: auto;
  z-index: 26;
  width: max-content;
  animation: none;
  display: flex; align-items: center; justify-content: center; gap: 16px;
  padding: 8px 16px;
  border-radius: 16px;
  background: rgba(31, 36, 34, 0.85);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); /* Figma BACKGROUND_BLUR=10（生成代码给的 5 是半倍缩略值） */
  border: 1px solid rgba(255, 255, 255, 0.07);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.4), inset 0 0 4px rgba(255, 255, 255, 0.25);
}
#toolbar[hidden] { display: none; }
/* 旧按钮皮整体清零（含 #delete 红底、hover 浮起） */
#toolbar button, #toolbar #delete, #toolbar #duplicate, #toolbar #detail-edit, #toolbar #soul-edit {
  background: none; border: none; box-shadow: none; padding: 0;
  min-width: 0; width: auto; height: auto; color: inherit; font: inherit; border-radius: 0;
}
#toolbar button:hover, #toolbar #delete:hover { background: none; transform: none; }
#toolbar .tb-act {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  cursor: pointer;
}
#toolbar .tb-act .tb-ico {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  /* 与首页顶部功能钮同一块底（home_toolButton_bg 切图缩放到 36），不再自画玻璃 */
  background: url("assets/homeui/home_toolButton_bg.png") center / 36px 36px no-repeat;
  border: none; border-radius: 50%;
  filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.28));
}
#toolbar .tb-act .tb-ico img { width: 20px; height: 20px; object-fit: contain; }
#toolbar .tb-act i {
  font: 400 11px/1.4 "PingFang SC", system-ui, sans-serif; font-style: normal;
  color: rgba(255, 255, 255, 0.7); letter-spacing: -0.13px;
}
#toolbar .tb-act.soul i { color: #ffa600; }
#toolbar .tb-act:active .tb-ico { transform: scale(0.92); }
#toolbar .tb-div { width: 1px; align-self: stretch; background: rgba(255, 255, 255, 0.2); }
/* 「完成」按新稿 252:19642（2026-09-10 对稿）：86×38 圆角 8，橙渐变 #fa7d4b→#fa612b，
   下唇 2px 深橙 #b73100 + 上缘 .5px 奶黄高光（都是 inset，不占尺寸），Inter 800 15px 白字带投影。
   旧版是 44 高红渐变（#ff7e6d→#f13b3d）+ 四道内阴影，与稿面不符。
   这套「橙糖果」就是游戏内小按钮的统一皮：新手引导的「下一步」同配方只缩尺寸（src/guide.js .gd-next）。
   按下手感与全局一致（2026-09-08 拍板）：轻微缩小 + 下沉 + 压 10% 黑，不再用 padding-top 顶字。 */
#toolbar #deselect {
  width: 86px; height: 38px; min-height: 38px; padding: 0; border: none; cursor: pointer;   /* min-height 压掉 style.css 的 40px 触控下限，否则量出来 40 */
  border-radius: 8px;
  background: linear-gradient(180deg, #fa7d4b, #fa612b);
  box-shadow: inset 0 -2px 0.5px #b73100, inset 0 0.5px 0 rgba(255, 254, 183, 0.57);
  font: 800 15px/1 "Inter", "PingFang SC", sans-serif; color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 0.08s ease, filter 0.08s ease;
}
#toolbar #deselect:hover { background: linear-gradient(180deg, #fa7d4b, #fa612b); transform: none; }
#toolbar #deselect:active { transform: translateY(1px) scale(0.96); filter: brightness(0.9); padding: 0; }

/* ── 左下虚拟摇杆（新稿 170:17116，2026-08-07 二版）：底环 + 四方向箭头 + knob 全切图 ── */
#stick {
  position: fixed;
  /* 贴左下角：底边与底部功能区（药丸/工具条，都是 --dock-bottom）齐平，
     左间距 = 底间距（2026-08-18 用户拍板）。 */
  left: var(--dock-left);
  bottom: var(--dock-bottom);
  width: var(--stick-size); height: var(--stick-size);
  /* 层级压过工具条(26)：两者在窄屏上必然重叠（都贴底、工具条又几乎占满屏宽），
     摇杆在下面的话，冲着摇杆按下去实际点到的是【删除】按钮——实测 375×812 下
     摇杆下缘 (105,790) 命中的是 #delete。宁可挡住按钮，也不能误删。
     仍低于语音药丸(30)：药丸居中、与摇杆只有一小条交叠，让药丸在上更合理。 */
  z-index: 27;
  touch-action: none;
  user-select: none; -webkit-user-select: none;
  opacity: 0.2;                          /* 闲置退后（2026-08-08 定：20%），拖动时全亮 */
  transition: opacity 0.2s ease;
}
#stick.active { opacity: 1; }
#stick .stick-base { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* 四方向示意箭头（Frame 162~165，16×16）：对称贴大圆内缘 4px（2026-08-08 口径确认：
   稿里右/下的 196 是摆歪的，以「四向距边缘 4px」为准） */
/* 箭头本身不缩放（16px 再小就糊了），只把定位改成相对居中 + 固定 4px 边距，
   这样任何环径下都贴着内缘四正方向。 */
#stick .stick-dir { position: absolute; width: 16px; height: 16px; pointer-events: none; }
#stick .stick-dir.up { left: calc(50% - 8px); top: 4px; }
#stick .stick-dir.down { left: calc(50% - 8px); bottom: 4px; }
#stick .stick-dir.left { left: 4px; top: calc(50% - 8px); }
#stick .stick-dir.right { right: 4px; top: calc(50% - 8px); }
/* 摇杆 knob 用【纯 CSS 圆】而不是切图（2026-08-18 用户报障「中心圆边缘有锯齿」）。
   原来是 assets/gamehud/stick_knob.svg，整个圆包在一个 SVG filter 里（外阴影
   stdDeviation 4 + 内阴影 stdDeviation 9）。SVG filter 的输出有自己的光栅化分辨率，
   不保证跟着 devicePixelRatio 走，dpr 3 的手机上边缘就可能出阶梯。
   border-radius + box-shadow 由合成器按设备分辨率直接画，不经过位图也不经过滤镜，
   在任何引擎上都是平滑的。数值 1:1 对着稿子：可见圆 76(r38)、填充白 20%、
   外发光 blur 8 黑 40%、内发光 blur 18 白 50% 内缩 1
   （Figma 的 blur 值 = SVG stdDeviation×2，所以 4→8、9→18）。 */
#stick-knob {
  position: absolute; left: 50%; top: 50%;
  /* 全部按环径的比例走：原稿 200 环配 76 的 knob = 38%，阴影 8/18 = 4%/9%。 */
  width: calc(var(--stick-size) * 0.38); height: calc(var(--stick-size) * 0.38);
  margin: calc(var(--stick-size) * -0.19) 0 0 calc(var(--stick-size) * -0.19);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
  box-shadow: 0 0 calc(var(--stick-size) * 0.04) rgba(0, 0, 0, 0.4),
              inset 0 0 calc(var(--stick-size) * 0.09) 1px rgba(255, 255, 255, 0.5);
  pointer-events: none;
  transition: transform 0.18s ease;      /* 松手回中的弹性；拖动时 JS 关掉 */
}
body.detail-mode #stick, body.soul-stage #stick { display: none !important; }
/* 打开对话面板时摇杆让路（面板在右，摇杆在左可留——但录音药丸展开会压到，先一并淡出） */
body:not(.chat-hidden) #stick { opacity: 0; pointer-events: none; transition: opacity 0.25s ease; }

/* ── 右侧升降油门（选中出现）：上滑升 / 下滑降，离心越远越快，松手回中 ── */
#lift {
  position: fixed;
  right: max(24px, env(safe-area-inset-right, 0px));
  /* 【不再无条件垂直居中】屏幕一矮就撞上面的小地图/能量条（横屏 812×375 实测：
     油门 top 103.5，两个都压住）。改成按【上边缘】定位，取「居中位置」与
     「能量条下方 10px」里靠下的那个——高屏幕保持居中，矮屏幕自动让开。
     用 transform 居中就做不到这件事（transform 里没法和 max() 比大小），
     所以这里改成算上边缘、transform 归零。JS 只监听指针，不写 #lift 的 transform。 */
  --lift-h: 168px;   /* 与 .lift-track 的 height 一致；SVG 是定高的 */
  top: max(calc(50% - var(--lift-h) / 2), calc(var(--map-bottom) + 10px));
  transform: none;
  z-index: 24;
  touch-action: none; user-select: none; -webkit-user-select: none;
}
#lift[hidden] { display: none; }
/* 沙漏形：两端宽、中间窄——形状本身就在说「越靠两端越快」，不用文字解释。
   【用 SVG 画，不用 clip-path】：clip-path 只能切直边多边形，四角和腰都是尖的，
   而且会连 border/border-radius 一起裁掉、还会把子元素（旋钮）一并切了
   （2026-08-14 用户两次报障）。SVG 路径上下各是一个【半圆球】（骨头/哑铃形，用户给的参考图；最宽处 42 ≈ 旋转轨道
   的 40 厚度，两个控件视觉重量一致）、每侧两段三次贝塞尔在腰部以
   【垂直切线】相接，所以腰是圆滑收束；描边由 path 的 stroke 出，样式与原胶囊一致。
   旋钮不再是 .lift-track 的子元素（见 index.html），从根上不可能被裁。 */
.lift-track {
  width: 46px; height: 168px;
  background: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2046%20168%27%3E%3Cpath%20d%3D%27M%202%2C24%20A%2021%2C21%200%200%201%2044%2C24%20C%2044%2C46%2034%2C52%2034%2C84%20C%2034%2C116%2044%2C122%2044%2C144%20A%2021%2C21%200%200%201%202%2C144%20C%202%2C122%2012%2C116%2012%2C84%20C%2012%2C52%202%2C46%202%2C24%20Z%27%20fill%3D%27rgba%2831%2C36%2C34%2C0.72%29%27%20stroke%3D%27rgba%28255%2C255%2C255%2C0.22%29%27%20stroke-width%3D%271.6%27%2F%3E%3C%2Fsvg%3E") center/100% 100% no-repeat;
  filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.35));
  position: relative;
}
/* 两端的方向提示：上球 ↑、下球 ↓。小而淡——它是「提示」不是按钮，
   不能跟中间的旋钮抢视线（用户要求：小点、视觉轻点）。 */
.lift-track::before, .lift-track::after {
  position: absolute; left: 0; right: 0; text-align: center;
  font-size: 13px; line-height: 1;
  color: rgba(255, 255, 255, 0.42);
  pointer-events: none;
  transition: opacity .15s ease;
}
/* 油门钮推近端点时端点箭头渐隐（JS 写 --fadeU/--fadeD）：否则钮上的符号
   与端点提示叠在一起（玩家实测指出） */
.lift-track::before { opacity: var(--fadeU, 1); }
.lift-track::after  { opacity: var(--fadeD, 1); }
.lift-track::before { content: "↑"; top: 16px; }
.lift-track::after  { content: "↓"; bottom: 16px; }
#lift-knob {
  position: absolute; left: 50%; top: 50%;   /* 相对 #lift 定位，不在被裁的轨道里 */
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0.22) 65%);
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  color: rgba(20, 24, 20, 0.75);
  font-size: 15px; font-style: normal;
  transition: transform 0.18s ease;
  pointer-events: none;
}
body.detail-mode #lift, body.soul-stage #lift { display: none !important; }

/* ── 右侧镜头升降（未选中时出现）：两个独立圆钮，按住持续升/降 ──
   与选中时的 #lift 油门互斥、占同一个位置（同样按【上边缘】定位，矮屏幕自动
   让开能量条，理由见上面 #lift 的注释），拇指不用在两种状态间换地方。
   配色取 .lift-track / .rot-track 那块深色玻璃，跟右侧控件是同一家族。 */
#elev {
  position: fixed;
  right: max(24px, env(safe-area-inset-right, 0px));
  --elev-h: 120px;                 /* 52 + 16 + 52，与下面的尺寸对齐 */
  /* 在【小地图底边】和【右下角对话入口顶边】之间垂直居中（2026-08-19 用户要求）。
     上下都钉住 + 固定高度 + margin: auto 0，靠过度约束自动居中，不用算中点 ——
     那样得把 --dock-bottom、按钮高度都塞进一条 calc，改一个数就得重推一遍。
     原来是 max(屏幕居中, 地图下方)，在这块右侧竖列里看着偏上。 */
  top: var(--map-bottom);
  bottom: calc(var(--dock-bottom) + 46px);   /* 46 = #panel-toggle 的高 */
  height: var(--elev-h);
  margin: auto 0;
  z-index: 24;
  display: flex; flex-direction: column; gap: 16px;
  touch-action: none; user-select: none; -webkit-user-select: none;
}
.elev-btn {
  width: 52px; height: 52px; padding: 0;
  border-radius: 50%;
  border: 1.6px solid rgba(255, 255, 255, 0.22);
  background: rgba(31, 36, 34, 0.72);
  filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.35));
  color: rgba(255, 255, 255, 0.85);
  display: grid; place-items: center;
  cursor: pointer;
  transition: transform .12s ease, background .12s ease, border-color .12s ease;
}
/* 箭头用切图，但走 mask 而不是 <img> —— 这样图标颜色由按钮的 color 决定，
   按住高亮时能跟着一起变白；<img> 是没法重新着色的。
   只有这一份资源，向下的那个用 CSS 转 180°，不另切一张。 */
.elev-ico {
  width: 26px; height: 26px;
  background: currentColor;
  -webkit-mask: url("assets/gamehud/ic_elev_arrow.svg") center / contain no-repeat;
  mask: url("assets/gamehud/ic_elev_arrow.svg") center / contain no-repeat;
  pointer-events: none;
}
.elev-ico.flip { transform: rotate(180deg); }
/* 按住的反馈：:active 在指针捕获下不可靠（手指滑出按钮就掉），所以用 JS 加 .on */
.elev-btn.on {
  transform: scale(.93);
  background: rgba(255, 216, 59, 0.22);
  border-color: rgba(255, 216, 59, 0.75);
  color: #fff;
}
/* 选中物体时让位给 #lift；拆解/赋魂展台/对话面板打开时一并收起（面板在右会盖住） */
body.has-selection #elev,
body.detail-mode #elev,
body.soul-stage #elev,
body:not(.chat-hidden) #elev { display: none !important; }

/* ── 右侧旋转拨盘（选中出现，在升降油门正下方）：左右拖＝转，松手回中 ──
   贴地转盘只留视觉指示（指针告诉你正前方朝哪），抓取交给这里：
   固定 168×40，不随模型大小变，手势永远落在 DOM 上，不与画布的视角拖动冲突。 */
#rot {
  /* 移到底部功能栏正上方（2026-08-17 玩家拍板）：原在右侧中部，与升降油门
     挤在一起，转物体时手会挡住物体本身 */
  position: fixed;
  left: 50%; transform: translateX(-50%);
  bottom: var(--rot-bottom);   /* 工具条上方 12px，见 style.css :root */
  z-index: 24;
  touch-action: none; user-select: none; -webkit-user-select: none;
}
#rot[hidden] { display: none; }
/* 拖动时在轨道正上方浮出当前角度（2026-08-18 用户要求：−180°…+180°，
   数值不再变化 2 秒后自动消失）。
   【为什么要让"已装载"彩带临时避让】--installed-bottom 就是轨道顶 +8px，
   那条彩带(#soul-installed，z-index 25)正好占着这块地方；角度牌压上去会两层字叠字。
   所以做成互斥：牌子在的时候彩带淡出，牌子消失再淡回来 —— 只有 2 秒，
   而这 2 秒里玩家的注意力本来就在角度上。 */
#rot-val {
  position: absolute; left: 50%; bottom: calc(100% + 8px);
  transform: translateX(-50%) translateY(4px);
  z-index: 26;                       /* 压过彩带的 25 */
  /* 【裸文字 + 投影，不给底色】（2026-08-18 用户拍板：加了药丸底显得太重）。
     没有底色兜底，唯一的可读性来源就是投影 —— 用两层：一层紧贴的硬投影压住
     浅色地面（草地是这游戏最常见的背景），一层散开的柔光把边缘从花纹里拎出来。 */
  font: 800 14px/1 "Inter", "PingFang SC", system-ui, sans-serif; font-style: normal;
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.75), 0 0 10px rgba(0, 0, 0, 0.5);
  white-space: nowrap; pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
body.rot-live #rot-val { opacity: 1; transform: translateX(-50%) translateY(0); }
/* 【必须 !important】soul.js 的 updateInstalledPanel 在运行时把 opacity 写成【内联样式】
   （它自己还有一套 5 秒自动淡出的定时器），内联优先级高于任何选择器，不加 !important
   这条规则在"彩带正亮着"的那几秒里完全不起作用 —— 而那几秒恰恰是唯一需要避让的窗口。
   （曾按建元素时那段 cssText 判断"它没写 opacity"，那是错的：opacity 是后来才内联上去的。） */
body.rot-live #soul-installed { opacity: 0 !important; transition: opacity var(--dur-2) var(--ease); }
.rot-track {
  width: 168px; height: 40px;
  border-radius: 100px;
  background: rgba(31, 36, 34, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.35), inset 0 0 4px rgba(255, 255, 255, 0.18);
  position: relative;
}
/* 轨道内【不画刻度线】（用户要求）：量程靠下方的 −180/0/+180 文字表达，
   轨道里干干净净，旋钮位置本身就是读数。旋转是有界的 360°，所以是绝对角度
   滑条（旋钮位置＝当前朝向），而不是像升降那样的持续推速度。 */
/* 两端的方向提示：左端 ↺ 逆时针、右端 ↻ 顺时针。同样小而淡。 */
/* 【上下镜像】（2026-08-18 用户拍板）：实测滑到右边物体是【逆时针】转的
   （three 的 +rotation.y 在俯视下是逆时针），而右端印的 ↻ 是顺时针，标反了。
   镜像会反转手性——↺ 上下翻过来就读作顺时针、↻ 翻过来读作逆时针，
   于是两端含义各自调转，正好对上，字符本身不用换。 */
.rot-track::before, .rot-track::after {
  position: absolute; top: 50%; transform: translateY(-50%) scaleY(-1);
  font-size: 14px; line-height: 1;
  color: rgba(255, 255, 255, 0.42);
  pointer-events: none;
  transition: opacity .15s ease;
}
/* 旋钮滑近端点时该端 ↺/↻ 渐隐（JS 写 --fadeL/--fadeR），见 lift 同款注释 */
.rot-track::before { opacity: var(--fadeL, 1); }
.rot-track::after  { opacity: var(--fadeR, 1); }
.rot-track::before { content: "↺"; left: 11px; }
.rot-track::after  { content: "↻"; right: 11px; }
/* 【底部量程文字已下线】（2026-08-17 用户要求）：−180°/0°/+180° 三个数字对玩家
   没有信息量——旋钮在轨道上的位置本身就是读数，两端的 ↺/↻ 已经说明了方向。
   去掉后旋转条的视觉底边就是它的盒子底边，与工具条的间距才量得准。 */
#rot-knob {
  position: absolute; left: 50%; top: 50%;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border-radius: 50%;
  display: grid; place-items: center;
  /* 这里【曾经】有一条 padding-bottom:3.3px，是给 ⟳ 字形做墨迹垂直补偿的
     （字形墨迹中心比行盒中心低 1.65px）。现在钮内换成了 SVG「环+指针」，
     它的几何中心就是自己盒子的中心，不需要补偿；留着反而会把图整体顶高 1.65px。 */
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0.22) 65%);
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  color: rgba(20, 24, 20, 0.75);
  font-size: 17px; font-style: normal;
  transition: transform 0.18s ease;
  pointer-events: none;
}
/* 钮内是「环 + 指针」的【绝对朝向指示器】，不是会转的 ⟳ 符号（2026-08-18 用户拍板）：
   ⟳ 的箭头方向是画死的，转起来也说不清是朝左还是朝右；换成指针后，
   指针朝正上＝0°，指哪就是物体正面朝哪，与选中物体时场景里那个贴地转盘
   （main.js buildRotDial 的 band + needle）是同一套视觉语言。
   【只转这个 SVG、不转圆圈】圆面是 radial-gradient(circle at 35% 30%) 的高光 +
   0 2px 6px 的投影，整体转会变成"光源在打转"；而且外层 transform 已被 JS
   占用来沿轨道 translateX 定位。SVG 几何中心＝盒中心，绕 50% 50% 自转即可。 */
#rot-face {
  display: block; width: 22px; height: 22px;   /* 34px 的钮里放 22：再小指针就糊了 */
  transform-origin: 50% 50%;
  transition: transform 0.18s ease;
}
body.detail-mode #rot, body.soul-stage #rot { display: none !important; }

/* 贴地转盘（three gizmo，main.js buildRotDial）现在只做视觉指示，不再吃手势 */

/* 选中态：小地图保留（稿 170:16926 右上仍有地图——盖掉 style.css 的隐藏）；
   语音药丸/对话气泡让位给工具条 */
body.has-selection #minimap { display: block !important; }
body.has-selection #mic-pill, body.has-selection #panel-toggle { opacity: 0; pointer-events: none; }

/* 消息流对版补丁（2026-08-07 二轮）：稿里没有的中间层全部收掉——
   「神笔马良」头像行（.turn-avatar/.turn-name）、「开工中 · N 个任务」头（.pl-head）、
   任务名行（.lane-subject）都不再露出；面板消息区只剩 角色玻璃卡 + 消息卡。
   （结构与逻辑不动，只是不渲染成可见层——历史/状态照常流转。） */
#chat .turn { display: block; margin: 0; }
#chat .turn-avatar, #chat .turn-name { display: none; }
#chat .turn-content { gap: 10px; }
#chat .pipeline { background: none; border: none; border-radius: 0; overflow: visible; }
#chat .pl-head { display: none; }
#chat .pl-reference {
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.05);
}
#chat .lane { display: block; padding: 0; border-top: none; background: none; }
#chat .lane-subject { display: none; }

/* 角色卡（工人/设计师…，170:16800）：玻璃卡 + 48 圆头像（建工头像从底部探出）。
   原 .flow-node 是 44px 缩进 + 头像绝对定位 + 节点连线——卡片化后连线撤掉、
   缩进扩到 64（48 头像 + 8 间距 + 8 卡内边距），头像随卡内边距落位。 */
#chat .flow-node {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  padding: 8px 8px 8px 64px;
  min-height: 64px;
  margin-bottom: 10px;
  display: flex; flex-direction: column; justify-content: center;
}
#chat .flow-node::before { display: none; }  /* 独立卡片，不再画角色连接线 */
#chat .flow-node:hover { transform: none; }
#chat .flow-av {
  position: absolute; left: 8px; top: 8px;
  width: 48px; height: 48px; flex: none;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 50%;
  overflow: hidden;
  box-shadow: none;
  font-size: 0;                 /* 兜底字符（角色缺切图时）不撑破布局 */
}
#chat .flow-av img {
  width: 35px; height: 42px;
  object-fit: cover; object-position: top center;
  border-radius: 0; display: block;
}
#chat .flow-nm { font: 800 15px/1.2 "PingFang SC", system-ui, sans-serif; color: #fff; letter-spacing: -0.13px; }

/* 建议词 + 输入行 */
#suggestions { padding: 0 12px; gap: 6px; }
#chat .chip {
  padding: 4px 10px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: #fff;
  font: 400 11px/1.4 "PingFang SC", system-ui, sans-serif;
  letter-spacing: -0.13px;
  box-shadow: none;
}
#chat .chip:hover { background: rgba(255, 255, 255, 0.12); transform: none; }
#chat-form {
  margin: 0 12px; padding: 0 5px 0 12px;
  height: 44px; flex: none;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
#chat-input {
  flex: 1; min-width: 0;
  background: none; border: none; box-shadow: none;
  /* padding-left 不能是 0：<input> 自带 overflow:clip，内容框左边缘就是裁剪边界，
     光标停在开头（位置 0）时正好画在这条线上，会被切掉一半（2026-09-07 报障）。
     跟圆角无关——border-radius 单独不裁任何东西，要配 overflow 才裁；这里
     #chat-form 有 100px 圆角但没开 overflow，而 #chat 的 20px 圆角离输入框 25px 够不着。
     负 margin 把这 2px 的布局位移抵消掉：文字起点一点不动（实测位移 0），
     只是内容框往左让出 2px 给光标。 */
  padding: 0 0 0 2px; margin-left: -2px;
  color: #fff;
  font: 500 13px/1.4 "PingFang SC", system-ui, sans-serif;
  letter-spacing: -0.13px;
}
#chat-input::placeholder { color: rgba(255, 255, 255, 0.3); }
#chat-input:focus { outline: none; box-shadow: none; background: none; }
#chat-send {
  width: 32px; height: 32px; flex: none; padding: 0;
  display: grid; place-items: center;
  border: 0.64px solid rgba(255, 229, 133, 0.7);
  border-radius: 16px;
  background: #f25b17;
  box-shadow: 0 0 3.2px rgba(255, 81, 0, 0.9), inset 0 0 3.2px rgba(255, 171, 92, 0.7);
  color: #fff; font-size: 0;
}
#chat-send img { width: 19px; height: 19px; display: block; transform: rotate(90deg); }
#chat-send:hover { transform: none; }
#chat-send:disabled { opacity: 0.5; background: #f25b17; }
/* 生成中变「停止」：切图藏起换成方块符号（JS 只切类不动 DOM） */
#chat-send.is-stop img { display: none; }
/* 停止方块 = CSS 画的圆角方块（2026-08-19 拍板加圆角；■ 字形圆不了角） */
#chat-send.is-stop::before { content: ""; width: 12px; height: 12px; border-radius: 3.5px; background: #fff; }

/* 生成中：药丸麦克风变「停止生成」（main.js setSendMode 同步切 .gen-stop，点击中断）。
   与文字输入的停止钮同款圆角方块。 */
#big-mic.gen-stop img { display: none; }
#big-mic.gen-stop::before {
  content: ""; width: 14px; height: 14px; border-radius: 4px; background: #fff;
}

/* ── 输入法上方的悬浮输入条（2026-08-19）：手机键盘会盖住面板里的输入框，聚焦时把
   「正在输入的内容 + 发送」浮到键盘正上方（JS 按 visualViewport 钉 translateY）。
   键盘没占高（<80px，含桌面）一律不出现；焦点始终留在原输入框，条上只做镜像。 ── */
#type-bar {
  position: fixed; left: 0; right: 0; top: 0; z-index: 90;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px;
  background: rgba(24, 27, 33, 0.96);
  border-top: 0.8px solid rgba(255, 255, 255, 0.1);
  will-change: transform;
}
#type-bar[hidden] { display: none; }
#type-echo {
  flex: 1; min-width: 0; height: 21px;
  font: 500 15px/21px "PingFang SC", system-ui, sans-serif; color: #fff;
  white-space: nowrap; overflow: hidden;   /* 超宽不截省略号：JS 滚到最尾（同 #mic-partial 房规） */
}
#type-echo:empty::before { content: "说点什么…"; color: rgba(255, 255, 255, 0.35); }
#type-send {
  flex: none; height: 32px; padding: 0 18px; border: none; border-radius: 999px;
  background: #f25b17; color: #fff;
  font: 800 13px/1 "PingFang SC", system-ui, sans-serif; cursor: pointer;
  box-shadow: 0 0 4px rgba(255, 81, 0, 0.5), inset 0 0 3px rgba(255, 171, 92, 0.6);
}
#type-send:active { transform: scale(0.95); }

/* ── 赋魂展台（body.soul-stage）：中间是「只剩这一个物体」的空旷场景，HUD 全部让开 ──
   【为什么写在 voice.css 末尾而不是 style.css】voice.css 里有
   `body.has-selection #minimap { display: block !important }`，与本条特异度相同
   （都是 0,1,1,1）且都带 !important —— 这时后声明的赢。写在 style.css 里会被它压掉：
   实测 body 上明明有 soul-stage、内联也是空的，小地图仍然 display:block。 */
body.soul-stage #minimap,
body.soul-stage #edit-toggle,
body.soul-stage #scene-toggle,
body.soul-stage #scene-menu,
body.soul-stage #scenery-del,
body.soul-stage #undo-bar,
body.soul-stage #toolbar,
body.soul-stage #sheet-handle,
body.soul-stage #energy-badge,
body.soul-stage #mic-pill,
body.soul-stage #panel-toggle,
body.soul-stage #soul-installed,
body.soul-stage #home-entry,
body.soul-stage #perf { display: none !important; }

/* ══════════ 进入地图的加载屏（Figma 732:24169「草地世界加载」）══════════
   稿面 852×393，三种地貌（grass/desert/snow）只有【背景图】【图标】【图标高度】不同，其余一样。
   本段在 style.css 之后加载，同特异性下后到者胜——style.css 里那份 demo 占位
   （🧱 emoji + "BlockWorld"）的每一条属性这里都要覆盖到，漏一条就会留下来。

   为什么不做 852×393 缩放舞台：游戏页 index.html 本来就是流式布局（HUD 全是固定 px +
   safe-area），不像首页/登录页那样有 --s 舞台。所以这里背景 cover 铺满视口、内容按设计 px
   居中——在目标机型（横屏 852×393pt）上就是 1:1 稿面。

   地貌怎么定的：<html data-biome="…">，由 index.html <head> 里那段经典脚本同步写入，
   第一帧就正确。 */

/* 兜底渐变垫在背景图下面：图还没解出来、或者某个地貌的图还没切，看到的也是这张图
   该有的天地色，不会闪白。值取自新背景图顶部 1/12 与底部 1/12 的实测均值。 */
#loading {
  display: block;                                  /* 稿面不再是居中一列，覆盖 style.css 的 flex 居中 */
  background-color: transparent;
  background-image: var(--load-bg, none), var(--load-sky);
  background-size: cover, auto;
  background-position: center center, center center;
  background-repeat: no-repeat, no-repeat;
}
html[data-biome="grass"]  #loading { --load-bg: url("assets/loading/bg_grass.jpg");  --load-sky: linear-gradient(180deg, #9cb1dc, #1f2420); }
html[data-biome="desert"] #loading { --load-bg: url("assets/loading/bg_desert.jpg"); --load-sky: linear-gradient(180deg, #828493, #332a28); }
html[data-biome="snow"]   #loading { --load-bg: url("assets/loading/bg_snow.jpg");   --load-sky: linear-gradient(180deg, #8498df, #222f54); }

/* 右上角 logo（稿面 image 185，210×69.808，left 642 → 右边缘正好贴到 852，所以 right:0）。
   ⚠️ 类名用 .load-brand 不用 .load-logo：style.css 里 .load-logo 是 demo 那枚会上下浮动的
   emoji（font-size:56px + load-bob 动画），沿用就得把它每条属性都覆盖一遍，不如换个名。 */
#loading .load-brand {
  position: absolute;
  top: 13px;
  right: env(safe-area-inset-right, 0px);
  width: 210px;
  height: 69.808px;
  object-fit: contain;
  pointer-events: none;
}

/* 底部那条带（稿面 Frame 279，852×88）。
   稿面把它导出成了位图，实际是一条【纯竖向 alpha 渐变】——实测底色恒为 rgb(10,22,2)、
   alpha 从 0 线性升到 0.494（对 y 线性拟合最大偏差 0.54，逐行跨列标准差 0.73），
   所以用 CSS 渐变还原，不落切图。
   高度不写死，由内容撑：12 + 20(唤醒行) + 6 + 10(进度条) + 7 + 15(轮播行) + 18 = 88，正好是稿面值。
   左右内边距稿面是 112（内容宽 628≈稿面进度条的 630）；窄屏按 13.03% 收，最小留 16。 */
#loading .load-band {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 12px clamp(16px, 13.03%, 111px) 18px;
  background: linear-gradient(to bottom, rgba(10, 22, 2, 0), rgba(10, 22, 2, 0.494));
}

/* 「正在唤醒{地貌}世界…」——【不带地图名】（2026-09-15 改稿）。
   地貌名由 index.html 那段经典脚本按 data-biome 写入，首帧就是对的。 */
#loading .load-head {
  font: 600 14px/20px "PingFang SC", "Hiragino Sans GB", sans-serif;
  color: #fff;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 进度条（稿面 882:6990/6991）：630×10 方角，底 #121212 + 1px 白 0.34 描边；
   填充 8px 高、橙底 + 右缘往里 9.1px 的亮黄内发光（inset 的 -6px 把光推到右端）。
   ⚠️ 这一版【是方角】，跟旧版那条 radius 35 的胶囊不是一回事。
   1px 描边 + border-box ⇒ 内容盒正好 8px 高，填充直接占满内容盒，不用再加 padding。
   width 走 100%：band 的内边距已经把内容宽收到稿面的 630。 */
#loading .load-bar {
  box-sizing: border-box;
  width: 100%;
  height: 10px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.34);
  border-radius: 0;
  background: #121212;
  overflow: hidden;
  box-shadow: none;
}
#loading .load-bar > i {
  display: block;
  height: 100%;
  width: 0%;
  border-radius: 0;
  background: #ffa600;
  box-shadow: inset -6px 0 9.1px #feeb4b;
  transition: width 0.25s ease;
}

/* 底部轮播行（稿面 882:6992）：地貌文案 + 「……{已建} / {总数} 个造物」。
   文案 5s 换一条，循环在 main.js 的 startLoadTips；换的时候淡一下，硬切太廉价。
   造物数每帧在变，tabular-nums 免得整行左右抖。 */
#loading .load-tip {
  margin-top: 1px;                                 /* 稿面 Frame278→文案是 gap 7，条内 gap 6，差这 1px */
  font: 500 10px/15px "PingFang SC", "Hiragino Sans GB", sans-serif;
  color: #f5f7fc;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#loading #load-tip { transition: opacity 0.25s ease; }
#loading #load-count { font-variant-numeric: tabular-nums; }

/* ══════════ 细节编辑顶部条的「退出」钮 ══════════
   demo 那版是纯色 #e8823a 的圆角丸，跟全站现行按钮不是一套——现行配方是
   「竖直渐变 + 下暗边 + 上高光 + 字投影 + 按下手感」，见个人信息「去补能」
   (.pf-btn)、设置那两颗大钮 (.st-lg)、登录三钮 (.lg-b)。统一过来。
   规则写在这里不写 style.css：那个文件要保持和 demo 好对 diff（本文件后加载）。
   ⚠️ demo 有一条 #detail-bar #detail-exit:hover 会把 background 换成纯色，
   必须同名覆盖掉，否则一悬停渐变就没了。 */
#detail-bar #detail-exit {
  padding: 7px 18px;
  border: none;
  border-radius: 8px;
  color: #fff;
  font: 800 13px/1 "Inter", "PingFang SC", sans-serif;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
  background: linear-gradient(180deg, #fe723a, #fa4f13);
  box-shadow: inset 0 -2px .5px rgba(183, 49, 0, .77),
              inset 0 1px .5px rgba(255, 255, 255, .3);
  transition: transform .08s ease, filter .08s ease;
}
#detail-bar #detail-exit:hover { background: linear-gradient(180deg, #fe723a, #fa4f13); }
#detail-bar #detail-exit:active { transform: translateY(1px) scale(.96); filter: brightness(.9); }

/* ══════════ 细节编辑：顶部提示条 + 底部输入条 ══════════
   都对齐游戏内右侧那条语音药丸的玻璃配方（#mic-pill，本文件 110 行附近）：
   rgba(32,36,34,.85) + blur(10)、0.8px 极淡白描边、外投影配内高光。
   demo 那版是 rgba(18,22,28,.9) 的纯深色块 + 14px 圆角，跟现行 HUD 不是一套。
   规则写这里不写 style.css：那个文件要保持和 demo 好对 diff（本文件后加载）。 */
#detail-bar, #detail-input {
  background: rgba(32, 36, 34, 0.85);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 0.8px solid rgba(255, 255, 255, 0.05);
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.4), inset 0 0 4px rgba(255, 255, 255, 0.25);
}
#detail-input { border-radius: 100px; padding: 4px 4px 4px 6px; gap: 6px; }
/* 输入框本身退成透明：玻璃已经在外层了，里面再垫一层灰块会显得脏 */
#detail-input input {
  background: transparent; border-radius: 100px; padding: 10px 14px;
  color: #e8ecf0; font-size: 14px;
}
/* 「修改」钮用全站现行按钮配方（同 .pf-btn / .st-lg / .lg-b），只是收成药丸形 */
#detail-input button {
  padding: 0 20px; min-height: 38px; border-radius: 100px;
  color: #fff; font: 800 14px/1 "Inter", "PingFang SC", sans-serif;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .25);
  background: linear-gradient(180deg, #fe723a, #fa4f13);
  box-shadow: inset 0 -2px .5px rgba(183, 49, 0, .77),
              inset 0 1px .5px rgba(255, 255, 255, .3);
  transition: transform .08s ease, filter .08s ease;
}
#detail-input button:active:not(:disabled) { transform: translateY(1px) scale(.96); filter: brightness(.9); }
/* 顶部条现在只剩提示文字，收紧左右内边距，别留原来给退出钮的那块空 */
#detail-bar { padding: 9px 18px; gap: 0; }

/* 赋魂展台 / 拆解态：收掉「＋扩建」那排浮钮。
   展台是「只剩这一个物体」的空旷场景，地上飘着扩建入口会穿帮（用户 2026-09-11）。
   3D 那层的地面虚影和边界线由 main.js 的 enterSoulStage 一并藏起来——
   两层分开收是因为它们本来就分属 DOM 和场景，没有共同开关。 */
body.soul-stage #expand-btns,
body.detail-mode #expand-btns { display: none !important; }
