/* VoiceCopy 文本配音 · 样式(手机优先,≥721px 应用 PC 增强) */
:root {
  --bg: #f3f5f9;
  --card: #ffffff;
  --ink: #1a2233;
  --muted: #7b8598;
  --line: #e6e9f0;
  --accent: #4c6ef5;
  --accent-2: #7048e8;
  --accent-dark: #3b5bdb;
  --ok: #2f9e44;
  --bad: #e03131;
  --radius: 16px;
  --r-btn: 12px;        /* 主按钮 */
  --r-btn-sm: 10px;     /* 次级/图标按钮,统一后不再出现 9/10/12 混用 */
  --shadow-1: 0 1px 2px rgba(23, 32, 60, .05), 0 1px 6px rgba(23, 32, 60, .04);
  --shadow-2: 0 8px 24px rgba(23, 32, 60, .10);
  --ring: 0 0 0 3px rgba(76, 110, 245, .15);

  /* 表面色:输入框/音色卡/历史条目这些"卡片里的卡片" */
  --surface: #fafbfe;      /* 默认底 */
  --surface-2: #f6f8ff;    /* 略带色的底,如工具条 */
  --surface-sel: #eef2ff;  /* 选中态 */
  --elevated: #ffffff;     /* 浮起来的:按钮、徽章、聚焦后的输入框 */
  --line-hover: #b6c2ff;
  --accent-soft: #b9c6fa;  /* 主按钮禁用态 */
  --bad-soft: #f0a8a8;
  --glow: #e8edff;         /* 页面顶部的光晕 */
  --topbar-bg: rgba(255, 255, 255, .85);
  --mask: rgba(15, 20, 35, .5);
  --on-accent: #fff;
  /* 让原生控件(音频播放器、下拉框、复选框、滚动条)跟着主题走,
     否则深色模式下会突兀地保持亮色 */
  color-scheme: light dark;
}

/* 深色模式:只改 token,不动任何布局。手机上晚上用差别很大 */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1420;
    --card: #171d2b;
    --ink: #e6eaf2;
    --muted: #98a3b8;
    --line: #2a3243;
    --accent: #7d97ff;
    --accent-2: #9d7bff;
    --accent-dark: #a8bbff;   /* 深色模式下 hover 应该更亮而不是更深 */
    --ok: #51cf66;
    --bad: #ff6b6b;
    --surface: #1c2331;
    --surface-2: #202a3c;
    --surface-sel: #24304a;
    --elevated: #202838;
    --line-hover: #3f5187;
    --accent-soft: #3b4770;
    --bad-soft: #7a3b3b;
    --glow: #1a2340;
    --topbar-bg: rgba(23, 29, 43, .85);
    --mask: rgba(5, 8, 15, .65);
    --shadow-1: 0 1px 2px rgba(0, 0, 0, .3), 0 1px 6px rgba(0, 0, 0, .25);
    --shadow-2: 0 8px 24px rgba(0, 0, 0, .45);
    --ring: 0 0 0 3px rgba(125, 151, 255, .22);
  }
}

* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }

html { -webkit-text-size-adjust: 100%; }

/* 弹窗打开时锁定背景滚动(iOS 用 position:fixed 方案,由 JS 记录/恢复位置) */
body.modal-open {
  position: fixed; left: 0; right: 0; width: 100%;
  overflow: hidden;
}

body {
  font-family: -apple-system, "PingFang SC", "HarmonyOS Sans SC",
    "Microsoft YaHei", "Segoe UI", Roboto, sans-serif;
  background:
    radial-gradient(1200px 400px at 50% -200px, var(--glow) 0%, transparent 70%),
    var(--bg);
  color: var(--ink);
  min-height: 100vh;
}

/* 输入控件的光标与选区:显式给色,避免默认渲染(尤其深色模式/部分内核
   与中文输入法组合态)把光标画成刺眼的白块、盖住候选词 */
textarea, input, select {
  caret-color: var(--accent);
}
textarea::selection, input::selection {
  background: var(--surface-sel);
}

