/* ══════════════════════════════════════════════════════════════════
   chat.css —— 对话站：会话区 .chat / 消息流 .msgs / 输入区 .composer / 右栏 .side
   纪律：本文件不得出现字面色值，一律 var() 令牌。
   来源：mockup/中台-统一原型.html 559–640 / 774–803 / 863–895 行。
   ══════════════════════════════════════════════════════════════════ */

/* ── 会话区：全屏唯一的亮面 e2 ── */
.chat{flex:1 1 auto;min-width:420px;display:flex;flex-direction:column;background:var(--e2);min-height:0}
.chat-hd{flex:0 0 56px;display:flex;align-items:center;gap:10px;padding:0 20px;border-bottom:1px solid var(--line)}
.chat-hd .cn{font-size:17px;font-weight:500;color:var(--t1)}
.chat-hd .chip{font-size:12px;color:var(--t2);background:var(--e4);border-radius:6px;padding:2px 8px}
/* ★ 2026-10-03（用户令「对话框界面优化」）：chip 里的**当前号小色点** —— 底色复用左栏
   头像的全局色类 `.c1–.c4`（shell.css:15，`--av1…--av4`），本文件只管形状 ⇒
   「会话头这枚点」与「左栏当前号头像」同色 = 同一个号的两处呈现，零新色值。
   inline-block + vertical-align 让点随基线对齐，不改 .chip 本身的 display（真机档
   chip 是纯文本，同一条规则不能把它改成 flex）。 */
.chat-hd .chip .adot{display:inline-block;width:8px;height:8px;border-radius:50%;
  margin-right:6px;vertical-align:1px}
/* ★ AI 托管标（2026-09-17）：设计稿会话头那枚小胶囊。
   视觉与链路条 / 候选卡**同族**（同一对 --ai-* 令牌：surface 底 + line 描边 + ai-fg 文字），
   三处合起来才是「这个会话在 AI 手里」的视觉签名 —— 只做其中一个就只剩装饰。
   ⚠ `hidden` 必须自己兜住：下面写了 `display:inline-flex`，而 UA 的 `[hidden]{display:none}`
     特异性最低 ⇒ 不补 `[hidden]` 这一条，**没有 AI 数据的会话也会显示一枚空胶囊**
     （元素在、尺寸在、只是里面没字）—— 这正是本项目最怕的"不报错的静默错"。 */
.chat-hd .aitag{display:inline-flex;align-items:center;gap:5px;height:20px;padding:0 8px;
  border-radius:6px;font-size:11px;font-weight:600;
  color:var(--ai-fg);background:var(--ai-surface);border:1px solid var(--ai-line)}
.chat-hd .aitag[hidden]{display:none}
.chat-hd .hd-tools{margin-left:auto;display:flex;align-items:center;gap:15px;color:var(--t3)}
.chat-hd .hd-tools svg{color:currentColor}
/* ── 会话头部「右栏」开关（2026-09-17 新增 · 仅窄窗出现）──
   ★ 为什么两种形态并存：宽窗右栏常驻，原「面板」图标是**纯装饰**（点了没反应也不会消失），
     窄窗右栏收成覆盖式抽屉 ⇒ 需要同一个位置变成**可点开关**。
     ⇒ `.hd-deco`（装饰，宽窗）+ `.hd-side`（按钮，窄窗）**DOM 并存、由 CSS 交换**：
        不靠 JS 增删节点，宽窗视觉零变化，也不会在宽窗多出一个无功能按钮。
   ★ 判据：宽窗 `#btnSide` display:none 且 `#hdDeco` display ≠ none；窄窗相反（见 _verify/narrow.mjs）。
   ⚠ 按钮是 `.chat-hd` 的直接子元素之一，字号/颜色继承自 `.hd-tools`；此处只做按钮 reset。 */
.chat-hd .hd-deco{display:inline-flex;align-items:center}
.chat-hd .hd-side{display:none;align-items:center;justify-content:center;
  width:30px;height:30px;padding:0;border:none;border-radius:9px;background:transparent;
  color:var(--t3);cursor:pointer;transition:background 120ms,color 120ms}
.chat-hd .hd-side:hover{background:var(--face-hover);color:var(--t1)}
.chat-hd .hd-side:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.chat-hd .hd-side.on{background:var(--accent-wash);color:var(--accent-text)}

/* ── 消息流：会话区 >680px 时自动居中收窄 ──
   ★★ 规格 §6.1 铁律 13：补 150px 地板，且必须成对（只加前半条不生效）★★
   现状 .chat-hd{flex:0 0 56px} 与 .composer{flex:0 0 auto} 的 shrink 均为 0，
   加 min-height 只会让 .chat 整体溢出，而不是"先压输入区"。必须同时把两者改为可收缩。*/
.msgs{flex:1 1 auto;min-height:150px;overflow-y:auto;overflow-x:hidden;
  padding:16px max(20px, calc((100% - 680px) / 2));
  display:flex;flex-direction:column;gap:12px;
  /* ★ 链路条的宽度自适应基准（见下方 .chain 的 container query 说明）。
     本元素是 .chat 的 flex item、横向为 cross 轴（stretch）⇒
     加 inline-size containment 不会改变它自己的宽度与高度（高度走 flex-grow + min-height）。 */
  container-type:inline-size}
.msgs::-webkit-scrollbar{width:8px}
.msgs::-webkit-scrollbar-thumb{background:var(--scroll);border-radius:4px}
.msgs::-webkit-scrollbar-track{background:transparent}
.tdiv{align-self:center;font-size:11px;color:var(--t3);letter-spacing:.02em}
.msg{display:flex;gap:8px;align-items:flex-start;max-width:min(78%,560px)}
.msg .av{width:34px;height:34px;border-radius:10px;font-size:13px}
.mb2{display:flex;flex-direction:column;min-width:0}
.msg.out{flex-direction:row-reverse;align-self:flex-end}
.msg.out .mb2{align-items:flex-end}
.bub{background:var(--bubble-in);border-radius:4px 14px 14px 14px;padding:10px 13px;font-size:15px;
  line-height:1.6;color:var(--t1);overflow-wrap:anywhere}
.msg.out .bub{background:var(--bubble-out);color:var(--bubble-out-text);
  border-radius:14px 4px 14px 14px;box-shadow:inset 0 1px 0 var(--bevel-acc)}
/* 语音转文字标识（2026-10-15）：与 `chatlog.css:.cl-voice-tag` 同款 —— 正文是语音**自动转出**的
   文字，非对方手打。用 accent（强调色）区别于红线/中性，`inline-flex` 紧跟正文、不换行。 */
.cl-voice-tag{display:inline-flex;align-items:center;font-size:11px;color:var(--accent-text);
  border:1px solid var(--accent-line);border-radius:var(--r-full);padding:0 6px;
  line-height:1.6;margin-left:5px;vertical-align:middle;white-space:nowrap}
.ai-note{font-size:12px;color:var(--t3);margin-top:5px;letter-spacing:.015em}
.sens{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:500;color:var(--amber);
  background:var(--amber-bg);border-radius:6px;padding:2px 8px;margin-top:6px;align-self:flex-start}
/* ── ★M3-D 发送态（§M3-2.1/§2.5）──────────────────────────────────────────
   只用**结构**（小字 + 描边 + ⚠）表达"发送中 / 失败"，**禁用 opacity** ——
   压低己方气泡明度会同时压低「对方 vs 己方」的 ΔE00。⇒ 气泡底色恒等于普通 out 气泡：
   判据 m3.send.sending.bg / m3.send.failed.bg 就是量这个 backgroundColor 不变。 */
.msg.out .sending-note{font-size:11px;color:var(--t3);margin-top:5px;letter-spacing:.015em}
/* ★ 2026-09-25：发送记录的**「已发出」态**小字 —— 与 `.sending-note` **同字号/同留白**，
   只**换色**（accent 而非 --t3）：它是"已完成"不是"进行中"，两者在同一列里必须一眼可分。
   （`.send-fail`（红）与 `.sending-note`（灰）直接复用，不新造 —— 语义本来就对得上。） */
.msg.out .sent-note{font-size:11px;color:var(--accent-text);margin-top:5px;letter-spacing:.015em}
.msg.out.failed .bub{outline:1px solid var(--danger-line)}
.msg.out .send-fail{display:inline-flex;align-items:center;gap:6px;font-size:11px;
  color:var(--danger-text);margin-top:5px}
