/* Voice Lab —— 手机通话形态 · 深夜蓝黑 + 顶部来电光晕
 * 状态机：body[data-call="idle|dialing|live"] 驱动整屏形态切换（拨号页 ⇄ 通话页）
 * 记忆点：头像声波涟漪——AI 说话泛蓝波(.ai-talking)、用户说话泛绿波(.user-talking)，
 *         既是氛围也是"谁在说话"的实时可视化（测打断时机全靠它）。 */
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
  --bg: #0b0e16; --panel: #161a25; --line: #262b38;
  --fg: #eef0f6; --dim: #7e8698;
  --green: #34d16b; --green-deep: #23a552;
  --red: #ff5d55; --red-deep: #d9423b;
  --ai-wave: #5b8cff;
  --glass: rgba(255,255,255,.06);
}
html, body { height: 100%; }
body {
  background:
    radial-gradient(120% 55% at 50% -12%, rgba(91,140,255,.16), transparent 60%),
    var(--bg);
  color: var(--fg);
  font: 15px/1.55 -apple-system, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}

.hidden { display: none !important; }

/* ---------- 登录门（z-index 最高，未登录挡住一切） ---------- */
.gate {
  position: fixed; inset: 0; z-index: 100;
  background: inherit;
  display: flex; align-items: center; justify-content: center;
}
.gate-box { width: min(320px, 86vw); text-align: center; }
.gate-title { font-size: 24px; font-weight: 700; margin-bottom: 6px; }
.gate-sub { color: var(--dim); font-size: 13px; margin-bottom: 20px; }
.gate-input {
  width: 100%; background: var(--panel); border: 1px solid var(--line); color: var(--fg);
  border-radius: 12px; padding: 13px 14px; font-size: 16px; text-align: center; margin-bottom: 12px;
}
.gate-btn { width: 100%; padding: 12px; }
.gate-error { color: var(--red); font-size: 13px; margin-top: 12px; }

/* ---------- 麦克风授权提示浮层 ---------- */
.mic-prompt {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(0,0,0,.6); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
}
.mic-prompt-box {
  width: min(340px, 88vw); text-align: center;
  padding: 28px 24px; border-radius: 20px;
  background: var(--panel); border: 1px solid var(--line);
}
.mic-prompt-icon { font-size: 40px; margin-bottom: 12px; }
.mic-prompt-title { font-size: 17px; font-weight: 700; margin-bottom: 8px; }
.mic-prompt-desc { color: var(--dim); font-size: 13px; line-height: 1.6; margin-bottom: 18px; }
.mic-prompt-btn { width: 100%; padding: 12px; margin-bottom: 8px; }
.mic-prompt-dismiss { width: 100%; padding: 10px; }

/* ---------- 顶部提示横幅（fixed 高层级，防被面板挡住——2026-07-08 修过的坑） ---------- */
.banner {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  padding: 10px 16px; font-size: 13px; text-align: center;
  background: #123c26; color: var(--green);
  box-shadow: 0 2px 12px rgba(0,0,0,.35);
}
.banner.err { background: #3d1512; color: #ff8a80; }

/* ---------- 通话主屏 ---------- */
#stage {
  height: 100dvh;
  display: flex; flex-direction: column; align-items: center;
  padding: max(20px, env(safe-area-inset-top)) 20px calc(30px + env(safe-area-inset-bottom));
}

/* 右上工具钮：半透明玻璃小圆钮，通话氛围里尽量隐形 */
.fabs {
  position: fixed; top: max(14px, env(safe-area-inset-top)); right: 14px;
  display: flex; flex-direction: column; gap: 10px; z-index: 10;
}
.fab {
  position: relative;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--glass); border: 1px solid rgba(255,255,255,.09);
  color: var(--fg); font-size: 16px; cursor: pointer;
  backdrop-filter: blur(8px); opacity: .72;
}
.fab:active { transform: scale(.92); }
/* 用 <a> 做的入口钮（跳到 /phone、/agent）：button 默认就居中，a 要自己居中 */
a.fab { display: grid; place-items: center; text-decoration: none; }
.fab-badge {
  position: absolute; top: -4px; right: -4px;
  min-width: 16px; height: 16px; border-radius: 8px; padding: 0 4px;
  background: var(--red); color: #fff; font-size: 10px; font-weight: 600;
  line-height: 16px; font-style: normal;
}
.fab-badge:empty, .fab-badge[data-zero] { display: none; }

/* 联系人卡：靠上 1/3 处，像真来电屏；margin-bottom:auto 把字幕和控制排一起推到屏底，
   字幕显隐都不影响控制排贴底 */