/* ================= 顶栏 ================= */
.topbar {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 16px;
  background: var(--topbar-bg);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.brand {
  font-size: 17px; font-weight: 800; letter-spacing: .2px;
  white-space: nowrap;
}
.brand span {
  color: var(--accent); font-weight: 600; font-size: 13px;
  margin-left: 2px;
}
.badge {
  font-size: 12px; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 11px;
  background: var(--elevated);
  max-width: 46vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.wrap {
  max-width: 780px; margin: 0 auto;
  padding: 14px 14px calc(32px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 14px;
}

/* ================= 卡片 ================= */
.card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 15px 14px;
  box-shadow: var(--shadow-1);
}
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px; gap: 8px;
}
h2 { font-size: 15.5px; font-weight: 700; }
.muted { color: var(--muted); }
.small { font-size: 12.5px; }
.hidden { display: none !important; }

/* ================= 音色选择 ================= */
.voice-grid {
  display: flex; flex-direction: column; gap: 6px;
  /* **只有手机端是列表**:内容全部展开在页面流里,不在列表内滚动——
     iOS 触摸屏没有可拖的滚动条,内嵌滚动会让后面的音色"够不着";
     全部展开后页面整体滚动,任何设备都能看到全部音色。
     桌面端(min-width:721px)横向还原成卡片网格。 */
}
.voice-item {
  display: flex; align-items: center; gap: 8px;
  border: 1.5px solid var(--line); border-radius: 10px;
  padding: 8px 10px; cursor: pointer; user-select: none;
  background: var(--surface);
  transition: border-color .15s, background .15s;
  min-height: 44px;                  /* 62 是旧卡片的高度,行内布局用不上;
                                        44 是保证手指点得准的下限 */
}
/* 选中标记。原来是右上角绝对定位的 ✓,行内布局会和按钮撞在一起,
   改成行首一个圆点:没选中时是个空圈,占位不变,不会有跳动 */
.voice-item .vcheck {
  flex: none; width: 17px; height: 17px; border-radius: 50%;
  border: 1.5px solid var(--line);
  font-size: 10px; font-weight: 700; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  background: transparent; color: transparent;
}
.voice-item.sel .vcheck {
  background: var(--accent); color: var(--on-accent);
  border-color: var(--accent);
}
.voice-item .vname {
  flex: 1; min-width: 0;              /* 名字最长,让它先被压缩 */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 13.5px; font-weight: 600;
}
.voice-item .vtag { flex: none; font-size: 11.5px; color: var(--muted); }
.voice-item.sel {
  border-color: var(--accent); background: var(--surface-sel);
}
.hint { color: var(--muted); font-size: 13px; line-height: 1.6; }

/* ================= 文本与控制 ================= */
textarea {
  width: 100%; resize: vertical; min-height: 130px;
  border: 1.5px solid var(--line); border-radius: 12px;
  padding: 12px 14px; font-size: 16px; line-height: 1.7;
  font-family: inherit; outline: none; background: var(--surface); color: var(--ink);
  transition: border-color .15s, background .15s, box-shadow .15s;
}
textarea:focus {
  border-color: var(--accent); background: var(--elevated); box-shadow: var(--ring);
}

.ctrl-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  margin-top: 12px;
}
/* 合成前校验提示独占一行 */
.synth-msg { flex-basis: 100%; }
/* 手机端行序:语音输入 + 开始合成并排,语速(和显示时的模型)整行落在下面 */
#btn-stt { order: 1; }
#btn-synth { order: 2; flex: 0 0 auto; min-width: 0; }
.speed-label { order: 3; flex-basis: 100%; }
.model-label { order: 4; flex-basis: 100%; }
/* 工具行(提取音频 + 新建克隆音色):独立 div,与主操作行相邻,两按钮并排 */
#tool-row { gap: 10px; }
#tool-row button { flex: 1; min-width: 0; }
/* 语音输入按钮:与开始合成同行、居左,宽度取主按钮(桌面 168px)的一半。
   按住说话:touch-action/user-select 关掉,避免长按触发系统菜单或文本选择 */
