/* 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; }
/* 坐席台：顶部显示"我是谁 + 坐席池状态"（多坐席改造） */
.who-txt{font-size:12px;opacity:.85;margin-right:8px}
