/* ==========================================================================
   微信聊天模拟器 —— 样式表（Fluent Design 重构版）
   --------------------------------------------------------------------------
   两套视觉语言，别混：

   ① 外层页面（顶栏 / 参数条 / 设置 / 说明 / FAQ / 弹窗）= Fluent Design：
      亚克力顶栏 + Mica 页面 + Layer 卡片（8px 圆角、分层投影）、Fluent 2 字号阶梯、
      下划线式输入框、分组的设置卡片、指针 Reveal 高光。
      颜色 / 圆角 / 投影全部收在下面 :root 的 token 里，亮暗两套（跟随系统）。

   ② 预览区（.wechat-wrapper 及其子树）= 复刻微信，必须像素级保真：
      颜色写死、圆角按素材算，**不参与 Fluent 重构**，不受上面那套 token 影响。
      整段原样保留在「预览区（微信复刻）」标题下，改之前先确认预览基线（外壳 450×922.5）。

   ⚠️ app.js 会间接依赖这几个旧 token 名：--green / --red / --ink-3 / --sunken / --r-sm
      （uploadIcon() 把 var(--r-sm)/var(--sunken)/var(--ink-3) 写进了行内样式），
      它们作为兼容别名保留在 token 区，改名或删掉会让「上传」占位块样式失效。
   ========================================================================== */

/* ==========================================================================
   Fluent Design token（亮色 = Windows 11 Light / Fluent 2 Web Light）
   ========================================================================== */
:root {
  /* —— 字体：Fluent 用 Segoe UI Variable；中文回落 微软雅黑 / 苹方。
        全程只用系统字体（本项目完全离线，不引外部字体）。
        等宽用 Cascadia（Windows Terminal 自带），正好配脚本编辑区。 —— */
  --font-ui: "Segoe UI Variable Text", "Segoe UI", -apple-system, BlinkMacSystemFont,
             "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei",
             "Noto Sans SC", "Helvetica Neue", Arial, sans-serif;
  --font-display: "Segoe UI Variable Display", "Segoe UI", -apple-system, BlinkMacSystemFont,
             "PingFang SC", "Microsoft YaHei UI", "Microsoft YaHei", "Noto Sans SC",
             "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Cascadia Mono", "Cascadia Code", ui-monospace, SFMono-Regular, Menlo,
             Consolas, "Liberation Mono", monospace;

  /* —— 表面：Mica（页面底）→ Layer/Card（卡片）→ Well（预览台）。
        三层各自一档明度，层级靠表面区分，而不是靠一堆重阴影。 —— */
  --mica: #f3f3f3;
  --card: #ffffff;
  --card-alt: #fafafa;
  --stage: #f5f5f5;
  --field: #ffffff;
  --field-hover: #f7f7f7;
  --field-border: #ededed;

  /* —— 文字 —— */
  --fg1: #1b1b1b;
  --fg2: #424242;
  --fg3: #616161;
  --fg4: #8a8a8a;
  --fg-disabled: #bdbdbd;

  /* —— 描边 —— */
  --stroke1: #d1d1d1;
  --stroke2: #e0e0e0;
  --stroke3: #f0f0f0;
  --stroke-strong: #616161;

  /* —— 强调色：只给「一个主操作 + 选中态 + 焦点」用。
        取的是微信绿的加深版（hue 153°，与预览里的 #07c160 同色相），
        既压得住 Fluent 的中性灰底，又不至于和预览区抢眼。 —— */
  --brand: #0e7c4a;
  --brand-hover: #0a5c36;
  --brand-pressed: #08492a;
  --brand-fg: #0e7c4a;
  --brand-tint: #e6f3ea;
  --brand-tint-strong: #cfe9da;

  /* —— 语义色：只用于删除 / 重置 / 解析提示 —— */
  --danger: #c42b1c;
  --warning-fg: #9a5b00;
  --success-fg: #0e7c4a;

  /* —— 交互反馈 / 材质 —— */
  --subtle-hover: rgba(0, 0, 0, .037);
  --subtle-press: rgba(0, 0, 0, .07);
  --acrylic: rgba(255, 255, 255, .72);
  --scrim: rgba(0, 0, 0, .4);
  --focus: #1b1b1b;
  --reveal: rgba(14, 124, 74, .5);
  --code-bg: #f0f0f0;
  --tooltip-bg: #2b2b2b;
  --tooltip-fg: #ffffff;
  --tooltip-border: transparent;
  --shell-shadow: rgba(20, 23, 33, .3);
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath d='M1.6 3.6 5 7.1l3.4-3.5' fill='none' stroke='%23616161' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='.4'/%3E%3C/svg%3E");

  /* —— 投影：Fluent 2 的 Elevation（两层：紧贴的暗影 + 一圈柔光） —— */
  --shadow2: 0 1px 2px rgba(0, 0, 0, .14), 0 0 2px rgba(0, 0, 0, .12);
  --shadow4: 0 2px 4px rgba(0, 0, 0, .14), 0 0 2px rgba(0, 0, 0, .12);
  --shadow8: 0 4px 8px rgba(0, 0, 0, .14), 0 0 2px rgba(0, 0, 0, .12);
  --shadow16: 0 8px 16px rgba(0, 0, 0, .14), 0 0 2px rgba(0, 0, 0, .12);
  --shadow64: 0 32px 64px rgba(0, 0, 0, .24), 0 0 8px rgba(0, 0, 0, .2);

  /* —— 圆角：控件 4、卡片 8（Fluent 只有两档常用值） —— */
  --r-ctl: 4px;
  --r-card: 8px;

  /* —— 预览区专用，勿动：微信复刻自己的固定色，不参与主题切换 —— */
  --wechat-green: #95ec69;
  --wechat-nav: #ededed;
  --wechat-bg: #ededed;
  --hongbao: #fa9d3b;

  /* —— 兼容别名（旧 token 名，仅供 app.js 行内样式与残留选择器使用） —— */
  --green: var(--brand);
  --red: var(--danger);
  --ink: var(--fg1);
  --ink-2: var(--fg2);
  --ink-3: var(--fg3);
  --line: var(--stroke2);
  --paper: var(--card);
  --bg: var(--mica);
  --sunken: var(--card-alt);
  --r-sm: var(--r-ctl);
  --r: var(--r-ctl);
  --r-lg: var(--r-card);

  color-scheme: light dark;
}

/* ==========================================================================
   Fluent Design token（深色 = Windows 11 Dark）
   预览区不跟随：微信界面本身是浅色素材，深色页面上它是一张「文档」。
   ========================================================================== */