#btn-stt { width: 84px; padding: 0 8px; font-size: 12.5px;
           touch-action: none; user-select: none; -webkit-user-select: none;
           overflow: hidden; }   /* 窄按钮防文字溢出(审计 T1) */
#btn-stt.recording {
  border-color: var(--bad); color: var(--bad);
  background: rgba(217, 48, 48, .08);
}
/* 开始合成与语音输入并排时同高:在 ghost 基准 36px 上加一点点到 42px,
   宽度按内容自适应(flex 0 0 auto,见上方手机行序)。
   width:auto 必须显式写:primary-btn 基础样式是 width:100%,flex:0 0 auto
   的 basis 会回落成 auto 并吃掉它,把按钮撑满整行挤到下一行 */
#btn-stt { min-height: 42px; }
/* margin-left:auto 把开始合成推到行尾右对齐,语音输入保持在左。
   (桌面端语速条 flex:1 已吃掉中间空间,不受影响) */
#btn-synth { min-height: 42px; padding: 0 20px; width: auto; margin-left: auto; }
/* 合成前的校验提示。手机上 .ctrl-row 是纵向的,它天然就落在"开始合成"正上方 */
.synth-msg {
  border: 1px solid var(--bad);
  background: rgba(217, 48, 48, .08);
  color: var(--bad);
  border-radius: 10px; padding: 9px 12px;
  font-size: 13px; text-align: center; line-height: 1.5;
}
.speed-label {
  display: flex; align-items: center; gap: 10px; font-size: 13.5px;
  color: var(--muted);
}
.speed-label input[type=range] {
  flex: 1; max-width: none; accent-color: var(--accent); height: 28px;
}
.speed-label b { color: var(--ink); min-width: 44px; text-align: right; }

/* ---- 按钮体系 ----
   统一三件事:圆角只用 --r-btn、控件高度只有 36/48 两档、按下都是同一个缩放。
   中文按钮不加 letter-spacing —— 汉字本身有字距,再拉开就散了。 */
button, .ghost-btn, .icon-btn, .chip {
  font-family: inherit;
}
button.primary-btn {
  width: 100%; min-height: 48px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: var(--on-accent); border: none; border-radius: var(--r-btn);
  padding: 12px 26px; font-size: 16px; font-weight: 600;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(76, 110, 245, .28);
  transition: filter .15s, transform .12s, box-shadow .15s;
}
button.primary-btn:hover {
  filter: brightness(1.05);
  box-shadow: 0 4px 14px rgba(76, 110, 245, .34);
}
button.primary-btn:active { transform: scale(.97); box-shadow: none; }
button.primary-btn:disabled {
  background: var(--accent-soft); box-shadow: none;
  cursor: not-allowed; transform: none; filter: none;
}

/* 次级按钮:浅底 + 细边,hover 才染上主色,不跟主按钮抢视线 */
button.ghost-btn, a.ghost-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  min-height: 36px; padding: 0 14px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-btn-sm);
  font-size: 13px; font-weight: 500; color: var(--ink);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background .15s, border-color .15s, color .15s, transform .12s;
}
button.ghost-btn:hover, a.ghost-btn:hover {
  border-color: var(--accent); color: var(--accent);
  background: var(--surface-sel);
}
button.ghost-btn:active, a.ghost-btn:active { transform: scale(.97); }
button.ghost-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

/* 键盘可见焦点:鼠标点击不出现,Tab 过来才有 */
button:focus-visible, a:focus-visible, select:focus-visible,
input:focus-visible, textarea:focus-visible {
  outline: none; box-shadow: var(--ring);
}

.progress-box { margin-top: 14px; }
.progress-track {
  height: 8px; background: var(--line); border-radius: 999px; overflow: hidden;
}
#progress-bar {
  height: 100%; width: 0; border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width .4s ease;
}
#progress-stage { margin-top: 7px; }

/* ================= 结果 ================= */
audio { width: 100%; margin-top: 2px; }
audio:focus { outline: none; }