.callcard {
  margin-top: 11dvh; margin-bottom: auto; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.avatar-wrap { position: relative; width: 108px; height: 108px; margin-bottom: 10px; }
.avatar {
  position: absolute; inset: 0; border-radius: 50%;
  background: linear-gradient(145deg, #223050, #141b2c);
  border: 1px solid rgba(255,255,255,.10);
  display: flex; align-items: center; justify-content: center;
  font-size: 46px;
  box-shadow: 0 10px 40px rgba(0,0,0,.5);
}
/* 声波涟漪：默认静止透明；说话时按说话方染色扩散 */
.ripple {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid transparent; opacity: 0; pointer-events: none;
}
.avatar-wrap.ai-talking .ripple { border-color: var(--ai-wave); animation: ripple 1.6s ease-out infinite; }
.avatar-wrap.user-talking .ripple { border-color: var(--green); animation: ripple 1.3s ease-out infinite; }
.avatar-wrap .r2 { animation-delay: .55s !important; }
@keyframes ripple {
  0%   { transform: scale(1);    opacity: .55; }
  100% { transform: scale(1.75); opacity: 0; }
}
/* 呼叫中：头像轻微脉动 */
body[data-call="dialing"] .avatar { animation: dialpulse 1.1s ease-in-out infinite; }
@keyframes dialpulse { 50% { transform: scale(1.05); } }
@media (prefers-reduced-motion: reduce) {
  .ripple, body[data-call="dialing"] .avatar { animation: none !important; }
  .avatar-wrap.ai-talking .ripple, .avatar-wrap.user-talking .ripple { opacity: .4; transform: scale(1.2); }
}

.callee { font-size: 26px; font-weight: 600; letter-spacing: .5px; }
.call-sub { color: var(--dim); font-size: 14px; min-height: 21px; }
body[data-call="live"] .call-sub { display: none; } /* 通话中：状态行让位给计时行 */

/* 通话语言切换：拨号前的分段开关，接通后隐藏（"打电话前选择"——通话中改了也不会生效） */
.lang-toggle {
  display: flex; margin-top: 4px;
  background: var(--glass); border: 1px solid rgba(255,255,255,.08);
  border-radius: 999px; padding: 3px; gap: 2px;
}
.lang-opt {
  border: none; background: transparent; color: var(--dim);
  font-size: 13px; padding: 5px 16px; border-radius: 999px; cursor: pointer;
  transition: background .2s, color .2s;
}
.lang-opt.active { background: var(--ai-wave); color: #fff; font-weight: 600; }
body:not([data-call="idle"]) .lang-toggle { display: none; }

/* 人设摘要（嗓子 · 语言）：只读，跟状态行一样通话时让位给计时 */
.persona-meta { margin-top: 8px; color: var(--dim); font-size: 12.5px;
                letter-spacing: .2px; opacity: .85; min-height: 18px; }
body:not([data-call="idle"]) .persona-meta { display: none; }

/* 测试场景切换（接听来电 / 模拟外呼）——外观同语言切换，拨号前选 */
.scene-toggle {
  display: flex; margin: 10px 0 4px;
  background: var(--glass); border: 1px solid rgba(255,255,255,.08);
  border-radius: 999px; padding: 3px; gap: 2px;
}
.scene-opt {
  border: none; background: transparent; color: var(--dim); flex: 1;
  font-size: 13px; padding: 6px 14px; border-radius: 999px; cursor: pointer;
  transition: background .2s, color .2s; white-space: nowrap;
}
.scene-opt.active { background: var(--ai-wave); color: #fff; font-weight: 600; }
body:not([data-call="idle"]) .scene-toggle { display: none; }
/* 📞 面板内的拨号语言选择是设置项，不随通话状态隐藏 */
.lang-toggle-inline { margin-top: 0; width: fit-content; }
body:not([data-call="idle"]) .lang-toggle-inline { display: flex; }
.call-meta {
  display: none; gap: 10px; align-items: center; justify-content: center;
  color: var(--dim); font-size: 14px; font-variant-numeric: tabular-nums;
}
.call-meta #callTimer { color: var(--fg); letter-spacing: 1px; }
.call-meta #modeBadge:not(:empty) {
  font-size: 11px; border: 1px solid var(--line); border-radius: 6px; padding: 1px 7px;
}
.call-meta #latency:not(:empty) { color: var(--green); font-size: 12px; }
body[data-call="live"] .call-meta { display: flex; }

/* ---------- 实时字幕胶囊（贴在控制排上方；关字幕/无内容时整体消失不占位） ---------- */
.captions {
  margin-bottom: 18px;
  width: min(480px, 100%);
  background: var(--glass); border: 1px solid rgba(255,255,255,.07);
  backdrop-filter: blur(10px);
  border-radius: 16px; padding: 10px 16px;
  display: flex; flex-direction: column; gap: 4px;
  cursor: pointer;
}
.cap { font-size: 14px; line-height: 1.5;
  overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.cap:has(> span:empty) { display: none; }         /* 空的那一行不占位 */
.cap.user { color: var(--dim); }
.cap.user span:not(:empty)::before { content: "你："; color: var(--green); }
.cap.ai { color: var(--fg); }
.cap.ai span:not(:empty)::before { content: "店家："; color: var(--ai-wave); }
.captions:not(:has(span:not(:empty))) { display: none; } /* 两行都空=整体消失 */
body:not([data-call="live"]) .captions { display: none; }

/* ---------- 通话控制排（贴屏底） ---------- */
.controls {
  display: flex; align-items: center; justify-content: center; gap: 44px;
  width: 100%;
}
.ctl {
  width: 58px; height: 58px; border-radius: 50%;
  background: var(--glass); border: 1px solid rgba(255,255,255,.09);
  color: var(--fg); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  backdrop-filter: blur(8px);
  /* opacity 不进 transition：瞬时显隐，不依赖渲染帧（无头/后台渲染环境过渡会卡在起点） */
  transition: background .2s, transform .1s;
}
.ctl:active { transform: scale(.92); }
.ctl.on { background: #fff; color: #0b0e16; }          /* 静音激活 = 白底反色（真手机习惯） */
body:not([data-call="live"]) .ctl { opacity: 0; pointer-events: none; }  /* 拨号页藏两侧钮 */

/* 拨打/挂断 同一颗按钮的两种形态 */
.call-btn {
  width: 78px; height: 78px; border-radius: 50%; border: none; cursor: pointer;
  color: #fff;
  background: linear-gradient(160deg, var(--green), var(--green-deep));
  box-shadow: 0 8px 28px rgba(52,209,107,.35);
  display: flex; align-items: center; justify-content: center;
  transition: transform .15s, background .25s, box-shadow .25s;
}
.call-btn:active { transform: scale(.92); }
.call-btn:disabled { opacity: .65; }
.call-btn svg { transition: transform .25s; }
body[data-call="live"] .call-btn,
body[data-call="dialing"] .call-btn {
  background: linear-gradient(160deg, var(--red), var(--red-deep));
  box-shadow: 0 8px 28px rgba(255,93,85,.35);
}
body[data-call="live"] .call-btn svg,
body[data-call="dialing"] .call-btn svg { transform: rotate(135deg); }  /* 听筒倒转 = 挂断 */

/* ---------- 面板通用（设置/调试/记录/商店）：移动全屏，桌面右侧抽屉 ---------- */
.panel {
  position: fixed; inset: 0; z-index: 20; background: var(--bg);
  display: flex; flex-direction: column;
}
@media (min-width: 720px) {
  .panel { left: auto; width: 420px; border-left: 1px solid var(--line); box-shadow: -12px 0 30px rgba(0,0,0,.45); }
}
.panel-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 14px 16px; border-bottom: 1px solid var(--line); font-weight: 600; flex-shrink: 0;
}
.close-x, .mini-btn {
  background: var(--panel); color: var(--fg); border: 1px solid var(--line);
  border-radius: 8px; padding: 4px 10px; cursor: pointer; font-size: 13px;
}
.panel-body { flex: 1; overflow-y: auto; padding: 16px; user-select: text; }
.panel-body.log { font: 11px/1.5 Consolas, monospace; color: var(--dim); white-space: pre-wrap; word-break: break-all; }

/* 通话记录气泡 */
#chat { display: flex; flex-direction: column; gap: 10px; }
.msg { max-width: 85%; padding: 9px 13px; border-radius: 14px; white-space: pre-wrap; word-break: break-word; }
.msg.user { align-self: flex-end; background: var(--green-deep); color: #fff; border-bottom-right-radius: 4px; }
.msg.ai { align-self: flex-start; background: var(--panel); border: 1px solid var(--line); border-bottom-left-radius: 4px; }
.msg.sys { align-self: center; background: none; color: var(--dim); font-size: 12px; }

/* 商店面板 */
.shop-sec { font-size: 12px; color: var(--dim); letter-spacing: .5px; margin: 6px 0 10px; }
.shop-sec + .shop-sec, .inv-chips + .shop-sec { margin-top: 18px; }
.inv-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.inv-chips + .shop-sec { margin-top: 18px; }
.chip {
  background: var(--panel); border: 1px solid var(--line); border-radius: 999px;
  padding: 3px 11px; font-size: 13px;
}
.chip.out { color: var(--red); border-color: rgba(255,93,85,.4); text-decoration: line-through; }
.order-list { display: flex; flex-direction: column; gap: 8px; }
.order-item {
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  padding: 8px 11px; font-size: 13px; line-height: 1.6;
}
.order-item b { color: var(--ai-wave); }
.o-status { color: var(--green); font-size: 11px; margin-left: 6px; }
.o-time { color: var(--dim); font-size: 11px; float: right; }
.o-goods { color: #f1c40f; }
.order-empty { color: var(--dim); font-size: 12px; }

/* 表单 */
.field { display: block; margin-bottom: 14px; }
.fname { display: block; font-size: 13px; color: var(--dim); margin-bottom: 5px; }
.fname em { color: #f1c40f; font-style: normal; font-size: 11px; }
.fname b { color: var(--ai-wave); }
.field input[type=text], .field input[type=password], .field input:not([type]),
.field select, .field textarea {
  width: 100%; background: var(--panel); border: 1px solid var(--line); color: var(--fg);
  border-radius: 10px; padding: 9px 11px; font-size: 14px; font-family: inherit;
}
.field textarea { resize: vertical; }
.field input[type=range] { width: 100%; accent-color: var(--ai-wave); }
.field.check { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.field.check input { width: 18px; height: 18px; accent-color: var(--ai-wave); }
/* 与左侧带 <em> 说明的输入框同处 row2 时，勾选项在底部对齐更整齐 */
.field.check.dead-hangup { align-self: end; padding-bottom: 10px; }
.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.divider {
  margin: 18px 0 12px; font-size: 12px; color: var(--dim);
  border-top: 1px solid var(--line); padding-top: 10px; letter-spacing: .5px;
}
.btn-row { display: flex; gap: 10px; margin-top: 6px; }
.btn {
  flex: 1; padding: 11px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--panel); color: var(--fg); font-size: 14px; cursor: pointer;
}
.btn.primary { background: var(--ai-wave); border-color: var(--ai-wave); color: #fff; }
/* 拨号等待态：/api/sip/dial 要阻塞到对方接听，按钮必须自己表明「在干活」，
   否则用户以为没点上。用呼吸动画而不是 opacity 过渡（后台/无头渲染不产帧，
   挂在显隐属性上的 transition 会卡在起点值）。 */
.btn.busy { opacity: .75; cursor: progress; animation: btn-breathe 1.2s ease-in-out infinite; }
@keyframes btn-breathe { 0%, 100% { filter: brightness(1); } 50% { filter: brightness(1.35); } }
.hint { font-size: 12px; color: var(--dim); margin-top: 10px; }

/* 电话线路状态 */
.sip-status {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; font-size: 14px;
}
.sip-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; background: var(--dim); }
.sip-dot.off { background: #5a6270; }
.sip-dot.ok { background: var(--green); box-shadow: 0 0 8px rgba(52,209,107,.6); }
.sip-dot.warn { background: #f0b429; }
.sip-dot.err { background: var(--red); }
.sip-dot.live { background: var(--ai-wave); animation: sipPulse 1.1s ease-in-out infinite; }
@keyframes sipPulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

/* 坐席台状态行 + 虚拟号码线路卡片（电话面板里三类线路的分区） */
.agent-line {
  display: flex; align-items: center; gap: 10px; margin-top: 10px;
  padding: 10px 14px; background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; font-size: 13.5px;
}
.btn.mini-link {
  flex: 0 0 auto; padding: 7px 12px; font-size: 12.5px;
  text-decoration: none; text-align: center; margin-left: auto;
}
.vline {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: 12px 14px; margin-bottom: 10px;
}
.vline-head { display: flex; align-items: center; gap: 8px; font-size: 13px; margin-bottom: 8px; }
.vline-head .vnum { font-weight: 600; font-variant-numeric: tabular-nums; }
.vline-head .vstate { font-size: 12px; color: var(--dim); }
.vline-head .vstate.ok { color: var(--green); }
.vline-del {
  margin-left: auto; background: transparent; border: 1px solid var(--line);
  color: var(--red); border-radius: 8px; padding: 4px 10px; font-size: 12px; cursor: pointer;
}
.vline .row2 { margin-bottom: 0; }
/* 线路故障原因：显示得下完整文字（含出口地址诊断），别让人对着"未注册"猜 */
.vline-err, .sip-err {
  background: #3a2126; border: 1px solid #5b2b33; color: #ffb4b4;
  border-radius: 9px; padding: 9px 11px; font-size: 12px; line-height: 1.65;
  white-space: pre-wrap; word-break: break-word; margin-bottom: 10px;
}
.sip-err { margin: 10px 0 0; }

/* 通话字幕 */
.phone-transcript {
  margin-top: 10px; max-height: 220px; overflow-y: auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 10px 12px; font-size: 13px; line-height: 1.5;
}
.tr-empty { color: var(--dim); font-size: 12px; text-align: center; padding: 14px 0; }
.tr-line { margin: 4px 0; padding: 6px 9px; border-radius: 8px; }
.tr-line b { font-size: 11px; opacity: .75; margin-right: 6px; }
.tr-ai { background: rgba(52,209,107,.12); }
.tr-caller { background: rgba(255,255,255,.04); }

/* 线路事件 / 诊断 */
.phone-events {
  max-height: 240px; overflow-y: auto;
  background: #0d1017; border: 1px solid var(--line); border-radius: 12px;
  padding: 8px 10px; font-size: 12px; line-height: 1.45;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.ev-line { padding: 3px 4px; border-bottom: 1px solid rgba(255,255,255,.04); color: var(--fg); }
.ev-line:last-child { border-bottom: none; }
.ev-t { color: var(--dim); margin-right: 6px; }
.ev-ok { color: var(--green); }
.ev-warn { color: #ffb454; }

/* ==========================================================================
   拨号界面 v2 重设计（仅作用于 dial.css / /dial 页；旧 / 测试台不受影响）
   目标：更好看、更好操作。只改外观，不动 body[data-call] 状态显隐机制。
   ========================================================================== */
:root{
  --accent2:#4f8cff;
  --card-bg:rgba(255,255,255,.045);
  --card-line:rgba(255,255,255,.09);
  --glass2:rgba(20,25,38,.55);
}
body{
  background:
    radial-gradient(90% 50% at 50% -10%, rgba(79,140,255,.18), transparent 62%),
    radial-gradient(70% 40% at 50% 112%, rgba(52,209,107,.10), transparent 60%),
    var(--bg);
}

/* 右上角工具钮 → 悬浮玻璃工具条 */
.fabs{position:absolute; top:16px; right:16px; z-index:6;
  display:flex; flex-direction:column; gap:6px; padding:6px;
  border-radius:16px; background:var(--glass2); border:1px solid rgba(255,255,255,.08);
  backdrop-filter:blur(14px); box-shadow:0 8px 28px rgba(0,0,0,.35);}
.fab{width:38px; height:38px; border-radius:11px; background:transparent; color:var(--dim);
  display:grid; place-items:center; border:0; position:relative; transition:background .15s,color .15s;}
.fab:hover{background:rgba(255,255,255,.09); color:var(--fg);}
.fab:active{transform:scale(.92);}
.fab svg{width:18px; height:18px;}
.fab-badge{position:absolute; top:2px; right:2px; min-width:15px; height:15px; padding:0 4px;
  border-radius:9px; background:var(--red); color:#fff; font-size:10px; font-weight:700;
  display:grid; place-items:center; line-height:1;}

/* 联系人卡 → 玻璃卡片 */
.callcard{max-width:400px; width:100%; margin-top:clamp(16px,5vh,52px);
  padding:30px 26px 24px; border-radius:26px;
  background:linear-gradient(180deg, rgba(255,255,255,.055), rgba(255,255,255,.02));
  border:1px solid var(--card-line); backdrop-filter:blur(10px);
  box-shadow:0 24px 70px rgba(0,0,0,.4); display:flex; flex-direction:column; align-items:center;}

/* 头像：更大、加渐变环，保留说话涟漪 */
.avatar-wrap{width:116px; height:116px; margin-bottom:14px;}
.avatar{background:linear-gradient(150deg,#2b3448,#1a2130);
  box-shadow:0 0 0 1px rgba(255,255,255,.1), 0 12px 34px rgba(0,0,0,.45); font-size:46px;}

.callee{font-size:23px; font-weight:700; letter-spacing:.2px;}
.call-sub{color:var(--dim); font-size:13.5px; margin-top:2px;}

/* 场景 / 语言 切换 → 分段控件（segmented） */
.scene-toggle,.lang-toggle{margin-top:16px; padding:4px; gap:4px;
  background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.08); border-radius:13px;}
.scene-opt,.lang-opt{padding:9px 16px; border:0; border-radius:9px; background:transparent;
  color:var(--dim); font-weight:600; font-size:13px; transition:background .15s,color .15s,box-shadow .15s;}
.scene-opt:hover,.lang-opt:hover{color:var(--fg);}
.scene-opt.active,.lang-opt.active{background:var(--accent2); color:#fff;
  box-shadow:0 3px 12px rgba(79,140,255,.45);}

/* 计时 / 状态行 */
.call-meta{gap:12px; margin-top:14px;}
.call-meta #callTimer{font-size:21px; font-weight:700; letter-spacing:1px; font-variant-numeric:tabular-nums;}

/* 字幕 → 磨砂胶囊 */
.cap{background:rgba(255,255,255,.06); border:1px solid rgba(255,255,255,.09);
  border-radius:14px; padding:9px 14px; backdrop-filter:blur(8px); font-size:13.5px;}

/* 控制排：更大更好点 */
.controls{gap:34px; margin-top:auto; padding-top:22px;}
.ctl{width:58px; height:58px; border-radius:50%; color:var(--fg);
  background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.11); transition:background .15s,transform .1s;}
.ctl:hover{background:rgba(255,255,255,.13);}

/* 主拨号键：绿色，带外发光 + 待机脉冲，接通/呼叫中转红 */
.call-btn{width:80px; height:80px; border-radius:50%; color:#04210f; border:0;
  background:radial-gradient(120% 120% at 30% 22%, #56e58a, var(--green-deep));
  box-shadow:0 12px 34px rgba(52,209,107,.45), 0 0 0 8px rgba(52,209,107,.12);}
.call-btn svg{width:34px; height:34px;}
body[data-call="idle"] .call-btn{animation:callpulse 2.3s ease-in-out infinite;}
@keyframes callpulse{
  0%,100%{box-shadow:0 12px 34px rgba(52,209,107,.42), 0 0 0 0 rgba(52,209,107,.4);}
  50%{box-shadow:0 12px 34px rgba(52,209,107,.5), 0 0 0 16px rgba(52,209,107,0);}
}
body[data-call="live"] .call-btn,
body[data-call="dialing"] .call-btn{
  background:radial-gradient(120% 120% at 30% 22%, #ff8078, var(--red-deep));
  color:#fff; box-shadow:0 12px 34px rgba(255,93,85,.45); animation:none;}

@media (prefers-reduced-motion:reduce){
  body[data-call="idle"] .call-btn{animation:none;}
}

/* ==========================================================================
   布局重构 v3：顶栏 + 左右双栏（左=通话 · 右=实时对话面板）
   #stage 改为 grid，直接子元素用 grid-area 定位；状态机(display)规则不受影响。
   ========================================================================== */
#stage{
  display:grid; height:100dvh; width:100%; max-width:1000px; margin:0 auto;
  grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);
  grid-template-rows:auto minmax(0,1fr) auto;
  grid-template-areas:"bar bar" "call live" "ctl live";
  gap:16px 22px;
  padding:18px 24px calc(18px + env(safe-area-inset-bottom, 0px));
  align-items:stretch;
}
/* 顶栏 */
.tb-top{grid-area:bar; display:flex; align-items:center; gap:12px;
  padding:2px 2px 14px; border-bottom:1px solid rgba(255,255,255,.08);}
.tb-brand{display:flex; align-items:center; gap:9px; font-size:15px; font-weight:700;
  letter-spacing:.4px; color:var(--fg);}
.tb-brand-ico{width:20px; height:20px; color:var(--accent2);}
.fabs{position:static; margin-left:auto; flex-direction:row; gap:5px; padding:5px;
  border-radius:12px; background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.08);
  backdrop-filter:none; box-shadow:none;}

/* 左栏：通话卡居中 + 控制排贴底 */
.callcard{grid-area:call; align-self:center; justify-self:center; margin-top:0;}
.controls{grid-area:ctl; align-self:end; margin-top:0; padding-top:0;}

/* 右栏：实时对话面板 */
.tb-live{grid-area:live; display:flex; flex-direction:column; min-height:0;
  background:linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.015));
  border:1px solid var(--card-line); border-radius:20px; padding:18px;}
.tb-live-head{font-size:11.5px; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
  color:var(--dim); margin-bottom:14px; flex:0 0 auto;}
.tb-live .captions{width:auto; max-width:100%; margin:0; background:none; border:0;
  backdrop-filter:none; padding:0; gap:12px;}
.tb-live-empty{margin:auto; text-align:center; color:var(--dim); font-size:13px; line-height:1.9;}
.tb-live-empty b{color:var(--fg); font-weight:600;}
body[data-call="live"] .tb-live-empty{display:none;}

/* 窄屏 / 控制台内嵌 → 单栏堆叠 */
@media(max-width:880px){
  #stage{grid-template-columns:1fr; grid-template-rows:auto auto minmax(150px,1fr) auto;
    grid-template-areas:"bar" "call" "live" "ctl";}
  .tb-live{min-height:150px;}
  .callcard{justify-self:stretch;}
}

/* ==========================================================================
   语音球 · 浅色版 v4（最终视觉）
   浅色调色板只作用于 #stage（浮层面板是 #stage 的兄弟节点，仍用 :root 深色）。
   头像 = 语音球；待机=旋转光晕+呼吸细环；接通=向外扩散光圈；字幕在球下方。
   ========================================================================== */
#stage{
  --bg:#eaeef6; --fg:#1b2233; --dim:#616d85; --line:#e2e7f1; --panel:#ffffff; --glass:rgba(27,34,51,.05);
  --ai-wave:#3b7bff; --orb-blue:#3b7bff; --orb-cyan:#12bccf; --orb-violet:#7b5cff; --sf2:#f1f4fa;
  display:flex; flex-direction:column; align-items:center; gap:0;
  grid-template-columns:none; grid-template-rows:none; grid-template-areas:none;
  height:100dvh; max-width:none; padding:20px 24px calc(22px + env(safe-area-inset-bottom,0px));
  background:
    radial-gradient(48% 38% at 16% 6%, rgba(59,123,255,.14), transparent 60%),
    radial-gradient(46% 36% at 86% 10%, rgba(123,92,255,.13), transparent 60%),
    radial-gradient(60% 44% at 50% 106%, rgba(18,188,207,.10), transparent 62%),
    #eaeef6;
}
body{background:#eaeef6;}

/* 顶栏（浅色）*/
.tb-top{width:100%; border-bottom:1px solid var(--line);}
.tb-brand{color:var(--fg);}
.tb-brand-ico{color:var(--orb-blue);}
.fabs{background:#fff; border:1px solid var(--line); box-shadow:0 1px 3px rgba(27,34,51,.06);}
.fab{color:var(--dim);}
.fab:hover{background:var(--sf2); color:var(--fg);}

/* 内容垂直居中、控制排贴底 */
.callcard{background:none!important; border:0!important; box-shadow:none!important; backdrop-filter:none!important;
  padding:0; margin:0 0 0 0; margin-top:auto; max-width:none; grid-area:auto;}
.captions{grid-area:auto;}
.controls{grid-area:auto; margin-top:24px; margin-bottom:auto; gap:34px;}

/* 语音球 */
.avatar-wrap{width:190px; height:190px; margin-bottom:18px; display:grid; place-items:center; overflow:visible;}
.avatar{width:130px; height:130px; border-radius:50%; font-size:0; z-index:3; position:relative;
  background:
    radial-gradient(58% 52% at 34% 30%, rgba(255,255,255,.78), transparent 55%),
    radial-gradient(120% 120% at 70% 82%, var(--orb-violet), transparent 60%),
    radial-gradient(120% 120% at 28% 20%, var(--orb-cyan), var(--orb-blue) 66%);
  box-shadow:0 14px 44px rgba(59,123,255,.42), inset 0 0 34px rgba(255,255,255,.22);
  animation:orbfloat 5s ease-in-out infinite;}
@keyframes orbfloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}

/* 旋转光晕（待机 + 通话恒有）*/
.orb-glow{position:absolute; width:150px; height:150px; border-radius:50%; z-index:1;
  background:conic-gradient(from 0deg,var(--orb-blue),var(--orb-cyan),var(--orb-violet),var(--orb-blue));
  filter:blur(13px); opacity:.5; animation:orbspin 8s linear infinite;}
@keyframes orbspin{to{transform:rotate(360deg)}}

/* 光圈基座 */
.orb-halo{position:absolute; width:150px; height:150px; border-radius:50%; z-index:2; opacity:0;
  border:2px solid var(--orb-cyan);}
/* 待机特效：一圈缓慢呼吸的细环 */
body[data-call="idle"] .orb-halo.h1{border-color:rgba(59,123,255,.30); animation:orbbreathe 3.6s ease-in-out infinite;}
@keyframes orbbreathe{0%,100%{transform:scale(.92);opacity:.22}50%{transform:scale(1.16);opacity:.7}}
/* 接通特效：向外扩散的光圈（连续三圈错开）*/
body[data-call="live"] .orb-halo{animation:orbhalo 2.4s ease-out infinite;}
body[data-call="live"] .orb-halo.h2{animation-delay:.8s;}
body[data-call="live"] .orb-halo.h3{animation-delay:1.6s;}
@keyframes orbhalo{0%{transform:scale(.8);opacity:.5;border-color:var(--orb-cyan)}
  70%{opacity:.12;border-color:var(--orb-blue)}100%{transform:scale(2);opacity:0}}
/* 说话可视化仍走 .ripple（app.js 加 ai-talking/user-talking）*/
.ripple{border-color:rgba(59,123,255,.32);}

/* 名称 / 状态 / 计时（浅色）*/
.callee{color:var(--fg); font-size:24px; font-weight:750;}
.call-sub{color:var(--dim);}
.call-meta #callTimer{color:var(--fg);}
.call-meta #latency:not(:empty){color:#0a9a52;}

/* 分段控件（浅色）*/
.scene-toggle,.lang-toggle{background:#fff; border:1px solid var(--line); box-shadow:0 1px 3px rgba(27,34,51,.05);}
.scene-opt,.lang-opt{color:var(--dim);}
.scene-opt:hover,.lang-opt:hover{color:var(--fg);}
.scene-opt.active,.lang-opt.active{background:linear-gradient(135deg,var(--orb-blue),var(--orb-violet)); color:#fff;
  box-shadow:0 3px 12px rgba(59,123,255,.4);}

/* 实时字幕面板（浅色，球下方）*/
.captions{width:min(460px,100%); background:#fff; border:1px solid var(--line);
  box-shadow:0 6px 22px rgba(27,34,51,.09); border-radius:16px; padding:12px 15px; margin:0;
  backdrop-filter:none;}
.cap{display:flex; gap:8px; align-items:baseline; background:none; border:0; padding:3px 0; font-size:13.5px; line-height:1.55;}
.cap.user::before{content:"你"; color:#0a9a52; font-weight:700; font-size:11px; flex:0 0 auto;}
.cap.ai::before{content:"AI"; color:var(--orb-blue); font-weight:700; font-size:11px; flex:0 0 auto;}
.cap span{color:var(--fg);}

/* 控制键（浅色）*/
.ctl{background:#fff; border:1px solid var(--line); color:var(--fg); box-shadow:0 1px 3px rgba(27,34,51,.06);}
.ctl:hover{background:var(--sf2);}
.call-btn{color:#053b1e;}

@media(prefers-reduced-motion:reduce){
  .avatar,.orb-glow,.orb-halo{animation:none!important;}
}

/* ==========================================================================
   v5 修复：内嵌/矮屏时内容显示不全（绿键被截）
   —— 顶栏吸顶；通话区 flex:1 自适应居中；超高可内部滚动；紧凑间距。
   ========================================================================== */
#stage{height:100dvh; overflow-y:auto; overflow-x:hidden; justify-content:flex-start; gap:10px;
  padding:0 24px calc(14px + env(safe-area-inset-bottom,0px));}
.tb-top{flex:0 0 auto; position:sticky; top:0; z-index:5; padding:16px 2px 12px;
  background:linear-gradient(180deg,#eaeef6 70%,rgba(234,238,246,.7)); backdrop-filter:blur(6px);}
.callcard{margin:0!important; flex:1 1 auto; justify-content:center; min-height:0;}
.avatar-wrap{margin-bottom:12px!important;}
.captions{flex:0 0 auto; margin:2px 0!important;}
.controls{margin:6px 0 8px!important; flex:0 0 auto;}

/* ==========================================================================
   v6 清晰度：去雾、收紧光晕、提高对比、边框更实 —— 让界面更锐利不发糊
   ========================================================================== */
#stage{
  --line:#d6dce8;                 /* 边框略深，边缘更清晰 */
  background:
    radial-gradient(44% 32% at 16% 6%, rgba(59,123,255,.08), transparent 60%),
    radial-gradient(42% 30% at 86% 10%, rgba(123,92,255,.07), transparent 60%),
    #eef1f7;                       /* 底色更干净，减少雾感 */
}
/* 顶栏去掉 backdrop-blur（会让自身文字走合成层、发虚），改纯色 */
.tb-top{backdrop-filter:none; background:#eef1f7;}
/* 语音球：光晕收紧、球体阴影更实，边缘更清楚 */
.orb-glow{filter:blur(9px); opacity:.42; width:140px; height:140px;}
.avatar{box-shadow:0 10px 28px rgba(59,123,255,.30), inset 0 0 28px rgba(255,255,255,.18);}
/* 文字加对比 */
.callee{color:#131a28;}
.call-sub{color:#48546a;}
.tb-brand{color:#131a28;}
/* 分段控件 / 字幕 / 控制键：边框更实、阴影收一点 */
.scene-toggle,.lang-toggle{border-color:#d6dce8; box-shadow:0 1px 2px rgba(27,34,51,.05);}
.captions{border-color:#d6dce8; box-shadow:0 3px 12px rgba(27,34,51,.07);}
.ctl{border-color:#d6dce8;}
.fabs{border-color:#d6dce8;}
/* 绿键：收掉过大的呼吸光环（那圈发散读着像糊），保留轻微脉冲 */
.call-btn{box-shadow:0 10px 26px rgba(18,184,102,.34);}
@keyframes callpulse{0%,100%{box-shadow:0 10px 26px rgba(18,184,102,.34),0 0 0 0 rgba(18,184,102,.3)}
  50%{box-shadow:0 10px 26px rgba(18,184,102,.4),0 0 0 9px rgba(18,184,102,0)}}

/* ==========================================================================
   v7 待机特效（明确可见，且与通话中区分）
   待机：球呼吸 + 光晕脉动 + 一圈慢速外扩；通话中：三圈连续快速光圈（v4 已定义）
   ========================================================================== */
/* 球缓慢呼吸 */
body[data-call="idle"] .avatar{animation:orbbreath 3.4s ease-in-out infinite;}
@keyframes orbbreath{0%,100%{transform:scale(1)}50%{transform:scale(1.055)}}
/* 光晕一边旋转一边明暗脉动 */
body[data-call="idle"] .orb-glow{animation:orbspin 7s linear infinite, orbglowpulse 3.4s ease-in-out infinite;}
@keyframes orbglowpulse{0%,100%{opacity:.32}50%{opacity:.62}}
/* 一圈慢速向外扩散的柔环（待机脉冲，只用 h1，一圈） */
body[data-call="idle"] .orb-halo.h1{opacity:0; animation:orbping 3.4s ease-out infinite;}
body[data-call="idle"] .orb-halo.h2,
body[data-call="idle"] .orb-halo.h3{animation:none; opacity:0;}
@keyframes orbping{
  0%{transform:scale(.82); opacity:.55; border-color:#5b93ff}
  100%{transform:scale(1.55); opacity:0; border-color:#37c7e0}
}

/* 嵌在控制台「AI 培训」弹窗里（?embed=1，app.js 置 html[data-embed]，2026-09-07 user：练习不开新网页）：
   顶栏整条藏掉——弹窗自己有标题和关闭钮；📱🎧 是去别的页面的链接、⚙️📞🐞📦 是管理员工具，练习时都不该出现 */
html[data-embed="1"] .tb-top{display:none;}

/* 麦克风选择 + 音量表（2026-09-08）：拨号页、通话中都显示；说话时图标变绿、音量条跳动 */
.mic-row{display:flex;align-items:center;gap:8px;margin-top:10px;padding:5px 8px 5px 11px;max-width:100%;
  border-radius:999px;background:var(--glass);border:1px solid var(--line);}
.mic-row .mic-ico{width:15px;height:15px;flex:0 0 auto;color:var(--dim);transition:color .12s;}
.mic-row.hot .mic-ico{color:var(--green);}
.mic-row select{flex:1;min-width:0;max-width:220px;font:inherit;font-size:12.5px;color:var(--fg);
  background:transparent;border:none;outline:none;cursor:pointer;text-overflow:ellipsis;}
.mic-row select option{background:var(--panel);color:var(--fg);}
.mic-test{flex:0 0 auto;font:inherit;font-size:11.5px;padding:3px 10px;border-radius:999px;cursor:pointer;
  border:1px solid var(--line);background:transparent;color:var(--dim);transition:background .12s,color .12s;}
.mic-test:hover{color:var(--fg);background:var(--glass);}
.mic-meter{flex:0 0 auto;width:64px;height:6px;border-radius:3px;background:rgba(127,134,152,.25);overflow:hidden;}
.mic-meter i{display:block;height:100%;width:0;border-radius:3px;
  background:linear-gradient(90deg,#34c06f,#f5b942 70%,#e5484d);transition:width .08s linear;}

/* ==========================================================================
   大屏适配（2026-09-12）：≥1600×900 语音球 / 名字 / 字幕按比例放大，别在 1080p 上显得孤零零；
   窄屏和内嵌培训弹窗（460 宽）不受影响。
   ========================================================================== */
@media (min-width:1600px) and (min-height:900px){
  .avatar-wrap{width:230px; height:230px;}
  .avatar{width:158px; height:158px;}
  .orb-glow{width:170px; height:170px;}
  .orb-halo{width:182px; height:182px;}
  .callee{font-size:28px;}
  .captions{width:min(560px,100%);}
  .cap{font-size:14.5px;}
}

/* 每句话的时间（2026-09-22）：hh:mm:ss，小字等宽，在气泡内第一行 */
.msg .mt { display: block; font-size: 10.5px; line-height: 1.2; margin-bottom: 3px; opacity: .6;
  font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.msg.user .mt { text-align: right; }
.msg.sys .mt { display: inline; margin: 0 6px 0 0; }

/* 模型临时切换（2026-09-22）：外观同麦克风行；选中 gpt-live-1 时描一道紫边提示"这通走 Live" */
.model-row{display:flex;align-items:center;gap:8px;margin-top:10px;padding:5px 11px;max-width:100%;
  border:1px solid var(--line);border-radius:999px;background:var(--glass);font-size:12.5px;color:var(--dim);}
.model-row .model-lbl{flex:0 0 auto;}
.model-row select{flex:1;min-width:0;max-width:260px;font:inherit;font-size:12.5px;color:var(--fg);
  background:transparent;border:0;outline:0;cursor:pointer;}
.model-row select option{background:var(--panel);color:var(--fg);}
.model-row.live{border-color:rgba(150,110,255,.65);box-shadow:0 0 0 3px rgba(150,110,255,.14);}
body:not([data-call="idle"]) .model-row{display:none;}
html[data-embed] .model-row{display:none;}

/* 系统标记（2026-09-29）：拟人强化推送（琥珀）/ 连接断开（红），和对话气泡区分开 */
.msg.sys.boost { color: #f5b041; }
.msg.sys.lost { color: var(--red); font-weight: 600; }

/* ===== 测试分享（2026-10-03） ===== */
#shareFab.on { color: var(--green); box-shadow: 0 0 0 2px rgba(52, 209, 107, .35); }
.share-body { display: flex; flex-direction: column; gap: 14px; }
.share-body .sh-title { font-size: 16px; font-weight: 700; }
.share-body .sh-row { display: flex; align-items: center; gap: 10px; font-size: 14px; }
.share-body .sh-row > label { flex: 0 0 72px; color: var(--dim); font-size: 13px; }
.share-body .sh-row.col { flex-direction: column; align-items: stretch; gap: 8px; }
.share-body .sh-row.col > label { flex: none; }
.share-body .sh-opt { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--fg); cursor: pointer; }
.share-body input[type=text], .share-body input[type=number], .share-body input[type=datetime-local], .share-body .sh-copy input {
  background: var(--panel); border: 1px solid var(--line); color: var(--fg); border-radius: 10px; padding: 8px 10px; font-size: 14px; }
.share-body input[type=number] { width: 72px; }
.share-body .sh-copy { display: flex; gap: 8px; flex: 1; min-width: 0; }
.share-body .sh-copy input { flex: 1; min-width: 0; }
.share-body .sh-note { color: var(--dim); font-size: 12.5px; line-height: 1.6; }
.share-body .sh-main { width: 100%; padding: 12px; margin-top: 4px; }
.btn.danger { background: rgba(255, 93, 85, .14); color: var(--red); border: 1px solid rgba(255, 93, 85, .35); }
/* 接收人：只留拨号界面，设置/模型/电话线路/调试/外链都藏掉 */
html[data-share] .fabs a, html[data-share] #settingsBtn, html[data-share] #phoneFab, html[data-share] #shopFab,
html[data-share] #debugBtn, html[data-share] #shareFab, html[data-share] #modelRow { display: none !important; }

/* 测试转人工（2026-10-04）：人工客服的话（蓝）+ 转接失败（红）/ 成功、人工挂断（绿）标记 */
.msg.human { align-self: flex-start; background: rgba(64, 140, 255, .14); border: 1px solid rgba(64, 140, 255, .45); border-bottom-left-radius: 4px; }
.msg.sys.xfer { color: var(--red); font-weight: 600; white-space: normal; text-align: center; max-width: 92%; }
.msg.sys.xferok { color: var(--green, #34d16b); font-weight: 600; }