.msg.out .send-fail .retry{color:var(--accent-text);cursor:pointer;font-weight:500}
/* ★ 2026-10-14（用户令「失败消息加删除」）：删除与重试并列，用 danger 色区分「重发」与「抹掉」。
   ⚠ 只改颜色，不新造交互形态 —— 与 `.retry` 同款 role=button 可点，视觉上仍是同族。 */
.msg.out .send-fail .retry.del{color:var(--danger-text)}
/* 离线行（仅"有后端且 navigator.onLine===false"时出现；file:// 下命中数恒 0，§2.6） */
/* ★ 接收暂停行（规格 §1.3/§1.4）：与 `.offline` **共用同一段声明**（同字号/同色/同上下留白），
   但**类名必须分开** —— 既有判据 `m3.send.file.offline` 按 `.offline` **计数**，复用类名会污染它。
   ⚠ 聊天记录站那份声明在 `chatlog.css`（`#chatlogbody > .recv-pause`）：font-size / color / 上下留白
     **三项与此处逐字同值** —— 免得"同一句话在两个站长得不一样"（符号不同形 = 需配图例族缺陷）。 */
.offline,
.recv-pause{font-size:11px;color:var(--t3);padding:0 1px 6px}
/* ── ★实时接收面：「N 条新消息」浮条（规格 §2.2 / §4.1 / §4.3）─────────────
   ⚠ 本块是 **2026-09-19 19:42 补的**：`chat.js` 的实时接收面（`pillHtml()` / `renderNewPill()`）
     已引 `data-newpill` 与 `.newpill`，但**全仓 css 里此前 0 命中**（`grep -rn newpill app/css/`
     无规则命中）⇒ 浮条会以"无定位、无底色、无圆角"的裸 div 出现 —— 这是"看着有、其实没样"的
     假完成级缺口（同族：`.recv-pause` 缺样式那一类）。本块按 ui-designer-4 规格 §4.3 **逐字落地**。
   配方 = `--e3` 底 + `--line` 内描边 + `--sh-1` 投影 + `--accent-text` 字色，**零字面色值**。
   ⚠★ 2026-09-25 修正（内描边令牌）：原写 `--hairline`，而它在 **A/B 两主题是 `transparent`**
     （`tokens.css` 原文「亮色专用 1px 发丝线；深色下 transparent」）
     ⇒ 默认主题下**这一圈根本不存在**，且**不报错、不出图差异**。
     同一文件 `:289`（`.pcard`）早已写下「描边必须用 `--line` 而不是 `--hairline`」的教训，
     **却只在那一处收口** ⇒ 本处是漏网的同族（元教训①「同族前科在册 ≠ 同形态已清」）。
     判据：本件对 `--hairline` 的 `var()` 引用数 —— 原 **1**，本批清 **0**（命令与读数见交付件）。
   位置 = `#msgs` 的**尾随子元素**（`renderNewPill()` 用 `insertAdjacentHTML('beforeend')` 追加）
     ⇒ `position:sticky;bottom:0` 相对最近的滚动容器（`.msgs`，`overflow-y:auto`）钉在视口底，
       `align-self:center` 居中；`newCount === 0` 时由 JS **移除节点**（不是 `visibility` 隐藏，
       否则 `.msgs` 的 `gap:12px` 会留一道空）。
   ⚠ 本元素是 `.msgs > *` ⇒ 吃 `.msgs > *{flex:0 0 auto}`（不伸不缩），与本规则不冲突。 */
.newpill{position:sticky;bottom:0;align-self:center;display:inline-flex;align-items:center;gap:6px;
  font-size:11.5px;color:var(--accent-text);cursor:pointer;
  background:var(--e3);box-shadow:inset 0 0 0 1px var(--line), 0 2px 10px var(--sh-1);
  border-radius:var(--r-full);padding:5px 12px}
.newpill .fg{width:9px;height:9px;position:relative}          /* 折角箭头，照抄 .chain-hd .fg 手法 */
.newpill .fg::before{content:"";position:absolute;left:1px;top:0;width:6px;height:6px;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(45deg)}
.newpill:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
/* ── ★ 中栏行「来源标」已删（2026-10-04 用户令「真机标识删除」）──────
   原 `.ntag.prov-real`（真机）整族撤除；`.ntag.prov-demo` **保留** —— 现被**输入区身份条**的
   「演示 / 离线」两态复用（见 `chat.js` `realComposerHtml`）。
   ⚠⚠ 类名仍须**前缀化** `prov-demo`、**不**用裸 `demo`：`shell.css:358` 有全局 `.demo{…display:none}`
     （「外观」演示浮层）⇒ 挂裸 `demo` 会被连坐成 `display:none`（实测：标"在 DOM 里、却不可见"）。 */
.ntag.prov-demo{background:var(--e4);color:var(--t3)}
/* ── ★ 2026-09-25 新增：下行「已发送」记录（对话站第 5 片）──────────────────────
   来源标**必须与前两族可分辨**（本仓「演示 / 真实必须可分辨」同一条纪律，只是这次分第三态）：
     · 真机 = accent（青玉）· 演示 = 中性（--e4/--t3）· **已发出 = info（蓝）**、异常 = danger（红）。
   为什么另起一族、不复用 `prov-real`：`prov-real` 说的是"这条会话是从真机**采到**的"，
   而"已发出"说的是"我们**从这儿发出去**了" —— 两回事；同形就等于教用户把两者当一种。
   ⚠ 同样**前缀化**（`prov-sent*`）、不落裸 `sent`：与全局单类选择器求交集时不会被连坐
     （同上方 `prov-real`/`prov-demo` 的教训）。色对取自 tokens 既有变量，**零新令牌**。 */
.ntag.prov-sent{background:var(--info-bg);color:var(--info)}
.ntag.prov-sentbad{background:var(--danger-bg);color:var(--danger-text)}
/* ★ 2026-09-25 挖缺陷补：**第三态「未确认」**（`queued` / `in_flight`）——
   后端 `_phase()` 把它单列（"还没被取走" / "发没发不知道"），前端来源标**必须也有它自己的样子**，
   否则会被并进「已发出」（那次合并正是本批抓到的一处"界面撒谎"）。
   色对 = amber（既有的"需注意"档，与 `.src.hand` 同族），随主题跟随，**零新令牌**。 */
.ntag.prov-sentwarn{background:var(--amber-bg);color:var(--amber)}
/* ★ 2026-09-25：无正文的指令（控制类动作 `pause`/`redetect`…）在气泡里给的**可读占位**。
   没有它就是一个**空气泡** —— "发了张图"与"坏了"用户一种也分不出（静默失败家族）。 */
.msg.out .bub .actph{color:var(--t3)}
/* ★ 2026-09-25：「已发送」入口 —— 在筛选行内，但**刻意不做第 5 片 `.fchip`**。
   理由（一句话）：那 4 片是**会话筛选**（同一集合的不同切片），「已发送」是**另一路数据**
   （中台下发的下行命令，与采集会话不同域）⇒ 语义不同，视觉上就必须能分辨：右对齐 + 描边。
   ⚠ 也正因为不是 `.fchip`，既有判据 `m3d ①`「筛选行四片 + 待处理同源徽标」
     **逐条不受影响**（它数 `.fchip`、且把 `chips[last]` 当「待处理」—— 混进去会把徽标读错）。
   ⚠ 类名同样**前缀化**（`.fgo` / `.fgn`），不落裸 `sent`（同上方 `prov-*` 的教训：
     全局单类选择器会连坐）。色对全部取自 tokens 既有变量，**零新令牌**。 */
/* ★★ 该行原本**不换行**（`shell.css:209` `.filters{flex:0 0 auto;display:flex}`）⇒ 中栏窄时
   第 5 个控件会被压成**竖排**（出图实测：`已/发/送` 三行 + 徽标错位）—— 这正是本仓
   「**PASS ≠ 图对**」的又一例：`_diag_sentview` 13/13 全绿，而**画出来是坏的**。
   ⇒ 让本行**允许折行**，并把入口钉成「不收缩 + 不折字 + 右对齐」：
      窄 ⇒ 整枚落到第二行右端；宽 ⇒ 与 4 片同排。两种形态都完整可读。 */