@media (prefers-color-scheme: dark) {
  :root {
    --mica: #202020;
    --card: #2b2b2b;
    --card-alt: #323232;
    --stage: #1a1a1a;
    --field: #333333;
    --field-hover: #3b3b3b;
    --field-border: #3a3a3a;

    --fg1: #ffffff;
    --fg2: #c8c8c8;
    --fg3: #9a9a9a;
    --fg4: #7a7a7a;
    --fg-disabled: #6b6b6b;

    --stroke1: #525252;
    --stroke2: #3d3d3d;
    --stroke3: #333333;
    --stroke-strong: #9a9a9a;

    --brand: #14804f;
    --brand-hover: #0e6b41;
    --brand-pressed: #0a5633;
    --brand-fg: #4cc38a;
    --brand-tint: #17352a;
    --brand-tint-strong: #1f4a35;

    --danger: #d13438;
    --warning-fg: #ffd166;
    --success-fg: #4cc38a;

    --subtle-hover: rgba(255, 255, 255, .06);
    --subtle-press: rgba(255, 255, 255, .1);
    --acrylic: rgba(32, 32, 32, .76);
    --scrim: rgba(0, 0, 0, .55);
    --focus: #ffffff;
    --reveal: rgba(76, 195, 138, .55);
    --code-bg: #3a3a3a;
    --tooltip-bg: #333333;
    --tooltip-fg: #ffffff;
    --tooltip-border: var(--stroke1);
    --shell-shadow: rgba(0, 0, 0, .6);
    --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath d='M1.6 3.6 5 7.1l3.4-3.5' fill='none' stroke='%23adadad' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

    --shadow2: 0 1px 2px rgba(0, 0, 0, .28), 0 0 2px rgba(0, 0, 0, .24);
    --shadow4: 0 2px 4px rgba(0, 0, 0, .28), 0 0 2px rgba(0, 0, 0, .24);
    --shadow8: 0 4px 8px rgba(0, 0, 0, .28), 0 0 2px rgba(0, 0, 0, .24);
    --shadow16: 0 8px 16px rgba(0, 0, 0, .28), 0 0 2px rgba(0, 0, 0, .24);
    --shadow64: 0 32px 64px rgba(0, 0, 0, .5), 0 0 8px rgba(0, 0, 0, .4);
  }
}

/* ==========================================================================
   基础
   ========================================================================== */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--mica);
  color: var(--fg1);
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 20px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, p, dl, dd, ol, ul { margin: 0; }

/* Fluent 的焦点环：2px 实色、留 2px 空隙（深色主题自动翻成白色）。
   输入框不用它 —— 那边用品牌色下边框，见 .input-text:focus。 */
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.input-text:focus-visible, .input-textarea:focus-visible { outline: none; }

/* ==========================================================================
   顶栏（亚克力 App Bar）
   原来四个命令挤在整列设置的最底部，现在提到常驻命令栏：
   主操作永远在第一屏、不需要先滚过一屏表单。
   ========================================================================== */
.app-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--acrylic);
  -webkit-backdrop-filter: blur(30px) saturate(1.6);
  backdrop-filter: blur(30px) saturate(1.6);
  border-bottom: 1px solid var(--stroke2);
}
/* 亚克力噪点层：Fluent 的 Acrylic 不是单纯模糊，还压了一层 2% 的噪点，
   纯色块才不会糊成一片死板。 */
.app-bar::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image: var(--noise);
  opacity: .025;
  pointer-events: none;
}
.app-bar-inner {
  position: relative;
  max-width: 1220px;
  margin: 0 auto;
  padding: 8px 24px;
  display: flex;
  align-items: center;
  gap: 16px;
}
.app-brand { display: flex; align-items: center; gap: 10px; min-width: 0; flex: 1 1 auto; }
.app-mark { display: inline-flex; flex: 0 0 auto; color: var(--brand-fg); }
.app-titles { display: flex; flex-direction: column; min-width: 0; }
.app-title {
  font-family: var(--font-display);
  font-size: 20px;
  line-height: 26px;
  font-weight: 600;
  letter-spacing: -.01em;
}
.app-subtitle {
  font-size: 12px;
  line-height: 16px;
  color: var(--fg3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app-commands { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* ==========================================================================
   按钮：一套 Fluent 层级 —— 品牌填充（只有一个）/ 描边 / 透明
   原来「绿 + 红 + 蓝」三个同权重的实心按钮，等于告诉用户三个都是主操作；
   现在只有「生成微信对话」是填充主操作，其余降为描边与透明。
   ========================================================================== */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 36px;
  padding: 0 14px;
  border: 1px solid transparent;
  border-radius: var(--r-ctl);
  font-family: inherit;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  color: var(--fg1);
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color .1s ease, border-color .1s ease, color .1s ease;
}
.btn svg { flex: 0 0 auto; }
.btn-primary { background: var(--brand); color: #fff; }
.btn-primary:hover { background: var(--brand-hover); }
.btn-primary:active { background: var(--brand-pressed); }
.btn-outline { background: var(--card); border-color: var(--stroke1); color: var(--fg1); }
.btn-outline:hover { background: var(--field-hover); }
.btn-subtle { background: transparent; color: var(--fg1); }
.btn-subtle:hover { background: var(--subtle-hover); }
.btn-ghost { background: var(--card); border-color: var(--stroke1); color: var(--fg1); }  /* 兼容旧类名 */
.btn-ghost:hover { background: var(--field-hover); }
.btn-sm { height: 32px; padding: 0 12px; font-size: 13px; font-weight: 500; }
.btn[disabled] { background: var(--subtle-hover); color: var(--fg-disabled); cursor: not-allowed; }
/* 透明按钮禁用时保持透明（Fluent 的 subtle disabled 不铺底），只把内容压灰 */
.btn-subtle[disabled] { background: transparent; }
.btn-primary[disabled] { background: var(--brand); color: rgba(255, 255, 255, .7); }

/* ==========================================================================
   参数工具条
   ========================================================================== */
.toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
  margin: 16px 0 14px;
}
.toolbar-gap { flex: 1 1 auto; }
.ctl { display: inline-flex; align-items: center; gap: 8px; }
.ctl-label { font-size: 12px; line-height: 16px; color: var(--fg3); }
.ctl-unit { font-style: normal; font-size: 12px; color: var(--fg3); }
#captureScale { width: 86px; }
#sendInterval { width: 72px; text-align: center; }
input[type=number] { -moz-appearance: textfield; appearance: textfield; }
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ==========================================================================
   输入控件：Fluent 的 TextField —— 浅填充 + 一圈极淡描边 + 明显加深的下边框，
   聚焦时下边框变 2px 品牌色（不是通用那套「整圈发光」）。
   ========================================================================== */
.input-text {
  width: 100%;
  height: 32px;
  padding: 0 10px;
  font-family: inherit;
  font-size: 14px;
  line-height: 20px;
  color: var(--fg1);
  background-color: var(--field);
  border: 1px solid var(--field-border);
  border-bottom: 1px solid var(--stroke-strong);
  border-radius: var(--r-ctl);
  outline: none;
  transition: background-color .1s ease, border-color .1s ease;
}
.input-text:hover { background-color: var(--field-hover); }
.input-text:focus { border-bottom: 2px solid var(--brand); background-color: var(--field); }
.input-text::placeholder { color: var(--fg4); }
.input-text:disabled { color: var(--fg-disabled); background-color: var(--subtle-hover); }

/* 下拉：去掉系统箭头，换成 Fluent 的细雪佛龙 */
select.input-text {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 26px;
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 10px 10px;
  cursor: pointer;
}

.input-textarea {
  width: 100%;
  height: 300px;
  min-height: 160px;
  padding: 10px 12px;
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.75;
  color: var(--fg1);
  background-color: var(--field);
  border: 1px solid var(--field-border);
  border-bottom: 1px solid var(--stroke-strong);
  border-radius: var(--r-ctl);
  outline: none;
  resize: vertical;
  transition: background-color .1s ease, border-color .1s ease;
}
.input-textarea:hover { background-color: var(--field-hover); }
.input-textarea:focus { border-bottom: 2px solid var(--brand); background-color: var(--field); }
.input-textarea:disabled { color: var(--fg-disabled); }

.row-2 { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; }
.row-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }

/* 开关：Fluent 的 ToggleSwitch —— 关是「描边胶囊 + 小圆点」，开是「实心品牌 + 白色大圆点」 */
.switch {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--fg2);
  cursor: pointer;
  user-select: none;
}
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  position: relative;
  display: block;
  flex: 0 0 auto;
  width: 40px;
  height: 20px;
  border-radius: 999px;
  background: transparent;
  border: 1px solid var(--stroke-strong);
  transition: background-color .12s ease, border-color .12s ease;
}
.switch-dot {
  position: absolute;
  top: 50%;
  left: 3px;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--fg2);
  transition: transform .12s ease, width .12s ease, height .12s ease,
              margin-top .12s ease, background-color .12s ease;
}
.switch input:checked + .switch-track { background: var(--brand); border-color: var(--brand); }
.switch input:checked + .switch-track .switch-dot {
  width: 14px; height: 14px; margin-top: -7px; background: #fff; transform: translateX(19px);
}
.switch input:focus-visible + .switch-track { outline: 2px solid var(--focus); outline-offset: 2px; }