/* ================= 历史 ================= */
.history {
  list-style: none; display: flex; flex-direction: column; gap: 8px;
  /* 历史会越攒越多:限高内滚,靠滚动条/触摸滚动列表本身(与音色列表
     的展开式相反——历史默认就该是个有界列表) */
  max-height: min(55vh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;      /* 滚到头不把整页一起带着滚 */
  -webkit-overflow-scrolling: touch;
  padding-right: 4px;                /* 给滚动条让位 */
}
.history::-webkit-scrollbar { width: 6px; }
.history::-webkit-scrollbar-thumb {
  background: var(--line); border-radius: 3px;
}
.history li {
  display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px;
  background: var(--surface);
  transition: border-color .15s;
}
.history li .t-info { flex: 1; min-width: 0; }
.history li.play .t-info { cursor: pointer; }
.history li.play:hover { border-color: var(--line-hover); }
.history li .t-sum {
  font-size: 13.5px; font-weight: 500; line-height: 1.45;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.history li .t-meta {
  margin-top: 2px; font-size: 11.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.history .st-done { color: var(--ok); }
.history .st-failed { color: var(--bad); }
.history .st-run { color: var(--accent); }
.icon-btn {
  flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; min-height: 36px;
  border: 1px solid var(--line); background: var(--elevated); color: var(--ink);
  border-radius: var(--r-btn-sm); padding: 0 9px; font-size: 14px;
  cursor: pointer; text-decoration: none;
  transition: background .15s, border-color .15s, color .15s, transform .12s;
}
.icon-btn:hover {
  border-color: var(--accent); color: var(--accent); background: var(--surface-sel);
}
.icon-btn:active { transform: scale(.94); }
.icon-btn.del:hover {
  border-color: var(--bad); color: var(--bad); background: transparent;
}
.icon-btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

.footer { text-align: center; margin-top: 6px; line-height: 1.7; padding: 0 8px; }

/* ================= 弹窗 ================= */
.modal-mask {
  position: fixed; inset: 0; z-index: 50;
  background: var(--mask);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  display: flex; align-items: flex-end; justify-content: center;
  touch-action: none; /* 蒙层自身不响应滚动 */
}
.modal {
  width: 100%; max-height: 92vh; overflow: auto;
  border-radius: 18px 18px 0 0; border-bottom: none;
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
  animation: sheet-up .22s ease;
  touch-action: pan-y;          /* 允许弹窗内容纵向滚动 */
  overscroll-behavior: contain; /* 滚到头不传导给背景 */
  -webkit-overflow-scrolling: touch;
}
@keyframes sheet-up { from { transform: translateY(30px); opacity: .6; } }
.field-label { display: block; font-size: 13px; margin: 12px 0 6px; font-weight: 600; }
.modal input[type=text], .modal input[type=password], .modal input[type=file] {
  width: 100%; border: 1.5px solid var(--line); border-radius: 10px;
  padding: 11px 12px; font-size: 16px; background: var(--surface); color: var(--ink);
  outline: none;
}
.modal input:focus { border-color: var(--accent); background: var(--elevated); }
.consent {
  display: flex; gap: 8px; align-items: flex-start;
  font-size: 12.5px; color: var(--muted); margin: 14px 0; line-height: 1.55;
}
.consent input { margin-top: 2px; flex-shrink: 0; }
.modal .primary-btn { margin-top: 4px; }
#clone-progress { margin-top: 10px; text-align: center; min-height: 18px; }

.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(28px + env(safe-area-inset-bottom));
  z-index: 99; background: #21262d; color: #fff;
  padding: 11px 19px; border-radius: 999px; font-size: 13.5px;
  max-width: 88vw; text-align: center; box-shadow: 0 6px 20px rgba(0,0,0,.3);
}
/* 贴在**可视区**底部。只写 bottom 是不够的:手机上地址栏展开时布局视口比
   可视区高,bottom 定位的元素会被推到屏幕外,要往下滚才看得见。
   dvh 跟着可视高度走,用 top + translateY(-100%) 把它拉回来。 */
@supports (height: 100dvh) {
  .toast {
    top: calc(100dvh - 28px - env(safe-area-inset-bottom));
    bottom: auto; transform: translate(-50%, -100%);
  }
}

/* ================= 现场录音(内嵌克隆弹窗) ================= */
.rec-inline {
  display: flex; align-items: center; gap: 14px;
  background: linear-gradient(135deg, #eef2ff, #f6f2ff);
  border: 1px solid #dfe5ff; border-radius: 12px;
  padding: 13px 14px; margin-top: 14px;
}
.rec-round-sm {
  flex-shrink: 0; width: 52px; height: 52px; border-radius: 50%;
  border: none; cursor: pointer;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  box-shadow: 0 4px 12px rgba(76, 110, 245, .35);
  position: relative; transition: transform .12s, filter .15s;
}
.rec-round-sm::after {
  content: ""; position: absolute; inset: 17px; border-radius: 50%;
  background: var(--elevated); transition: inset .15s, border-radius .15s;
}
.rec-round-sm.recording::after {
  inset: 20px; border-radius: 4px;
  animation: rec-pulse 1.1s ease-in-out infinite;
}
.rec-round-sm:active { transform: scale(.93); }
.rec-round-sm:hover { filter: brightness(1.07); }
@keyframes rec-pulse {
  0%, 100% { opacity: 1; } 50% { opacity: .4; }
}
.rec-inline-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.rec-line1 { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.rec-timer-sm {
  font-size: 19px; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.rec-note {
  color: #8a5a1d; background: #fff8f0; border: 1px solid #ffe3c2;
  border-radius: 8px; padding: 6px 9px; line-height: 1.6; font-size: 12px;
  margin-top: 3px;
}
#rec-auto-status { margin-top: 8px; color: var(--ok); font-weight: 600; }
#rec-player { margin-top: 8px; }

/* ================= PC 增强(≥721px)================= */
@media (min-width: 721px) {
  .topbar { padding: 14px 28px; }
  .brand { font-size: 19px; }
  .brand span { font-size: 14px; }

  .wrap { padding: 22px 24px 48px; gap: 16px; }

  .card { padding: 22px 26px; border-radius: var(--radius); }
  .card { transition: box-shadow .2s ease; }
  .card:hover { box-shadow: 0 4px 18px rgba(23, 32, 60, .08); }
  h2 { font-size: 16.5px; }

  /* 桌面端还原成卡片网格:横向排得下,不需要封高滚动,信息也能多显一行 */
  .voice-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
    gap: 12px; max-height: none; overflow: visible; padding-right: 0;
  }
  .hint { grid-column: 1/-1; }
  .voice-item {
    position: relative; overflow: hidden;
    flex-direction: column; align-items: stretch; gap: 3px;
    padding: 11px 12px; border-radius: 12px; min-height: 62px;
    transition: border-color .15s, background .15s, transform .15s, box-shadow .15s;
  }
  .voice-item .vname {
    white-space: normal; overflow: visible; text-overflow: clip;
    line-height: 1.35; padding-right: 14px; flex: none;
  }
  .voice-item .vtag { display: block; }
  .voice-item .vbtns { margin-top: 6px; }
  /* 卡片布局下选中标记回到右上角,行首那个圆点收起来 */
  .voice-item .vcheck { display: none; }
  .voice-item.sel { box-shadow: var(--ring); }
  .voice-item.sel::after {
    content: "✓"; position: absolute; top: 6px; right: 8px;
    width: 18px; height: 18px; border-radius: 50%;
    background: var(--accent); color: var(--on-accent);
    font-size: 11px; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
  }
  .voice-item:hover {
    border-color: var(--line-hover); transform: translateY(-2px);
    box-shadow: var(--shadow-2);
  }
  .voice-item.sel:hover { border-color: var(--accent); }

  textarea { min-height: 150px; font-size: 15px; }
  textarea:focus { box-shadow: 0 0 0 4px rgba(76, 110, 245, .10); }

  /* 桌面端这一行是横排的,提示要 order:-1 + 占满一行,才会独占一行落在按钮上方 */
  .ctrl-row { flex-direction: row; align-items: center; flex-wrap: wrap; }
  .synth-msg { flex-basis: 100%; order: -1; }
  /* 桌面端取消手机端的并排/换行排序,回到 DOM 顺序 */
  #btn-stt { order: 0; }
  #btn-synth { order: 0; flex: none; }
  .speed-label { flex: 1; order: 0; flex-basis: auto; justify-content: center; }
  .model-label { order: 0; flex-basis: auto; }
  .speed-label input[type=range] { max-width: 260px; }
  /* 开始合成比通用主按钮窄一点(128 vs 168),高度保持 42px 与语音输入一致 */
  #btn-synth { min-width: 128px; }
  button.primary-btn { width: auto; min-width: 168px; min-height: 46px; font-size: 15px; }

  .history li { padding: 11px 14px; gap: 10px; }
  .icon-btn { min-height: 32px; }

  .modal-mask { align-items: center; padding: 18px; }
  .modal {
    width: min(480px, 100%); border-radius: var(--radius);
    border: 1px solid var(--line); border-bottom: 1px solid var(--line);
    padding-bottom: 0; animation: pop .18s ease;
    box-shadow: var(--shadow-2);
  }
  @keyframes pop { from { transform: scale(.96); opacity: .5; } }
}

/* 大屏稍微放宽 */
@media (min-width: 1100px) {
  .wrap { max-width: 860px; }
}

/* 动画偏好 */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------- 登录态 / AI 配额 ---------- */
.top-right { display: flex; align-items: center; gap: 8px; min-width: 0; }
.userbox {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--muted); min-width: 0;
  /* 放不下时徽章/按钮换到下一行,而不是挤用户名 */
  flex-wrap: wrap; justify-content: flex-end;
}
.userbox .uname {
  font-weight: 600; color: var(--ink);
  /* 吃满剩余空间,超宽才省略号;min-width:0 是 flex 里 ellipsis 生效的前提 */
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.userbox .uquota {
  border: 1px solid var(--line); border-radius: 999px; padding: 3px 9px;
  background: var(--elevated); white-space: nowrap; flex: 0 0 auto;
}
.userbox .uquota.low { color: var(--bad); border-color: var(--bad); }
.userbox .ghost-btn { min-height: 30px; padding: 0 10px; font-size: 12px; }

/* 只有零成本的音色会带这个标(Edge 走微软免费接口,没有计费身份) */
.voice-item .vcost { flex: none; font-size: 11px; }
/* 窄屏优先保名字:标签是"预设/系统音色"这类可推断的信息,先让它 */
@media (max-width: 380px) {
  .voice-item .vtag { display: none; }
}
.voice-item .vcost.free { color: var(--ok); font-weight: 600; }

/* 登录框是"拦路"弹窗,不是操作面板:居中的卡片比贴底的 sheet 合适 */
#login-mask {
  align-items: center;
  justify-content: center;      /* 不靠 .modal-mask 继承,自己写死两个轴 */
  padding: 20px;
}
/* 手机上地址栏收起/展开会改变可视高度,vh 参照的是最大高度,居中的卡片会被顶下去。
   dvh 跟着实际可视区走。不支持的浏览器退回 inset:0 的老行为,不会更差。 */
@supports (height: 100dvh) {
  #login-mask { height: 100dvh; }
}
.login-card {
  max-width: 360px;
  border-radius: 16px;          /* 四角都圆,不再是上圆下方的 sheet */
  border-bottom: 1px solid var(--line);
  padding: 20px 20px 18px;
  margin: auto;                 /* flex 容器里 margin:auto 两个轴都居中,比只靠 align-items 稳 */
  align-self: center;           /* 双保险:万一 margin 被别处覆盖还有它兜着 */
  animation: none;              /* sheet-up 是从下往上推,居中的卡片不该有 */
}
/* .card-head 是 space-between,只有标题一个子元素时会被推到最左,
   而下面的按钮、提示、切换链接全是居中的,看着就像整个框歪了 */
.login-card .card-head { justify-content: center; }
.login-card h2 { text-align: center; }
/* 登录按钮不用通栏:紧凑一些、水平居中,更像个登录框 */
.login-card .primary-btn {
  width: auto; min-width: 170px;
  display: block; margin: 18px auto 0;
  min-height: 42px; padding: 10px 28px; font-size: 15px;
  letter-spacing: .5px;
}
.login-card .field-label { margin: 10px 0 5px; }
.login-msg { min-height: 18px; margin-top: 8px; color: var(--bad); text-align: center; }

/* 确认框:和登录框一样是"拦路"弹窗,居中的小卡片比贴底 sheet 合适。
   两个轴都写死,不靠 .modal-mask 的 flex-end 继承 */
#confirm-mask {
  align-items: center;
  justify-content: center;
  padding: 20px;
}
@supports (height: 100dvh) {          /* 同登录框:地址栏伸缩时不被顶下去 */
  #confirm-mask { height: 100dvh; }
}
.confirm-card {
  max-width: 340px;
  border-radius: 16px;
  border-bottom: 1px solid var(--line);
  padding: 18px 18px 16px;
  margin: auto;
  align-self: center;
  animation: none;                    /* sheet-up 是从下往上推,居中卡片不该有 */
}
.confirm-card .card-head { margin-bottom: 10px; }
.confirm-body {
  font-size: 13.5px; line-height: 1.65; color: var(--muted);
  overflow-wrap: anywhere;            /* 音色名可能是一长串没有空格的字符 */
}
/* 需要填字时才出现(重命名)。字号 16px 是硬要求:iOS 上小于 16px 的
   输入框获得焦点会把整页放大,弹窗就跑出视口了 */
.confirm-input {
  width: 100%; margin-top: 14px;
  border: 1.5px solid var(--line); border-radius: 10px;
  padding: 11px 12px; font-size: 16px;
  background: var(--surface); color: var(--ink); outline: none;
}
.confirm-input:focus { border-color: var(--accent); background: var(--elevated); }
/* 已载入的参考音频提示。绿色是"已就绪"的意思,和下面的授权勾选区分开 */
.sample-note {
  margin-top: 8px; padding: 8px 10px;
  border: 1px solid var(--ok); border-radius: 8px;
  background: rgba(47, 158, 68, .08);
  font-size: 12.5px; line-height: 1.55; color: var(--ink);
  overflow-wrap: anywhere;          /* 文件名可能很长且没有空格 */
}
.confirm-btns { display: flex; gap: 10px; margin-top: 18px; }
/* 两个按钮等宽:确定键不该比取消键更大更好按,删除类操作尤其。
   选择器带 .modal 是为了压过 `.modal .primary-btn { margin-top: 4px }` */
.modal .confirm-btns button {
  flex: 1 1 0; width: auto; min-width: 0;
  min-height: 44px; margin-top: 0; font-size: 15px;
}

/* 模型选择(仅在该服务商有多个模型时出现) */
.model-label {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--muted);
}
.model-label select {
  font-size: 13px; padding: 5px 8px; max-width: 46vw;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--elevated); color: var(--ink);
}