.filters{flex-wrap:wrap}
.filters .fgo{flex:0 0 auto;white-space:nowrap;margin-left:auto;display:flex;align-items:center;gap:6px;
  padding:3px 10px;border-radius:9999px;border:1px solid var(--e4);
  font-size:12px;color:var(--t2);cursor:pointer}
.filters .fgo:hover{color:var(--t1)}
.filters .fgo.on{background:var(--sel);border-color:transparent;color:var(--accent-text);font-weight:500}
.filters .fgo .fgn{font-size:10.5px;line-height:1;padding:2px 5px;border-radius:9999px;
  background:var(--info-bg);color:var(--info)}
/* 自动档"首个字符转手动"后的一次性提示（§1.7-2；本条发送后不再显示） */
.takenote{font-size:11px;color:var(--t3);padding:6px 1px 0}
/* 发送中防双击：`.send` 置灰（视觉 + 不可点由 JS 的 aria-disabled 兜） */
.send[aria-disabled="true"]{background:var(--e4);color:var(--t3);cursor:not-allowed}
.sysline{align-self:center;font-size:11px;color:var(--t3);background:var(--e4);border-radius:9999px;
  padding:4px 11px}

/* ── 消息骨架（加载态 · 简版）── */
.msgs.skeleton{gap:14px}
.sk-msg{display:flex;gap:8px;align-items:flex-start}
.sk-msg.out{flex-direction:row-reverse;align-self:flex-end}
.sk-av{width:34px;height:34px;border-radius:10px;background:var(--e4);flex:0 0 auto}
.sk-bub{height:44px;border-radius:14px;background:var(--e4);flex:0 0 auto}

/* ── 输入区 ── */
.composer{flex:0 0 auto;background:var(--e2);border-top:1px solid var(--line);
  padding:0 max(20px, calc((100% - 680px) / 2)) 12px}
#composer .sending-note{font-size:var(--fs-micro);color:var(--amber)}
.idbar{display:flex;align-items:center;gap:8px;padding:9px 1px 8px;font-size:12px;color:var(--t2)}
.idbar .dot{width:7px;height:7px;border-radius:50%;background:var(--accent);flex:0 0 auto}
.idbar b{color:var(--t1);font-weight:500}
.seg3{display:inline-flex;margin-left:auto;background:var(--e4);border-radius:10px;padding:2px}
.seg3 .m{font-size:12px;color:var(--t2);padding:3px 11px;border-radius:6px;cursor:pointer;
  border:1px solid transparent;transition:background 120ms,color 120ms,border-color 120ms}
.seg3 .m:hover{background:var(--e5);color:var(--t1)}
.seg3 .m.on{background:var(--accent-wash);color:var(--accent-text);font-weight:500;border-color:var(--hairline)}
.seg3 .m:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.tools{display:flex;align-items:center;gap:16px;padding-top:9px;color:var(--t3)}
.tools svg{color:currentColor}
.inputbox{background:var(--e4);border:none;border-radius:10px;height:44px;
  padding:11px 13px;font-size:15px;color:var(--t1);width:100%;resize:none;outline:none;
  font-family:inherit;line-height:1.45;display:block}
.inputbox::placeholder{color:var(--t2)}
.inputbox:focus{outline:2px solid var(--focus);outline-offset:1px}
.inputbox[disabled]{color:var(--t3);cursor:not-allowed}
.send{margin-left:auto;background:var(--accent);color:var(--on-accent);font-size:13px;font-weight:500;
  padding:6px 18px;border-radius:10px;cursor:pointer}
.send:hover{background:var(--accent-text)}
/* §10.10.4 焦点可见：发送键（role="button"）的键盘焦点环（既有 --focus，不新增色）。 */
.send:focus-visible{outline:2px solid var(--focus);outline-offset:1px}

/* ── 输入行：输入框与「素材」「发送」**同一行**（2026-10-18 用户令
   「素材和发送直接放到输入对话框的后边，把输入框可以放大到 2 行」）──
   ★ 为什么用固定 2 行高、不做"内容多了自己长高"：发送键与输入框**同行**，
     自动长高会让右侧两个键每次换行都跟着跳（点发送时手正在那个位置）⇒ 定高更好点。
   ★ `.send` 自带 `margin-left:auto`，在 flex 行里会和 `flex:1` 的输入框**争夺剩余空间**
     （同族坑：两个 auto 把空白对半分，见 2026-10-18 上一版工具条）。
     故这里显式归零，让"占满剩余宽度"只由 `.inputbox` 的 `flex:1` 一个执行者负责。
   ★ `.crow .inputbox`(0,2,0) 特异性高于 `.inputbox`(0,1,0) 与低高度段的同名规则，
     所以下面 `@media (max-height:820px)` 里那条 `height:38px` 不会把 2 行压回 1 行。 */
.crow{display:flex;align-items:flex-end;gap:8px}
.crow .inputbox{flex:1 1 auto;min-width:0;height:62px;padding:9px 13px;line-height:22px}
.crow .send{margin-left:0;flex:0 0 auto;height:36px;padding:0 16px;
  display:inline-flex;align-items:center;justify-content:center}
.autorow{display:flex;align-items:center;gap:9px;font-size:12px;color:var(--t2);padding:2px 0 8px}
.autorow .pill{display:flex;align-items:center;gap:6px;background:var(--sel);color:var(--accent-text);
  border-radius:9999px;padding:4px 11px;font-weight:500}
.autorow .pill i{width:6px;height:6px;border-radius:50%;background:var(--accent-text);display:block}
.autorow .lk{margin-left:auto;color:var(--t2);cursor:pointer}
.autorow .lk:hover{color:var(--t1)}

/* ── 输入区「N 条候选待选 · 拟稿可用度偏低」提示（建 2026-09-17；词面同日改齐）──
   词面 = 源① 上屏名「候选待选」+ 源① 数值词「拟稿可用度」（规格 §M3-4.7 ③ 映射表）；
   旧注释引的「N 条低把握候选待复核」用的是源② 的旗标名 + 状态名，已作废（RULES 53 / 规格 §M3-7 `m3.cand.hint.word`）。
   落在发送键左边（设计稿位置）。`.send` 自带 margin-left:auto ⇒ 提示自然靠右贴住发送键。
   块由 chat.js 在**条数 > 0 时**才渲染：0 条时整块不在 DOM 里（不是 display:none）——
   「0 条候选待选」是纯噪音，而"藏起来"与"没渲染"在验收脚本里是两回事。 */
.tools .hint{display:flex;align-items:center;gap:6px;color:var(--t2)}
.tools .hint b{color:var(--accent-text);font-weight:400}

/* ── 语音库面板（2026-10-17 · 用户令「语音发送是在语音库里选择发送」）──────────
   位置：固定内容条之下、输入框之上（`chat.js::realComposerHtml` 的 `vbar`）。
   ⚠ 面板**不做弹层**：它长在 composer 里，由 4 秒一次的整段重绘跟着重建
     （理由见 `chat.js::VOICE` 那段注：body 级浮层要自己跟着切会话/切号收拾）。
   ⚠ 类名一律 `.vlib-*`，引前全仓 0 命中（R58：不占别人的类名）。
   ⚠ 零颜色字面量：一律令牌（`consistency.mjs` ④ 守 js；css 这边同样不给裸色值）。 */
.vlib{background:var(--e2);border:1px solid var(--line2);border-radius:10px;
  padding:8px 10px;margin-bottom:8px;max-height:188px;display:flex;flex-direction:column}
.vlib-h{display:flex;align-items:center;gap:8px;padding-bottom:6px;
  border-bottom:1px solid var(--line);flex:0 0 auto}