/* ==========================================================================
   工作台：左＝设置（6 组平铺），右＝预览；说明 / FAQ 在下方通栏并排
   ========================================================================== */
.page { max-width: 1220px; margin: 0 auto; padding: 0 24px 40px; }

.workbench {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 506px;
  gap: 20px;
  align-items: start;
}
.col-main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

.pane-head { display: flex; align-items: baseline; gap: 10px; padding: 0 2px; margin-bottom: 10px; }
.pane-title { font-size: 16px; line-height: 22px; font-weight: 600; }
.pane-note { font-size: 12px; line-height: 16px; color: var(--fg3); }

/* ==========================================================================
   设置：6 个分组平铺（不折叠、不隐藏）
   每组 = Mica 上的小标题行 + 一张白卡片。小标题行右侧可以挂操作（如「上传」）。
   ========================================================================== */
.settings-group + .settings-group { margin-top: 22px; }
.group-head {
  position: relative;                /* 格式说明气泡的定位参照 */
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 8px;
  padding: 0 2px;
}
.group-title { font-size: 14px; line-height: 20px; font-weight: 600; color: var(--fg1); }

/* ==========================================================================
   设置卡片：一行一个设置项，左标题 + 右控件，行间一条发丝线。
   说明、头像、背景、图片四个模块的标题行右侧留了一个「上传」入口。
   ========================================================================== */
.settings-card {
  background: var(--card);
  border: 1px solid var(--stroke1);
  border-radius: var(--r-card);
  box-shadow: var(--shadow2);
}

.setting-row { display: flex; align-items: center; gap: 16px; padding: 12px 16px; }
.setting-row + .setting-row { border-top: 1px solid var(--stroke3); }
.setting-row-stack { flex-direction: column; align-items: stretch; gap: 10px; }
.setting-text { flex: 1 1 auto; min-width: 0; }
.setting-control { flex: 0 1 auto; min-width: 220px; max-width: 340px; }
.setting-row-stack .setting-control { min-width: 0; max-width: none; }
.setting-label { display: block; font-size: 14px; line-height: 20px; font-weight: 600; color: var(--fg1); }
.setting-desc { margin-top: 2px; font-size: 12px; line-height: 16px; color: var(--fg3); }

.setting-block { padding: 12px 16px 16px; }
/* 卡片里第一行就是说明的（头像 / 背景 / 图片三组）：说明紧贴上沿，
   与下方控件之间的间距交给控件自己的 margin-top */
.setting-block > .setting-desc:first-child { margin-top: 0; }
.setting-block .setting-desc code { margin: 0 1px; }

code {
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--code-bg);
  color: var(--fg1);
  font-family: var(--font-mono);
  font-size: 12px;
}

/* 文字型动作：Fluent 的 Hyperlink —— 品牌色、hover 才加下划线 */
.btn-link {
  margin-left: auto;
  padding: 3px 6px;
  border: 0;
  border-radius: var(--r-ctl);
  background: none;
  color: var(--brand-fg);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  line-height: 18px;
  cursor: pointer;
}
.btn-link:hover { text-decoration: underline; }
.btn-link + .btn-link { margin-left: 0; }

.upload-inline {
  margin-left: auto;
  padding: 3px 6px;
  border-radius: var(--r-ctl);
  color: var(--brand-fg);
  font-size: 13px;
  font-weight: 500;
  line-height: 18px;
  cursor: pointer;
}
.upload-inline:hover { text-decoration: underline; }