/* 风格指令(free-style instruct) */
.instruct-box {
  margin-top: 10px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
}
.instruct-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; font-size: 13px; margin-bottom: 6px;
}
.instruct-box input[type="text"] {
  width: 100%; padding: 8px 10px; font-size: 14px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--elevated);
}
.instruct-box input[type="text"]:focus {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}
#instruct-count.over { color: var(--bad); font-weight: 600; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.chip {
  font-size: 12px; padding: 5px 12px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--elevated); color: var(--muted);
  transition: background .15s, border-color .15s, color .15s, transform .12s;
}
.chip:hover {
  border-color: var(--accent); color: var(--accent); background: var(--surface-sel);
}
.chip:active { transform: scale(.95); }

.login-switch { margin-top: 10px; text-align: center; }
.login-switch a { color: var(--accent); cursor: pointer; margin-left: 4px; }

/* 获取邀请码:展开微信二维码 */
.invite-qr {
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--line);
  text-align: center;
}
.invite-qr .qr-img {
  width: 100%; max-width: 210px;
  /* 原图底部有一行"扫一扫上面的二维码图案,加我为朋友",和下面自己写的说明重复;
     用 cover + 顶部对齐把它裁掉,二维码本身完整保留 */
  aspect-ratio: 950 / 1100;
  object-fit: cover; object-position: top;
  border-radius: 12px;
  /* 二维码必须是浅底才扫得出来,深色主题下也得给它铺白 */
  background: #fff; padding: 6px;
  border: 1px solid var(--line);
}
.invite-qr .qr-tip { margin-top: 8px; line-height: 1.5; }
.invite-qr .qr-goto {
  display: inline-block; margin-top: 10px;
  color: var(--accent); cursor: pointer; font-size: 13px;
}