.vlib-h b{color:var(--t1);font-size:var(--fs-caption);font-weight:500}
.vlib-sub{color:var(--t3);font-size:var(--fs-micro);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.vlib-x{margin-left:auto;color:var(--t2);font-size:var(--fs-micro);cursor:pointer;
  padding:2px 6px;border-radius:6px}
.vlib-x:hover{background:var(--e4);color:var(--t1)}
.vlib-b{overflow-y:auto;min-height:0;padding:2px 0}
.vlib-i{display:flex;align-items:center;gap:8px;padding:5px 2px;border-radius:6px}
.vlib-i:hover{background:var(--e3)}
.vlib-i.on{background:var(--accent-wash)}
.vlib-t{color:var(--t1);font-size:var(--fs-small);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto;min-width:0}
.vlib-m{color:var(--t3);font-size:var(--fs-micro);flex:0 0 auto;font-family:var(--font-num)}
.vlib-a{color:var(--t2);font-size:var(--fs-micro);cursor:pointer;flex:0 0 auto;
  padding:3px 8px;border-radius:6px;background:var(--e4)}
.vlib-a:hover{background:var(--e5);color:var(--t1)}
.vlib-a:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.vlib-a.go{background:var(--accent-wash);color:var(--accent-text)}
.vlib-a.go:hover{background:var(--accent);color:var(--on-accent)}
.vlib-a.del:hover{background:var(--stop-bg);color:var(--stop)}
.vlib-empty{color:var(--t3);font-size:var(--fs-micro);padding:10px 2px;line-height:1.6}
.vlib-f{display:flex;align-items:center;gap:10px;padding-top:6px;
  border-top:1px solid var(--line);flex:0 0 auto}
.vlib-up{color:var(--accent-text);font-size:var(--fs-micro);cursor:pointer;
  padding:3px 8px;border-radius:6px;background:var(--e4)}
.vlib-up:hover{background:var(--e5)}
.vlib-up:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.vlib-tip{color:var(--t3);font-size:var(--fs-micro);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
/* 工具条上「语音」按钮打开时的态（`.mbtn` 的内联样式只管基础外观） */
.mbtn.on{background:var(--accent-wash);color:var(--accent-text)}

/* ══ 素材库面板（2026-10-18 · 四页签：文字 / 图片 / 视频 / 语音）════════════
   ★ 与 `.vlib-*` 的关系：**视觉同源、类名不同**。
     刻意**保留** `.vlib-*` 不删：`/js/` 是 30 天强缓存，部署半途 / 强缓存混跑时
     旧 `chat.js` 仍会用那套类名渲染语音面板，删掉 CSS 会让那一段变成无样式裸文本
     （"面板还在、样子塌了"比"面板没出来"更难判断是哪一边的问题）。
   ⚠ 零颜色字面量：一律 var(--…)、**不留兜底值**（`consistency.mjs` ④ 守这条）。 */
.mat{background:var(--e2);border:1px solid var(--line2);border-radius:10px;
  padding:8px 10px;margin-bottom:8px;display:flex;flex-direction:column;
  min-height:0;max-height:210px}
.mat-h{display:flex;align-items:center;gap:6px;padding-bottom:6px;
  border-bottom:1px solid var(--line2);flex:0 0 auto;flex-wrap:wrap}
.mat-h b{color:var(--t1);font-size:var(--fs-caption);font-weight:500;margin-right:2px}
.mat-t{color:var(--t3);font-size:var(--fs-micro);cursor:pointer;
  padding:2px 7px;border-radius:5px}
.mat-t:hover{background:var(--e4);color:var(--t2)}
.mat-t.on{background:var(--accent-wash);color:var(--accent-text)}
.mat-t:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.mat-rc{margin-left:auto;color:var(--t3);font-size:var(--fs-micro);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;max-width:38%}
.mat-x{color:var(--t2);font-size:var(--fs-micro);cursor:pointer;
  padding:2px 8px;border-radius:5px;flex:0 0 auto}
.mat-x:hover{background:var(--e4);color:var(--t1)}
.mat-x:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.mat-b{overflow-y:auto;min-height:0;padding:2px 0;flex:1 1 auto}
.mat-i{display:flex;align-items:center;gap:8px;padding:5px 2px;border-radius:6px;
  cursor:pointer}
.mat-i:hover{background:var(--e3)}
.mat-i.on{background:var(--accent-wash)}
.mat-i:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.mat-th{width:34px;height:34px;border-radius:5px;object-fit:cover;flex:0 0 auto;
  background:var(--e4);border:1px solid var(--line2)}
/* 视频没有缩略图：用一块同尺寸的方块占位（**不是**假装有封面）。 */
.mat-thv{display:inline-flex;align-items:center;justify-content:center;
  color:var(--t3);font-size:10px}
.mat-tx{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}
.mat-n{color:var(--t1);font-size:var(--fs-small);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.mat-m{color:var(--t3);font-size:var(--fs-micro);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.mat-a{color:var(--t2);font-size:var(--fs-micro);cursor:pointer;flex:0 0 auto;
  padding:2px 7px;border-radius:5px}
.mat-a:hover{background:var(--e5);color:var(--t1)}
.mat-a:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.mat-a.del:hover{background:var(--stop-bg);color:var(--stop)}
.mat-empty{color:var(--t3);font-size:var(--fs-micro);padding:10px 2px;line-height:1.6}
.mat-empty.err{color:var(--stop)}
.mat-f{display:flex;align-items:center;gap:10px;padding-top:6px;
  border-top:1px solid var(--line);flex:0 0 auto;flex-wrap:wrap}
.mat-up{color:var(--accent-text);font-size:var(--fs-micro);cursor:pointer;
  padding:3px 8px;border-radius:6px;background:var(--e4);flex:0 0 auto}
.mat-up:hover{background:var(--e5)}
.mat-up:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.mat-tip{color:var(--t3);font-size:var(--fs-micro);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.mat-msg{color:var(--t2);font-size:var(--fs-micro);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.mat-msg.err{color:var(--stop)}

/* ── 素材 · 采集链接（2026-10-18）─────────────────────────────────────
   前缀 `mat-lnk-` 而不是 `lnk-`：面板在 composer 里，任何**裸类名**都可能撞上
   customers.css / colleagues.css 的同名规则（血训：`.cm` 撞过一次，整块变灰格子）。
   面板宽约 320px，所以这里只用「一行标题 + 一两行说明 + 一块 url」三层，
   不做表格、不做两列。 */
.mat-lnk-hd{display:flex;align-items:baseline;gap:8px;padding:4px 2px 2px}
.mat-lnk-hd b{color:var(--t1);font-size:var(--fs-small);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;max-width:60%}
.mat-lnk-st{color:var(--t3);font-size:var(--fs-micro);flex:0 0 auto}
.mat-lnk-st.ok{color:var(--accent-text)}
.mat-lnk-sub{color:var(--t3);font-size:var(--fs-micro);line-height:1.55;padding:1px 2px}
.mat-lnk-sub b{color:var(--t2);font-weight:600}
.mat-lnk-sub.warn{color:var(--accent-text)}
/* url 用等宽 + 可换行：教练要**肉眼确认**这是 ge.moiv.cn 那条（而不是别的域的旧链接）。 */
.mat-lnk-url{margin:6px 2px 0;padding:6px 8px;border-radius:6px;background:var(--e3);
  color:var(--t1);font-size:var(--fs-micro);line-height:1.5;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  word-break:break-all;user-select:all}
.mat-lnk-ops{display:flex;align-items:center;gap:6px;padding:5px 2px 0}
.mat-lnk-tip{color:var(--t3);font-size:var(--fs-micro);line-height:1.5;padding:4px 2px 0}
/* 采集链接页签比素材页签多三行说明 + 一块 url ⇒ 210px 会被裁掉尾部提示
   （实测第一版末尾那句"明文只显示这一次"正好在可视区外 ⇒ 教练不知道刷新会丢）。
   只给这一页签放宽，不动另外四个页签的高度（它们行数多、需要原高度）。 */
.mat.mat-wide{max-height:340px}

/* ── 右栏：客户资料 ── */
.side{flex:0 0 var(--side-w,300px);width:var(--side-w,300px);background:var(--e1);
  padding:14px;display:flex;flex-direction:column;min-height:0;overflow-y:auto}
/* ★ 去掉程序化聚焦时的焦点环（2026-09-17）：窄窗抽屉打开时 JS 会把焦点移进来（#side 带
   `tabindex="-1"`，供读屏软件宣布"客户资料栏"），Chrome 会因此画一圈 `-webkit-focus-ring`
   —— 一条蓝色描边挂在抽屉左缘，看着像"选中态"。它**不是**键盘导航来的焦点（tabindex=-1
   的元素 Tab 不到，永远不会进入 `:focus-visible`）⇒ 视觉上必须无痕。 */
.side:focus{outline:none}
.side::-webkit-scrollbar{width:8px}
.side::-webkit-scrollbar-thumb{background:var(--scroll);border-radius:4px}
.side::-webkit-scrollbar-track{background:transparent}
/* ── 段（sect）────────────────────────────────────────────────────────
   段头 = 竖条 + 小标题（取代原来「素文字 slab + 一条分隔线」）。
   ★ 为什么改：右栏从 2 段变 4 段之后，一条分隔线只表达"这里断开"，
   已经读不出结构；段头同时表达"这一段叫什么"。
   ★ 绿色竖条不是"符号"：它没有任何需要背的含义（不需要图例），
   只是段头的视觉锚点 ⇒ 不违反「符号必须配图例 = 培训成本 = 不要」。 */
.sect{flex:0 0 auto}
.sect + .sect{margin-top:11px}
.sect .sh{display:flex;align-items:center;gap:7px;margin-bottom:7px}
.sect .sh .ln{width:3px;height:11px;border-radius:2px;background:var(--accent);flex:0 0 auto}
.sect .sh .t{font-size:12px;font-weight:500;color:var(--t2);letter-spacing:.5px}
.sect .sh .n{margin-left:auto;font-size:11px;color:var(--t3);white-space:nowrap}
.sect .sh .n .num{font-family:var(--font-num);font-variant-numeric:tabular-nums}

/* ★ AI 副驾 · 会话研判 —— 右栏第一眼。
   底/描边走 --ai-cop / --ai-line（四主题各一套）⇒ 组件里**不出现字面色值**，
   否则在 B/C/D 主题下会直接崩（暖主题里冒出一块冷蓝）。 */
.cop{border-radius:12px;padding:11px 12px 10px;background:var(--ai-cop);
  border:1px solid var(--ai-line)}
.cop-hd{display:flex;align-items:center;gap:7px;margin-bottom:8px}
.cop-hd .ci{width:20px;height:20px;border-radius:6px;flex:0 0 auto;display:inline-flex;
  align-items:center;justify-content:center;color:var(--on-accent);
  background:linear-gradient(135deg,var(--ai-1),var(--ai-2))}
.cop-hd b{font-size:12.5px;font-weight:600;color:var(--ai-fg)}
/* LIVE 标：绿 = 「正在实时研判」的语义，四主题一律绿（--ai-em × --ai-live-*）——
   不跟主题强调色走：强调色在 B 主题是香槟金，与"在线"的语感相反。 */
.cop-hd .clv{margin-left:auto;font-size:10px;font-weight:700;letter-spacing:.5px;
  color:var(--ai-em);background:var(--ai-live-bg);
  border:1px solid var(--ai-live-line);border-radius:5px;padding:1px 6px}
.cop .cr{display:flex;align-items:flex-start;gap:8px;padding:4px 0;font-size:12px;line-height:1.45}
.cop .cr .k{width:34px;flex:0 0 34px;color:var(--t3)}
.cop .cr .v{flex:1;min-width:0;color:var(--t1);overflow-wrap:anywhere}
.cop .cr .v.hot{color:var(--danger-text);font-weight:600}
.cop .cr .v.warn{color:var(--amber);font-weight:600}
/* 研判把握条 —— 与聊天列链路条的「把握度条」**同一套度量语言**（同令牌：
   --ai-node-line 当轨、ai-1→ai-2 渐变当填充、--ai-em 当读数）⇒ 零学习成本。
   ★ 这也是"一个度量只有一种画法"：同一个把握度，换到右栏也不该换个样子。 */
.cop .cg{display:flex;align-items:center;gap:8px;margin-top:8px;padding-top:8px;
  border-top:1px solid var(--ai-line)}
.cop .cg .gk{font-size:11px;color:var(--t3);flex:0 0 auto}
.cop .cg .gbar{flex:1 1 auto;height:3px;border-radius:2px;background:var(--ai-node-line);
  display:block;overflow:hidden}
.cop .cg .gbar i{display:block;height:100%;border-radius:2px;
  background:linear-gradient(90deg,var(--ai-1),var(--ai-2))}
.cop .cg .gv{font-size:12px;font-weight:700;color:var(--ai-em);flex:0 0 auto}

/* ★ 置顶行（要紧的事里"顶上去"的那条）
   两态由**数据**决定，不靠文案猜：`red` ⇒ 危险色（敏感/合规红线）；
   只有 `hot` 没有 `red` ⇒ 琥珀（待复核类）。 */
.hotrow{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:9px;
  margin-bottom:6px;font-size:12.5px;
  background:var(--danger-bg);border:1px solid var(--danger-line)}
.hotrow .k{color:var(--t2);font-size:11.5px;flex:0 0 auto}
.hotrow .v{color:var(--danger-text);font-weight:600;min-width:0;overflow-wrap:anywhere}
.hotrow .j{margin-left:auto;color:var(--accent-text);font-weight:500;font-size:11.5px;
  cursor:pointer;white-space:nowrap}
.hotrow .j:hover{text-decoration:underline}
.hotrow.warn{background:var(--amber-bg);border-color:var(--amber-line)}
.hotrow.warn .v{color:var(--amber)}

/* ★ 卡片：承载「要紧的事 / 这人是谁」里的普通行。
   描边必须用 --line 而不是 --hairline：--hairline 在深色主题下是 transparent
   （深色靠明度跳变、不靠描边），而卡片是**浮在右栏底色上的面**，没有边就只剩一块色。 */
.pcard{background:var(--e3);border:1px solid var(--line);border-radius:10px;padding:2px 11px}
.prow{display:flex;align-items:flex-start;gap:8px;padding:5px 0;border-bottom:1px solid var(--line);
  font-size:12.5px;color:var(--t1);line-height:1.5}
.prow:last-child{border-bottom:none}
.prow .k{width:64px;flex:0 0 64px;font-size:11.5px;color:var(--t3);padding-top:1px}
.prow .v{flex:1;min-width:0;overflow-wrap:anywhere}
.prow .v.mute{color:var(--t3)}
/* 健康红线的值要「重读」：加粗 + 危险色（设计稿 `.krow .v b` 同款）。
   由数据里的 `bold` 驱动（不是靠文案里有没有"红线"两个字去猜）。 */
.prow .v b{color:var(--danger-text);font-weight:600}
.jump{color:var(--info);font-weight:500;font-size:12px;margin-left:6px;cursor:pointer;white-space:nowrap}
.jump:hover{text-decoration:underline}

/* ★ 资料三来源 → 三色标。三色各有其义，且**零新令牌**（各取本主题既有语义色）：
     聊到 = AI 从聊天里识别   → 青玉（--accent-text / --accent-wash，本主题的 AI 色系）
     自填 = 客户在表单里填的   → 蓝（--info / --info-bg）
     手填 = 操作员手抄进来的   → 金（--amber / --amber-bg）
   ⚠ 第二类名取 `talk` 而非 `chat`：`.chat` 是本文件**已有**的全局类
   （会话区那一列，`.chat{min-width:420px}`）⇒ `class="src chat"` 会被它命中，
   来源标宽度被钉成 420px、右栏多出一条横向滚动条（实测溢出 342px，图看着"还在"）。
   **新增类名必须与全局单类选择器求一次交集** —— 差一个字母就是静默错。 */
.src{font-size:10px;font-weight:500;border-radius:4px;padding:1px 5px;flex:0 0 auto;
  line-height:1.5;white-space:nowrap}
.src.talk{color:var(--accent-text);background:var(--accent-wash)}
.src.self{color:var(--info);background:var(--info-bg)}
.src.hand{color:var(--amber);background:var(--amber-bg)}

/* ★ 减脂三数 —— 客户档案里最要紧的三个数，第一眼就要扫到
   ★ 来源标**另起一行**而不是跟在数字后面：实测右栏收到最窄（--side-w:208px ⇒
   内容 172px、每格 53px）时，"165" + "自填" 一行放不下会被压成省略或溢出。
   竖直方向右栏本来就可滚（.side{overflow-y:auto}），横向挤坏才是不可恢复的。 */
/* ★ 减脂三数 = 右栏第一眼（设计稿源码 `.vit`：`padding:8px 6px 9px` + 强调面 + 19px/700 数字 + 单位做注脚）。
   设计稿的底/描边是**字面色值** `rgba(63,208,138,…)` —— 本文件一律走令牌：
   `--accent-wash` + `--accent-line`（本项目与 --danger-line / --amber-line 同族的 `-line`）。
   三张卡**同一强调**（设计稿里三张绿调一致），「目标」另用 --accent-text 提亮 = 方向。 */
.vitals{display:flex;gap:6px;margin-bottom:8px}
.vit{flex:1;min-width:0;border-radius:10px;padding:8px 5px 9px;text-align:center;
  background:var(--accent-wash);border:1px solid var(--accent-line);box-sizing:border-box}
.vit .l{display:block;font-size:10.5px;color:var(--t3);margin-bottom:4px}
.vit .n{display:block;font-size:19px;font-weight:700;color:var(--t1);line-height:1.15;
  letter-spacing:-.3px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
/* 单位 = 注脚。**值缺测时 JS 不渲染它** ⇒ 这里不写 :empty 之类的兜底（兜底会让「—斤」也能出）。 */
.vit .n em{font-style:normal;font-size:10.5px;font-weight:500;color:var(--t3);
  margin-left:2px;letter-spacing:0}
.vit .n .src{display:block;width:fit-content;margin:4px auto 0;font-size:9.5px;padding:0 4px}
/* 目标 = 方向，与「现重」构成一对照 ⇒ 单独着色 */
.vit.tgt .n{color:var(--accent-text)}

/* ★ 跨账号提示：独立一段 + 卡片（此前只是一行看不出能点的灰字）
   「关联 ↗」现在有 data-xa 委派（toast），与 data-jump / data-gap 同一口径。 */
.otherrow{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:9px;
  font-size:12px;color:var(--t2);background:var(--info-bg);border:1px solid var(--line2)}
.otherrow .oi{color:var(--info);display:inline-flex;flex:0 0 auto}
.otherrow .lk{margin-left:auto;color:var(--accent-text);font-weight:500;cursor:pointer;white-space:nowrap}
.otherrow .lk:hover{text-decoration:underline}

.side-bottom{margin-top:auto;flex:0 0 auto;padding-top:11px;border-top:1px solid var(--line)}
.gap-line{font-size:13px;font-weight:500;color:var(--accent-text);padding-bottom:10px;cursor:pointer}

/* ── ★ 右栏窄栏压缩：按**容器宽**降级，不用 media query ────────────────────
   `.side` 的宽度有**三个自变量**：
     ① 视口（--side-w 断点 208/300/320）
     ② 左右栏拖拽（splitter 写内联 --side-w）
     ③ 窄窗抽屉态（min(300px, 100vw−68)）
   media query 只看得见① ⇒ 会出现「窗口没动、只是把右栏拖窄了，行就折成两行」这种
   静默剪裁。⇒ 与本项目 `.chain` 同一处理：容器查询。
   实测（2026-09-17）：`--side-w` 收到 208（内容宽 172）时，几乎每行都折成 2 行
   ⇒ 右栏内容高 1084 / 可用 754，要滚 330px（**内容全对，只是要滚**；1440 档
   content 754 == avail 754，正好不滚）。本段把折行带来的空隙压掉一部分。
   ⚠ 折行本身压不掉：「未兑现承诺」这种 5 字标签 + 10 字值在 172px 里就是放不下一行。 */
.side{container-type:inline-size}
/* 内边距**不能**写进 @container：容器查询只作用于**后代**，`.side` 查不到自己。
   ⇒ 走 media query，断点与 shell.css 里 `--side-w:208px` 那条**对齐**；
   border-box 下把左右内边距 18→13 等于内容宽 172→182（+10px ⇒ 少折几行）。 */
@media (max-width:1279px){
  .side{padding:16px 13px 12px}
}
@container (max-width:232px){
  .sect + .sect{margin-top:10px}
  .sect .sh{margin-bottom:5px}
  .cop{padding:9px 10px 8px}
  .cop .cr{padding:3px 0}
  .cop .cg{margin-top:6px;padding-top:6px}
  .pcard{padding:1px 9px}
  .prow{padding:4px 0}
  .prow .k{width:54px;flex:0 0 54px}
  .hotrow{padding:6px 9px;margin-bottom:6px}
  .vitals{margin-bottom:6px}
  /* 窄栏（右栏声明 208）实测可用内容宽 ≈ 50px/卡：19px 数字 + 10.5px 单位会折行
     ⇒ 降到 16px + 9.5px 单位，实测单行放得下（否则那一折行会顶掉下方余量）。 */
  .vit{padding:5px 3px 6px}
  .vit .n{font-size:16px}
  .vit .n em{font-size:9.5px;margin-left:1px}
  .vit .n .src{margin-top:3px}
  .otherrow{padding:7px 9px}
}

/* ══ ★ AI 推理链路条（本轮新增）════════════════════════════════════════
   令牌：--ai-* 共 18 个，四主题各一套（tokens.css）。
   ★ 纪律（《交互规格-采集机AI视觉》§CJ-1.1）：**落 app/ 只读令牌、不抄字面值** ——
     hero / mockup 里的那些**局部色值**（具体十六进制值见该册 §CJ-1.1 对照表）**只准留在 mockup/ 出图件**，
     迁到 app 一律写 `var(--ai-*)`；且**注意角色对调**：**hero 的主强调绿 = app 的 `--ai-2`（青玉）**、
     **hero 的次强调蓝 = app 的 `--ai-1`（信息色）**。下方各属性的值即示范（本组件零字面色值）。
   两个正交维度，都用属性选择器表达，互不覆盖：
     [data-lv="lite|std|rich"]  body 的信息量        （lite 只留节点名）
     [data-fold="1"]            body 显不显示（折叠成一行摘要）
   ★ 宽度自适应用 **container query 而不是 media query**：
     本组件的可用宽度取决于「会话区宽度 − 两侧居中留白」，而会话区宽度会被
     左栏拖拽（#sp1）改变 —— 视口宽度根本不是它的自变量。用 media query 会出现
     「窗口没动、拖了一下左栏，链路条就溢出了」这种静默剪裁（RULES 5 的典型形态）。 */
.chain{
  align-self:stretch;max-width:100%;position:relative;overflow:hidden;
  border-radius:12px;padding:8px 12px 9px 14px;
  background:var(--ai-surface);border:1px solid var(--ai-line);
  display:flex;flex-direction:column;gap:8px;
}
.chain::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2.5px;
  background:linear-gradient(180deg,var(--ai-1),var(--ai-2))}
.chain::after{content:"";position:absolute;left:0;right:0;top:0;height:1.5px;opacity:.5;
  background:linear-gradient(90deg,transparent,var(--ai-1),var(--ai-2),transparent)}
.chain .num{font-family:var(--font-num);font-variant-numeric:tabular-nums}

/* ── 头部（也是折叠开关）── */
.chain-hd{display:flex;align-items:center;gap:7px;font-size:11.5px;cursor:pointer;
  user-select:none;border-radius:6px}
.chain-hd .cb{color:var(--ai-1);font-size:11px;line-height:1.5}
.chain-hd b{color:var(--ai-fg);font-weight:600;font-size:11.5px}
.chain-hd .sum{margin-left:auto;font-size:11px;color:var(--ai-sum);white-space:nowrap}
/* 摘要里的「耗时 · 知识门」只在折叠态出现 —— 展开时 mtx 行已给出同样两个数，
   同屏重复读数不是"信息更多"而是"不知道信哪个"。 */
.chain:not([data-fold="1"]) .chain-hd .sum .s2{display:none}
.chain-hd .fg{width:13px;height:13px;flex:0 0 13px;position:relative;color:var(--ai-fold)}
.chain-hd .fg::before{content:"";position:absolute;left:3px;top:4.5px;width:6px;height:6px;
  border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:rotate(-135deg);transition:transform var(--dur-2)}
.chain[data-fold="1"] .chain-hd .fg::before{transform:rotate(45deg)}
.chain-hd:hover .fg{color:var(--ai-1)}
.chain-hd:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* ── body：档位与折叠的显隐矩阵 ── */
.chain-bd{display:flex;flex-direction:column;gap:7px}
.chain[data-fold="1"] .chain-bd{display:none}
.chain[data-lv="lite"] .nodes,
.chain[data-lv="lite"] .mtx,
.chain[data-lv="lite"] .rich{display:none}
.chain:not([data-lv="lite"]) .chain-lite{display:none}
.chain[data-lv="std"] .rich{display:none}

/* lite 档：一行节点名，用箭头串起来（无连线、无序号、无指标）*/
.chain-lite{display:flex;align-items:center;flex-wrap:wrap;gap:6px;font-size:12px}
.chain-lite .lt{color:var(--ai-fg);font-weight:500}
.chain-lite .ar{color:var(--ai-fold);font-size:10px}

/* ── 节点（std / rich）──
   nb 用 grid 跨两行：不额外包一层 div，避免为了布局改 DOM 结构。
   节点内一律 k 在上、v 在下 —— 单行排（k 与 v 并排）四节点实测 ≥780px，
   在 chat.min=420 下必被 overflow:hidden 静默吃掉尾部，正是 RULES 5 那一类。 */
.chain .nodes{display:flex;align-items:center;flex-wrap:wrap;row-gap:6px}
.chain .nd{display:grid;grid-template-columns:auto 1fr;grid-template-rows:auto auto;
  column-gap:5px;row-gap:1px;align-items:center;flex:0 0 auto;
  padding:5px 8px 5px 7px;border-radius:8px;
  background:var(--ai-node-bg);border:1px solid var(--ai-node-line)}
.chain .nd .nb{grid-row:1/3;grid-column:1;width:15px;height:15px;border-radius:5px;
  background:var(--ai-chip-bg);color:var(--ai-chip-fg);font-style:normal;font-size:9.5px;
  font-weight:600;font-family:var(--font-num);display:flex;align-items:center;justify-content:center}
.chain .nd .k{grid-column:2;font-size:10.5px;color:var(--ai-node-k);white-space:nowrap}
.chain .nd .v{grid-column:2;font-size:12.5px;font-weight:500;color:var(--ai-node-v);white-space:nowrap}
.chain .nd .v em{font-style:normal;color:var(--ai-em);font-size:11px;margin-left:4px}

/* ── 连线 ── */
.chain .lk{flex:1 1 16px;min-width:12px;height:1.5px;border-radius:1px;
  background:var(--ai-track);opacity:.75}
/* 「两路召回 → 融合排序」之间是**两股汇入**，不是一条直线：这是本组件
   唯一承载「并行召回」语义的地方（数据里 steps[1].track = true 才渲染）。 */
.chain .tracks{flex:0 0 30px;align-self:center}
.chain .tracks .tra{stroke:var(--ai-1);stroke-width:1.6;stroke-linecap:round}
.chain .tracks .trb{stroke:var(--ai-2);stroke-width:1.6;stroke-linecap:round}

/* ── 指标行（std / rich）── */
.chain .mtx{display:flex;align-items:center;gap:14px;padding-top:6px;
  border-top:1px solid var(--ai-line);font-size:10.5px}
.chain .mtx .mx{display:flex;align-items:center;gap:5px}
.chain .mtx .mk{color:var(--ai-sum)}
.chain .mtx .mv{color:var(--ai-node-v);font-weight:600;font-size:11px}

/* ── rich 档增量：检索口径链 + 把握度 ── */
.chain .rich{display:flex;align-items:center;gap:12px;padding-top:6px;
  border-top:1px solid var(--ai-line);font-size:10.5px;color:var(--ai-sum)}
.chain .rich .scan b{color:var(--ai-node-v);font-weight:600;font-size:11px}
.chain .rich .ar{margin:0 4px;color:var(--ai-fold);font-size:9.5px}
.chain .rich .conf{margin-left:auto;display:flex;align-items:center;gap:6px;flex:0 0 auto}
.chain .rich .cbar{width:88px;height:4px;border-radius:2px;background:var(--ai-node-line);
  display:block;overflow:hidden}
.chain .rich .cbar i{display:block;height:100%;border-radius:2px;
  background:linear-gradient(90deg,var(--ai-1),var(--ai-2))}
.chain .rich .cv{color:var(--ai-node-v);font-weight:600;font-size:11px}

/* ══ ★ AI 候选卡（2026-09-17 · 首屏「AI 感」方案）══════════════════════════════
   位置 = 消息流内、链路条之后（见 chat.js candHtml 头注）。视觉与链路条**同族**：
   同一对 --ai-* 令牌（--ai-surface 底 / --ai-line 描边 / ::before 左缘 ai-1→ai-2 竖条），
   因为两者是同一条「AI 出品链」的两段 —— 上面答"它怎么想出来的"、下面答"它想出了什么"。
   ★ `.cands` 是本组件的**唯一作用域根**（RULES 58）：`cnt` / `bg` / `bar` / `conf` 这类
     短名在全库已有别的消费者，不裹唯一根迟早互相串味。根名 `cands` 全库匹配数 = 0。
   ★★ RULES 58 实例登记（C3，2026-09-17）：类名 `.conf` 在**两处**出现，靠祖先选择器区分：
        · `.chain .rich .conf`（:372） = 链路条的**研判把握**（同源 `a.conf`，与副驾同词）
        · `.cands .conf`       （下文）  = 候选卡的**拟稿可用度**（另一个量，另一个词）
      ⇒ 同名两个组件、两种含义 —— 正是 RULES 58 说的那种"短名跨组件复用"。
      **类名不改**（改就要动 css+js 两处，且断言锚在此名上）：靠 `.chain .rich` / `.cands`
      两个唯一祖先根把规则彻底隔开；日后任一处放宽选择器即串味。
   ★ 全部选择器**零字面色值**（四主题只换令牌值）；唯一"手写"的是渐变停靠点，用的也是令牌。 */
.cands{align-self:stretch;max-width:100%;display:flex;flex-direction:column}
.cands .candhd{display:flex;align-items:center;gap:8px;font-size:11.5px;
  color:var(--t2);margin:3px 0 4px}
.cands .candhd .cnt{color:var(--ai-1);font-weight:600;font-variant-numeric:tabular-nums}
.cands .candhd .rr{margin-left:auto;color:var(--accent-text);font-weight:600;font-size:11.5px;
  cursor:pointer;border-radius:5px}
.cands .candhd .rr:hover{text-decoration:underline}
.cands .candhd .rr:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.cands .cand{position:relative;overflow:hidden;border-radius:11px;
  padding:9px 13px 9px 15px;margin-bottom:6px;
  background:var(--ai-surface);border:1px solid var(--ai-line)}
.cands .cand:last-child{margin-bottom:0}
/* ★ 2026-10-14（性能整改 P1-C3）：「AI 正在想」占位态。来信已到、候选未出的
   那几秒，占位卡以**弱化描边 + 呼吸闪烁**提示正在生成，候选一到即被真实卡顶掉。
   用 `.pending` 而不是新组件根：同属 `.cands` 作用域（RULES 58），验收锚点不变。 */
.cands .cand.pending{background:transparent;border-style:dashed;opacity:.72;
  animation:candPending 1.6s ease-in-out infinite}
.cands .cand.pending::before{background:var(--ai-line)}
.cands .cand.pending .ctx{color:var(--t2)}
@keyframes candPending{0%,100%{opacity:.45}50%{opacity:.85}}
.cands .cand::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2.5px;
  background:linear-gradient(180deg,var(--ai-1),var(--ai-2))}
/* 首推候选的顶缘细渐变线 = 链路条顶端同一手法（同族标识）⇒ 两条候选里一眼可辨哪个是首推。
   与链路条那条的差别：**不透明渐变到底**（链路条那条两端透明）—— 首推要更"实"。 */
.cands .cand.best::after{content:"";position:absolute;top:0;left:0;right:0;height:1.5px;
  background:linear-gradient(90deg,var(--ai-1),var(--ai-2));opacity:.8}
.cands .cand.best{border-color:var(--ai-live-line);
  box-shadow:0 0 22px var(--ai-chip-bg),inset 0 1px 0 var(--ai-src-bg)}
.cands .cand .ctx{font-size:14.5px;line-height:1.5;color:var(--t1)}
/* ★ 方式徽章（2026-10-15 用户令「候选前面标方式」）：正文前的来源标签。
   主候选 `.best` 用主色系实底（一眼看出是首推）；变体用弱描边底（次推）。
   ⚠ 用 inline-flex 与正文同行、留右间距，不独占一行（省高度）。 */
.cands .cand .ctx .cmod{display:inline-flex;align-items:center;margin-right:7px;vertical-align:1px;
  font-size:10.5px;font-weight:600;line-height:1;border-radius:5px;padding:3px 7px;
  white-space:nowrap;flex:0 0 auto}
.cands .cand .ctx .cmod.best{color:var(--on-accent);background:linear-gradient(180deg,var(--accent-text),var(--accent))}
.cands .cand .ctx .cmod:not(.best){color:var(--t2);background:var(--ai-src-bg);border:1px solid var(--ai-src-line)}
.cands .cand .cft{display:flex;align-items:center;flex-wrap:wrap;gap:10px 10px;margin-top:7px}
/* 依据标 `.bg` 是**逐条上屏**的 —— 它是「AI 候选不是黑箱」这句话的唯一物证，
   所以既不能被压没（min-width:0 + 不截断），也不能把置信度挤出行外（见下方 620 容器查询）。
   ★ 2026-10-14（用户报「话术下方『模型、依据』那一行显示不全、超出了」）：
     依据标签**必须换行完整显示**（flex-wrap）—— 之前 `.cbase` 默认 nowrap，
     多条依据（知识库/画像/人设/闲聊·模型生成）横排一行，溢出 `.cand{overflow:hidden}`
     被裁掉，后半条依据看不见 ⇒ 违反「候选不是黑箱」这条产品底线。 */
.cands .cand .cbase{display:flex;align-items:center;flex-wrap:wrap;gap:6px;row-gap:4px;min-width:0;
  font-size:10.5px;color:var(--t3)}
.cands .cand .cbase .bg{color:var(--t2);background:var(--ai-src-bg);border:1px solid var(--ai-src-line);
  border-radius:5px;padding:1px 6px;white-space:normal;word-break:break-word;max-width:100%}
.cands .conf{margin-left:auto;display:flex;align-items:center;gap:7px;flex:0 0 auto}
/* ★C2 标签「拟稿可用度」：与链路条的「研判把握」**同款位置不同词**（一个量一个词）。
   用 --t3 弱化：它是标签不是读数，读数靠右边的 `.pc` 数字承担。 */
.cands .conf .cl{font-size:10.5px;color:var(--t3);white-space:nowrap}
.cands .conf .bar{width:62px;height:3px;border-radius:2px;overflow:hidden;background:var(--ai-node-line)}
.cands .conf .bar i{display:block;height:100%;border-radius:2px;
  background:linear-gradient(90deg,var(--ai-1),var(--ai-2));box-shadow:0 0 8px var(--ai-chip-bg)}
.cands .conf .pc{font-size:10.5px;font-weight:700;color:var(--ai-2);font-variant-numeric:tabular-nums}
.cands .cact{display:flex;gap:7px;margin-top:7px}
.cands .cbtn{font-size:11.5px;border-radius:7px;padding:3px 12px;cursor:pointer;
  border:1px solid var(--line2);background:var(--e3);color:var(--t2)}
.cands .cbtn:hover{border-color:var(--line3);color:var(--t1)}
.cands .cbtn:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
/* 「采用并发送」= 唯一会真的发消息的按钮 ⇒ 视觉上必须是这一组里最重的那一个
   （实底 + --on-accent 文字；--on-accent 随主题反转，所以四主题都保证可读）。 */
.cands .cbtn.go{background:linear-gradient(180deg,var(--accent-text),var(--accent));
  color:var(--on-accent);font-weight:700;border-color:transparent;
  box-shadow:0 4px 16px var(--ai-chip-bg)}

/* ── 窄会话区降级：依据 + 置信度并肩放不下时**竖排**（依据绝不允许被截断 —— 截断即黑箱）──
   拐点 620px 与链路条同一判据：同一个容器（.msgs）、同一个断点，两处行为必须一致。 */
@container (max-width:620px){
  .cands .cand .cft{flex-direction:column;align-items:flex-start;gap:6px}
  .cands .conf{margin-left:0}
  .cands .cand .cbase{flex-wrap:wrap;row-gap:4px}
}

/* ── 窄会话区降级：横向流程线放不下时改 2×2 网格（序号仍在 ⇒ 顺序语义不丢）──
   实测拐点：会话区 ≈660px（= 容器 620px + 两侧最小留白 40） */
@container (max-width:620px){
  .chain .nodes{display:grid;grid-template-columns:1fr 1fr;gap:6px}
  .chain .lk,.chain .tracks{display:none}
  .chain .nd{width:100%}
  .chain .rich{flex-wrap:wrap;row-gap:5px}
  .chain .rich .conf{margin-left:0}
}

/* ── 低高度降级：输入区各件按序让位（配合 shell.css 的 ≤820 段）── */
@media (max-height:820px){
  .chain{padding:6px 10px 7px 12px;gap:6px}
  .chain .nd{padding:4px 7px 4px 6px}
  .chain .nd .v{font-size:12px}
  .chain .mtx,.chain .rich{padding-top:5px}
}

/* ── 低高度降级：输入区各件按序让位（配合 shell.css 的 ≤820 段）── */
@media (max-height:820px){
  .composer{padding:0 max(20px, calc((100% - 680px) / 2)) 8px}
  .idbar{padding:6px 1px 6px}
  .autorow{padding:0 0 6px}
  .inputbox{height:38px;padding:9px 12px}
  /* 低高度：2 行仍保 2 行，只压行高与留白（退回 1 行会让"能打两行"这条能力在
     小屏笔记本上直接消失，且用户是按 2 行提的需求） */
  .crow .inputbox{height:52px;padding:7px 11px;line-height:19px}
  .crow .send,.crow .mbtn{height:32px}
  .tools{padding-top:6px}
  .msgs{padding:12px max(20px, calc((100% - 680px) / 2));gap:11px}
  .side{padding:13px 18px 11px}
}

/* ★★ 规格 §6.1 铁律 13：源序必须排在 .chat-hd / .composer 规则之后，覆盖才生效 ★★
   只加 .msgs{min-height:150px} 不够 —— chat-hd/composer 必须真的可收缩，否则是 .chat 整体溢出。*/
.msgs{min-height:150px}
.chat-hd,.composer{flex-shrink:1}

/* ★ 滚动容器的直接子项不参与压缩 —— 这不是"美观微调"，是防静默剪裁。
   .msgs 的 display 是 flex column，子项默认 flex-shrink:1：窗口一矮，flex 会
   **压缩子项**而不是让容器滚动。链路条 .chain 恰好带 overflow:hidden，于是它被
   压矮后超出的内容被**静默剪掉**（实测 700px 高：条高被压到 90px、内容溢 47px、
   退出码 0 不报错）。同理气泡 .msg 被压也会剪掉行。
   ⇒ 子项一律 flex:0 0 auto（不伸不缩，按内容定高），超高交给 .msgs 滚动。 */
.msgs > *{flex:0 0 auto}

/* ═══════════ 固定内容条（2026-10-05）═════════════════════════════════════
   ⚠ 设计约束（都来自实测，不是审美偏好）：
   · 放在**输入框上方** —— 点完就能看见草稿，不用找"我点的那条去哪了"
   · 高度**只够一行**（超过就换行）⇒ 会话列表不能被顶掉太多
   · 「没取到 / 取不到 / 没配」**三态各有各的说法**（见 fixedBarHtml）：
     合成一种就是把排查引到错方向
   · 取色跟随四主题令牌（bar=--e3 / chip=--e4 / hover=--e5 / amber / danger），不硬编码、不引新色系
   ══════════════════════════════════════════════════════════════════════ */
.fxbar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;
  padding:5px 8px;margin:0 0 6px;
  background:var(--e3);border:1px solid var(--line);
  border-radius:8px;font-size:12px;line-height:1.5}
.fxlab{color:var(--t2);font-weight:600;flex:none}
.fxplat{color:var(--t3);font-size:11px;flex:none;
  border-left:1px solid var(--line3);padding-left:6px}
.fxrow{display:flex;flex-wrap:wrap;gap:5px;align-items:center;min-width:0}
.fxchip{appearance:none;cursor:pointer;max-width:220px;
  padding:3px 9px;border-radius:999px;
  background:var(--e4);color:var(--t1);
  border:1px solid var(--line3);font-size:12px;line-height:1.4;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fxchip:hover{background:var(--e5)}
.fxchip:active{transform:translateY(1px)}
.fxchip:focus-visible{outline:2px solid var(--focus);outline-offset:1px}
.fxfollow{background:var(--amber-bg);border-color:var(--amber-line);color:var(--amber);font-weight:600}
.fxfollow:hover{background:var(--amber-bg);border-color:var(--amber);color:var(--amber)}
.fximg{border-style:dashed}
.fximg.bad{color:var(--danger-text);border-color:var(--danger-line);background:var(--danger-bg)}
.fximg.bad:hover{background:var(--danger-bg);border-color:var(--danger-text)}
.fxnone{color:var(--t3);font-size:11px}
.fxnone.err,.fxmsg.err{color:var(--danger-text)}
.fxmsg{color:var(--t3);font-size:11px;flex-basis:100%}