/* 格式说明气泡 */
.tip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  border-radius: var(--r-ctl);
  color: var(--fg3);
  cursor: help;
  user-select: none;
}
.tip-icon:hover, .tip-icon:focus { color: var(--brand-fg); background: var(--subtle-hover); }
.tip-bubble {
  position: absolute;
  left: 0;
  top: 30px;
  z-index: 30;
  width: min(400px, calc(100vw - 72px));
  padding: 12px 14px;
  border: 1px solid var(--tooltip-border);
  border-radius: var(--r-card);
  background: var(--tooltip-bg);
  color: var(--tooltip-fg);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.95;
  box-shadow: var(--shadow16);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity .12s ease, transform .12s ease, visibility .12s;
}
.tip-icon:hover + .tip-bubble,
.tip-icon:focus + .tip-bubble { opacity: 1; visibility: visible; transform: translateY(0); }
.tip-bubble code { background: rgba(255, 255, 255, .14); color: #fff; }
.tip-dim { color: #9aa0c4; }

/* 解析结果提示 */
.parse-hint { margin-top: 10px; font-size: 12px; line-height: 18px; color: var(--fg3); }
.parse-hint.warn { color: var(--warning-fg); }
.parse-hint.ok { color: var(--success-fg); }

/* ==========================================================================
   头像 / 背景 / 图片 选择器
   （这些元素的 HTML 由 app.js 生成，类名必须保持原样）
   ========================================================================== */
.avatar-groups { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
/* 头像网格：**一行固定 10 格**（女 5 + 分组线 + 男 5），上传的自定义头像接在后面，
   超过 10 个就换到下一行 —— 不再把 10 个格子越挤越窄。
   ⚠️ 必须用 flex + 固定 flex-basis，不能用 grid：分组线一旦占一个网格轨道，
      第 10 个头像就会被挤到第二行（历史上踩过这个坑）。
      所以分组线做成「0 宽度 item + 伪元素画线」，既不占格子也保留分组提示。 */
.avatar-options {
  --av-cols: 10;                       /* 一行几个头像 */
  --av-gap: 6px;
  display: flex; flex-wrap: wrap; align-items: stretch; gap: var(--av-gap);
}
.avatar-options .avatar-option,
.avatar-options .avatar-slot {
  /* 每行放满 --av-cols 个所需的宽度；换行交给 flex-wrap */
  flex: 0 0 calc((100% - var(--av-gap) * var(--av-cols)) / var(--av-cols));
  min-width: 0;
}
/* 分组线：0 宽度的 item（两侧各留一个 gap，所以两组之间比普通间距宽一点），
   线用伪元素画在间隙正中，完全不占格子。 */
.avatar-options .av-sep { position: relative; flex: 0 0 0; width: 0; align-self: stretch; }
.avatar-options .av-sep::before {
  content: ''; position: absolute; left: -0.5px; top: 4px; bottom: 4px;
  width: 1px; background: var(--stroke2);
}
/* 自定义头像与预设同格显示，只在右上角多一个删除按钮 */
.avatar-slot { position: relative; }
.avatar-slot .avatar-option { width: 100%; }
.avatar-del {
  /* 完全落在格子内：探到格外会占用 6px 的间隙，多张自定义时容易压到邻格 */
  position: absolute; right: 1px; top: 1px; width: 18px; height: 18px; padding: 0;
  display: block; border: 0; border-radius: 50%; background: rgba(0, 0, 0, .55); color: #fff;
  font-size: 12px; line-height: 18px; text-align: center; cursor: pointer;
}
.avatar-del:hover { background: var(--danger); }
.avatar-option {
  position: relative; padding: 0; border: 2px solid transparent; border-radius: 6px;
  overflow: hidden; cursor: pointer; background: var(--card-alt); line-height: 0;
  transition: border-color .12s ease;
}
.avatar-option img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block; }
.avatar-option:hover { border-color: var(--stroke1); }
.avatar-option.selected { border-color: var(--brand); }
.avatar-option.selected::after {
  content: '✓'; position: absolute; right: 2px; bottom: 2px; width: 17px; height: 17px;
  border-radius: 50%; background: var(--brand); color: #fff;
  font-size: 11px; line-height: 17px; text-align: center; font-weight: 700;
}

.bg-options {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
  gap: 8px; margin-top: 10px;
}
.bg-option {
  position: relative; padding: 0; height: 54px; border: 2px solid transparent;
  border-radius: var(--r-ctl); overflow: hidden; cursor: pointer; background: var(--card-alt);
  background-size: cover; background-position: center;
}
.bg-option.selected { border-color: var(--brand); }
.bg-option span {
  position: absolute; left: 0; right: 0; bottom: 0; font-size: 10px; line-height: 15px;
  color: #fff; background: rgba(0, 0, 0, .45); text-align: center;
}

.image-options {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(68px, 1fr));
  gap: 8px; margin-top: 10px;
}
.image-option {
  position: relative; padding: 0; aspect-ratio: 1 / 1; border: 2px solid transparent;
  border-radius: var(--r-ctl); overflow: hidden; cursor: pointer; background: var(--card-alt); line-height: 0;
}
.image-option img { width: 100%; height: 100%; object-fit: cover; }
.image-option .idx {
  position: absolute; left: 0; bottom: 0; padding: 1px 6px; border-radius: 0 var(--r-ctl) 0 0;
  background: rgba(0, 0, 0, .55); color: #fff; font-size: 10px; line-height: 15px;
}
.image-option .del {
  position: absolute; right: 2px; top: 2px; width: 17px; height: 17px; border: 0;
  border-radius: 50%; background: rgba(0, 0, 0, .5); color: #fff; font-size: 11px;
  line-height: 17px; text-align: center; cursor: pointer; padding: 0;
}
.image-option .del:hover { background: var(--danger); }

.upload-tile {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1 / 1; border: 1px dashed var(--stroke1); border-radius: var(--r-ctl);
  background: var(--card-alt); cursor: pointer; color: var(--fg3);
  transition: border-color .12s ease, color .12s ease, background-color .12s ease;
}
.upload-tile:hover { border-color: var(--brand); color: var(--brand-fg); background: var(--brand-tint); }
.upload-tile svg { width: 20px; height: 20px; }

/* 播放按钮：图标与文案随播放状态切换（.playing 由 app.js 切换）
   ⚠️ 这条规则别丢：丢了会出现「▶ 和 ‖ 同时显示」 */
#btnPlay .ico-pause, #btnPlay.playing .ico-play { display: none; }
#btnPlay.playing .ico-pause { display: block; }

/* 播放 / 录制中的锁定态（class 由 app.js 切换） */
.playing-lock { opacity: .55; pointer-events: none; }

/* ==========================================================================
   预览面板（右栏）：卡片 = Layer，里面那块中性底 = 预览台，手机落在台上
   ========================================================================== */
.preview-pane {
  background: var(--card);
  border: 1px solid var(--stroke1);
  border-radius: var(--r-card);
  box-shadow: var(--shadow4);
  overflow: hidden;
}
.preview-pane .pane-head {
  padding: 12px 16px;
  margin-bottom: 0;
  border-bottom: 1px solid var(--stroke3);
}
.stage {
  padding: 26px 28px 46px;
  background: var(--stage);
}
.preview-tip {
  padding: 12px 20px 14px;
  border-top: 1px solid var(--stroke3);
  text-align: center;
  font-size: 12px;
  line-height: 18px;
  color: var(--fg3);
}
.preview-tip b { color: var(--fg1); font-weight: 600; }

/* 深色主题下预览台下方的投影要加深，否则手机上那圈投影完全看不见 */
@media (prefers-color-scheme: dark) {
  .wechat-wrapper { filter: drop-shadow(0 16px 30px var(--shell-shadow)); }
  .wechat-wrapper.no-shell { filter: none; }
}

/* ==========================================================================
   使用说明：内容是「写对话 → 换头像 → 配图 → 导出」四步流程，
   所以用带编号的竖向步骤条（编号在这里承载顺序信息，不是装饰）。
   ========================================================================== */
/* 参考区：使用说明 + 常见问题并排通栏（各占一半，窄屏堆叠） */
.reference {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
  margin-top: 16px;
}
.block {
  background: var(--card);
  border: 1px solid var(--stroke1);
  border-radius: var(--r-card);
  box-shadow: var(--shadow2);
  padding: 16px;
}
.block-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 14px; }
.block-title { font-size: 16px; line-height: 22px; font-weight: 600; }
.block-note { font-size: 12px; line-height: 16px; color: var(--fg3); }