/* 添加到主屏幕 */
#btn-install { min-height: 30px; padding: 0 10px; font-size: 12px; }
.top-right { flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 400px) { .badge { max-width: 38vw; } }

/* 历史记录多选 */
.head-btns { display: flex; gap: 6px; }
.hist-actions {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 10px; margin-bottom: 8px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2);
}
.hist-actions .pick-all { display: flex; align-items: center; gap: 5px; font-size: 13px; }
.hist-actions #hist-count { margin-right: auto; }
.history li.picking { cursor: pointer; }
.history li.picking:hover { background: var(--surface-2); }
.history input.pick { width: 18px; height: 18px; flex-shrink: 0; margin-right: 2px; }
button.danger-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 36px; padding: 0 16px;
  background: var(--bad); color: var(--on-accent); border: none;
  border-radius: var(--r-btn-sm);
  font-size: 13px; font-weight: 600; cursor: pointer;
  transition: filter .15s, transform .12s;
}
button.danger-btn:hover { filter: brightness(1.08); }
button.danger-btn:active { transform: scale(.97); }
button.danger-btn:disabled {
  background: var(--bad-soft); cursor: not-allowed; transform: none; filter: none;
}

/* 音色卡片上的试听/改名/删除按钮 */
.voice-item .vbtns {
  flex: none; display: flex; gap: 6px; align-items: center;
}
.voice-item .vprev, .voice-item .vren, .voice-item .vdel {
  border: 1px solid var(--line); background: var(--elevated);
  border-radius: 999px; padding: 3px 10px; font-size: 11.5px;
  cursor: pointer; line-height: 1.6; margin-top: 0;
  transition: background .15s, border-color .15s, color .15s;
}
.voice-item .vprev { color: var(--accent); }
.voice-item .vprev:hover { background: var(--surface-sel); border-color: var(--accent); }
.voice-item .vprev:disabled { color: var(--muted); cursor: default; }
.voice-item .vren { color: var(--muted); }
.voice-item .vren:hover { border-color: var(--accent); color: var(--accent); }
.voice-item .vdel { color: var(--bad); }
.voice-item .vdel:hover { border-color: var(--bad); }