.steps { list-style: none; padding: 0; }
.step { position: relative; display: flex; gap: 12px; padding-bottom: 18px; }
.step:last-child { padding-bottom: 0; }
/* 竖线：从本步编号下沿连到下一条编号上沿 */
.step:not(:last-child)::before {
  content: '';
  position: absolute;
  left: 13px;
  top: 31px;
  bottom: 0;
  width: 1px;
  background: var(--stroke2);
}
.step-num {
  position: relative;
  z-index: 1;
  flex: 0 0 26px;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--brand-tint-strong);
  border-radius: 50%;
  background: var(--brand-tint);
  color: var(--brand-fg);
  font-size: 12px;
  font-weight: 600;
}
.step-text { min-width: 0; padding-top: 2px; }
.step-title { font-size: 14px; line-height: 20px; font-weight: 600; }
.step-body { margin-top: 2px; max-width: 64ch; font-size: 13px; line-height: 20px; color: var(--fg2); }

/* ==========================================================================
   常见问题：Fluent Expander —— 左侧雪佛龙，展开时转 90°
   ========================================================================== */
details { border-top: 1px solid var(--stroke3); }
details:first-of-type { border-top: 0; }
summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 4px;
  border-radius: var(--r-ctl);
  cursor: pointer;
  font-size: 14px;
  line-height: 20px;
  font-weight: 500;
  color: var(--fg1);
  list-style: none;
  transition: color .1s ease;
}
summary::-webkit-details-marker { display: none; }
summary::marker { content: ''; }
summary:hover { color: var(--brand-fg); }
.expander-chevron {
  flex: 0 0 8px;
  width: 8px;
  height: 8px;
  margin-left: 4px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform .15s ease;
}
details[open] .expander-chevron { transform: rotate(45deg); }
details p { margin: 0 0 12px 22px; max-width: 66ch; font-size: 13px; line-height: 20px; color: var(--fg2); }

.page-footer { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--stroke2); }
.page-footer p { text-align: center; font-size: 12px; line-height: 18px; color: var(--fg3); }

/* ==========================================================================
   截图预览弹窗（Fluent Dialog）
   ========================================================================== */
.shot-modal {
  position: fixed; inset: 0; z-index: 100; display: none;
  align-items: center; justify-content: center; padding: 24px;
  background: var(--scrim);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.shot-modal.open { display: flex; }
.shot-box {
  width: min(520px, 100%); max-height: 90vh; display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--stroke1);
  border-radius: var(--r-card); box-shadow: var(--shadow64); overflow: hidden;
}
.shot-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 12px 14px 18px;
}
.shot-title { font-size: 16px; line-height: 22px; font-weight: 600; }
.shot-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0; border: 0; border-radius: var(--r-ctl);
  background: transparent; color: var(--fg2); cursor: pointer;
  transition: background-color .1s ease, color .1s ease;
}
.shot-x:hover { background: var(--subtle-hover); color: var(--fg1); }
.shot-body {
  flex: 1 1 auto; overflow: auto; margin: 0 18px; padding: 12px;
  background: var(--stage); border: 1px solid var(--stroke3);
  border-radius: 6px; text-align: center;
}
.shot-body img { max-width: 100%; border-radius: var(--r-ctl); box-shadow: var(--shadow8); }
.shot-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 18px 16px;
}
.shot-note { font-size: 12px; line-height: 16px; color: var(--fg3); }
.shot-foot .btn { text-decoration: none; }

/* ==========================================================================
   指针 Reveal 高光（Fluent 的招牌交互）：边框上跟着指针走的一小段品牌色光。
   只给顶栏的 4 个命令用 —— 全页铺开就成一层雾了。
   --mx / --my 由 js/fluent-shell.js 写入。
   ========================================================================== */
.reveal { position: relative; }
.reveal::after {
  content: '';
  position: absolute;
  inset: 0;
  padding: 1px;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(140px circle at var(--mx, 50%) var(--my, 50%), var(--reveal), transparent 70%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
  opacity: 0;
  transition: opacity .18s ease;
}
.reveal:hover::after { opacity: 1; }

/* 关掉动效时：指示条直接落位、Reveal 不要「跟着指针跑」的那层光 */
@media (prefers-reduced-motion: reduce) {
  .reveal::after,
  .expander-chevron,
  .btn,
  .avatar-option,
  .switch-track,
  .switch-dot,
  .upload-tile,
  .input-text,
  .input-textarea { transition: none; }
  .reveal::after { display: none; }
}

/* ==========================================================================
   ★★★ 预览区（微信复刻）—— 以下整段不参与 Fluent 重构，改动前先看文件头注释 ★★★
   ========================================================================== */

/* ---------------------------------------------------------------- 手机外壳 */
/* 外壳用真实素材 assets/ui/wechat-box.png（873×1789，RGBA）：四角与机身之外的白色背底
   都是真透明（alpha=0），所以透出的是它底下的东西（面板的底色与 drop-shadow），
   换背景或叠在深色底上也不会出现白色直角。屏幕区在素材里填成了 --wechat-bg。
   素材直接做 .wechat-wrapper 的 background 铺在内容下面，
   屏幕内容靠 wrapper 的 padding 内缩，正好叠进素材的屏幕范围内。
   素材实测（亚像素拟合）：屏幕区 x 19.5 起、y 14.3 起、宽 834、高 1760.5、圆角 125。
   换到 450px 宽的外壳上，屏幕落在 left 10.1 / top 7.4，尺寸 429.9 × 907.5 —— 见下方 padding 注释。
   ⚠️ 素材必须保持带 alpha 的 RGBA：若被换成无 alpha 的 RGB（PNG/JPG），四角会退回成白色方块，
   下面的 drop-shadow 也会改成照外框画直角影子。 */
.wechat-wrapper {
  position: relative;
  width: 450px;
  margin: 0 auto;
  /* 内容区 410px 宽。四边各内缩约 10px，落进素材屏幕的中央：
     屏幕左边距 10.1、上边距 7.4，所以 padding 取 20 / 18 再多留一点余量。 */
  padding: 18px 20px 17px 20px;
  overflow: hidden;
  background: url(../assets/ui/wechat-box.png) no-repeat top;
  background-size: cover;
  /* 阴影只能用 drop-shadow：它跟着 background 的 alpha 描边（四个透明圆角不会出现直角投影），
     box-shadow 会照外框画一个矩形影子 */
  filter: drop-shadow(0 16px 30px var(--shell-shadow));
}
/* ⚠️ background-size: cover 在这里等价于「按外壳宽缩放」，前提是 wrapper 的总高不能超过
   450 × 1789 / 873 = 922.2px。超了 cover 会改成按高缩放，素材被拉宽、左右两边裁掉，
   屏幕和内容就对不上。所以「上下 padding + 头部 + 列表 + 底部」要卡在 922.2px 以内，
   当前是 18 + 95.67 + 713 + 78.85 + 17 = 922.5px（贴边，改动任一高度都要回来核一遍）。 */

/* 隐藏手机容器（#showShell 关掉时由 js 加 .no-shell）
   注意：不能只把 background 设成 none —— background-size:cover 是相对 450×922 的外壳算的，
   外壳一去，cover 就会把素材按内容盒尺寸重新缩放（内容只剩 1px 的 padding，画面会被放大到糊）。
   所以这里连同 padding 一起收回，让 chat 界面直接铺在卡片上。
   clip-path 也一并撤掉：它是按外壳屏幕弧线定的，没有外壳时那圈圆角就没有意义了。 */
.wechat-wrapper.no-shell {
  padding: 0;
  background-image: none;
  filter: none;
  clip-path: none;
}
.wechat-wrapper.no-shell .wechat-container { clip-path: none; border-radius: 8px; }

/* 屏幕内容：宽度取满 wrapper 的内容区（450 − 20 − 20 = 410px），
   高度由「头部素材图 + 消息列表 + 底部素材图」自然撑开。 */
.wechat-container {
  position: relative;
  overflow: hidden;
  /* 四角必须是圆的，否则直角会戳到素材屏幕的弧线外面（内容盒只比屏幕内缩 10px，
     而屏幕圆角有 64.5px）。这里用 clip-path 裁出圆角，而不是给容器加 border-radius：
     容器本身的盒模型仍是直角，只是绘制时被裁掉四角，改成「切掉四个角」的语义。
     半径 = 让裁剪弧线与素材屏幕弧线贴合的取值（与屏幕圆心同心）。 */
  clip-path: inset(0 round 54px);
  background: var(--wechat-bg);
  /* 截图时隔离出独立的层叠上下文 */
  isolation: isolate;
}

/* ------------------------------------------------- 手机头部（真实截图素材做底） */
/* 底图 1170×273，内部承载「返回箭头 / 更多按钮」；状态栏与昵称、角标为动态叠加层。
   图片走常规流决定容器高度，叠加层用百分比定位，缩放时对齐不跑偏。 */
.chat-header {
  position: relative;
  overflow: hidden;
  background: var(--wechat-nav);
}
.header-img {
  display: block; width: 100%; height: auto;
  user-select: none; -webkit-user-drag: none; pointer-events: none;
}

/* 状态栏：内容垂直居中于底图图标行（约 34.5%），右侧留白与底图电池右缘对齐 */
.status-bar {
  position: absolute; left: 0; right: 0; top: 0; height: 69%;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 7.2%;
  font-size: 16px; font-weight: 600; color: #111;
}
.status-right { display: inline-flex; align-items: center; gap: 9px; }
.header-network { font-size: 16px; font-weight: 600; }
.ico-signal { display: block; }
.ico-battery { display: inline-flex; align-items: center; }
.battery-shell {
  display: inline-block; width: 30px; height: 15px; border-radius: 4px;
  border: 1px solid #111; padding: 1px;
}
.battery-level {
  display: block; height: 100%; width: 88%; border-radius: 2.5px;
  background: #34c759; transition: width .2s;
}
.battery-tip {
  display: inline-block; width: 3px; height: 7px; margin-left: 1.5px;
  border-radius: 0 2px 2px 0; background: #111;
}

/* 导航栏：垂直居中于底图箭头行（约 75.8%）；返回箭头与更多按钮已在底图内 */
.nav-bar {
  position: absolute; left: 0; right: 0; top: 51.5%; bottom: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 4.33%;
}
.nav-left { display: inline-flex; align-items: center; padding-left: 14px; }
.header-nickname {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 17px; font-weight: 500; color: #111;
}

/* 标题栏底部分割线。
   颜色取真机实测值换算：原图分隔线亮度 213 / 底色 237 → 约 8% 黑。
   厚度必须用实打实的 1px：实测「1px + scaleY(.5)」与「height:.5px」这类发丝线写法
   在 html2canvas 里各倍率下覆盖率不稳定（1x/2x/3x 分别只有 60%/100%/50%），
   会被渲染得忽深忽浅甚至整个丢失；1px 实色在 1x/2x/3x 下均稳定落成 1 CSS px、强度完整。 */
.header-line {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: rgba(0, 0, 0, .08);
  pointer-events: none;
}

/* ---------------------------------------------------------------- 聊天区 */
.chat-messages {
  /* 固定高度：撑到「外壳总高 922.5px = 上下 padding 35 + 头部 95.67 + 列表 713 + 底部 78.85」
     （头部 1170×273、底部 1170×225 两张素材图按内容宽 410px 等比换算而来） */
  height: 713px;
  padding: 18px 14px 22px; overflow-y: auto;
  background-color: var(--wechat-bg);
  background-repeat: no-repeat; background-position: center center; background-size: cover;
  scrollbar-width: none; -ms-overflow-style: none;
  font-size: 15px;
}
.chat-messages::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* 消息通用
   ⚠️ 新增卡片类消息时**必须把行容器 class 加进这三个选择器**：
   漏加的话该行不是 flex，头像会掉到卡片下方、行高被撑高、右侧消息也不靠右。
   转账（.transfer-row）就踩过这个坑。 */
.message, .hongbao-row, .transfer-row {
  display: flex; align-items: flex-start; gap: 11px;
  width: 100%; margin-bottom: 15px;
}
.message.me, .hongbao-row.me, .transfer-row.me { justify-content: flex-end; }
.message.other, .hongbao-row.other, .transfer-row.other { justify-content: flex-start; }

/* 动态发送：新消息上屏动画。
   只做 opacity + 位移动画（不用 max-height 之类会触发重排的写法），
   逐条上屏时旧消息的节点不会被重建，所以只有新的一条会播动画，不会整屏闪。
   注意别加 will-change：动画期间 transform 自己就会建合成层，常驻 will-change
   反而让每条消息长期占一个层，逐条上屏时不断新建/销毁层。 */
@keyframes msg-in {
  from { opacity: 0; transform: translateY(16px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
.msg-appear { animation: msg-in .34s cubic-bezier(.2, .7, .3, 1) both; }
/* 录制视频时**不加任何上屏动画**（见 generateVideo）：抓帧是离线按时间点进行的，
   CSS 动画走真实时钟，两边不同步会截到半透明/位移的中间态。 */

@media (prefers-reduced-motion: reduce) {
  .msg-appear { animation-duration: .01ms; }
}

.avatar-img {
  width: 45px; height: 45px; flex: 0 0 45px; border-radius: 8px;
  object-fit: cover; background: #dfe3ea;
}

/* 文字气泡 */
.message-content {
  position: relative; min-height: 45px; max-width: calc(75% - 30px);
  display: flex; align-items: center;
  padding: 8px 12px; border-radius: 6px;
  font-size: 15px; line-height: 1.55; color: #000;
  word-break: break-word; white-space: pre-wrap;
}
.me .message-content { background: var(--wechat-green); margin-left: 8px; }
.me .message-content::after {
  content: ''; position: absolute; right: -5px; top: 15px;
  width: 0; height: 0; border: 6px solid transparent;
  border-left-color: var(--wechat-green); border-right: 0;
}
.other .message-content { background: #fff; margin-right: 8px; }
.other .message-content::before {
  content: ''; position: absolute; left: -5px; top: 15px;
  width: 0; height: 0; border: 6px solid transparent;
  border-right-color: #fff; border-left: 0;
}

/* 图片消息 + 横竖自适应 */
.message-image img {
  display: block; border-radius: 4px; object-fit: cover;
  /* 灰色描边：图片底色跟聊天区背景接近时会糊在一起，加 1px 灰边把边界勾出来。
     用 box-sizing: border-box 让描边算在 max-width/max-height 里面，
     否则图片实际渲染尺寸会比 150/186px 各多 2px，横向图会跟设定宽度对不上。 */
  box-sizing: border-box; border: 1px solid #d8d8d8;
  max-width: 150px; max-height: 150px;
}
.message-image.horizontal-image img { max-width: 186px; max-height: 150px; }
.message-image.vertical-image   img { max-width: 150px; max-height: 186px; }

/* 语音消息 */
.message-voice {
  position: relative; min-height: 45px;
  display: flex; align-items: center;
  /* 图标与时长之间统一留 8px。这里必须用 gap，不能用 margin：
     margin 在 row-reverse 下会被摆到图标外侧，导致图标离气泡右边缘多出 8px 空白；
     gap 不管主轴方向都只作用在两项之间，图标才能正好贴住 padding 内边缘。 */
  gap: 8px;
  padding: 8px 12px; border-radius: 6px;
  font-size: 15px; line-height: 1.5; color: #000;
}
.other .message-voice { background: #fff; margin-right: 8px; }
.other .message-voice::before {
  content: ''; position: absolute; left: -5px; top: 15px;
  width: 0; height: 0; border: 6px solid transparent;
  border-right-color: #fff; border-left: 0;
}
.me .message-voice { background: var(--wechat-green); margin-left: 8px; flex-direction: row-reverse; }
.me .message-voice::after {
  content: ''; position: absolute; right: -5px; top: 15px;
  width: 0; height: 0; border: 6px solid transparent;
  border-left-color: var(--wechat-green); border-right: 0;
}
/* 语音图标：微信原生喇叭 + 两道声波（窄高的 SVG，横竖比例固定 230:307，靠 currentColor 上色）。
   SVG 本身朝右，即「对方发送」的形态：喇叭朝右、声波朝左。
   框取 12×17；素材原比例是 230:307（12 宽对应 16.02 高），SVG 默认
   preserveAspectRatio 会等比内缩，所以实际画出来是 12×16.02 居中，不拉伸变形。 */
.voice-icon {
  display: block; flex: 0 0 auto;
  width: 12px; height: 17px;
  color: #111;
}
/* 自己发送的气泡是 row-reverse：DOM 里「图标、时长」渲染成「时长在左、图标在右」。
   两种气泡的图标方向整体相反 —— 对方喇叭朝右、自己喇叭朝左。
   间距交给 .message-voice 的 gap，这里不再给图标加外边距。 */
.me .message-voice .voice-icon { transform: scaleX(-1); }
/* 红包卡片 */
.hongbao-card {
  position: relative; width: 246px; border-radius: 6px; overflow: hidden;
  background: linear-gradient(180deg, #fa9d3b 0%, #f68b33 100%);
}
.me .hongbao-card { margin-left: 8px; }
.other .hongbao-card { margin-right: 8px; }
.me .hongbao-card::after {
  content: ''; position: absolute; right: -5px; top: 15px;
  width: 0; height: 0; border: 6px solid transparent;
  border-left-color: #fa9d3b; border-right: 0;
}
.other .hongbao-card::before {
  content: ''; position: absolute; left: -5px; top: 15px;
  width: 0; height: 0; border: 6px solid transparent;
  border-right-color: #fa9d3b; border-left: 0;
}
.hb-main { display: flex; align-items: center; height: 74px; padding: 0 12px; }
.hb-icon { flex: 0 0 42px; width: 42px; height: 42px; }
.hb-icon svg { display: block; width: 42px; height: 42px; }
.hb-title {
  margin-left: 12px; color: #fff; font-size: 14.5px; line-height: 1.35;
  max-width: 164px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hb-foot {
  display: flex; align-items: center; height: 22px; padding-left: 12px;
  border-top: 1px solid rgba(255, 255, 255, .38);
  color: rgba(255, 255, 255, .88); font-size: 11px;
}

/* 转账卡片
   结构照真机截图（与红包卡片刻意不同，别互相套用）：
     主区 = 左「大圆环转账图标」+ 右两行文字（¥金额大字 / 状态说明）
     底栏 = 一行「微信转账」小字（无分隔线）
   底色真机实测为**纯色 #FA9E3B**（不是渐变——红包才是渐变），别统一成红包色。
   比例取自真机：环外径 ÷ 卡片高 = 0.507，这里卡片高 76 → 图标 42（含内边距）；
   文字层级取的是真机 51/40/40px 三档的等比缩小（14/11/11）。
   ⚠️ 底栏是「微信转账」四个字（旧版写的「转账」与真机不符，2026-09 三张真机截图已核对）。 */
.transfer-card {
  position: relative; width: 246px; border-radius: 8px;
  background: #fa9e3b;
}
.me .transfer-card { margin-left: 8px; }
.other .transfer-card { margin-right: 8px; }
/* 气泡尖角：真机是个圆角小尾巴，这里沿用项目里红包卡片的三角做法保持一致 */
.me .transfer-card::after {
  content: ''; position: absolute; right: -5px; top: 15px;
  width: 0; height: 0; border: 6px solid transparent;
  border-left-color: #fa9e3b; border-right: 0;
}
.other .transfer-card::before {
  content: ''; position: absolute; left: -5px; top: 15px;
  width: 0; height: 0; border: 6px solid transparent;
  border-right-color: #fa9e3b; border-left: 0;
}
/* 已收款 / 已退还：底色换成 30% 品牌橙（真机实测 #FCE2C3 = #FA9E3B × alpha .30 叠白），
   文字仍是白色 —— 真机就是这种偏淡的观感（微信靠环内「打钩 / 掉头箭头」区分这两态）。
   尖角要跟着换色，否则浅底卡片上会留一个橙色小三角。 */
.transfer-card.is-done { background: #fce2c3; }
.me .transfer-card.is-done::after { border-left-color: #fce2c3; }
.other .transfer-card.is-done::before { border-right-color: #fce2c3; }
.tf-main { display: flex; align-items: center; height: 62px; padding: 0 10px; }
/* 图标尺寸：真机环外径 ÷ 卡片高 = 0.507，卡片高约 80 → 图标 = 40px（实测比例 0.50，与真机一致）。
   历史：42px（比例 0.64）明显偏大，36px（0.45）又偏小，40 是量出来的值。 */
.tf-icon { flex: 0 0 40px; width: 40px; height: 40px; }
.tf-icon svg { display: block; width: 40px; height: 40px; }
.tf-body { margin-left: 7px; display: flex; flex-direction: column; min-width: 0; }
/* 金额一行：**¥ 与数字同字号、同基线**（2026-09 用户要求）。
   历史：原来是 `font-size: 9px + vertical-align: 2px`（小一档、抬到数字上沿，
   当时的笔记写的「真机 ¥ 不跟数字等高」）—— 观感偏小，已按用户口径作废。
   想再调大小时，改 .tf-amount 的 font-size 即可，¥ 会跟着一起变。 */
.tf-amount {
  color: #fff; font-size: 14px; font-weight: 500; line-height: 1.15; white-space: nowrap;
}
.tf-amount i { font-style: normal; font-size: inherit; }
.tf-sub {
  margin-top: 2px; color: rgba(255, 255, 255, .92); font-size: 11px; line-height: 1.2;
  white-space: nowrap;
}
.tf-foot {
  padding: 0 0 7px 10px;
  color: rgba(255, 255, 255, .92); font-size: 10.5px; line-height: 1.2;
}

/* 系统消息 */
.message-time {
  width: 100%; text-align: center; font-size: 12px; font-weight: 500;
  color: #7d7d7d; padding: 6px 0 12px;
  text-shadow: 0 1px 1px rgba(255, 255, 255, .6);
}
.message-recalled, .message-hongbao {
  width: fit-content; margin: 0 auto 15px; padding: 3px 13px;
  border-radius: 999px; background: rgba(246, 246, 246, .78);
  font-size: 12px; font-weight: 500; color: #6f6f6f; text-align: center;
}
.message-hongbao span { color: #ff8300; margin-left: 1px; }
/* 转账状态行：同款灰胶囊，左侧一枚小徽标图标 */
.message-transfer-notice {
  display: flex; align-items: center; gap: 5px;
  width: fit-content; margin: 0 auto 15px; padding: 3px 12px;
  border-radius: 999px; background: rgba(246, 246, 246, .78);
  font-size: 12px; font-weight: 500; color: #6f6f6f;
}
.message-transfer-notice svg { display: block; width: 12px; height: 12px; flex: 0 0 12px; }
.message-transfer-notice em { font-style: normal; color: #39b54a; }

/* ---------------------------------------------------------------- 底部输入栏 */
/* 底图 1170×225，图片走常规流撑开容器高度（宽 100% 跟随手机内容区等比缩放）。
   注意：不设 width 时图片会按原始 1170px 溢出，被 .wechat-container 的 overflow 裁掉右半边。 */
.chat-footer { position: relative; background: #f7f7f7; }
.footer-img {
  display: block; width: 100%; height: auto;
  user-select: none; -webkit-user-drag: none; pointer-events: none;
}

/* 截图模式：统一字体渲染，避免滚动条影响 */
.capturing .chat-messages { overflow: hidden !important; }

/* ==========================================================================
   响应式
   ========================================================================== */
@media (max-width: 1120px) {
  .workbench { grid-template-columns: minmax(0, 1fr) 506px; gap: 16px; }
  .stage { padding: 22px 28px 42px; }
}

@media (max-width: 900px) {
  .reference { grid-template-columns: minmax(0, 1fr); }
}

/* 预览卡片比设置栏高得多，窄屏起就改成上下排：预览在上，设置在下 */
@media (max-width: 1020px) {
  .workbench { grid-template-columns: minmax(0, 1fr); }
  .preview-pane { order: -1; width: 100%; max-width: 506px; margin: 0 auto; }
  .stage { padding: 24px 28px 44px; }
}

@media (max-width: 720px) {
  .app-subtitle { display: none; }
  /* 命令只留图标。文字用「视觉隐藏」而不是 display:none ——
     display:none 会把文字从无障碍树里摘掉，按钮就没名字了。 */
  .cmd-label-secondary {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .app-commands .btn { padding: 0 10px; }
}

@media (max-width: 560px) {
  /* 顶栏在手机上不再吸顶：竖向空间太贵，命令跟着页面滚就好 */
  .app-bar { position: static; }
  .app-bar-inner { flex-wrap: wrap; row-gap: 8px; padding: 10px 14px; }
  .app-commands { margin-left: 0; width: 100%; flex-wrap: wrap; }
  .app-commands .btn { flex: 1 1 auto; padding: 0 8px; }
  .cmd-label, .cmd-label-secondary {
    position: static; width: auto; height: auto; margin: 0; padding: 0;
    overflow: visible; clip: auto; white-space: nowrap;
  }

  .page { padding: 0 8px 32px; }
  .toolbar { gap: 12px; margin: 14px 0 12px; }
  .toolbar-gap { display: none; }
  .setting-row { flex-direction: column; align-items: stretch; gap: 10px; }
  .setting-control { min-width: 0; max-width: none; }
  .row-3 { grid-template-columns: minmax(0, 1fr); }
  .tip-bubble { width: min(300px, calc(100vw - 48px)); }
  .avatar-options { --av-gap: 5px; }
  .image-options { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); }

  /* 整机缩到 348px 宽：宽度 / padding / 列表高度一起按 348 ÷ 450 等比缩，
     屏幕素材才会继续和内容对上（348 − 15.5 × 2 = 317px 内容宽） */
  .wechat-wrapper { width: 348px; padding: 13.9px 15.5px 13.2px; }
  /* 外壳缩到 348px，屏幕圆角随之等比缩小，容器裁剪半径同步缩 */
  .wechat-container { clip-path: inset(0 round 42px); }
  .chat-messages { height: 552px; padding: 14px 10px 18px; }
  .header-nickname { font-size: 16px; }
  .stage { padding: 18px 0 34px; }
  .shot-modal { padding: 14px; }
  .shot-foot { flex-direction: column; align-items: stretch; gap: 10px; }
  .shot-foot .btn { width: 100%; }
}