/* 合成结果卡片上的下载/分享 */
#result-card .head-btns a.ghost-btn { display: inline-flex; align-items: center; }

/* 新建克隆音色:浅色底 + 主色描边与文字。比普通次级按钮显眼,
   又不会像实底那样跟 48px 的「开始合成」抢主次。
   两种配色都走 token,深色模式下自动是深蓝底 + 亮蓝字。 */
#btn-add-voice {
  background: var(--surface-sel);
  border-color: var(--accent);
  color: var(--accent); font-weight: 600;
  padding: 0 16px;
}
#btn-add-voice:hover {
  background: var(--surface-sel);
  border-color: var(--accent-dark);
  color: var(--accent-dark);
}
#btn-add-voice:disabled { opacity: .5; }

/* 未勾选授权就点创建时,抖一下把注意力引过去 */
.consent.shake { animation: consent-shake .5s; border-radius: 8px; }
@keyframes consent-shake {
  0%, 100% { transform: translateX(0); background: transparent; }
  20% { transform: translateX(-5px); background: rgba(224, 49, 49, .08); }
  40% { transform: translateX(5px); background: rgba(224, 49, 49, .08); }
  60% { transform: translateX(-3px); background: rgba(224, 49, 49, .08); }
  80% { transform: translateX(3px); background: rgba(224, 49, 49, .08); }
}
@media (prefers-reduced-motion: reduce) {
  .consent.shake { animation: none; background: rgba(224, 49, 49, .1); }
}
