/* ============================================================
   科研课题管理平台 — 视觉系统
   深青绿主色 · 琥珀=进行中 · 苔绿=已归档 · 实验记录本质感
   ============================================================ */
:root {
  --ink: #1c2b2a;
  --ink-2: #3d5150;
  --ink-3: #6b807e;
  --line: #dfe7e4;
  --line-soft: #eef3f1;
  --bg: #f6f8f7;
  --panel: #ffffff;
  --teal: #0f5c54;
  --teal-2: #178277;
  --teal-soft: #e6f2f0;
  --amber: #b8791a;
  --amber-soft: #fdf3e2;
  --moss: #4a7c48;
  --moss-soft: #ecf4ea;
  --violet: #6d4aa8;
  --blue: #2b6cb0;
  /* z63：团队层标识色（蓝），与课题组(靛紫) / 房间(青) 明确区分 */
  --blue-soft: #e8f1fa;
  --danger: #b4443a;
  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 1px 2px rgba(28, 43, 42, .05), 0 8px 24px rgba(28, 43, 42, .06);
  --serif: "Songti SC", "Source Han Serif SC", "Noto Serif CJK SC", Georgia, serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
  --mono: "SF Mono", "Cascadia Code", Consolas, monospace;
  /* ★★ z218（用户需求 2）：全站字号倍率。
     · 由 js/app.js 的 applyFontScale() 写到 <html> 的 style 上（本设备偏好，localStorage 保存）；
     · 样式表里**所有** font-size 都是 `calc(Npx * var(--fs, 1))` —— 只放大文字，
       图标、间距、圆角、边框一概不动，所以放大后不会挤坏排版、也不会冒出横向滚动条；
     · 默认 1（标准）。四档：0.92 小 / 1 标准 / 1.12 大 / 1.25 特大。 */
  --fs: 1;
  --panel-soft: var(--panel-soft);
  --ink-4: var(--ink-4);
  --violet-soft: #f5f0fe;
  --danger-soft: #fdeeec;
  --grad-a: #eef4f2;
  --grad-b: #e4ece9;
  --topbar-bg: rgba(255, 255, 255, .92);
  --bubble-me-bg: #d8f3c4;
  --bubble-me-bd: #bde3a3;
  --bubble-other-bg: #ffffff;
  --bubble-other-bd: #e4ecea;
  --bubble-ai-bg: #f6faf9;
  --bubble-ai-bd: #e2eeeb;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--sans);
  background: var(--bg);
  color: var(--ink);
  font-size: calc(14px * var(--fs, 1));
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
button { font-family: inherit; cursor: pointer; }
a { color: var(--teal-2); text-decoration: none; }
.hidden { display: none !important; }

/* ============================================================
   z98：文件输入框**不能**用 `display:none`
   ------------------------------------------------------------
   现象：手机「桌面安装版」（PWA 独立窗口 / 各家浏览器自己的 WebApp 内核）
   里，所有上传入口（聊天「+」面板、📎、知识库「选择文件」…）**点了没反应**；
   手机浏览器版与电脑版正常。
   病根：这些入口都是 `document.getElementById('xxxInput').click()` ——
   对 `display:none` 的 `<input type=file>` 调 `.click()`，
   在部分 WebView / WebApp 内核里会被直接忽略（浏览器标签页里正常）。
   正解：用「视觉隐藏但**仍在布局树里**」的标准写法（sr-only）：
   尺寸 1×1、裁掉、透明，但 **display 不是 none** → `.click()` 一定生效。
   ⚠️ 不要再给它加 `.hidden`（那会 `display:none !important`，病复发）。
   ============================================================ */
input[type=file].vis-input {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  border: 0 !important;
  overflow: hidden !important;
  clip: rect(0 0 0 0) !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
  opacity: 0 !important;
  pointer-events: none;
}

/* ============================================================
   z99：把「真实 file input」直接**铺在按钮/上传区上面**（透明、可点）
   ------------------------------------------------------------
   为什么还要再改一次：z98 把 input 藏成 1×1 的 sr-only，靠 JS `.click()` /
   `<label for>` 转发去唤起。实测在**桌面安装版（WebView/WebApp 外壳）**里
   两条路都不生效 —— 用户点上传按钮「完全没反应」（浏览器标签页里正常）。
   正解：让**用户的手指直接落在 input 上**。
   input 铺满宿主元素、`opacity:0`、`pointer-events:auto`，
   于是没有任何中间层 —— 浏览器只能走它最原生的那条「打开文件选择器」逻辑。
   ⚠️ 宿主必须有 `position: relative`（见 .upload-zone / .attach-btn / .sheet-item；.data-btn 已于 z108 移除）。
   ⚠️ 不要再给它们加 `pointer-events: none`（那等于没铺）。
   ============================================================ */
input[type=file].vis-overlay {
  position: absolute !important;
  left: 0 !important;
  top: 0 !important;
  width: 100% !important;
  height: 100% !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  opacity: 0 !important;
  clip: auto !important;
  clip-path: none !important;
  cursor: pointer !important;
  pointer-events: auto !important;
  z-index: 3 !important;
}

/* ---------- 启动 ---------- */
.boot { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; gap: 14px; color: var(--ink-3); }
.spinner { width: 26px; height: 26px; border: 2px solid var(--line); border-top-color: var(--teal-2); border-radius: 50%; animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- 登录 ---------- */
.login-wrap { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; padding: 20px; background: linear-gradient(160deg, var(--grad-a) 0%, var(--grad-b) 100%); }
.login-card { width: 100%; max-width: 400px; background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 30px 28px; box-shadow: var(--shadow); }
.login-brand { text-align: center; margin-bottom: 22px; }
.brand-mark { width: 46px; height: 46px; margin: 0 auto 12px; border-radius: 12px; background: linear-gradient(140deg, var(--teal), var(--teal-2)); color: #fff; display: flex; align-items: center; justify-content: center; font-family: var(--serif); font-size: calc(22px * var(--fs, 1)); }
.login-brand h1 { font-family: var(--serif); font-size: calc(20px * var(--fs, 1)); margin: 0 0 5px; letter-spacing: .5px; }
.login-brand p { margin: 0; color: var(--ink-3); font-size: calc(12.5px * var(--fs, 1)); }
.tabs { display: flex; gap: 4px; background: var(--line-soft); padding: 3px; border-radius: 9px; margin-bottom: 18px; }
.tab { flex: 1; padding: 7px 4px; border: 0; background: transparent; border-radius: 7px; font-size: calc(13px * var(--fs, 1)); color: var(--ink-2); }
.tab.active { background: var(--panel); color: var(--teal); font-weight: 600; box-shadow: 0 1px 3px rgba(0,0,0,.06); }
.auth-form { display: flex; flex-direction: column; gap: 13px; }
.auth-form label { display: flex; flex-direction: column; gap: 6px; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); font-weight: 500; }
.auth-form input { padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; font-size: calc(14px * var(--fs, 1)); font-family: inherit; background: var(--panel-soft); }
.auth-form input:focus { outline: 0; border-color: var(--teal-2); box-shadow: 0 0 0 3px var(--teal-soft); }
.otp-row { display: flex; gap: 8px; }
.otp-row input { flex: 1; }
#sendOtpBtn { white-space: nowrap; font-size: calc(12.5px * var(--fs, 1)); }
#authErr:empty { display: none; }
.err { color: var(--danger); font-size: calc(12.5px * var(--fs, 1)); min-height: 18px; }
.login-hint { margin: 16px 0 0; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); text-align: center; }

/* ---------- 按钮 ---------- */
.btn-primary { padding: 10px 16px; border: 0; border-radius: 9px; background: var(--teal); color: #fff; font-size: calc(14px * var(--fs, 1)); font-weight: 500; transition: background .15s; }
.btn-primary:hover { background: var(--teal-2); }
.btn-primary:disabled { opacity: .5; cursor: not-allowed; }
.btn-ghost { padding: 8px 14px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel); color: var(--ink-2); font-size: calc(13px * var(--fs, 1)); }
.btn-ghost:hover { border-color: var(--teal-2); color: var(--teal); }
.btn-ghost.sm { padding: 5px 10px; font-size: calc(12px * var(--fs, 1)); }
/* z194：附件卡四按钮 —— 不可用的按钮要看得出来不可用（不是消失） */
.btn-ghost.sm:disabled { opacity: .45; cursor: not-allowed; border-style: dashed; }
.btn-ghost.sm:disabled:hover { border-color: var(--line); color: var(--ink-2); }
.btn-ghost.sm.danger { color: #b4443a; border-color: #e7c9c6; }
.btn-ghost.sm.danger:hover { border-color: #b4443a; background: #fdf4f3; }
/* z194：上传失败的附件卡（原件没保存）—— 卡片仍在，但状态一眼可辨 */
.word-card.att-fail { background: #fdf6f4; border-color: #eccdc7; }
.word-card.att-fail .word-meta { color: #a5524a; }
.word-ico.err { background: #b4443a; }
.word-card.att-fail .word-name { color: #7f3a33; }
.btn-amber { background: var(--amber); border: 0; color: #fff; padding: 8px 14px; border-radius: 8px; font-size: calc(13px * var(--fs, 1)); }
.btn-danger { background: transparent; border: 1px solid #e8c9c5; color: var(--danger); padding: 4px 10px; border-radius: 7px; font-size: calc(12px * var(--fs, 1)); }

/* ---------- 顶栏 ---------- */
/* ⚠️ z218：字号放大后这里不能再写死 `height: 54px`。
   `.topnav a` 原来可以被 flex 压到只剩两三个字的宽度，字号一调大
   （大 112% / 特大 125%）就被压成「一字一行」的竖排（实测「工作台」三个字叠起来）。
   现在：标签 `white-space: nowrap` 不许断行 + 导航整体换行，顶栏高度自适应。 */
.topbar { position: sticky; top: 0; z-index: 30; display: flex; align-items: center; gap: 26px; padding: 6px 22px; min-height: 54px; flex-wrap: wrap; row-gap: 4px; background: var(--topbar-bg); backdrop-filter: blur(10px); border-bottom: 1px solid var(--line); }

/* ------------------------------------------------------------
   z91：品牌区改成一个色块，平台名 + 用户名**同一排**但字号不同
   ------------------------------------------------------------
   用户原话：「这个区域可以美化一下，比如单独搞个颜色块，
   然后科研课题管理平台和外观名字推出在一排，但是字体不同。」
   （"外观名字"= 顶栏那一排「外观 / 肖先金 / 退出」）

   原布局是三行：① 圆点 + 平台名  ② 外观/用户名/退出  ③ 导航。
   在手机上尤其浪费 —— 顶栏高度直接吃掉首屏。
   z91：把①和②合成**一个色块内的一行**（平台名大、用户名小）。
   ⚠️ 只改视觉，DOM 结构不动 —— 移动端的 `.me` 隐藏规则依赖原有 class。
   ⚠️ 色块用 `var(--teal-soft)` 这类已有变量，深色主题下自动适配。
   ------------------------------------------------------------ */
.brand-inline {
  display: inline-flex; align-items: center; gap: 8px;
  cursor: pointer;
  padding: 5px 12px 5px 10px;
  border-radius: 10px;
  background: var(--teal-soft, rgba(45, 138, 114, .09));
  border: 1px solid var(--line);
  transition: background .14s ease, border-color .14s ease;
}
.brand-inline:hover { border-color: var(--teal-2); background: var(--teal-soft); }
.brand-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--teal-2); flex: none; }
/* 平台名：衬线、稍大 */
.brand-inline strong { font-family: var(--serif); font-size: calc(15px * var(--fs, 1)); letter-spacing: .3px; white-space: nowrap; }
/* 分隔竖线 + 用户名：无衬线、更小、更淡 —— 同排但层次分明 */
.brand-user {
  display: inline-flex; align-items: center; gap: 6px;
  padding-left: 8px; margin-left: 2px;
  border-left: 1px solid var(--line);
  font-family: var(--sans, inherit);
  font-size: calc(12px * var(--fs, 1));
  color: var(--ink-3);
  white-space: nowrap;
}
.brand-user .bu-name { max-width: 8em; overflow: hidden; text-overflow: ellipsis; }
/* 用户名在品牌块内显示后，顶栏右侧那个「肖先金」就重复了 —— 隐藏它。
   ⚠️ 保留「外观 / 退出」两个按钮（那是操作，不是身份）。 */
#meName { display: none; }
.topnav { display: flex; gap: 4px; flex: 1; flex-wrap: wrap; row-gap: 2px; }
/* z218：字号放大后不许把标签压成竖排（每个标签自己不许断行，放不下就让导航换行） */
.topnav a { padding: 6px 13px; border-radius: 8px; font-size: calc(13.5px * var(--fs, 1)); color: var(--ink-2); white-space: nowrap; }
/* z218：窄一点的桌面（1280 这类）把标签内边距收 4px，标准字号下仍能一行放完，
   不至于一进来顶栏就变两行。实测 1280px 标准字号：内边距 13→9 正好从 84px 回到 54px。 */
@media (max-width: 1360px) { .topnav a { padding: 6px 9px; } }
.topnav a:hover { background: var(--line-soft); }
.topnav a.on { background: var(--teal-soft); color: var(--teal); font-weight: 600; }
/* ⚠️⚠️ z92：这条原来是 `.me {…}`（裸类名），**必须限定到 .topbar**。
   `.me` 这个 class 被两处复用：顶栏用户区（index.html 的 <div class="me">）
   与消息气泡（app.js 的 <div class="msg me">，表示「我自己发的」）。
   裸类名会让顶栏样式泄漏到消息上；下面 @media(max-width:620px) 里的
   `.me { flex: 0 1 auto; overflow: hidden }` 泄漏后
   → `flex` 简写把 flex-shrink 重置回 1 → 消息在手机端被压扁裁切。
   全项目凡styling 顶栏用户区，一律写 `.topbar .me`。 */
.topbar .me { display: flex; align-items: center; gap: 10px; font-size: calc(13px * var(--fs, 1)); color: var(--ink-2); }
#meName { cursor: pointer; padding: 3px 8px; border-radius: 7px; }
/* ============================================================
   z100：手机端顶栏「单行工具条」（方案 A）
   ------------------------------------------------------------
   用户反馈（附截图）：手机版顶栏占三行、还占得很难看 ——
     行1 品牌名 + 肖先金 + 外观 + 退出   （挤满，必换行）
     行2 z99 + 说明                      （只剩两个小东西飘在右边，纯浪费）
     行3 工作台 / AI 团队 / 知识库 / 经验 / 模型设置
   病根：`.brand-inline`（含用户名 + 外观 + 退出）本身就宽约 300px，
        再和 `.me`（z99 + 说明）抢同一行 → `.me` 被 `flex-wrap` 挤到第二行，
        而 `.topnav` 的 `order:3` 又占了第三行。
   正解：**窄屏把「用户名 / 外观 / 退出」整块收进右侧 ⋮**，第一行只剩
        「● 平台名 … z99 说明 ⋮」，一律放得下；页签回到第二行。
        三行 ≈140px → 两行 ≈80px，首屏多出约 60px。
   ⚠️ 桌面端一行都不动：⋮ 由基础规则 `display:none` 隐藏（下面这条），
      `.brand-user` 只在 ≤620px 里被隐藏。
   ============================================================ */
.topbar-more { display: none; }
.sheet-head {
  padding: 2px 8px 12px; margin-bottom: 4px;
  font-size: calc(15px * var(--fs, 1)); font-weight: 600; color: var(--ink);
  border-bottom: 1px solid var(--line-soft);
}
.sheet-list { display: flex; flex-direction: column; }
.sheet-row {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 14px 8px; border: 0; border-bottom: 1px solid var(--line-soft);
  background: transparent; color: var(--ink); font-size: calc(15px * var(--fs, 1));
}
.sheet-row:last-child { border-bottom: 0; }
.sheet-row:active { background: var(--line-soft); }
.sheet-row.danger { color: var(--danger); }
#meName:hover { background: var(--teal-soft); color: var(--teal); }
.build-tag {
  font-family: var(--mono); font-size: calc(10px * var(--fs, 1)); color: var(--ink-4);
  background: var(--line-soft); padding: 2px 6px; border-radius: 5px;
  letter-spacing: .3px; white-space: nowrap;
}

main { max-width: 1240px; margin: 0 auto; padding: 24px 22px 60px; }

/* ---------- 工作台 ---------- */
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.page-head h2 { font-family: var(--serif); font-size: calc(22px * var(--fs, 1)); margin: 0 0 4px; }
.page-head p { margin: 0; color: var(--ink-3); font-size: calc(13px * var(--fs, 1)); }
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 22px; }
.stat { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; }
.stat .n { font-family: var(--serif); font-size: calc(26px * var(--fs, 1)); line-height: 1.2; }
.stat .l { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); margin-top: 2px; }

.room-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 14px; }

/* 工作台 · 团队卡（课题讨论区只展示团队卡，点卡片进入团队页看房间） */
.team-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.team-card { display: flex; flex-direction: column; gap: 8px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 14px; background: var(--panel); cursor: pointer; transition: border-color .15s, box-shadow .15s, transform .15s; }
.team-card:hover { border-color: var(--teal-2); box-shadow: var(--shadow); transform: translateY(-1px); }
.team-card .tc-top { display: flex; align-items: center; gap: 8px; }
.team-card .team-ico { font-size: calc(16px * var(--fs, 1)); }
.team-card .tc-top b { font-size: calc(15px * var(--fs, 1)); }
.team-card .tc-rooms { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.team-card .tc-foot { display: flex; align-items: center; justify-content: space-between; margin-top: auto; }
.team-card .team-count { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.team-card .team-enter { font-size: calc(12px * var(--fs, 1)); color: var(--teal); font-weight: 600; }
.team-room-wrap { display: flex; flex-direction: column; gap: 6px; }
.team-room-wrap > .room-card { flex: 1; }
.team-adjust { align-self: flex-start; }

/* 调整分组弹窗 */
.pick-list { display: flex; flex-direction: column; gap: 6px; margin: 10px 0; max-height: 46vh; overflow-y: auto; }
.pick-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border: 1px solid var(--line-soft); border-radius: 8px; background: var(--panel-soft); cursor: pointer; font-size: calc(13px * var(--fs, 1)); }
.pick-item:hover { border-color: var(--teal-2); }
.pick-item input { margin: 0; }
.room-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px; cursor: pointer; transition: border-color .15s, box-shadow .15s; position: relative; }
.room-card:hover { border-color: var(--teal-2); box-shadow: var(--shadow); }
.room-card h3 { margin: 0 0 3px; font-family: var(--serif); font-size: calc(16.5px * var(--fs, 1)); }
.room-card .meta { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); margin-bottom: 10px; }
.room-card .desc { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); margin: 0 0 12px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 34px; }
.room-card .foot { display: flex; justify-content: space-between; align-items: center; padding-top: 11px; border-top: 1px solid var(--line-soft); font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.badge { display: inline-block; padding: 2px 8px; border-radius: 20px; font-size: calc(11px * var(--fs, 1)); font-weight: 500; }
/* z148：房间卡上的「实验成员」chip。
   原来这里显示「我是管理员」，对看卡片的人没有信息量；改成"这个房间是谁在做实验"。
   卡片宽度有限，所以**必须 nowrap + 省略号**：名字多时由 JS 截成「等 N 人」，
   完整名单在 title 里（悬停可看）。 */
.badge.exp { background: var(--teal-soft); color: var(--teal); max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; }
.badge.exp.none { background: var(--line); color: var(--ink-3); }
.badge.exp.loading { background: var(--line); color: var(--ink-3); }
.badge.mentor { background: var(--amber-soft); color: var(--amber); }
.badge.student { background: var(--teal-soft); color: var(--teal); }

.empty { text-align: center; padding: 56px 20px; color: var(--ink-3); border: 1px dashed var(--line); border-radius: var(--radius); background: var(--panel); }
.empty h3 { font-family: var(--serif); color: var(--ink-2); margin: 0 0 6px; }

/* ---------- 房间 ---------- */
.room-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 14px; flex-wrap: wrap; }
.room-head h2 { font-family: var(--serif); font-size: calc(20px * var(--fs, 1)); margin: 0; }
.room-head .sub { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }
/* z103：房间头的「返回上级」（`‹`）**只在手机端出现** —— 桌面端已经有全局顶栏，
   再放一个就是重复入口。手机端房间页会把全局顶栏整个隐藏（body.in-chat .topbar），
   没有这个按钮就彻底出不去，详见 js/app.js 里 initMobileRoomChrome 的说明。 */
.room-head .rm-back { display: none; }
/* z94：这排按钮（房间信息/分组/AI模型/刷新/归档/删除）。
   原来靠内联 style="display:flex;gap:8px;…"，z94 要在手机端隐藏它，
   但**内联样式优先级高于样式表**，写 `.room-acts{display:none}` 会被覆盖 ——
   所以内联样式已删到样式表这里（桌面端外观不变，手机端才 display:none）。 */
.room-head .room-acts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

.room-layout { display: grid; grid-template-columns: 1fr 330px; gap: 14px; align-items: start; }

/* 讨论区 */
.discuss { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; display: flex; flex-direction: column; }
/* ⚠️ z96：`flex: none` 是**必须**的，不是装饰。
   `.discuss` 是 column flex 容器，`.scope-tabs` / `.msgs` / `.composer` 是它的三个子项。
   页签原来没写 flex → 取默认 `flex: 0 1 auto`（flex-shrink:1）→ **会被压缩**；
   而它自己 `overflow-x: auto` 又让「自动最小尺寸」变成 0（规范：overflow 非 visible
   的 flex item，min-height:auto 解析为 0）→ 没有任何下限兜底。
   实测（390×844）：页签基准 39px、`.msgs` 基准 = 内容高度。
   溢出按 flex-shrink × 基准 的比例分摊：
     - 空房间（内容 683 < 容器 791）不溢出 → 页签 39px 正常；
     - design 分区溢出 146px → 页签 39 → 31px；
     - 历史几千 px 时溢出几千 px → 页签被压到 ≈5px = **看起来完全消失**。
   这正是「有历史消息的房间三个分区出不来、没消息的房间能出来」的真正原因。
   修法：页签锁死不压缩（与 `.composer` 已有的 `flex: none` 同理，当初漏了页签）。 */
.scope-tabs { flex: none; display: flex; border-bottom: 1px solid var(--line); background: var(--panel-soft); }
.scope-tabs button { flex: 1; padding: 11px 8px; border: 0; background: transparent; font-size: calc(13.5px * var(--fs, 1)); color: var(--ink-3); border-bottom: 2px solid transparent; }
.scope-tabs button.on { color: var(--teal); border-bottom-color: var(--teal); font-weight: 600; background: var(--panel); }
/* z61 分区未读红点：贴在分区 tab 标题右侧，进房间就知道「哪个分区有我没看的」 */
.scope-unread { display: inline-block; margin-left: 5px; padding: 0 5px; min-width: 16px; height: 16px; line-height: 16px; border-radius: 8px; background: #e5484d; color: #fff; font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; vertical-align: 1px; text-align: center; }
.scope-tabs button.on .scope-unread { display: none; }
/* z61 房间卡上的未读分区提示行 */
.unread-hint { margin: -2px 0 6px; font-size: calc(11px * var(--fs, 1)); color: #e5484d; }
.msgs { flex: 1; overflow-y: auto; padding: 16px; max-height: 52vh; min-height: 340px; display: flex; flex-direction: column; gap: 14px; }
/* ⚠️ z92：.msgs 的**所有**直接子项都不许被压缩（flex item 的 flex-shrink 默认 1）。
   不只是 .msg —— 未读分隔线 / 历史折叠卡 / 空态提示同样是 flex item，
   内容一多它们也会被压扁。统一在这里兜底，比逐个加更不容易漏。
   （真正的病根与逐条解释见下面 .msg 那段注释） */
.msgs > * { flex-shrink: 0; }

/* ⚠️⚠️ z91：这里原来只有 `display: flex; gap: 10px`，缺三样东西，
   后果在窄屏（手机）上**自己的消息整行溢出容器、被裁掉右侧**：
   用户报「手机端我自己发的消息看不到了，电脑端可以看到」。
   原因（三个都要补，缺一个都还会溢出）：
     ① `align-items: flex-start` —— flex 子项默认 `stretch`，
        .msg.me 是 row-reverse，头像会被拉成和气泡一样高。
     ② `min-width: 0` —— flex item 的 min-width 默认 auto，
        里面的 .txt 按内容宽度撑开，把整行顶出容器。
     ③ `width: 100%` —— .msgs 是 flex column 容器，
        .msg 作为子项宽度由内容决定，不写 100% 就没有确定宽度可供收缩。
   （这是 z90 移动端输入框同一个坑的另一个表现，见红线 115。） */
.msg { display: flex; gap: 10px; align-items: flex-start; min-width: 0; width: 100%; }
.msg .av { width: 30px; height: 30px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: calc(12px * var(--fs, 1)); font-weight: 600; flex-shrink: 0; }
.msg .body { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; }
/* 气泡本身也要能收缩：.txt 是 inline-block，max-width:100% 相对的是 .body，
   而 .body 曾经没有 min-width:0 → 链条断在最上游。 */
.msg .txt { min-width: 0; overflow-wrap: anywhere; }
/* ⚠️⚠️⚠️ z92：这条修的是「手机端看不到自己发的消息」，与横向溢出无关。
   `.msgs` 是 `display:flex; flex-direction:column`，所以每条 `.msg` 都是一个
   **column 方向的 flex item**，而 flex item 的 `flex-shrink` 默认值是 **1**。
   于是只要消息总高度超过 `.msgs` 的 `max-height`（手机端 60vh），浏览器不会
   让容器滚动，而是**按比例把每条消息压扁**来「塞进去」：
     · 自己刚发的那条（永远在最后、最靠近底边）被压得最狠
     · 气泡文字被从中间切断、z89 的「N 人未读」回执被 overflow 直接裁掉
   → 用户看到的正是「自己的消息像跳到屏幕外面去了」。
   为什么电脑端没事：`max-height` 是 52vh 且视口高，内容通常没超；手机端
   60vh 的盒子在窄屏上更容易被填满，一超就触发。
   为什么「刷新后依然看不到」：这是纯布局行为，与数据、缓存、Service Worker 全无关。
   ⚠️ 判据（下次排查同类问题记牢）：`.msgs` 里若出现 `scrollHeight === clientHeight`
      却「内容明明超了」，就是 flex-shrink 在压缩，而不是容器没滚动。 */
.msg { flex-shrink: 0; }
.msg .who { display: flex; align-items: center; gap: 7px; margin-bottom: 3px; }
.msg .who b { font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; }
.msg .who .time { font-size: calc(11px * var(--fs, 1)); color: var(--ink-4); }
.msg .who .model { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); background: var(--line-soft); padding: 1px 7px; border-radius: 20px; }
.msg .who .doc-tag { font-size: calc(10.5px * var(--fs, 1)); color: var(--amber); background: var(--amber-soft); padding: 1px 7px; border-radius: 20px; }
.msg .txt { font-size: calc(13.5px * var(--fs, 1)); color: var(--ink); word-break: break-word; position: relative; max-width: 100%; }
/* ===== 微信式对话：自己的消息靠右（绿色气泡），他人与 AI 靠左 ===== */
.msg.me { flex-direction: row-reverse; }
.msg.me .body { align-items: flex-end; }
.msg.me .who { justify-content: flex-end; }
.msg.me .av { background: var(--teal); color: #fff; }
.msg.ai .av { color: #fff; }
.msg.other .txt, .msg.ai .txt, .msg.me .txt {
  display: inline-block; max-width: 100%; border-radius: 12px; padding: 9px 12px;
  border: 1px solid transparent; box-shadow: 0 1px 1px rgba(28, 43, 42, .03);
}
.msg.other .txt { background: var(--bubble-other-bg); border-color: var(--bubble-other-bd); border-top-left-radius: 4px; }
.msg.ai .txt { background: var(--bubble-ai-bg); border-color: var(--bubble-ai-bd); border-top-left-radius: 4px; }
.msg.me .txt { background: var(--bubble-me-bg); border-color: var(--bubble-me-bd); border-top-right-radius: 4px; }
/* 气泡小尾巴（指向头像） */
.msg.other .txt::before, .msg.ai .txt::before, .msg.me .txt::before {
  content: ''; position: absolute; top: 10px; width: 8px; height: 8px;
  transform: rotate(45deg); border: 1px solid transparent; background: inherit;
}
.msg.other .txt::before { left: -5px; border-left-color: var(--bubble-other-bd); border-bottom-color: var(--bubble-other-bd); }
.msg.ai .txt::before { left: -5px; border-left-color: var(--bubble-ai-bd); border-bottom-color: var(--bubble-ai-bd); }
.msg.me .txt::before { right: -5px; border-top-color: var(--bubble-me-bd); border-right-color: var(--bubble-me-bd); }
.msg .txt p { margin: 0 0 7px; }
.msg .txt p:last-child { margin-bottom: 0; }
.msg .txt ul, .msg .txt ol { margin: 5px 0; padding-left: 20px; }
.msg .txt li { margin: 2px 0; }
.msg .txt h3, .msg .txt h4 { margin: 9px 0 5px; font-size: calc(13.5px * var(--fs, 1)); font-family: var(--serif); }
.msg .txt code { background: var(--line-soft); padding: 1px 5px; border-radius: 4px; font-family: var(--mono); font-size: calc(12px * var(--fs, 1)); }
.msg .txt pre { background: var(--line-soft); padding: 9px 11px; border-radius: 7px; overflow-x: auto; font-family: var(--mono); font-size: calc(12px * var(--fs, 1)); }
/* 表格样式统一由 .tbl-wrap 提供（md 渲染的所有表格都包裹其中） */
.msg .txt .refs { margin-top: 9px; padding-top: 8px; border-top: 1px dashed var(--line); font-size: calc(12px * var(--fs, 1)); }
.msg .txt .refs .refs-title { font-weight: 600; color: var(--ink-3); font-size: calc(11.5px * var(--fs, 1)); margin-bottom: 3px; }
.msg .txt .refs .ref-item { margin: 3px 0; line-height: 1.5; }
.msg .txt .refs .ref-no { color: var(--teal); font-weight: 600; margin-right: 5px; }
.msg .txt .refs a { color: var(--blue); word-break: break-all; }
.msg .txt blockquote { margin: 6px 0; padding: 3px 12px; border-left: 3px solid var(--line); color: var(--ink-2); }
.typing::after { content: '▋'; color: var(--teal-2); animation: blink 1s steps(2) infinite; }
@keyframes blink { 50% { opacity: 0; } }

/* 推理模型思考中的进度提示（否则长时间空白，用户以为没回复） */
.thinking-box {
  display: flex; align-items: center; gap: 7px;
  font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3);
}
.thinking-box em {
  font-style: normal; color: var(--ink-4); font-size: calc(11.5px * var(--fs, 1));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60%;
}
.dot-pulse {
  width: 7px; height: 7px; border-radius: 50%; background: var(--teal-2); flex-shrink: 0;
  animation: pulse 1.1s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: .25; transform: scale(.8); } 50% { opacity: 1; transform: scale(1.15); } }

.composer { border-top: 1px solid var(--line); padding: 10px 12px; background: var(--panel-soft); }
.composer-row { display: flex; gap: 8px; align-items: flex-end; }
.composer textarea { flex: 1; border: 1px solid var(--line); border-radius: 9px; padding: 9px 11px; font-size: calc(13.5px * var(--fs, 1)); font-family: inherit; resize: none; min-height: 40px; max-height: 130px; background: var(--panel); line-height: 1.6; }
.composer textarea:focus { outline: 0; border-color: var(--teal-2); box-shadow: 0 0 0 3px var(--teal-soft); }
.composer-hint { display: flex; justify-content: space-between; font-size: calc(11px * var(--fs, 1)); color: var(--ink-4); margin-top: 5px; }

/* z214：@ 弹层必须能在手机上**滚**（用户报障 2：只能看到 5 个人）
   - touch-action: pan-y  → 手指纵向拖动交给本层滚动（原来 JS 在 pointerdown 里
     preventDefault，把滚动一起掐死了；JS 已改，这里把浏览器侧的判定也写明确）
   - -webkit-overflow-scrolling: touch → iOS 惯性滚动
   - overscroll-behavior: contain       → 滚到头不要把整页/消息区带着滚
   - max-height 用 min()：键盘弹起后可视高度只剩一半时，别让弹层顶出屏幕 */
.at-pop { position: absolute; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow); overflow: hidden; z-index: 40; min-width: 172px; max-width: calc(100vw - 24px); max-height: 232px; max-height: min(232px, 46vh); overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; touch-action: pan-y; }
.at-item { padding: 8px 12px; display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: calc(13px * var(--fs, 1)); }
.at-item:hover, .at-item.sel { background: var(--teal-soft); }
.at-item .dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.at-hint { padding: 5px 10px; border-top: 1px solid var(--line-soft); font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); }

/* ---- 对话内附件 ---- */
.attach-btn {
  width: 34px; height: 34px; flex-shrink: 0; padding: 0;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel);
  color: var(--ink-2); display: flex; align-items: center; justify-content: center;
  position: relative; overflow: hidden;   /* z99：铺满式 file input 的定位宿主 */
}
.attach-btn:hover { border-color: var(--teal-2); color: var(--teal); background: var(--teal-soft); }
/* z108：「实验数据」专用上传入口已删除（改走「消息右键 → 归档到课题文件 → 实验数据」），
   对应 .data-btn 样式一并移除。实验数据经由 .attach-btn / 手机端「+」面板发送。 */
.attach-list { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.attach-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 8px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-soft); font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); max-width: 240px;
}
.attach-chip .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attach-chip .sz { color: var(--ink-3); font-size: calc(11px * var(--fs, 1)); flex-shrink: 0; }
.attach-chip .x { cursor: pointer; color: var(--ink-3); font-size: calc(14px * var(--fs, 1)); line-height: 1; flex-shrink: 0; }
.attach-chip .x:hover { color: var(--danger); }
.attach-chip img { width: 20px; height: 20px; object-fit: cover; border-radius: 4px; flex-shrink: 0; }
.attach-chip.uploading { opacity: .6; }
.msg .txt .attach-view { margin: 6px 0; }
.msg .txt .attach-view img { max-width: 220px; border-radius: 8px; border: 1px solid var(--line); cursor: pointer; }
.msg .txt .attach-file {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel-soft); font-size: calc(12.5px * var(--fs, 1));
}

/* 侧栏 */
.side { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; position: sticky; top: 68px; }
.side-tabs { display: flex; border-bottom: 1px solid var(--line); background: var(--panel-soft); }
.side-tabs button { flex: 1; padding: 9px 2px; border: 0; background: transparent; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); border-bottom: 2px solid transparent; }
.side-tabs button.on { color: var(--teal); border-bottom-color: var(--teal); font-weight: 600; background: var(--panel); }

/* z64：房间内「待我处理」徽标 —— 侧栏页签小红点 + 头部 chip。
   原来房间内完全没有待审批提示，用户必须切到「成员」/「知识库」页签才知道有事要处理。 */
.side-tab-badge {
  display: inline-block; margin-left: 4px; padding: 0 5px; min-width: 15px; height: 15px;
  line-height: 15px; border-radius: 8px; background: var(--amber); color: #fff;
  font-size: calc(10px * var(--fs, 1)); font-weight: 600; vertical-align: 1px; text-align: center;
}
.side-tabs button.on .side-tab-badge { background: var(--teal); }
.pending-chip {
  display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 12px;
  background: var(--amber-soft); color: var(--amber); border: 1px solid var(--amber);
  font-size: calc(11.5px * var(--fs, 1)); font-weight: 600; cursor: help; white-space: nowrap;
}
.side-body { padding: 13px; max-height: 62vh; overflow-y: auto; }
.side-sec { margin-bottom: 16px; }
.side-sec:last-child { margin-bottom: 0; }
.side-sec h4 { margin: 0 0 8px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); font-weight: 600; letter-spacing: .3px; text-transform: uppercase; }

.file-item { display: flex; gap: 9px; align-items: flex-start; padding: 8px; border: 1px solid var(--line-soft); border-radius: 8px; margin-bottom: 6px; background: var(--panel-soft); }
.file-item .ico { width: 25px; height: 25px; border-radius: 6px; background: var(--teal-soft); color: var(--teal); display: flex; align-items: center; justify-content: center; font-size: calc(11px * var(--fs, 1)); flex-shrink: 0; }
.file-item .n { font-size: calc(12.5px * var(--fs, 1)); font-weight: 500; word-break: break-all; }
.file-item .m { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); margin-top: 1px; }

/* 课题文件条目（z29）：单行精简卡，与 .kb-row 一致；完整信息在 title tooltip */
.file-row { display: flex; align-items: center; gap: 5px; padding: 4px 8px; border-radius: 8px; background: var(--panel-soft); border: 1px solid var(--line-soft); margin-bottom: 4px; font-size: calc(12px * var(--fs, 1)); }
.file-row:hover { border-color: var(--line); background: var(--panel); }
.file-row .fdot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.file-row .fn { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; font-size: calc(12px * var(--fs, 1)); }
.file-row .kbadge { font-size: calc(10px * var(--fs, 1)); padding: 0 5px; border-radius: 20px; background: var(--teal-soft); color: var(--teal); flex-shrink: 0; }
/* z149：待确认的实验结论 —— 一眼能看出"还没归档"，且不能被当成已归档文件 */
.file-row.is-pending { border-style: dashed; border-color: var(--amber); background: var(--amber-soft); }
.file-row .kbadge.pend { background: #fff4e0; color: #a4620a; border: 1px solid #f0cf9a; }
.file-row .fr-wait { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); flex-shrink: 0; }
.file-row .fsz { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); flex-shrink: 0; }
.file-row .fact { display: flex; gap: 3px; align-items: center; flex-shrink: 0; }
.file-row .fact .btn-ghost.sm { padding: 1px 6px; font-size: calc(10.5px * var(--fs, 1)); }
.file-row .fact .btn-danger { padding: 1px 5px; font-size: calc(10.5px * var(--fs, 1)); }

/* ⚠️ z91：这里原来没有 min-width / flex-wrap，窄屏下姓名会被压成**逐字竖排**
   （用户截图里「李龙杰 / 导师 / 1 个房间」全竖着占了好几行）。
   原因同红线 115：flex item 的 min-width 默认 auto，且 nowrap 的中文文本不会断行。
   正解：① 姓名容器 min-width:0 + 允许省略；② 整行可换行，按钮另起一行。 */
.member-row { display: flex; align-items: center; gap: 9px; padding: 6px 0; min-width: 0; flex-wrap: wrap; }
.member-row > * { min-width: 0; }
.member-row .m-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member-row .av { width: 26px; height: 26px; border-radius: 50%; background: var(--teal-soft); color: var(--teal); display: flex; align-items: center; justify-content: center; font-size: calc(11.5px * var(--fs, 1)); font-weight: 600; }

/* z98：上传区改成 `<label for="…">`（原生唤起系统选择器，最可靠）
   → label 默认是 inline，这里显式 block 才撑得开。 */
/* z99：position:relative + overflow:hidden —— 作为铺满式 file input 的定位宿主 */
.upload-zone { display: block; position: relative; overflow: hidden; border: 1.5px dashed var(--line); border-radius: 10px; padding: 16px; text-align: center; color: var(--ink-3); font-size: calc(12.5px * var(--fs, 1)); cursor: pointer; margin-bottom: 12px; }
.upload-zone:hover { border-color: var(--teal-2); color: var(--teal); background: var(--teal-soft); }
.cat-filter { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 10px; }
.cat-filter button { padding: 3px 10px; border: 1px solid var(--line); background: var(--panel); border-radius: 20px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); }
.cat-filter button.on { background: var(--teal); color: #fff; border-color: var(--teal); }

.kb-item { border-left: 3px solid var(--violet); background: var(--violet-soft); padding: 8px 10px; border-radius: 0 8px 8px 0; margin-bottom: 7px; }
.kb-item .t { font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; }
.kb-item .b { font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); margin-top: 2px; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.kb-item .m { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); margin-top: 3px; }

/* 知识库条目：单行小卡（标题 + 徽标 + 操作按钮），完整信息在 tooltip */
.kb-row { display: flex; align-items: center; gap: 5px; padding: 4px 8px; border-radius: 8px; background: var(--panel-soft); border: 1px solid var(--line-soft); margin-bottom: 4px; font-size: calc(12px * var(--fs, 1)); flex-wrap: wrap; }
.kb-row.pending { background: var(--amber-soft); }
.kb-row .kdot { width: 7px; height: 7px; border-radius: 50%; background: var(--violet); flex-shrink: 0; }
.kb-row.pending .kdot { background: var(--amber); }
/* z221（用户报障）：未卡片化的行里「预览 / 生成资料卡 / 打开 / 下载 / 删」五个按钮是
   flex-shrink:0，而标题原来是 `flex:1; min-width:0` → 侧栏只有 ~370px 时标题被压成 **0 宽**，
   整行只剩一个圆点和几个按钮，标题、编号全看不见。
   改法：标题给最小宽度（宁可省略号，也不能消失）；按钮放不下就整块换到第二行。 */
.kb-row .kt { flex: 1 1 90px; min-width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; font-size: calc(12px * var(--fs, 1)); }
.kb-row .kb-badge { font-size: calc(10px * var(--fs, 1)); padding: 0 5px; }
.kb-row .kacts { display: flex; gap: 3px; align-items: center; flex-shrink: 0; margin-left: auto; }
.kb-row .kacts .btn-ghost.sm { padding: 1px 6px; font-size: calc(10.5px * var(--fs, 1)); }
.kb-row .kacts .btn-danger { padding: 1px 5px; font-size: calc(10.5px * var(--fs, 1)); }
.kb-row .st { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); }

/* z144：圆角角色标签。**必须 nowrap** —— 之前没有这条，中文被逐字折行竖排，
   把模型设置里每一行撑到近百像素高（用户截图报障）。横排后 5 行高度减少约 2/3。 */
.tag-chip { display: inline-block; padding: 1px 7px; border-radius: 20px; font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; white-space: nowrap; }

/* ---------- Agent 专区 / 模型设置 ---------- */
.agent-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); gap: 14px; }
.agent-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 17px; }
.agent-card .hd { display: flex; align-items: center; gap: 11px; margin-bottom: 9px; }
.agent-card .av { width: 38px; height: 38px; border-radius: 10px; color: #fff; display: flex; align-items: center; justify-content: center; font-size: calc(14px * var(--fs, 1)); font-weight: 600; }
.agent-card h3 { margin: 0; font-size: calc(15px * var(--fs, 1)); font-family: var(--serif); }
.agent-card .duty { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); margin: 0 0 12px; min-height: 36px; }
.agent-card .m { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); margin-top: 9px; }

.priv-chat { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.priv-chat .msgs { max-height: 58vh; }

.panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 17px; margin-bottom: 14px; }
.panel h3 { margin: 0 0 4px; font-family: var(--serif); font-size: calc(16px * var(--fs, 1)); }
.panel .hint { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); margin: 0 0 14px; }
.field { margin-bottom: 12px; }
.field label { display: block; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); font-weight: 500; margin-bottom: 5px; }
.field input, .field select, .field textarea { width: 100%; padding: 8px 11px; border: 1px solid var(--line); border-radius: 8px; font-size: calc(13.5px * var(--fs, 1)); font-family: inherit; background: var(--panel-soft); }
.field input:focus, .field select:focus, .field textarea:focus { outline: 0; border-color: var(--teal-2); box-shadow: 0 0 0 3px var(--teal-soft); }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

.prov-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--line-soft); }
.prov-row:last-child { border-bottom: 0; }
.prov-row .nm { font-size: calc(13.5px * var(--fs, 1)); font-weight: 500; }
.prov-row .mt { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); font-family: var(--mono); }

/* z144：角色行整体压扁。左列不再放角色全名（只剩一个 nowrap 的圆角标签），
   所以列宽从 1fr 收成 auto —— 让 select 拿到更多宽度，行高也随之由标签高度决定。 */
.role-row { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: center; padding: 5px 0; border-bottom: 1px solid var(--line-soft); }
.role-row:last-child { border-bottom: 0; }
.role-row .lbl { display: flex; align-items: center; font-size: calc(13.5px * var(--fs, 1)); font-weight: 500; min-width: 0; }

/* ---------- 弹窗 / toast ---------- */
.modal { position: fixed; inset: 0; background: rgba(28,43,42,.42); display: flex; align-items: center; justify-content: center; padding: 20px; z-index: 60; }
.modal-card { width: 100%; max-width: 470px; background: var(--panel); border-radius: 14px; padding: 22px; box-shadow: 0 20px 60px rgba(0,0,0,.2); max-height: 86vh; overflow-y: auto; }
.modal-card h3 { margin: 0 0 5px; font-family: var(--serif); font-size: calc(17px * var(--fs, 1)); }
.modal-card .mhint { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); margin: 0 0 15px; }
.modal-actions { display: flex; gap: 9px; justify-content: flex-end; margin-top: 17px; }
.pick-row { display: flex; align-items: center; gap: 9px; padding: 9px 11px; border: 1px solid var(--line); border-radius: 9px; margin-bottom: 7px; cursor: pointer; }
.pick-row.on { border-color: var(--teal-2); background: var(--teal-soft); }
.pick-row input { margin: 0; }

.toast { position: fixed; bottom: 26px; left: 50%; transform: translateX(-50%); background: var(--ink); color: #fff; padding: 10px 18px; border-radius: 9px; font-size: calc(13px * var(--fs, 1)); z-index: 80; box-shadow: 0 8px 26px rgba(0,0,0,.24); max-width: 88vw; }

.doc-preview { border: 1px solid var(--line); border-radius: 10px; padding: 15px; background: var(--panel-soft); max-height: 46vh; overflow-y: auto; font-size: calc(13.5px * var(--fs, 1)); }
.doc-preview h3 { font-family: var(--serif); }
.doc-preview pre { white-space: pre-wrap; font-family: var(--sans); margin: 0; }

/* Word 卡片（文档生成后在聊天中呈现，不暴露 Markdown 源码） */
.word-card { position: relative; display: flex; align-items: center; gap: 11px; padding: 11px 13px; border: 1px solid var(--line); border-radius: 10px; background: #fff; }
.word-card.archived { background: var(--moss-soft); border-color: #cfe0cd; }
/* 预览文件卡（Agent 自行判断生成的预览版文件，未归档） */
.word-card.pv { border-style: dashed; background: var(--panel-soft); }
.word-card.pv .word-ico { background: var(--ink-3); }
.word-ico { width: 34px; height: 34px; border-radius: 8px; background: #2b6cb0; color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; font-family: var(--serif); flex-shrink: 0; }
.word-ico.ok { background: var(--moss); }
.word-info { min-width: 0; }
.word-name { font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; word-break: break-all; }
.word-meta { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin-top: 2px; }
.word-actions { display: flex; gap: 7px; align-items: center; margin-top: 9px; flex-wrap: wrap; }
.word-hint { font-size: calc(11.5px * var(--fs, 1)); color: var(--amber); }
/* 聊天消息里的归档文件卡片：与消息文字之间留出呼吸感 */
.msg .txt .word-card { margin-top: 9px; }
.msg .txt .word-card + .word-actions { margin-top: 8px; }
.word-ico.ppt { background: #b4443a; }
.word-ico.xls { background: #217346; }
.word-ico.pdf { background: #c1272d; }
.word-ico.csv { background: #0f766e; }
.word-ico.vid, .word-ico.video { background: #8b5cf6; }
.word-ico.img { background: #0e7490; }
.word-ico.zip { background: #6b807e; }

/* 纯文件卡消息（上传 / 生成 / 数据归档）：独立成卡，不再折叠在说明文字下 */
.word-card.fcard { background: #fff; border-color: var(--line); box-shadow: 0 1px 2px rgba(0, 0, 0, .04); }
.msg .txt .word-card.fcard { margin-top: 0; }
.msg .txt .word-card.fcard .word-name { font-weight: 600; }
.fcard + .word-actions { margin-top: 8px; }
/* 聊天附件卡（拖拽 / 📎 发来的文件）：与归档卡同款，但正文本身就是卡片，顶部不额外留白 */
.msg .txt .word-card.attach-fcard { margin-top: 0; }
.msg .txt .word-card.attach-fcard + .word-actions { margin-bottom: 2px; }
.msg .txt .word-card.attach-fcard .word-name { font-weight: 600; }
/* 模型没出正文时的提示（避免留下一个空白气泡，让人以为「没反应」） */
.ai-empty-hint { margin: 0; font-size: calc(12.5px * var(--fs, 1)); line-height: 1.65; color: var(--ink-3); }
.ai-empty-hint::before { content: '提示 '; font-weight: 600; color: var(--ink-2); }

/* 更早的聊天记录（7 天前的对话折叠成卡片） */
.hist-block { margin-bottom: 14px; }
.hist-card { display: flex; align-items: center; gap: 11px; padding: 10px 13px; border: 1px dashed var(--line); border-radius: 10px; background: var(--line-soft); }
.hist-ico { width: 32px; height: 32px; border-radius: 8px; background: var(--teal-soft); display: flex; align-items: center; justify-content: center; font-size: calc(15px * var(--fs, 1)); flex-shrink: 0; }
.hist-info { flex: 1; min-width: 0; }
.hist-title { font-size: calc(13px * var(--fs, 1)); font-weight: 600; color: var(--ink-2); }
.hist-meta { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin-top: 2px; }
.hist-actions { display: flex; gap: 6px; flex-shrink: 0; }
.hist-digest { margin-top: 8px; }
.hist-none { text-align: center; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); padding: 18px 0; }
.hist-list { max-height: 52vh; overflow-y: auto; border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: var(--panel-soft); }
.hist-msg { padding: 8px 0; border-bottom: 1px dashed var(--line-soft); }
.hist-msg:last-child { border-bottom: 0; }
.hist-who { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin-bottom: 3px; }
.hist-txt { font-size: calc(13px * var(--fs, 1)); line-height: 1.75; color: var(--ink); }
.hist-txt p { margin: 3px 0; }

/* 引用回复：输入区引用条 + 消息内引用块 */
.reply-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; padding: 7px 10px; border-left: 3px solid var(--teal-2); background: var(--line-soft); border-radius: 6px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); }
.reply-bar .rb-ico { color: var(--teal); flex-shrink: 0; }
.reply-bar .rb-body { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reply-bar .rb-body b { margin-right: 6px; color: var(--teal); }
.reply-bar .rb-x { border: 0; background: transparent; font-size: calc(15px * var(--fs, 1)); line-height: 1; color: var(--ink-3); cursor: pointer; padding: 0 3px; }
.reply-bar .rb-x:hover { color: var(--danger); }
.quote-block { display: block; margin: 2px 0 8px; padding: 6px 10px; border-left: 3px solid var(--line); background: var(--line-soft); border-radius: 6px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); cursor: pointer; }
.quote-block:hover { background: var(--teal-soft); border-left-color: var(--teal-2); }
.quote-block .q-who { color: var(--teal); margin-right: 6px; font-weight: 600; }
.msg.flash { animation: msgFlash 1.3s; }
@keyframes msgFlash { 0% { background: var(--teal-soft); } 100% { background: transparent; } }

/* 消息右键菜单（引用 / 复制） */
/* z75：overflow 必须 visible —— 原先是 hidden（为圆角裁剪背景），
   会把二级菜单裁掉。圆角改由 .ctx-menu > button:first-child / :last-child 单独处理。 */
.ctx-menu { position: fixed; z-index: 60; background: var(--panel); border: 1px solid var(--line); border-radius: 9px; box-shadow: var(--shadow); overflow: visible; min-width: 140px; }
/* z220（用户报障）：条目多、屏幕矮时菜单要被裁掉 —— 一屏放不下就内部滚动，
   否则最下面几项（引用回复 / 复制文本 / 撤回 / 删除）永远点不到。
   ⚠️ 只在 .ctx-scroll 时才开滚动：平时必须保持 overflow:visible，否则二级菜单会被裁掉。 */
.ctx-menu.ctx-scroll { overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.ctx-menu > button:first-child { border-top-left-radius: 9px; border-top-right-radius: 9px; }
.ctx-menu > button:last-child { border-bottom-left-radius: 9px; border-bottom-right-radius: 9px; }
.ctx-menu button { display: block; width: 100%; padding: 9px 14px; border: 0; background: transparent; text-align: left; font-size: calc(13px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; }
.ctx-menu button:hover { background: var(--teal-soft); color: var(--teal); }
.ctx-menu button.danger { color: var(--danger); }
.ctx-menu button.danger:hover { background: #fbeeec; color: var(--danger); }

/* ---- z75：二级菜单 ---- */
.ctx-sub-wrap { position: relative; }
.ctx-menu .ctx-has-sub { display: flex; align-items: center; gap: 6px; justify-content: space-between; }
.ctx-arrow { color: var(--ink-3); font-size: calc(14px * var(--fs, 1)); line-height: 1; }
.ctx-sub-wrap.on > .ctx-has-sub { background: var(--teal-soft); color: var(--teal); }
.ctx-sub {
  position: absolute; left: 100%; top: -5px; z-index: 61;
  min-width: 158px; padding: 4px 0;
  background: var(--panel); border: 1px solid var(--line); border-radius: 9px;
  box-shadow: var(--shadow); display: none;
}
/* 右侧空间不足时改为向左展开（否则会超出窗口） */
.ctx-sub.to-left { left: auto; right: 100%; }
.ctx-sub-wrap.on > .ctx-sub { display: block; }
.ctx-sub button { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; padding: 8px 13px; white-space: nowrap; }
.ctx-sub-tip { color: var(--ink-3); font-size: calc(11px * var(--fs, 1)); margin-left: auto; }
/* 触屏没有 hover：点一级项即展开，子菜单改为在下方铺开（不悬浮），避免被视口裁掉 */
@media (hover: none), (max-width: 720px) {
  .ctx-sub { position: static; display: none; box-shadow: none; border: 0; border-left: 2px solid var(--teal); border-radius: 0; margin: 2px 0 2px 14px; padding: 0; background: var(--panel-soft, var(--panel)); }
  .ctx-sub.to-left { right: auto; }
  .ctx-sub-wrap.on > .ctx-sub { display: block; }
}
.ctx-sep { height: 1px; background: var(--line-soft); margin: 3px 0; }

/* 消息徽标（z29）：被存入知识库 / 归档到课题文件的标识 */
.tax-badge { display: inline-flex; align-items: center; gap: 3px; padding: 0 6px; height: 16px; border-radius: 20px; font-size: calc(10px * var(--fs, 1)); font-weight: 600; vertical-align: 1px; margin-right: 5px; user-select: none; }
.tax-badge.kb { background: var(--violet-soft); color: var(--violet); border: 1px solid var(--violet); }
/* 房间知识库徽标（z31）：与课题组知识库（.kb / 紫）区分，用琥珀色 */
.tax-badge.kb.room { background: var(--amber-soft); color: var(--amber); border-color: var(--amber); }
.tax-badge.file { background: var(--teal-soft); color: var(--teal); border: 1px solid var(--teal); }
/* 经验徽标（z212）：三层三色 —— 课题组经验（苔绿，最长期）／团队经验（蓝）／房间经验（灰，范围最窄）。
   与知识库（紫）刻意不同色系：资料是「参考」，经验是「必须遵守」，一眼分得开。 */
.tax-badge.rule { background: var(--moss-soft); color: var(--moss); border: 1px solid var(--moss); }
.tax-badge.rule.team { background: var(--blue-soft); color: var(--blue); border-color: var(--blue); }
.tax-badge.rule.room { background: var(--line-soft); color: var(--ink-3); border-color: var(--ink-3); }

/* 文档卡上的「盖章」标识（z32）：把徽标直接打在文档卡右上角，像盖了个戳。
   用 rotate 轻微倾斜 + 双线描边，视觉上更像印章；不遮挡右侧的预览/下载按钮区。 */
.card-stamps { position: absolute; top: -7px; right: -5px; display: flex; gap: 4px; align-items: center; pointer-events: none; z-index: 3; }
.card-stamps .tax-badge {
  margin: 0; height: 19px; padding: 0 8px; border-radius: 5px;
  font-size: calc(10.5px * var(--fs, 1)); letter-spacing: .5px; font-weight: 700;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .14);
  transform: rotate(-6deg);
  background: var(--panel);
}
.card-stamps .tax-badge:not(:first-child) { transform: rotate(3deg); }
/* 盖章时把徽标做成实心（更醒目），仍是同一套主题变量 */
.card-stamps .tax-badge.kb { background: var(--violet); color: #fff; border: 1.5px solid var(--violet-soft); }
.card-stamps .tax-badge.kb.room { background: var(--amber); color: #fff; border: 1.5px solid var(--amber-soft); }
.card-stamps .tax-badge.file { background: var(--teal); color: #fff; border: 1.5px solid var(--teal-soft); }
/* z212：盖在文档卡上的经验徽标同样做实心（沿用上面的三层配色） */
.card-stamps .tax-badge.rule { background: var(--moss); color: #fff; border: 1.5px solid var(--moss-soft); }
.card-stamps .tax-badge.rule.team { background: var(--blue); color: #fff; border-color: var(--blue-soft); }
.card-stamps .tax-badge.rule.room { background: var(--ink-3); color: #fff; border-color: var(--line-soft); }

/* 归档类别选择弹窗（z29） */
.cat-pick { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 4px; }
.cat-pick button { padding: 5px 12px; border: 1px solid var(--line); background: var(--panel); border-radius: 20px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; }
.cat-pick button:hover { border-color: var(--teal-2); color: var(--teal); }
.cat-pick button.on { background: var(--teal); color: #fff; border-color: var(--teal); }

/* 系统消息：撤回提示（居中灰条）；删除墓碑不渲染 */
.msg-sys { text-align: center; margin: 9px 0; }
.msg-sys .t { display: inline-block; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-4); background: var(--line-soft); padding: 2px 12px; border-radius: 11px; }

/* @提及高亮 + @ 面板中的成员项 */
.mention { color: var(--teal); font-weight: 600; }
.at-item.member .dot { background: #5b7c7a; }
.at-sub { margin-left: auto; font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); background: var(--line-soft); padding: 1px 6px; border-radius: 10px; }

/* 拖拽上传：拖到对话框区域即上传 */
.composer { position: relative; }
.discuss.drag-over, .priv-chat.drag-over { outline: 2px dashed var(--teal-2); outline-offset: -4px; border-radius: var(--radius); }
.discuss.drag-over .composer::after, .priv-chat.drag-over .composer::after { content: '松开即可上传文件 / 图片'; position: absolute; inset: -6px; display: flex; align-items: center; justify-content: center; border: 2px dashed var(--teal-2); border-radius: 12px; background: rgba(47, 163, 107, .10); color: var(--teal); font-size: calc(13px * var(--fs, 1)); font-weight: 600; z-index: 6; pointer-events: none; }

/* 撰写页：类型选择条 */
.write-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; flex-wrap: wrap; }
.write-types { display: flex; gap: 6px; flex-wrap: wrap; flex: 1; }
.wtype { padding: 6px 11px; border: 1px solid var(--line); border-radius: 20px; background: var(--panel); font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; }
.wtype:hover { border-color: var(--teal-2); color: var(--teal); }
.wtype.on { background: var(--teal-soft); border-color: var(--teal-2); color: var(--teal); font-weight: 600; }

/* PPT 预览：幻灯片样式卡片（不显示 Markdown 源码） */
.slide-deck { display: flex; flex-direction: column; gap: 10px; }
.slide { border: 1px solid var(--line); border-radius: 8px; padding: 12px 15px; background: var(--panel); min-height: 76px; }
.slide.cover { background: #0e4a44; border-color: #0e4a44; text-align: center; }
.slide.cover .slide-title { color: #fff; font-size: calc(17px * var(--fs, 1)); margin-top: 8px; }
.slide.cover .slide-sub { color: #bbd6d2; font-size: calc(11.5px * var(--fs, 1)); margin-top: 5px; }
.slide.cover .slide-no { color: #8fbfb8; }
.slide-no { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); margin-bottom: 4px; }
.slide-title { font-size: calc(14.5px * var(--fs, 1)); font-weight: 700; color: var(--teal); margin-bottom: 5px; }
.slide ul { margin: 0; padding-left: 18px; }
.slide li { font-size: calc(12.5px * var(--fs, 1)); line-height: 1.7; color: var(--ink-2); margin: 2px 0; }
/* z48：幻灯片 DSL 预览元素——栏目词 chip / 结论强调行 / 图占位 / 讲者备注 */
.slide-tag { display: inline-block; margin: 0 6px 4px 0; padding: 1.5px 9px; border-radius: 999px; background: var(--teal-soft); color: var(--teal); font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; vertical-align: middle; }
.slide-concl { display: block; margin-top: 8px; padding: 2px 0 2px 10px; border-left: 3px solid var(--teal); font-size: calc(12.5px * var(--fs, 1)); font-weight: 700; color: var(--teal); }
.slide-fig { margin: 9px 0 3px; padding: 12px; border: 1.5px dashed var(--line); border-radius: 8px; text-align: center; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); background: var(--line-soft); }
.slide-note { margin-top: 8px; padding-top: 6px; border-top: 1px dashed var(--line); font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.slide table { border-collapse: collapse; margin: 8px 0 2px; font-size: calc(12px * var(--fs, 1)); }
.slide th, .slide td { border: 1px solid var(--line); padding: 4px 9px; text-align: left; line-height: 1.5; }
.slide thead th { background: var(--teal-soft); color: var(--teal); font-weight: 600; }

/* 文档预览：按排版呈现，不显示源码符号 */
.docpage { background: var(--panel); line-height: 1.85; }
.docpage h2.doc-title { font-size: calc(18px * var(--fs, 1)); text-align: center; margin: 6px 0 14px; }
.docpage h3 { font-size: calc(16px * var(--fs, 1)); margin: 12px 0 8px; }
.docpage h4 { font-size: calc(14px * var(--fs, 1)); margin: 10px 0 6px; }
.docpage h5 { font-size: calc(13px * var(--fs, 1)); margin: 8px 0 4px; color: var(--ink-2); }
.docpage ul, .docpage ol { padding-left: 22px; margin: 6px 0; }
.docpage li { margin: 3px 0; }
.docpage blockquote { border-left: 3px solid var(--line); margin: 6px 0; padding: 2px 12px; color: var(--ink-2); }
.docpage .refs { border-top: 1px dashed var(--line); margin-top: 10px; padding-top: 6px; font-size: calc(12px * var(--fs, 1)); }
/* z38：md() 现在会把 ![alt](data:image/...) 渲染成真正的 <img>（此前完全没有图片处理，
   图片在预览里是以字面量 ![alt](data:image...) 整段显示的）。新样式一律用 CSS 变量以适配五套主题。 */
.md-img { display: block; max-width: 100%; height: auto; margin: 10px auto; border-radius: 8px; border: 1px solid var(--line); background: var(--panel); }
.doc-preview .md-img { max-height: 60vh; width: auto; }
.img-missing { display: inline-block; padding: 3px 9px; border: 1px dashed var(--line); border-radius: 6px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); background: var(--line-soft); }
/* z38：文档内嵌图区块（Word/PPT/Excel 里的图，随预览显示） */
.doc-imgs { margin: 0 0 14px; padding-bottom: 12px; border-bottom: 1px dashed var(--line); }
.doc-imgs-t { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); margin-bottom: 8px; }
.doc-imgs .md-img { margin: 8px auto; }

/* 表格：Markdown 表格渲染为真正的 <table>（表头底色 + 斑马纹 + 窄屏横向滚动） */
.tbl-wrap { overflow-x: auto; margin: 9px 0; border-radius: 8px; }
.tbl-wrap table { border-collapse: collapse; width: 100%; font-size: calc(12.5px * var(--fs, 1)); background: var(--panel); }
.tbl-wrap th, .tbl-wrap td { border: 1px solid var(--line); padding: 6px 10px; text-align: left; vertical-align: top; line-height: 1.65; }
.tbl-wrap thead th { background: var(--teal-soft); color: var(--teal); font-weight: 600; white-space: nowrap; }
.tbl-wrap tbody tr:nth-child(even) { background: var(--line-soft); }
.doc-title { text-align: center; }

/* ---------- 响应式 ---------- */
@media (max-width: 940px) {
  .room-layout { grid-template-columns: 1fr; }
  .side { position: static; }
  .msgs { max-height: 60vh; }
  .grid2 { grid-template-columns: 1fr; }
  .flow { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  main { padding: 16px 13px 48px; }
  .topbar { gap: 12px; padding: 0 13px; height: auto; min-height: 54px; flex-wrap: wrap; padding-bottom: 8px; }
  .topnav { order: 3; width: 100%; }
  .topbar .me { margin-left: auto; }   /* z92：限定 .topbar，别泄漏到 .msg.me */
  .room-head h2 { font-size: calc(18px * var(--fs, 1)); }
  .flow { grid-template-columns: 1fr; }
  .role-row { grid-template-columns: 1fr; }
}

/* ---------- 加入申请（申请 - 审批） ---------- */
.join-req { border: 1px solid var(--line); border-left: 3px solid var(--amber); border-radius: 9px; padding: 9px 11px; margin-bottom: 8px; background: #fffdf8; }
.join-req .who { display: flex; align-items: baseline; gap: 8px; font-size: calc(13px * var(--fs, 1)); }
.join-req .who .t { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.join-req .msg { margin-top: 5px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); line-height: 1.6; white-space: pre-wrap; }
.join-req .acts { margin-top: 7px; display: flex; gap: 7px; }
.jr-done { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 6px; }
.jr-chip { font-size: calc(10.5px * var(--fs, 1)); padding: 1px 8px; border-radius: 10px; background: var(--line-soft); color: var(--ink-3); }
.jr-chip.approved { background: var(--teal-soft); color: var(--teal); }
.jr-chip.rejected { background: var(--amber-soft); color: var(--amber); }

/* ---------- 工作台分区（课题讨论区 / 公共撰写区） ---------- */
.home-sec { margin-top: 26px; }
.home-sec-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; margin-bottom: 12px; flex-wrap: wrap; }
.home-sec-head h3 { margin: 0; font-family: var(--serif); font-size: calc(17px * var(--fs, 1)); }
.home-sec-head p { margin: 4px 0 0; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); max-width: 640px; line-height: 1.65; }
.room-card.writing { border-left: 3px solid var(--amber); }
.room-card.writing:hover { border-color: var(--amber); }
.room-tag { display: inline-block; font-size: calc(10.5px * var(--fs, 1)); font-weight: 400; padding: 1px 8px; border-radius: 10px; background: var(--amber-soft); color: var(--amber); vertical-align: middle; margin-left: 6px; }

/* ---------- AI 团队页：课题组知识库模块 ---------- */
.kb-panel { margin-top: 22px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); overflow: hidden; }
.kb-panel-head { display: flex; align-items: baseline; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--line-soft); flex-wrap: wrap; }
.kb-panel-head h3 { margin: 0; font-family: var(--serif); font-size: calc(15px * var(--fs, 1)); }
.kb-sub { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.kb-grid { display: grid; grid-template-columns: 1fr 1fr; }
.kb-col { padding: 12px 16px; min-width: 0; }
.kb-col + .kb-col { border-left: 1px solid var(--line-soft); }
.kb-col h4 { margin: 0 0 9px; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); }
.kb-empty { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); margin: 6px 0; }
.kb-item.pending { background: var(--amber-soft); border-left-color: var(--amber); }
.kb-badge { display: inline-block; font-size: calc(10.5px * var(--fs, 1)); padding: 1px 7px; border-radius: 10px; background: var(--amber-soft); color: var(--amber); vertical-align: middle; font-weight: 400; }

/* z61 经验页（顶栏独立页）：条目卡之间留白更宽，适合整页浏览 */
/* ============================================================
   z65 经验页 + 录入弹窗重做（可读性）
   方向：白底条目卡、编号清单、一句话一整行 —— 少颜色、少分割线，靠留白与字重分层。
   ============================================================ */

/* ---- 经验页：折叠分区改成「白底卡片组」---- */
.rule-fold {
  margin: 14px 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel);          /* 白底，不再是灰底 */
  box-shadow: 0 1px 2px rgba(28,43,42,.04);
  overflow: hidden;
}
.rule-fold .rf-head {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 14px 18px; background: var(--panel);
  border: 0; border-bottom: 1px solid transparent;
  font: inherit; color: var(--ink); text-align: left; cursor: pointer;
}
.rule-fold.open .rf-head { border-bottom-color: var(--line-soft); }
.rule-fold .rf-head:hover { background: var(--line-soft); }
.rule-fold .rf-title { font-size: calc(14.5px * var(--fs, 1)); font-weight: 600; flex: 0 0 auto; letter-spacing: .2px; }
.rule-fold .rf-count {
  font-size: calc(11.5px * var(--fs, 1)); padding: 2px 10px; border-radius: 11px;
  background: var(--line-soft); color: var(--ink-2); font-weight: 600;
}
.rule-fold .rf-count.zero { background: transparent; color: var(--ink-3); font-weight: 400; }
.rule-fold .rf-caret { margin-left: auto; color: var(--ink-3); font-size: calc(13px * var(--fs, 1)); }
.rule-fold .rf-body { padding: 4px 18px 18px; display: none; }
.rule-fold.open > .rf-body { display: block; }
/* z68 加固：嵌套层（团队 → 房间）必须各自独立折叠。
   用子选择器 + 更高特异性写死，避免任何后续规则或主题覆盖导致「收起但内容仍可见」。 */
.rule-fold.nested > .rf-body { display: none; }
.rule-fold.nested.open > .rf-body { display: block; }
/* 折叠标题可点击的视觉暗示 */
.rule-fold .rf-head { cursor: pointer; }
.rule-fold .rf-head:hover .rf-title { color: var(--teal); }
.rule-fold .rf-hint {
  font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); margin: 0;
  padding: 11px 0 12px; line-height: 1.6;
}

/* ---- 条目卡：白底、左侧色条表作用域、正文一整句 ---- */
.rule-row {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 13px 15px; margin: 10px 0 0;
  border: 1px solid var(--line); border-left: 3px solid var(--teal);
  border-radius: 10px; background: var(--panel);
  transition: border-color .12s, box-shadow .12s;
}
.rule-row:hover { border-color: var(--line); box-shadow: 0 1px 5px rgba(28,43,42,.06); }
.rule-row.pending { border-left-color: var(--amber); background: #fffdf7; }

/* 经验正文：一整句话，字号加大、行高放松 —— 这是可读性的关键 */
.rule-sentence {
  font-size: calc(14px * var(--fs, 1)); line-height: 1.75; color: var(--ink);
  font-weight: 500; word-break: break-word;
}
.rmeta { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin-top: 6px; }
.rtag {
  flex: 0 0 auto; font-size: calc(10.5px * var(--fs, 1)); line-height: 1.6; padding: 2px 8px; border-radius: 8px;
  border: 1px solid var(--line); color: var(--ink-2); white-space: nowrap; font-weight: 500;
  margin-top: 1px;
}
/* z63/z65：三个作用域各有配色 —— 房间=青、课题组=靛紫、团队=蓝（z65 重做时勿删） */
.rtag.room { border-color: var(--teal); color: var(--teal); }
.rtag.group { border-color: var(--indigo, #5B6ABF); color: var(--indigo, #5B6ABF); }
.rtag.team { border-color: var(--blue); color: var(--blue); background: var(--blue-soft); }
.racts { flex: 0 0 auto; display: flex; gap: 5px; align-items: center; }
@media (max-width: 720px) {
  .rule-row { flex-wrap: wrap; }
  .racts { width: 100%; justify-content: flex-end; }
}
/* 分区标题（如「团队层 · 对该团队所有房间生效（3）」） */
.rule-sub {
  margin: 16px 0 2px; font-size: calc(12px * var(--fs, 1)); font-weight: 600; color: var(--ink-2);
  display: flex; align-items: center; gap: 8px;
}
.rule-sub:first-child { margin-top: 10px; }

/* ---- 录入弹窗：白底 + 编号清单 ---- */
.rule-add h3 { margin-top: 0; }
.rule-add-rules { margin: 14px 0 4px; }
.rule-add-rules > label {
  display: block; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); margin-bottom: 9px; line-height: 1.6;
}
.rule-add-rules textarea {
  width: 100%; border: 1px solid var(--line); border-radius: 10px;
  padding: 12px 14px; font: inherit; font-size: calc(14px * var(--fs, 1)); line-height: 2;   /* 行高 2：一行一条不挤 */
  background: var(--panel); color: var(--ink); resize: vertical;
}
.rule-add-rules textarea:focus { outline: none; border-color: var(--teal); box-shadow: 0 0 0 3px rgba(15,92,84,.10); }
/* 左侧实时编号清单：把「黑框」变成可扫读的清单 */
.rule-add-numlist {
  border: 1px solid var(--line-soft); border-radius: 10px; background: var(--line-soft);
  padding: 8px 10px; margin-bottom: 10px; max-height: 168px; overflow-y: auto;
}
.rule-add-numlist:empty { display: none; }
.rule-add-num {
  display: flex; gap: 9px; align-items: baseline;
  font-size: calc(12.5px * var(--fs, 1)); line-height: 1.65; color: var(--ink-2); padding: 2px 0;
}
.rule-add-num b {
  flex: 0 0 auto; width: 19px; height: 19px; line-height: 19px; text-align: center;
  border-radius: 50%; background: var(--teal); color: #fff; font-size: calc(11px * var(--fs, 1)); font-weight: 600;
}
.rule-add-num span { word-break: break-word; }
.rule-add-num i { font-style: normal; color: var(--ink-3); }
.rule-add-num.empty { color: var(--ink-3); font-size: calc(12px * var(--fs, 1)); padding-left: 2px; }
.rule-add-count { font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); margin-top: 8px; font-weight: 600; }
.rule-add-more { margin-top: 14px; }
.rule-add-more summary { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); cursor: pointer; }
/* 逐条输入行：编号 + 单输入框 + 删除 */
.br-row { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.br-no {
  flex: 0 0 auto; width: 22px; height: 22px; line-height: 22px; text-align: center;
  border-radius: 50%; background: var(--teal); color: #fff; font-size: calc(11.5px * var(--fs, 1)); font-weight: 600;
}
.br-one {
  flex: 1 1 auto; min-width: 0; border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 11px; font: inherit; font-size: calc(13.5px * var(--fs, 1)); background: var(--panel); color: var(--ink);
}
.br-one:focus { outline: none; border-color: var(--teal); box-shadow: 0 0 0 3px rgba(15,92,84,.10); }
.br-del { flex: 0 0 auto; }

@media (max-width: 720px) { .br-row { gap: 6px; } }

/* 团队页顶部：知识库 / 经验 / 借鉴库 三个入口
   z153（用户要求）：手机端原来只有图标、没有文字，看不出是哪个库；
   现在**三张卡永远一行**，卡内是「图标 + 名称」，去掉了「进入 →」（整卡可点，不需要提示）。 */
.team-space {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px; margin: 14px 0 20px;
}
@media (max-width: 620px) {
  /* 手机：竖排（图标在上、文字在下），三列等宽 —— 比横排更能放下 3 个中文词 */
  .team-space { gap: 8px; margin: 10px 0 14px; }
  .team-space-card {
    flex-direction: column; justify-content: center; gap: 4px;
    padding: 9px 4px; border-radius: 9px;
  }
  .team-space-card .tsc-ico { width: auto; height: auto; font-size: calc(18px * var(--fs, 1)); }
  .team-space-card .tsc-txt b { font-size: calc(12px * var(--fs, 1)); }
}
.team-space-card {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 14px 16px; text-align: left; cursor: pointer;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-sm, 10px); font: inherit; color: var(--ink-1);
  transition: border-color .12s, box-shadow .12s, transform .12s;
}
.team-space-card:hover {
  border-color: var(--teal);
  box-shadow: 0 2px 10px rgba(0,0,0,.07);
  transform: translateY(-1px);
}
.team-space-card .tsc-ico { font-size: calc(22px * var(--fs, 1)); flex: 0 0 auto; line-height: 1; }
.team-space-card .tsc-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.team-space-card .tsc-txt b { font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; }
.team-space-card .tsc-txt i {
  font-style: normal; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); line-height: 1.45;
}
.team-space-card .tsc-go { margin-left: auto; font-size: calc(12px * var(--fs, 1)); color: var(--teal); flex: 0 0 auto; }

/* 批量录入弹窗：一行一条（标题 + 正文 + 删除） */
.br-row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) auto;
  gap: 6px; margin-bottom: 6px; align-items: center;
}
.br-row input { min-width: 0; }
@media (max-width: 720px) {
  .br-row { grid-template-columns: 1fr; }
}

/* 会话中的系统提示（如：已提交知识库） */
.priv-sys { text-align: center; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); margin: 10px 0; }

@media (max-width: 940px) {
  .kb-grid { grid-template-columns: 1fr; }
  .kb-col + .kb-col { border-left: 0; border-top: 1px solid var(--line-soft); }
}

/* ============================================================
   角色标签与用量统计（导师 / 组长 / 学生 · 积分与 tokens）
   ============================================================ */
.role-chip { display: inline-block; padding: 1px 8px; border-radius: 20px; font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; background: var(--line-soft); color: var(--ink-2); }
.role-chip.mentor { background: var(--amber-soft); color: var(--amber); }
.role-chip.leader { background: var(--violet-soft); color: var(--violet); }
.role-chip.student { background: var(--teal-soft); color: var(--teal); }
.badge.leader { background: var(--violet-soft); color: var(--violet); }

.usage-chip { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); background: var(--panel); border: 1px solid var(--line); border-radius: 20px; padding: 4px 11px; white-space: nowrap; }
.usage-chip b { color: var(--teal); }
.usage-chip.warn { border-color: #ecd9b0; background: var(--amber-soft); }
.usage-chip.warn b { color: var(--amber); }
.usage-chip.over { border-color: #e6c3bf; background: #fbeeec; color: var(--danger); }
.usage-chip.over b { color: var(--danger); }

.usage-sum { display: flex; flex-wrap: wrap; gap: 14px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); margin-bottom: 8px; }
.usage-sum b { color: var(--teal); }
.usage-table { border: 1px solid var(--line-soft); border-radius: 8px; overflow: hidden; }
.usage-row { display: grid; grid-template-columns: 62px 1fr 48px 96px 76px; gap: 6px; padding: 5px 9px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); border-bottom: 1px solid var(--line-soft); align-items: center; }
.usage-row:last-child { border-bottom: 0; }
.usage-row:nth-child(odd) { background: var(--panel-soft); }
.usage-row b { color: var(--teal); text-align: right; }
.usage-row span:nth-child(3), .usage-row span:nth-child(4) { text-align: right; }

.room-usage-row { padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
.room-usage-row:last-child { border-bottom: 0; }
.ru-head { display: flex; justify-content: space-between; gap: 10px; align-items: flex-start; }
.ru-head .nm { font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ru-head .mt { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin-top: 3px; }
.ru-bar { height: 6px; border-radius: 4px; background: var(--line-soft); margin: 9px 0 5px; overflow: hidden; }
.ru-fill { height: 100%; background: var(--teal-2); border-radius: 4px; transition: width .3s; }
.ru-fill.warn { background: var(--amber); }
.ru-fill.over { background: var(--danger); }
.ru-meta { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.ru-meta b { color: var(--ink-2); }

@media (max-width: 640px) {
  .usage-row { grid-template-columns: 56px 1fr 76px; row-gap: 2px; }
  .usage-row span:nth-child(4) { display: none; }
}

/* 用量与积分管理页：每房间的上限设置行 */
.ua-limit { display: flex; align-items: center; gap: 8px; margin-top: 9px; flex-wrap: wrap; }
.ua-limit label { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }
.ua-limit input { width: 180px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px; font-family: inherit; font-size: calc(12.5px * var(--fs, 1)); background: var(--panel); color: var(--ink); }
.ua-limit input:disabled { background: var(--line-soft); color: var(--ink-3); }

/* 管理员角色徽标（应用级角色：管理员 / 导师 / 组长 / 学生） */
.role-chip.admin { background: var(--danger-soft); color: var(--danger); }
.badge.admin { background: var(--danger-soft); color: var(--danger); }

/* ============================================================
   课题归档 / 删除（归档区、归档详情、打包进度）
   ============================================================ */
.arch-banner { background: var(--line-soft); border: 1px solid var(--line); color: var(--ink-2); border-radius: 10px; padding: 9px 14px; font-size: calc(12.5px * var(--fs, 1)); margin-bottom: 12px; }
.arch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; margin-top: 4px; }
.arch-card { background: var(--line-soft); border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px; cursor: pointer; opacity: .92; transition: opacity .15s, border-color .15s; }
.arch-card:hover { opacity: 1; border-color: #cfd8d6; }
.arch-card h4 { margin: 0 0 4px; font-size: calc(13.5px * var(--fs, 1)); color: var(--ink); }
.arch-card .m { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.arch-steps { display: flex; align-items: center; gap: 8px; font-size: calc(13px * var(--fs, 1)); color: var(--ink-2); padding: 8px 0 2px; }

/* ============================================================
   外观主题（html[data-theme] 切换；不设属性 = 青瓷原版）
   ============================================================ */
html[data-theme="warm"] {           /* 暖阳陶土：奶油纸底 × 陶土橘 */
  --ink: #33291f; --ink-2: #5f5142; --ink-3: #8f7f6b; --ink-4: #b3a492;
  --line: #eadfcd; --line-soft: #f5ede1;
  --bg: #faf5ee; --panel: #fffdfa; --panel-soft: #f8f2e8;
  --teal: #b05f2e; --teal-2: #c4713e; --teal-soft: #f8e9dc;
  --amber: #a9721a; --amber-soft: #fbf0d9;
  --moss: #5f7d3a; --moss-soft: #eff3e0;
  --violet: #7a5aa8; --violet-soft: #f4effc;
  --danger: #b4443a; --danger-soft: #fdece9; --blue: #3a6ea5;
  --grad-a: #f7efe4; --grad-b: #efe3d2;
  --topbar-bg: rgba(255, 252, 247, .92);
  --bubble-me-bg: #d7eec0; --bubble-me-bd: #bce0a0;
  --bubble-other-bg: #fffdfa; --bubble-other-bd: #ecdfcb;
  --bubble-ai-bg: #fbf6ec; --bubble-ai-bd: #eddfc9;
  --shadow: 0 1px 2px rgba(88, 62, 38, .05), 0 10px 26px rgba(88, 62, 38, .07);
}
html[data-theme="tea"] {            /* 杏茶：杏色纸底 × 温润茶绿 */
  --ink: #2e372f; --ink-2: #56624f; --ink-3: #85917c; --ink-4: #a9b3a0;
  --line: #e3e2cf; --line-soft: #f1f0e2;
  --bg: #f7f4ea; --panel: #fffdf7; --panel-soft: #f5f2e5;
  --teal: #4f7c56; --teal-2: #639a6c; --teal-soft: #e7f0e0;
  --amber: #a97c1e; --amber-soft: #f9f0d8;
  --moss: #6a8a3f; --moss-soft: #eff3df;
  --violet: #7a5aa8; --violet-soft: #f4effc;
  --danger: #b0453b; --danger-soft: #fdece9; --blue: #3a6ea5;
  --grad-a: #f3f0e2; --grad-b: #e9e7d2;
  --topbar-bg: rgba(255, 253, 246, .92);
  --bubble-me-bg: #d3ecc2; --bubble-me-bd: #b8e09b;
  --bubble-other-bg: #fffdf7; --bubble-other-bd: #e6e3cd;
  --bubble-ai-bg: #f7f5e8; --bubble-ai-bd: #e6e3cd;
  --shadow: 0 1px 2px rgba(70, 78, 55, .05), 0 10px 26px rgba(70, 78, 55, .07);
}
html[data-theme="sakura"] {         /* 晚樱：暖灰底 × 豆沙粉 */
  --ink: #362d2e; --ink-2: #5d4f51; --ink-3: #94807f; --ink-4: #b7a5a2;
  --line: #ecdcdb; --line-soft: #f6eae9;
  --bg: #f9f4f3; --panel: #fffbfa; --panel-soft: #f8efee;
  --teal: #a85a63; --teal-2: #bd6d76; --teal-soft: #f8e7e9;
  --amber: #b2791d; --amber-soft: #faf0da;
  --moss: #6a8a4a; --moss-soft: #f0f4e4;
  --violet: #7a5aa8; --violet-soft: #f5effc;
  --danger: #ae463f; --danger-soft: #fdecea; --blue: #45679f;
  --grad-a: #f7eef0; --grad-b: #efe1e3;
  --topbar-bg: rgba(255, 250, 249, .92);
  --bubble-me-bg: #dcecc9; --bubble-me-bd: #c5e2ae;
  --bubble-other-bg: #fffbfa; --bubble-other-bd: #eeddda;
  --bubble-ai-bg: #faf1f0; --bubble-ai-bd: #eeddda;
  --shadow: 0 1px 2px rgba(96, 66, 68, .05), 0 10px 26px rgba(96, 66, 68, .07);
}
html[data-theme="bamboo"] {         /* 暖竹青：保留青绿主色，暖化中性色 */
  --ink: #26302c; --ink-2: #4b5852; --ink-3: #7d8a82; --ink-4: #a3aea7;
  --line: #dcdfd2; --line-soft: #ecefe5;
  --bg: #f7f6ef; --panel: #fffefa; --panel-soft: #f6f5eb;
  --teal: #15655a; --teal-2: #1f7d70; --teal-soft: #e3efec;
  --amber: #a9721a; --amber-soft: #f9f0d9;
  --moss: #4f7c48; --moss-soft: #edf3e8;
  --violet: #6d4aa8; --violet-soft: #f4effc;
  --danger: #b4443a; --danger-soft: #fdecea; --blue: #2b6cb0;
  --grad-a: #f1f2e8; --grad-b: #e6e9da;
  --topbar-bg: rgba(255, 254, 249, .92);
  --bubble-me-bg: #d5efc6; --bubble-me-bd: #bce2a5;
  --bubble-other-bg: #fffefa; --bubble-other-bd: #e2e6d8;
  --bubble-ai-bg: #f6f8ef; --bubble-ai-bd: #e2e6d8;
  --shadow: 0 1px 2px rgba(45, 60, 50, .05), 0 10px 26px rgba(45, 60, 50, .07);
}

/* 主题选择器（外观弹窗） */
.theme-list { display: flex; flex-direction: column; gap: 9px; }
.theme-item { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 11px; cursor: pointer; transition: border-color .15s, background .15s; }
.theme-item:hover { border-color: var(--teal-2); background: var(--panel-soft); }
.theme-item.on { border-color: var(--teal); background: var(--teal-soft); }
.theme-swatches { display: flex; flex-shrink: 0; }
.theme-swatches span { width: 20px; height: 20px; border-radius: 50%; border: 2px solid var(--panel); margin-left: -6px; box-shadow: 0 0 0 1px var(--line); }
.theme-swatches span:first-child { margin-left: 0; }
.theme-info { flex: 1; min-width: 0; }
.theme-info b { display: block; font-size: calc(13.5px * var(--fs, 1)); }
.theme-info span { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.theme-check { width: 18px; text-align: center; color: var(--teal); font-weight: 700; }

/* ============================================================
   工具调用：气泡内进度、痕迹标签、工具开关列表、技能面板
   ============================================================ */
.tool-steps { display: flex; flex-direction: column; gap: 5px; margin-bottom: 8px; }
.tool-step { display: flex; align-items: center; gap: 7px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); background: var(--panel-soft); border: 1px dashed var(--line); border-radius: 8px; padding: 5px 9px; width: fit-content; max-width: 100%; }
.tool-step em { font-style: normal; color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 240px; }
.tool-step.run { border-style: solid; border-color: var(--amber); background: var(--amber-soft); }
.tool-step.err { border-color: #e6c3bf; background: var(--danger-soft); color: var(--danger); }
.tool-trace { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 7px; align-items: center; }
.tt-chip { font-size: calc(11px * var(--fs, 1)); color: var(--ink-2); background: var(--line-soft); border: 1px solid var(--line); border-radius: 20px; padding: 2px 9px; }
/* z144「本轮依据」：刻意与 🔧 工具标签**长得不一样**（虚线边 + 前缀标题）——
   用户一眼要能分清「系统自动给的料」和「Agent 主动拉的料」，这是本需求的核心。 */
.src-trace { margin-bottom: 8px; }
.src-hd { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); font-weight: 600; margin-right: 2px; letter-spacing: .3px; }
.src-chip { border-style: dashed; background: var(--panel-soft); color: var(--ink-2); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.src-chip:hover { border-color: var(--teal-2); }
.tool-toggle-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; }
.tool-toggle { display: flex; gap: 9px; align-items: flex-start; border: 1px solid var(--line-soft); border-radius: 9px; padding: 8px 11px; cursor: pointer; }
.tool-toggle:hover { border-color: var(--teal-2); }
.tool-toggle input { margin-top: 3px; }
.tool-toggle b { font-size: calc(12.5px * var(--fs, 1)); display: block; }
.tool-toggle i { font-style: normal; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); display: block; line-height: 1.5; }

/* ============================================================
   实时消息：不贴底时的新消息提示条（点击回到底部）
   ============================================================ */
.discuss { position: relative; }
.new-msg-chip { position: absolute; left: 50%; transform: translateX(-50%); bottom: 92px; z-index: 30; border: 1px solid var(--teal-2); background: var(--teal); color: #fff; font-size: calc(12px * var(--fs, 1)); padding: 6px 14px; border-radius: 20px; cursor: pointer; box-shadow: var(--shadow); font-family: inherit; }
.new-msg-chip:hover { background: var(--teal-2); }

/* ============================================================
   AI 画图：```svg 示意图 / ```chart 数据图
   ============================================================ */
.fig-block { margin: 10px 0; border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: var(--panel); }
.fig-body { padding: 12px 12px 6px; }
.fig-body svg { max-width: 100%; height: auto; display: block; margin: 0 auto; }
.fig-body canvas { width: 100% !important; height: 280px !important; }
.fig-bar { display: flex; justify-content: space-between; align-items: center; padding: 6px 12px; border-top: 1px dashed var(--line); background: var(--panel-soft); }
.fig-tag { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.fig-err { font-size: calc(12.5px * var(--fs, 1)); color: var(--danger); margin: 0 0 6px; }

/* ============================================================
   AI 团队管理：Agent 列表行 + 技能包上传与适用 Agent 配置
   ============================================================ */
.agent-manage-list { display: flex; flex-direction: column; gap: 6px; }
.agent-manage-row { display: flex; align-items: center; gap: 9px; padding: 7px 11px; border: 1px solid var(--line-soft); border-radius: 9px; background: var(--panel-soft); font-size: calc(13px * var(--fs, 1)); }
.agent-manage-row.off { opacity: .58; }
.agent-manage-row .av.sm { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: calc(11.5px * var(--fs, 1)); font-weight: 600; color: #fff; flex-shrink: 0; }
.agent-manage-row .amr-keys { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.agent-manage-row .amr-sp { flex: 1; }
.badge.off { background: var(--line); color: var(--ink-3); }
.up-btns { display: flex; gap: 6px; }
.up-btns .upload-zone { flex: 1; text-align: center; font-size: calc(11.5px * var(--fs, 1)); padding: 9px 4px; margin-bottom: 0; }
.sk-pack-hint { font-size: calc(11.5px * var(--fs, 1)); color: var(--teal); background: var(--teal-soft); border-radius: 8px; padding: 7px 9px; margin-top: 7px; line-height: 1.6; }
.sk-scope-box { display: flex; flex-wrap: wrap; gap: 6px; }
.sk-scope-item { display: inline-flex; align-items: center; gap: 4px; font-size: calc(12.5px * var(--fs, 1)); padding: 4px 10px; border: 1px solid var(--line); border-radius: 20px; cursor: pointer; background: var(--panel); }
.sk-scope-item.on { border-color: var(--teal-2); background: var(--teal-soft); color: var(--teal); font-weight: 600; }
.sk-scope-item:has(input:checked) { border-color: var(--teal-2); background: var(--teal-soft); color: var(--teal); font-weight: 600; }
.sk-file-item { cursor: pointer; justify-content: space-between; }
.sk-file-item.on { border-color: var(--teal-2); background: var(--teal-soft); }
.sk-file-item .sf-path { font-family: var(--mono); font-size: calc(12px * var(--fs, 1)); word-break: break-all; }
.sk-file-item .sf-size { color: var(--ink-3); font-size: calc(11px * var(--fs, 1)); flex-shrink: 0; }
.kb-badge.scoped { background: #efe8fb; color: var(--violet); }
.kb-badge.pk { background: #e8f4fb; color: var(--blue); }

/* ============================================================
   折叠区块（z35）：成员管理的人员/房间可能上百个，默认折叠以免淹没页面。
   颜色一律走 CSS 变量，五套主题自适应。
   ============================================================ */
.fold > .fold-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; cursor: pointer; user-select: none;
  padding: 2px 0; border-radius: 8px;
}
.fold > .fold-head:hover { background: var(--hover, rgba(127, 127, 127, .07)); }
.fold > .fold-head h3 { margin: 0; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.fold > .fold-head .fold-n {
  font-size: calc(11.5px * var(--fs, 1)); font-weight: 400; color: var(--ink-3);
}
.fold > .fold-head .fold-arrow {
  flex: none; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3);
  transition: transform .18s ease;
}
.fold.collapsed > .fold-head .fold-arrow { transform: rotate(-90deg); }
.fold > .fold-body { padding-top: 10px; }
.fold > .fold-body.hidden { display: none; }
/* 折叠态下标题与内容之间不需要额外留白 */
.fold.collapsed > .fold-body.hidden { padding-top: 0; }
.sub-panel {
  border: 1px solid var(--line, rgba(127, 127, 127, .18));
  border-radius: 10px; padding: 10px 12px; margin: 8px 0;
  background: var(--panel-soft, transparent);
}
.sub-panel > h4 {
  margin: 0 0 4px; font-size: calc(13.5px * var(--fs, 1));
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
}

/* ============================================================
   z46：工作台徽标 —— 团队卡 / 房间卡上的「未读消息 + 待审批」计数
   ============================================================ */
.cnt-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 17px; padding: 0 5px;
  border-radius: 999px; background: #d64541; color: #fff;
  font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; line-height: 1;
  vertical-align: middle; margin-left: 6px;
  box-shadow: 0 1px 2px rgba(0,0,0,.18);
}
.stat.attention .n { color: #d64541; }

/* z53：内置 Agent 提示词状态徽标（已自定义 / 与内置一致） */
.badge.ok { background: #e8f4ec; color: #2f7d4f; border-color: #b9dfc8; }
.badge.warn { background: #fdf1e3; color: #a4620a; border-color: #f0cf9a; }

/* z66：通用分页条（技能 5/页、模型与用量 10/页、知识库已入库 15/页） */
.pager {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 11px; border-top: 1px dashed var(--line-soft);
}
.pager .pg-info { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin-right: 6px; }
.pager .pg-btn {
  min-width: 26px; height: 26px; padding: 0 7px; border-radius: 7px;
  border: 1px solid var(--line); background: var(--panel); color: var(--ink-2);
  font: inherit; font-size: calc(12px * var(--fs, 1)); cursor: pointer;
}
.pager .pg-btn:hover:not(:disabled) { border-color: var(--teal); color: var(--teal); }
.pager .pg-btn.on { background: var(--teal); border-color: var(--teal); color: #fff; font-weight: 600; }
.pager .pg-btn:disabled { opacity: .4; cursor: default; }
.pager .pg-dots { color: var(--ink-3); font-size: calc(12px * var(--fs, 1)); padding: 0 2px; }

/* z66：成员管理按团队分组折叠 */
.team-fold { margin-top: 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); overflow: hidden; }
.team-fold .tf-head {
  display: flex; align-items: center; gap: 9px; width: 100%; padding: 11px 14px;
  border: 0; background: var(--panel); font: inherit; text-align: left; cursor: pointer; color: var(--ink);
}
.team-fold .tf-head:hover { background: var(--line-soft); }
.team-fold .tf-name { font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; }
.team-fold .tf-n {
  font-size: calc(11px * var(--fs, 1)); padding: 1px 9px; border-radius: 10px;
  background: var(--line-soft); color: var(--ink-2); font-weight: 600;
}
.team-fold .tf-roles { margin-left: auto; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.team-fold .tf-caret { color: var(--ink-3); font-size: calc(12px * var(--fs, 1)); }
.team-fold .tf-body { padding: 2px 14px 12px; }
.team-fold.collapsed .tf-body { display: none; }
.team-fold .member-row { border-bottom: 1px dashed var(--line-soft); }
.team-fold .member-row:last-child { border-bottom: 0; }

/* z68：嵌套折叠（团队经验按团队、房间经验按团队→房间两级） */
.rule-fold.nested { margin: 10px 0; border-radius: 10px; box-shadow: none; background: var(--panel); }
.rule-fold.nested .rf-head { padding: 9px 13px; }
.rule-fold.nested .rf-title { font-size: calc(13px * var(--fs, 1)); }
.rule-fold.nested .rf-body { padding: 2px 13px 13px; }
/* 内层（房间）再收一档，视觉上区分层级 */
.rule-fold.nested .rule-fold.nested { border-left: 2px solid var(--line); background: var(--panel-soft, var(--panel)); }

/* z70：待审批徽标（与「未读消息」红点分开标识） */
.apv-badge {
  display: inline-flex; align-items: center; gap: 2px; margin-left: 4px;
  padding: 1px 7px; border-radius: 9px; background: var(--amber-soft, #fdf3e0);
  color: var(--amber, #b8860b); border: 1px solid var(--amber, #d9a441);
  font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; white-space: nowrap; cursor: help;
  vertical-align: 1px;
}

/* z74：顶栏「知识库 / 经验」待确认徽标 */
.nav-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 5px; margin-left: 5px;
  border-radius: 8px; background: var(--amber); color: #fff;
  font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; vertical-align: 1px; cursor: help;
}
.nav-badge[hidden] { display: none; }

/* z77：经验条目上的「确认」标注（谁放行的） */
.r-ok { color: var(--teal); font-weight: 500; cursor: help; }

/* z79：经验「人工裁定」弹窗 —— 选经验库 + 判重 + 你拍板 */
.rule-review { min-width: 380px; max-width: 560px; }
.rule-review h3 { margin: 0 0 10px; display: flex; align-items: center; gap: 10px; }
.rr-step {
  font-size: calc(11.5px * var(--fs, 1)); font-weight: 500; color: var(--ink-2);
  background: var(--line-soft); padding: 2px 9px; border-radius: 10px;
}
.rr-text {
  width: 100%; border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 12px; font: inherit; font-size: calc(14px * var(--fs, 1)); line-height: 1.7;
  background: var(--panel); color: var(--ink); resize: vertical;
}
.rr-text:focus { outline: none; border-color: var(--teal); box-shadow: 0 0 0 3px rgba(15,92,84,.10); }
.rr-tiphint { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin: 5px 0 0; }
.rr-sec-t { font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; color: var(--ink-2); margin: 14px 0 7px; }
.rr-scopes { display: flex; flex-direction: column; gap: 6px; }
.rr-scope {
  display: flex; align-items: baseline; gap: 8px; padding: 9px 12px;
  border: 1px solid var(--line); border-radius: 9px; cursor: pointer; background: var(--panel);
}
.rr-scope:hover { border-color: var(--teal); }
.rr-scope.on { border-color: var(--teal); background: var(--teal-soft); }
.rr-scope-t { font-size: calc(13px * var(--fs, 1)); font-weight: 600; color: var(--ink); }
.rr-scope-d { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.rr-dup { margin-top: 12px; padding: 11px 13px; border-radius: 10px; border: 1px solid; }
.rr-dup.ok { background: var(--teal-soft); border-color: var(--teal); }
.rr-dup.warn { background: #fdf6e7; border-color: var(--amber); }
.rr-dup b { display: block; font-size: calc(13px * var(--fs, 1)); margin-bottom: 3px; }
.rr-dup.ok b { color: var(--teal); }
.rr-dup.warn b { color: var(--amber); }
.rr-dup span { font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); }
.rr-dup-item { margin-top: 9px; padding: 9px 11px; background: var(--panel); border: 1px solid var(--line); border-radius: 8px; }
.rr-dup-t { font-size: calc(13px * var(--fs, 1)); font-weight: 500; color: var(--ink); }
.rr-dup-m { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); margin-top: 2px; }
.rr-dup-f { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); margin-top: 3px; }
.rr-hint { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin: 12px 0 0; line-height: 1.6; }
@media (max-width: 720px) { .rule-review { min-width: 0; } }

/* z81：相似 / 矛盾 两类判定结果的视觉区分 */
.rr-tag {
  display: inline-block; margin-right: 6px; padding: 0 7px;
  border-radius: 8px; font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; vertical-align: 1px;
}
.rr-tag.conflict { background: #fbe9e7; color: #b4443a; border: 1px solid #e8b3ab; }
.rr-tag.similar  { background: #fdf3e0; color: #a8790c; border: 1px solid #e8cf9a; }
.rr-dup-item.conflict { border-left: 3px solid #b4443a; }
.rr-dup-item.similar  { border-left: 3px solid #d9a441; }
.rr-dup.loading b { color: var(--teal); }
.rr-dup.loading span { color: var(--ink-3); }

/* ============================================================
   z82：PWA「安装到桌面」入口（渲染在使用说明弹窗里）
   ============================================================ */
.pwa-box {
  margin: 10px 0 12px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel-soft);
}
.pwa-box .pwa-tip,
.pwa-box .pwa-ios,
.pwa-box .pwa-done {
  margin: 8px 0 0;
  font-size: calc(13px * var(--fs, 1));
  line-height: 1.7;
  opacity: .82;
}
.pwa-box .pwa-done { color: var(--moss); opacity: 1; font-weight: 600; }
.pwa-box .btn-primary.sm { padding: 7px 14px; font-size: calc(13px * var(--fs, 1)); }

/* z99：使用说明弹窗里的「运行环境自检」块（含最原生文件输入的自测格） */
.env-box {
  margin: 10px 0 12px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel-soft);
}
.env-box .env-line { font-size: calc(12.5px * var(--fs, 1)); line-height: 1.7; color: var(--ink-2); }
.env-box .env-line b { color: var(--ink); }
.env-box .env-ua {
  margin-top: 6px;
  font-size: calc(10.5px * var(--fs, 1));
  line-height: 1.5;
  color: var(--ink-3);
  word-break: break-all;
  user-select: text;
}
.env-box .upload-zone { padding: 10px 8px; font-size: calc(12.5px * var(--fs, 1)); }

/* ============================================================
   z89：独立窗口（桌面版 PWA）的应用内导航条
   ------------------------------------------------------------
   装成 PWA 后是独立窗口，没有浏览器地址栏与工具栏，所以补一排后退/前进/刷新。
   ⚠️ z89 改为**内联进顶栏右侧**（z88 那一版是 fixed 的独立一行）：
       ① 独立一行会压住顶栏文字（用户反馈"把下面的工作台那三个字压住了"）；
       ② 还白白多占 34px 高度。内联后既不遮挡、也不占额外行。
   ============================================================ */
.pwa-nav {
  display: flex; align-items: center; gap: 2px;
  margin-left: auto;            /* 顶栏是 flex，.me 之前自动靠右 */
  flex: none;
}
.pwa-nav button {
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  font-size: calc(17px * var(--fs, 1)); line-height: 1;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 7px;
  cursor: pointer;
  transition: background .12s ease;
}
.pwa-nav button:hover { background: var(--panel-soft); border-color: var(--line); }
.pwa-nav button:active { transform: scale(.92); }
.pwa-nav button:disabled { opacity: .3; cursor: default; }
.pwa-nav button:disabled:hover { background: transparent; border-color: transparent; }
/* 顶栏里 .me 自己也靠右排，nav 插在它前面，所以 .me 要让出位置 */
.topbar .me { margin-left: 0; }

/* ============================================================
   z89：移动端顶栏压缩
   ------------------------------------------------------------
   用户反馈「手机版上面这个固有部分太大了，占据屏幕太多」。
   病根：`@media (max-width:620px)` 里 `.topnav { order:3; width:100% }`
   但 `.topnav` 本身是 flex 且允许换行，7 个项在窄屏被挤成
   「每个字竖着排」的高个子（见截图：工作台/AI团队/知识库…全变竖排）。
   正解：改成**单行横向滚动**，这是移动端导航的标准做法；
        同时缩小字号与内边距，把纵向空间还给内容。
   ============================================================ */
@media (max-width: 620px) {
  /* z100：单行工具条（方案 A）—— 见文件上方 `.topbar-more` 处的完整说明。
     第一行 = 「● 平台名 … z99 说明 ⋮」；「用户名 / 外观 / 退出」进 ⋮ 菜单。 */
  .topbar {
    gap: 6px; padding: 0 10px; min-height: 46px; height: auto;
    padding-bottom: 0; background: var(--panel); backdrop-filter: none;
    flex-wrap: wrap;
  }
  .brand-inline { flex: 1 1 auto; min-width: 0; }
  .brand-user { display: none; }                      /* ★ 收进 ⋮ 菜单 */
  .brand-inline strong { font-size: calc(15px * var(--fs, 1)); }
  .topbar-more {
    display: inline-flex; align-items: center; justify-content: center;
    font-size: calc(16px * var(--fs, 1)); line-height: 1; padding: 3px 8px;
  }
  .topbar .me .topbar-more { font-size: calc(16px * var(--fs, 1)); padding: 3px 8px; }
  .topnav {
    order: 3; width: 100%;
    /* ★★ 必须显式覆盖 flex: 1 —— 它的 flex-basis 是 0%，会把 width:100% 顶掉，
     *    结果这一项既不换行、又被压成零宽（实测手机端整个导航区直接消失）。 */
    flex: 0 0 100%;
    flex-wrap: nowrap !important;      /* ★ 不许换行，否则每项被压窄 */
    overflow-x: auto;                 /* 放不下就横向滚 */
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    gap: 4px;
    padding: 5px 0 6px;
    /* 关键：给容器一个最小宽度，让每个项保持自身内容宽度而不是被压缩 */
    justify-content: flex-start;
  }
  .topnav::-webkit-scrollbar { display: none; }
  .topnav a {
    flex: 0 0 auto;                   /* ★ 不许被压缩（原来没有这条，是病根） */
    white-space: nowrap;              /* ★ 不许竖排 */
    font-size: calc(13px * var(--fs, 1));
    padding: 5px 10px;
  }
  /* 窄屏第一行宽度就 390px，「标题 + 用户区 + 导航键」必然挤爆（实测导航键被推出屏幕）。
     手机端**不显示**导航键：① 用户要的是「桌面版」缺导航，手机本来就有系统返回键与下拉刷新；
     ② 把它去掉才换得来顶栏更矮、内容区更大 —— 这正是用户要的「占屏更小」。
     JS 侧不用改（照样渲染），这里只负责隐藏；改宽度断点即可恢复。 */
  .pwa-nav { display: none; }
  /* ⚠️⚠️ z92：这 6 条原来是裸 `.me`，**全部泄漏到了消息气泡 `.msg.me` 上**，
     且只在 max-width:620px 内生效 —— 这正是「电脑端正常、手机端看不到自己发的消息」
     的直接原因。三条具体的伤害：
       ① `flex: 0 1 auto`（简写）把 flex-shrink 重置为 1 → `.msgs` 这个 column flex
          容器开始压缩消息高度 → 自己的气泡被从中间切断、「N 人未读」回执被裁掉；
       ② `.me > * { flex: none }` 覆盖了 `.msg.me .body { flex: 1 1 0 }`
          → 气泡容器不再撑开，宽度随内容走；
       ③ `.me button` 命中了消息里的「N 人未读」按钮 → 回执按钮被改成 12px 小字。
     正解：**顶栏样式一律加 `.topbar` 前缀**，与本文件既有的 `.topbar .me` 惯例一致。 */
  .topbar .me { gap: 4px; min-width: 0; margin-left: auto; flex: none; overflow: visible; }
  .topbar .me > * { flex: none; }
  /* z98：手机顶栏改「白底 + 扁平」，并把「使用说明 / 版本号」**收回来**。
     之前这两条被 `display:none` 藏掉了（用户反馈「以前一直有，调了顶部样式就没了」）。 */
  .brand-inline { background: transparent; border-color: transparent; padding: 2px 0; gap: 6px; }
  .brand-inline:hover { background: transparent; border-color: transparent; }
  .topbar .me #guideBtn { display: inline-flex; align-items: center; }
  .topbar .me button { font-size: calc(12px * var(--fs, 1)); padding: 4px 8px; }
  /* 版本号在手机上只显示尾部（z97），完整值放 title —— 全串 11 字符会挤爆第一行 */
  .topbar .me .build-tag, #buildTag { display: inline-block; padding: 1px 5px; font-size: calc(9.5px * var(--fs, 1)); }
  .topbar .me > span:not([class]) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
}
/* 极窄屏（如 360px 小屏手机）：连「外观」与用户名也收起，保证标题与导航键不被挤掉 */
@media (max-width: 380px) {
  .topbar .me #themeBtn { display: none; }
  .brand-user .bu-name { display: none; }
}

/* ============================================================
   z88/z89：未读分隔线（微信 / 企业微信那套「以下是未读消息」）
   ------------------------------------------------------------
   只在**确实有漏读**时出现。位置稳定不动，避免每 4 秒刷新就闪。
   （这一段曾在 z89 改导航条时被误删 —— 用「从某个注释截到文件末尾」的方式重写 CSS
     会连带删掉它后面追加的所有规则。以后改样式要**按块替换**，不要截断到 EOF。）
   ============================================================ */
.unread-divider {
  display: flex; align-items: center; gap: 10px;
  margin: 14px 0 10px;
  color: var(--danger, #c0392b);
  font-size: calc(12px * var(--fs, 1));
  user-select: none;
}
.unread-divider::before,
.unread-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: currentColor;
  opacity: .38;
}
.unread-divider span { font-weight: 600; white-space: nowrap; }

/* ============================================================
   z89：移动端输入框加大
   ------------------------------------------------------------
   用户反馈「手机端的输入框太小了」。
   原因：`font-size: calc(13.5px * var(--fs, 1))` 在手机上会触发 iOS Safari 的**自动缩放**
   （字号 < 16px 时聚焦会放大页面），而且行高偏挤，中文输入体验差。
   正解：字号提到 16px（刚好避开自动缩放）+ 加大行高与最小高度 + 底部留出安全区。
   ============================================================ */
@media (max-width: 620px) {
  /* ★ 必须 ≥16px，否则 iOS 聚焦时整页会被放大。
     z218：放大档位照用 --fs，但**下限锁 16px**（缩小档不能把输入框降到 16 以下）。 */
  .composer textarea {
    font-size: max(16px, calc(16px * var(--fs, 1)));
    line-height: 1.55;
    min-height: 52px;           /* z90：再放大一点 */
    padding: 12px 13px;
    border-radius: 11px;
  }
  .composer .btn-primary { padding: 11px 18px; font-size: calc(15px * var(--fs, 1)); }
  .composer-hint { font-size: calc(11.5px * var(--fs, 1)); line-height: 1.6; }
  /* 给 iPhone 的底部手势条留位置，输入框不会被遮住 */
  .composer { padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px)); }

  /* ------------------------------------------------------------
     z90：📎 与「实验数据」移到输入框**上方**，输入框独占一整行
     ------------------------------------------------------------
     用户反馈「手机端输入框本身太小了，把上传按钮、数据上传按钮都放到输入框上面去，
     输入框就可以宽很多」。

     为什么原来窄：四个元素（📎 / 实验数据 / 输入框 / 发送）挤在同一行抢宽度，
     390px 屏上留给输入框只剩一百多像素。

     ⚠️⚠️ 踩了两次才做对，记住这两条：
       1. **flex-wrap + order 这条路走不通**。第一行里 `.data-btn` 用 `flex:1 1 auto`
          会把它撑到接近满宽，于是「输入框 + 发送」这两个 order 靠后的元素
          被挤到**第二行却仍然横向排列**，于是输入框整体溢出屏幕右边界
          （渲染实测：输入框被截掉一半，发送按钮完全看不见）。
       2. **flex item 的 `min-width` 默认是 `auto`**，textarea 按内容宽度撑开，
          即便写了 `flex:1 1 0` 也不肯缩 —— 必须显式 `min-width:0`。

     正解：**改用 grid，两行两列，位置完全确定**，不依赖任何自动计算。
        第 1 行：📎 + 实验数据
        第 2 行：输入框（自适应）+ 发送
     ⚠️ 📎 与「实验数据」现在是 `<label>` + 内嵌**铺满式真实 file input**（z99；
        z98 的 `.vis-input` 写法已废弃 —— 安装版外壳里仍点不动），
        input 是 `position:absolute` 且不是网格项，同样不占位、不参与网格。
     ⚠️ 桌面端完全不受影响（这些规则都在 max-width:620px 之内）。
     ------------------------------------------------------------ */
  .composer-row {
    display: grid;
    /* 两列两行：
         第1行  📎(固定) | 实验数据(自适应)
         第2行  输入框(自适应) | 发送(固定)
       ⚠️ 列模板用 `minmax(0, 1fr)` 而不是 `1fr`：1fr 的最小尺寸是 auto（= 内容宽度），
          输入框一有内容就会把整行撑破、把「发送」挤出屏幕（实测按钮直接消失）。
       ⚠️ 不要图省事写成三列（auto / 1fr / auto）：那样输入框与「发送」不在同一格，
          grid 会把第 3 列排到第 1 行右侧，整个布局错乱。 */
    grid-template-columns: auto minmax(0, 1fr);
    gap: 7px;
    align-items: center;
  }
  .attach-btn {
    grid-column: 1; grid-row: 1;
    width: 40px; height: 40px;
  }
  /* z108：.data-btn 已移除（原占据第 2 格），输入框自然左移。 */
  /* 第 2 行：输入框自适应，发送固定宽。 */
  .composer textarea {
    grid-column: 1; grid-row: 2;
    min-width: 0;
    width: 100%;
    /* ⚠️ grid 行默认 align-items 由父级控制；这里必须显式 stretch，
       否则 textarea 按自身高度居中、看起来被压扁（实测只剩两行高，
       placeholder 第二行被裁掉）。 */
    align-self: stretch;
    height: auto;
  }
  #send {
    grid-column: 2; grid-row: 2;
    min-height: 44px;
    padding: 0 16px;          /* 内边距收窄，给输入框多留一点宽度 */
    white-space: nowrap;
  }

  /* z94：第 2 行（输入框 + 发送）的高度由 #send 的 min-height:44px 决定，
     而 textarea 是 16px 字号 + 12px 上下 padding → 需要 ≈68px 才放得下，
     44px 就会把 placeholder 的第二行裁掉一半（实测「讨论实验，@ Agent
     才会回复」只显示上半截）。
     正解：给第 2 行一个明确高度，别让它被按钮的 min-height 拖着走。 */
  /* z218：行高跟着字号放大（否则「大/特大」档里输入框只有一行高，拼音候选条会把文字挤没）。 */
  .composer-row { grid-template-rows: auto calc(48px * var(--fs, 1)); }
  /* 手机端输入框净宽有限（≈180px），placeholder 要短才不会被折行裁切；
     同时给它 48px 一行高（16px 字号 × 1.55 行高 + 上下 padding ≈ 45px）。 */
  .composer textarea { min-height: calc(48px * var(--fs, 1)); height: calc(48px * var(--fs, 1)); padding: 10px 12px; }
  /* z99：📎 改成 <label class="attach-btn">，**真实 file input 直接嵌在按钮里**
     （.vis-overlay 铺满）→ 它不再是网格项（不是 .composer-row 的直接子元素），
     既不参与网格/order 也不占位。z108 起「实验数据」入口已删除。
     （z98 那条 `.composer-row input[type=file].vis-input{position:absolute}` 已删除：）
     不再有元素使用 .vis-input。 */
}

/* ============================================================
   z89：房间侧栏里的「经验库」卡片改紧凑
   ------------------------------------------------------------
   用户反馈「经验库这个卡片太占地方了」。
   原因：`.rule-row` 原本是按**顶栏「经验」整页**设计的（padding 13/15、gap 12、
   正文 14px/1.75），塞进只有两百多像素宽的侧栏后：一行文字被折成三四行、
   元信息与按钮又各占一行，单条经验能吃掉小半屏。
   正解：给侧栏单独一套**紧凑规格**（不是全局缩小 —— 整页那边可读性是刻意做大的，
   改小会伤可读性）。按钮在窄容器里另起一行右对齐。
   ============================================================ */
.side-body .rule-row {
  padding: 8px 10px;
  margin: 7px 0 0;
  gap: 8px;
  border-radius: 8px;
  flex-wrap: wrap;              /* 窄容器里让按钮换行，而不是把文字挤成竖条 */
}
.side-body .rule-sentence { font-size: calc(13px * var(--fs, 1)); line-height: 1.5; }
.side-body .rmeta { font-size: calc(10.5px * var(--fs, 1)); margin-top: 3px; line-height: 1.5; }
.side-body .rtag { font-size: calc(10px * var(--fs, 1)); padding: 1px 6px; border-radius: 6px; }
.side-body .rmain { min-width: 0; flex: 1 1 140px; }   /* 允许收缩，文字不再撑破容器 */
.side-body .racts { width: 100%; justify-content: flex-end; gap: 4px; }
.side-body .racts .btn-ghost.sm { padding: 3px 9px; font-size: calc(11.5px * var(--fs, 1)); }
.side-body .side-body-sub, .side-body .kb-sub { margin-bottom: 4px; }

/* ============================================================
   z89：已读回执（「我发的消息，别人读了没」）
   ------------------------------------------------------------
   贴在**自己**的气泡下方、右对齐，字号很小 —— 回执是辅助信息，
   做大了一屏都是它，正文反而看不清（与未读分隔线正好相反：
   一个要显眼、一个要安静）。
   ============================================================ */
.rr-rcpt {
  margin-top: 4px;
  text-align: right;
  font-size: calc(11px * var(--fs, 1));
  line-height: 1.5;
  color: var(--ink-4, #9aa8a6);
  user-select: none;
}
.rr-rcpt.none { opacity: .75; }              /* 未读：更淡，别制造压力 */
.rr-rcpt.all  { color: var(--teal); opacity: .9; }   /* 全部已读：唯一的彩色强调 */

/* ============================================================
   z90：「待我处理」待办清单
   ------------------------------------------------------------
   用户反馈「首页有个待处理 1，却找不到在哪处理」。这一格现在可点，
   点开是逐类的清单 + 处理入口。样式上要让人一眼看出「能点」。
   ============================================================ */
.stat-clickable { cursor: pointer; transition: transform .12s ease, box-shadow .12s ease; }
.stat-clickable:hover { transform: translateY(-1px); box-shadow: 0 3px 10px rgba(0, 0, 0, .10); }
.stat-clickable::after {
  content: '▸';
  margin-left: 4px;
  font-size: calc(11px * var(--fs, 1));
  opacity: .5;
}
.pend-list { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 4px; }
.pend-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel-soft);
}
.pend-row.sub { background: transparent; border-style: dashed; }
.pend-row .pr-l { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pend-row .pr-l b { font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; }
.pend-row .pr-l span { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); line-height: 1.5; }
.pend-row .pr-n {
  flex: none; min-width: 26px; text-align: center;
  font-size: calc(15px * var(--fs, 1)); font-weight: 700; color: var(--danger, #c0392b);
}
.pend-row.sub .pr-n { display: none; }
.pend-links { display: flex; flex-wrap: wrap; gap: 5px; }

/* ============================================================
   z90：已读回执改为「N 人未读 + 点击展开名单」
   ------------------------------------------------------------
   用户要求：「显示的『N人未读』，点击可以展开，查看具体是谁没读；
             如果全部已读就显示『全部已读』。」
   默认收起时也要能看出是谁 —— 所以常驻一行「前 3 个名字 + 等 N 人」，
   点一下展开完整名单（不跳转到成员页，省一次操作）。
   ============================================================ */
.rr-rcpt.has { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.rr-rcpt-btn {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 6px 1px 7px;
  font: inherit;
  color: var(--ink-4, #9aa8a6);
  background: var(--panel-soft, rgba(127, 127, 127, .08));
  border: 1px solid transparent;
  border-radius: 9px;
  cursor: pointer;
  transition: color .12s ease, border-color .12s ease;
}
.rr-rcpt-btn:hover { color: var(--ink-2); border-color: var(--line); }
.rr-rcpt-caret { font-size: calc(9px * var(--fs, 1)); transition: transform .15s ease; opacity: .7; }
.rr-rcpt-btn.open .rr-rcpt-caret { transform: rotate(180deg); }
.rr-rcpt-quick {
  font-size: calc(11px * var(--fs, 1)); color: var(--ink-4, #9aa8a6);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rr-rcpt-list {
  flex-basis: 100%;                    /* 展开后另起一行，不挤压上面的内容 */
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: 3px;
  font-size: calc(11px * var(--fs, 1));
}
.rr-rcpt-list.hidden { display: none; }
.rr-rcpt-li {
  padding: 1px 7px;
  border-radius: 8px;
  background: var(--panel-soft, rgba(127, 127, 127, .08));
  border: 1px solid var(--line);
  color: var(--ink-3);
}
/* 全部已读：唯一的彩色强调 */
.rr-rcpt.all { color: var(--teal); opacity: .95; font-weight: 600; }

/* ============================================================
   z91：成员行（成员管理页 / 房间侧栏共用）
   ------------------------------------------------------------
   用户反馈：「手机端这个页面布局太不合理了，每一条都占据太多行了」。
   根因：窄屏下「姓名」那一栏被 flex 压到极窄 → 中文逐字竖排，
   元信息跟着断成三四行，按钮再换行 → 一条成员占满一屏。
   正解（桌面端不受影响，只在 ≤620px 生效）：
     ① 姓名占满宽、允许省略，永不竖排
     ② 元信息压成一行小字并可横向省略
     ③ 按钮另起一行靠右，不再挤在姓名右边
   ============================================================ */
.member-row .mr-main { flex: 1 1 auto; min-width: 0; }
.member-row .mr-name {
  font-size: calc(13px * var(--fs, 1)); color: var(--ink-1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.member-row .mr-meta {
  font-size: calc(11px * var(--fs, 1)); color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.member-row .mr-acts {
  display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; flex: none;
}
@media (max-width: 620px) {
  .member-row { gap: 8px; padding: 8px 0; }
  /* 姓名与元信息各占一整行；按钮另起一行靠右 —— 一条成员只占 3 行 */
  .member-row .mr-main { flex: 1 1 100%; order: 1; }
  .member-row .av { order: 0; }
  .member-row .mr-acts { order: 2; flex: 1 1 100%; justify-content: flex-start; }
  .member-row .mr-acts .btn-ghost.sm,
  .member-row .mr-acts .btn-danger { padding: 5px 10px; font-size: calc(12px * var(--fs, 1)); }
}

/* ============================================================
   z91：手机端工作台统计行压缩
   ------------------------------------------------------------
   用户反馈：「工作台这个区域太大了，信息量很小，搞这么大完全没意义。」
   原 `.stat-row { grid-template-columns: repeat(auto-fit, minmax(150px,1fr)) }`：
   390px 屏放不下两张 150px 卡 + 间距 → 每行只放两张 → 5 个统计占三行，
   纵向吃掉近半屏。而每个统计只有「一个数字 + 三个字」。

   正解（只在 ≤620px 生效）：
     ① 两列网格（minmax(0,1fr)，注意不是 1fr —— 见红线 115）
     ② 数字缩小、内边距收紧
     ③ z233：统计卡本身减到 4 项（去掉「我主持的」）→ 两行读完，**一项都不藏**
        （旧版是靠隐藏第 5 项凑合的，导致手机上永远看不到「未读消息」）
   ============================================================ */
@media (max-width: 620px) {
  /* ⚠️ 三列试过，**放不下**：390px 减去容器边距约剩 366px，
     三列 + 两个 7px 间距 → 每列约 117px，而「10月13日」这种内容撑得下、
     「我主持的」四个字 + 18px 数字就会溢出被裁（实测第三列被切掉一半）。
     两列才是这个宽度的正解：每列约 180px，4 个统计正好两行读完。
     ⚠️ 依然要写 minmax(0,1fr) 而不是 1fr（红线 115）。 */
  .stat-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 7px; margin-bottom: 14px;
  }
  .stat { padding: 8px 10px; border-radius: 9px; min-width: 0; }
  .stat .n { font-size: calc(19px * var(--fs, 1)); line-height: 1.15; }
  .stat .l { font-size: calc(11px * var(--fs, 1)); margin-top: 1px; }
  /* z233：原来这里靠 `nth-child(5) { display:none }` 把第 5 项（未读消息）藏掉，
     等于手机上永远看不到未读 —— 用户明确否掉了这个做法。
     现在统计卡只有 4 项（去掉「我主持的」），2×2 正好两行读完，谁都不用藏。 */
  .stat-clickable::after { display: none; }   /* 手机上箭头符号反而挤位置 */
}

/* ============================================================
   z91：品牌色块里的按钮与「使用说明」的处理
   ------------------------------------------------------------
   ① 品牌块里的「外观 / 退出」字号更小、内边距更紧 —— 它们在色块里是配角，
      不该和平台名抢视觉重量。
   ② 手机上「使用说明」让位：z89 已经隐藏过它，但这次品牌块变宽后又挤回来了。
      手机上顶栏宽度紧张，这个入口价值也低 → 继续隐藏。
   ============================================================ */
.brand-user .brand-act { padding: 3px 9px; font-size: calc(12px * var(--fs, 1)); border-radius: 7px; }
.brand-user .bu-name { font-weight: 600; color: var(--ink-2); }
@media (max-width: 620px) {
  /* z100：这几条原为「把用户名 + 外观 + 退出 硬塞进第一行」，现在整块收进 ⋮，
     第一行只需「● 平台名 + z99 + 说明 + ⋮」，可以从容放 15px 的衬线平台名。
     ⚠️ flex 必须是 1 1 auto —— 原来写 `flex: none`，会让平台名按内容宽度定死，
        窄屏下与右侧 `.me` 一起超宽 → 又把 `.me` 挤到第二行（回到三行的老问题）。 */
  .brand-inline { padding: 2px 0; background: transparent; border-color: transparent; gap: 6px; flex: 1 1 auto; min-width: 0; }
  .brand-inline strong { font-size: calc(15px * var(--fs, 1)); }
  .brand-user { display: none; }                   /* ★ 用户名 / 外观 / 退出 → ⋮ 菜单 */
  .topbar .me #guideBtn { display: inline-flex; align-items: center; }
  .topbar .me { margin-left: auto; flex: none; }         /* z92：限定 .topbar */
}
/* ============================================================
   z94：手机端「完整聊天页」改造（方案 B · 全屏抽屉）
   ------------------------------------------------------------
   用户诉求（配图：微信聊天页 + 聊天信息页）：
     ① 房间页要像一个**完整聊天页**：顶部房间名 → 下面切换分区 → 聊天区 → 输入框
     ② 知识库 / 经验 / 成员 / 房间信息 / AI 模型… 全部收进右上角「☰」
     ③ 抽屉占 **100%** 宽（微信/QQ 的聊天信息页都是全屏）
     ④ 私聊页同样改造
     ⑤ 成员做成**头像墙**

   ⚠️⚠️ 本段**全部**限定在 `@media (max-width: 620px)` 内 —— 桌面端一行都不受影响。
      （红线 130/123：窄屏一律 `minmax(0,1fr)`；z91 的教训是桌面版绝对不能被牵连。）

   结构上采取「**同一份 DOM，两套布局**」：
     · DOM 里房间头 / 分区页签 / 右侧栏**都保留**（桌面端照常用）；
     · 手机端用 CSS 把 `.side` 变成 `position:fixed` 的全屏抽屉，
       默认 `translateX(100%)` 移出屏幕，点「☰」加 `.on` 滑入。
     这样**不需要复制一套 DOM**，桌面端零风险，也不会让两套代码各自漂移。
   ============================================================ */
@media (max-width: 620px) {
  /* ---------- ① 房间头：返回 + 房间名 + ⋯ + ☰ ---------- */
  .room-head {
    display: flex; align-items: center; gap: 8px;
    padding: 9px 11px; margin: 0;
    background: var(--panel); border-bottom: 1px solid var(--line-soft);
    border-radius: 0; position: sticky; top: 0; z-index: 12;
  }
  .room-head > div:first-child { flex: 1; min-width: 0; }
  /* 房间名一行、超长省略（原来 sub 那行信息在手机端藏进抽屉，标题只留名字） */
  .room-head h2 {
    font-size: calc(16.5px * var(--fs, 1)); font-weight: 600; margin: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .room-head .sub { display: none; }          /* 成员数/身份/领域 → 抽屉里的房间信息卡 */
  .room-head .usage-chip { display: none; }   /* 用量 → 抽屉里单独一行 */
  /* 原来那排按钮（房间信息/分组/AI模型/刷新/归档/删除）在手机端全部进抽屉 */
  /* 用语义 class 隐藏这排按钮（不能靠 div:last-child —— z94 会 append .rm-tools 把 last-child 抢走） */
  .room-head .room-acts { display: none; }

  /* 顶栏右侧两个图标按钮：⋯（更多）与 ☰（打开抽屉） */
  .rm-tools { display: flex; align-items: center; gap: 2px; flex: none; }
  .rm-ico {
    width: 34px; height: 34px; border: 0; border-radius: 9px;
    background: transparent; color: var(--ink-2);
    font-size: calc(19px * var(--fs, 1)); line-height: 1; display: flex; align-items: center; justify-content: center;
    cursor: pointer;
  }
  .rm-ico:active { background: var(--line-soft); }

  /* z103：手机房间页最左侧的「‹ 返回上级」。
     `body.in-chat .topbar{display:none}` 把全局顶栏藏了，这是房间里**唯一**
     不依赖浏览器历史的出口（左滑一旦没有上一条就是退出网页）—— 所以必须常显。
     ⚠️ 用 order:-1 改**视觉顺序**，DOM 上它仍是 .room-head 的最后一个孩子，
        这样 `.room-head > div:first-child`（房间名的 flex:1 / min-width:0）不会被顶掉。 */
  .room-head .rm-back { display: flex; order: -1; margin-left: -6px; }

  /* ---------- ② 分区页签：房间名正下方，微信式下划线 ---------- */
  .scope-tabs {
    position: static; top: auto; z-index: 11;
    /* ⚠️ flex: none —— 见文件上方 .scope-tabs 基础规则处的完整推导。
       少了它，消息一多页签就被压成几 px，看起来像「三个聊天区没出现」。 */
    flex: none;
    background: var(--panel); border-bottom: 1px solid var(--line-soft);
    /* 只抵消左右 padding 让页签通栏；上边不缩（贴住房间头），下边留 8px 呼吸 */
    margin: 0 -13px 8px; padding: 0 4px;
    display: flex; overflow-x: auto; scrollbar-width: none;
  }
  .scope-tabs::-webkit-scrollbar { display: none; }
  .scope-tabs button {
    flex: 1 1 0; min-width: 0;
    position: relative; border: 0; background: none;
    padding: 10px 2px; font-size: calc(13px * var(--fs, 1)); color: var(--ink-3);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .scope-tabs button.on { color: var(--teal); font-weight: 600; }
  .scope-tabs button.on::after {
    content: ''; position: absolute; left: 26%; right: 26%; bottom: 0;
    height: 2px; border-radius: 2px; background: var(--teal);
  }

  /* ---------- ③ 聊天区占满，输入框固定底部 ---------- */
  /* 聊天页占满**且不超过**视口高度。
     ⚠️ 上一版写的是 min-height:100dvh —— **min-height 挡不住内容更高**：
     消息一多，#view 就涨到 769px（视口只有 568px），
     底部的输入框自然被顶到屏幕外（实测 composerTop=587 > 568）。
     正解：用 height:100dvh + overflow:hidden 把它**钉死**在视口高度，
     多出来的内容交给 .msgs 自己滚（flex 链里 .msgs 是 flex:1 + overflow-y:auto）。
     dvh 在移动端浏览器地址栏收起/展开时会跟着变，比 vh 准。 */
  /* ══ z217（用户报障 1）：手机端发完消息被键盘挡住 ══
     高度改用 app.js 写入的 --wb-vvh（= visualViewport.height）。键盘弹起时它**会变矮**，
     于是整条 flex 链（#view → .discuss → .msgs）跟着变矮，输入框永远停在键盘正上方，
     刚发出的那条消息也一定在可见区内。
     ⚠️ 同一个属性写两行是有意的：不支持 var 的老浏览器认第二行会失效，自动退回 100dvh。
        （100dvh 在 Android 上默认不跟键盘变，只在 iOS/地址栏收起时准。） */
  body.in-chat { height: 100dvh; height: var(--wb-vvh, 100dvh); overflow: hidden; }
  body.in-chat #view { height: 100dvh; height: var(--wb-vvh, 100dvh); overflow: hidden; display: flex; flex-direction: column; }
  body.in-chat .room-layout { flex: 1 1 auto; min-height: 0; }
  .room-layout { grid-template-columns: 1fr; gap: 10px; }
  .side { display: block; }                   /* 手机端抽屉化，见下 */
  .discuss { border-radius: var(--radius); overflow: hidden; }
  /* ⚠️⚠️ 手机端让消息区「恰好占满剩余高度」：之前写 max-height:none 是错的 ——
     那样消息区会按**内容高度**撑开，内容一多就把底部的输入框顶出屏幕
     （实测 320x568 时输入框顶部落在 587px > 视口 568px，完全看不见）。
     正解：用 flex 链把高度约束传递下去 ——
       .discuss（column flex，flex:1 + min-height:0）
         └ .msgs（flex:1 + min-height:0 + overflow-y:auto）  ← 自己在内部滚
             └ .composer（flex:none，永远贴底可见）
     min-height:0 两个都要写：不写的话 flex 子项按 min-content 撑开，约束断链。 */
  /* ⚠️ .discuss 是 .room-layout（grid）的子项，grid item 的 min-height 默认 auto
     → 内容多高它就多高，**不受 .room-layout 的行高约束**（实测行高 515px、
     .discuss 却涨到 716px，把输入框顶出屏幕）。必须显式 min-height:0 断掉这条链。 */
  .discuss { flex: 1 1 auto; min-height: 0; }
  body.in-chat .room-layout > * { min-height: 0; }
  /* ★ 真正的病根：.room-layout 有 align-items:start（桌面端为了侧栏顶部对齐），
     grid item 在这个对齐下按**内容高度**排布，**不会**被行高拉伸 ——
     所以行高 515px 而 .discuss 实测 716px，输入框被顶到 587px（视口 568px）。
     手机端聊天页要「钉死一行铺满」，必须改成 stretch。 */
  body.in-chat .room-layout { align-items: stretch; grid-template-rows: minmax(0, 1fr); }
  .msgs { flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto; padding: 12px 12px 4px; }
  .composer { flex: none; }
  .priv-chat { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
  .priv-chat .msgs { flex: 1 1 auto; min-height: 0; max-height: none; }
  .priv-chat .composer { flex: none; }

  /* ---------- ④ 全屏抽屉（方案 B，100% 宽） ---------- */
  .side {
    position: fixed; inset: 0; z-index: 60;
    width: 100%; max-width: none; height: 100dvh;
    background: var(--bg); border: 0; border-radius: 0;
    transform: translateX(100%);
    transition: transform .24s ease;
    overflow: hidden;
  }
  .side.on { transform: translateX(0); }
  /* 抽屉内部竖向滚动（微信聊天信息页就是这样整页滚） */
  .side-tabs {
    position: sticky; top: 0; z-index: 2;
    display: flex; background: var(--panel);
    padding: 8px 10px calc(8px + env(safe-area-inset-bottom, 0px));
    gap: 6px; overflow-x: auto; scrollbar-width: none;
  }
  .side-tabs::-webkit-scrollbar { display: none; }
  .side-tabs button {
    flex: 0 0 auto; border: 1px solid var(--line); border-radius: 16px;
    background: var(--panel); color: var(--ink-2);
    padding: 6px 13px; font-size: calc(13px * var(--fs, 1)); white-space: nowrap;
  }
  .side-tabs button.on { background: var(--teal); color: #fff; border-color: var(--teal); font-weight: 600; }
  /* z98：手机端抽屉正文必须**撑满可用高度**。
     原来只写了 `height: calc(100% - 52px)`，但基础规则里的 `max-height: 62vh`
     仍然生效 → 实际高度被卡在 62vh（844 屏 ≈ 523px），下面空出 260 多像素，
     用户看到「一大片浪费」、一屏也显示不了几条。
     正解：走 flex 撑满（`flex:1 1 auto + min-height:0`）并**显式取消 62vh 上限**。 */
  .side-body {
    overflow-y: auto; -webkit-overflow-scrolling: touch; padding-bottom: 24px;
    flex: 1 1 auto; min-height: 0; height: auto; max-height: none;
  }

  /* 抽屉顶部条：返回箭头 + 「聊天信息」标题（关闭抽屉） */
  .side-back {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 12px; background: var(--panel);
    border-bottom: 1px solid var(--line-soft);
    font-size: calc(15.5px * var(--fs, 1)); font-weight: 600;
  }
  .side-back .bk { font-size: calc(20px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; padding: 0 4px; }

  /* 抽屉里的「房间信息卡」+ 四宫格 + 设置行 */
  .dw-card {
    display: flex; align-items: center; gap: 11px;
    background: var(--panel); margin: 10px 12px; padding: 13px;
    border-radius: 12px; border: 1px solid var(--line-soft);
  }
  .dw-card .ava {
    width: 46px; height: 46px; border-radius: 12px; flex: none;
    background: var(--teal); color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: calc(17px * var(--fs, 1)); font-weight: 600;
  }
  .dw-card .dwm { flex: 1; min-width: 0; }
  .dw-card .dwm b { display: block; font-size: calc(15px * var(--fs, 1)); }
  .dw-card .dwm span { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }

  /* 四宫格入口（课题文件 / 知识库 / 经验 / 成员） */
  .dw-grid {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    background: var(--panel); margin: 0 12px 10px; padding: 13px 4px;
    border-radius: 12px; border: 1px solid var(--line-soft);
  }
  .dw-grid > div { text-align: center; font-size: calc(11px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; }
  .dw-grid > div:active { opacity: .6; }
  .dw-grid i {
    display: flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; margin: 0 auto 5px; border-radius: 11px;
    background: var(--teal-soft); color: var(--teal);
    font-size: calc(18px * var(--fs, 1)); font-style: normal;
  }

  /* 设置行列表 */
  .dw-list { background: var(--panel); margin: 0 12px 10px; border-radius: 12px; border: 1px solid var(--line-soft); overflow: hidden; }
  .dw-list .rw {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 13px 14px; border-bottom: 1px solid var(--line-soft);
    font-size: calc(14px * var(--fs, 1)); cursor: pointer;
  }
  .dw-list .rw:last-child { border-bottom: 0; }
  .dw-list .rw:active { background: var(--line-soft); }
  .dw-list .rw .rt { color: var(--ink-3); font-size: calc(12.5px * var(--fs, 1)); display: flex; align-items: center; gap: 4px; }
  .dw-list .rw.danger { color: var(--danger, #b4443a); }

  /* ---------- ⑤ 成员头像墙 ---------- */
  .mz-title { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); margin: 14px 16px 8px; }
  .mz-grid {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 14px 6px; padding: 4px 12px 8px;
  }
  .mz-cell { text-align: center; cursor: pointer; min-width: 0; }
  .mz-cell:active { opacity: .6; }
  .mz-cell .av {
    width: 46px; height: 46px; margin: 0 auto 5px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    color: #fff; font-size: calc(16px * var(--fs, 1)); font-weight: 600;
    border: 2px solid var(--panel); box-shadow: 0 0 0 1px var(--line-soft);
  }
  .mz-cell .nm {
    font-size: calc(11px * var(--fs, 1)); color: var(--ink-2);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .mz-cell .rl { font-size: calc(10px * var(--fs, 1)); color: var(--ink-4); }
  /* 头像右上角的身份小标（房主 / 我）。用绝对定位挂在头像右上角外沿，
     挂在头像**里面**会盖住首字（实测「房主」压到了名字上）。 */
  .mz-cell .bdg {
    position: absolute; top: -3px; left: 30px;
    font-size: calc(9px * var(--fs, 1)); line-height: 1.5; padding: 0 4px; border-radius: 6px;
    background: var(--panel); border: 1px solid var(--line-soft); color: var(--ink-3);
  }
  .mz-cell { position: relative; }
  .mz-cell .avwrap { position: relative; display: inline-block; }
  /* ★★ z202：头像右上角的「×」= 移出成员（只有管理员/导师/房间创建者看得到）。
     · 必须比头像大一圈的**可点面积**（≥28px），否则手机上点不中；
     · 用绝对定位贴在圆形头像的右上角外沿，不遮挡首字（与 .bdg 同一思路）。 */
  .mz-cell .mz-rm {
    position: absolute; top: -6px; right: -8px;
    width: 20px; height: 20px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: calc(13px * var(--fs, 1)); line-height: 1; font-weight: 700;
    background: var(--danger); color: #fff;
    border: 1.5px solid var(--panel);
    box-shadow: 0 1px 3px rgba(0,0,0,.18);
    cursor: pointer;
  }
  .mz-cell .mz-rm::before {
    /* 扩大可点区域到 32×32（视觉仍是 20pt 的圆点） */
    content: ''; position: absolute; inset: -7px;
  }
  .mz-cell .mz-rm:active { transform: scale(.92); }

  /* ---------- ⑥ 输入框：微信式一行 + 圆角输入框 ---------- */
  .composer { padding: 7px 9px calc(7px + env(safe-area-inset-bottom, 0px)); }
  /* z90 已把 📎/实验数据 移到输入框上方一行（grid 两行两列），这里只调尺寸与观感 */
  /* 圆角输入框；z90 给的 min-height:52px 在手机上让 placeholder 第二行被裁，
     这里压回单行高度（内容变多时 JS 会按 scrollHeight 自适应增高） */
  .composer textarea { border-radius: 17px; min-height: 40px; }

  /* z94：手机端隐藏输入区下面那两行长提示（「@ 指定 Agent 才会回复 · 📎 可发…」/
     「右键（长按）消息…」）。理由有二：
     ① 它们是给「第一次用的人」看的操作指引，聊天页已经很挤；
     ② 实测它占了 3 行高度，把消息区压得只剩一半。
     操作指引并没有丢：要点「☰ → 资料」或长按/右键消息时仍然可用。 */
  .composer-hint { display: none; }

  /* z94：分区页签 sticky 时必须**盖住**下方的消息，不能被消息穿透看到
     （实测页签文字与消息重叠在一起，两层都半透明，看着像渲染错乱）。
     给页签一个不透明背景 + 提高z-index，并让 sticky 顶到房间头正下方。 */
  .scope-tabs { background: var(--panel) !important; z-index: 13; box-shadow: 0 1px 0 var(--line-soft); }
  body.in-chat .scope-tabs { top: 53px; }
  /* 房间头 sticky 要比页签更高（它在上层） */
  body.in-chat .room-head { z-index: 14; }
  .composer .btn-primary { border-radius: 8px; }
  /* 手机端不显示「@ 指定 Agent 才会回复…」这类长提示（省一屏高度） */
  /* z94：私聊页顶部说明文字在手机端隐藏（内容已进「会话信息」抽屉） */
  .page-head .pc-sub { display: none; }
  /* z94：聊天页（房间/私聊）在手机端**隐藏全局顶栏**，把整屏让给聊天。
     顶栏那排「工作台/AI团队/知识库/…」在小屏要占两行 ≈100px，
     不让开的话输入框会被顶到屏幕外（实测 320x568 输入框顶在 687px）。
     房间头自带「返回 / 房间名 / ☰」，顶栏的功能在抽屉里都能到，
     所以这里不丢功能，只是不占常驻空间。 */
  body.in-chat .topbar { display: none; }
  body.in-chat main { padding: 0 0 0; }
  /* 房间头/页签的 sticky 顶部也随之归零 */
  body.in-chat .room-head { top: 0; }
  body.in-chat .scope-tabs { top: 52px; }
  /* 页面头部（私聊页用）同样归零边距，紧贴顶部 */
  body.in-chat .page-head { margin: 0; padding: 9px 11px; }
  /* 私聊抽屉复用 .side 的全屏定位（这里只补 display，定位/宽度全走 .side 那套） */
  .side.pc-drawer { display: block; }
}

/* ============================================================
   z95：聊天页三项修正
   ------------------------------------------------------------
   用户反馈（配两张手机截图）：
     ① 房间里「上面三个聊天区」（分区页签）**没出现、点不到**；
     ② 手机端没有微信那种「+」号，点开应弹出底部面板（上传文件 / 图片 / 数据）；
     ③ 点 ☰ 进抽屉后，手机侧滑返回会**直接退出房间**，应改为「收起抽屉」。
   ⚠️ 所有**布局**改动都严格限定在 `@media (max-width: 620px)` 内，桌面端一行不动。
   ============================================================ */

/* ---------- ⑤ 补齐 z94 漏写的「桌面端隐藏」----------
   z94 的注释里写明「桌面端这些元素虽然也在 DOM 里，但被 CSS 隐藏/移出屏幕」，
   可实际只写了手机端的样式，**没写桌面端的隐藏规则** → 桌面端多出三样东西：
     · 房间头右侧多出两个裸按钮「⋯」「☰」；
     · 侧栏顶部多出一行「‹ 聊天信息」；
     · 私聊页右侧多出一个空侧栏（.side.pc-drawer）。
   z95 补齐（下面的媒体查询里再恢复成手机端该有的样子）。 */
.rm-tools { display: none; }
.side-back { display: none; }
.side.pc-drawer { display: none; }

/* ---------- ④ 微信式底部面板（「+」弹出）----------
   面板本身用 position:fixed，只在手机端由 JS 创建；样式写在外面无所谓。
   「+」按钮默认隐藏，只在 ≤620px 显示（见下面的媒体查询）。 */
.plus-btn { display: none; }
.sheet-mask {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(20, 18, 14, .38);
  opacity: 0; transition: opacity .2s ease;
}
.sheet-mask.on { opacity: 1; }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 91;
  background: var(--panel);
  border-radius: 16px 16px 0 0;
  padding: 16px 12px calc(16px + env(safe-area-inset-bottom, 0px));
  transform: translateY(110%);
  transition: transform .26s cubic-bezier(.22,.61,.36,1);
  box-shadow: 0 -6px 24px rgba(0, 0, 0, .14);
}
.sheet.on { transform: translateY(0); }
.sheet-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px 6px; }
.sheet-item { text-align: center; cursor: pointer; position: relative; overflow: hidden; }
/* z218：截图那一格是 <button>（图片/文件是 <label>+真实 file input）——
   把按钮的默认边框/底色/字体抹掉，三格看起来必须一样。 */
button.sheet-item {
  border: 0; background: none; padding: 0; margin: 0; width: 100%;
  font-family: inherit; color: inherit;
}
.sheet-item:active { opacity: .6; }
.sheet-item i {
  display: flex; align-items: center; justify-content: center;
  width: 54px; height: 54px; margin: 0 auto 7px; border-radius: 15px;
  background: var(--panel-soft); border: 1px solid var(--line-soft);
  font-size: calc(24px * var(--fs, 1)); font-style: normal;
}
.sheet-item span { font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); }
.sheet-cancel {
  display: block; width: 100%; margin-top: 14px; padding: 13px;
  border: 0; border-radius: 12px; background: var(--panel-soft);
  color: var(--ink-2); font-size: calc(15px * var(--fs, 1)); cursor: pointer;
}
.sheet-cancel:active { background: var(--line-soft); }

@media (max-width: 620px) {
  /* ---------- ① 分区页签：去掉 sticky ----------
     ⚠️ 病根（实测定位，不是猜的）：`.scope-tabs` 原本写 `position: sticky; top: 52px`，
        而它的父级 `.discuss` 是 `overflow: hidden`（z94 为了圆角裁切）——
        **overflow:hidden 会让 .discuss 成为「滚动容器」**，sticky 的偏移量于是
        相对 .discuss 计算：页签常规位置本是 53px（紧贴房间头），却被强行往下顶
        52px → 实测渲染在 top=106（压在第一屏消息上），而它原本占的 53~99px
        位置**留下一条空白**（= 用户看到的空白带），页签本体被消息盖住几乎看不见
        → 用户看到的就是「三个聊天区没出现、点不到」。
        正解：手机端**不要 sticky**。只有 .msgs 内部滚动，页签本来就常驻，
        sticky 纯属多余；去掉后它老实待在房间头正下方。
        （.discuss 的 overflow:hidden 保留 —— 圆角裁切还需要它。） */
  .scope-tabs {
    position: static;
    top: auto; z-index: auto;
    box-shadow: none;
    margin: 0 -13px 0; padding: 0 4px;
    /* ⚠️ z96：这里**必须** flex: none。
       z95 去掉 sticky 是对的，但只解决了一半 —— 页签仍旧是 flex-shrink:1，
       消息一多就被按比例压缩（实测长历史下压到 ≈5px），
       于是「有历史消息的房间三个分区出不来」。锁死不压缩即可。 */
    flex: none;
  }
  .scope-tabs button { font-size: calc(12.5px * var(--fs, 1)); padding: 11px 2px; }
  body.in-chat .scope-tabs { top: auto; }

  /* ---------- ② 输入区：微信式「+ 输入框 发送」一行 ----------
     原先 z90 把 📎/实验数据 挪到输入框上面一行（两行两列 grid）。
     用户要微信那种：底部只有「+ / 输入框 / 发送」一行，上传入口收进「+」面板。 */
  .composer-row {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  /* 📎 在手机端不再常驻（改由「+」面板唤起）。
     DOM 保留 → 桌面端与既有逻辑（#attachInput）完全不受影响 */
  .composer-row .attach-btn { display: none !important; }
  .plus-btn {
    display: flex;
    flex: none; width: 42px; height: 44px;
    border: 1px solid var(--line); border-radius: 12px;
    background: var(--panel); color: var(--ink-2);
    align-items: center; justify-content: center;
    cursor: pointer; padding: 0;
  }
  .plus-btn:active { background: var(--line-soft); }
  .plus-btn svg { width: 22px; height: 22px; }
  .composer textarea {
    flex: 1 1 auto; min-width: 0; width: auto;
    height: 44px; min-height: 44px; max-height: 110px;
    /* z96：上下内边距 10→8px。
       手机端字号 16px × 行高 1.55 = 24.8px 的行盒，
       而 height:44px − 2px 边框 − 20px 内边距 = 22px 内容盒 < 24.8px
       → scrollHeight 45 > clientHeight 42，右侧会冒出一条细滚动条。
       改 8px 后内容盒 26px ≥ 24.8px，正好放得下（顺带把单行垂直居中得更准）。 */
    padding: 8px 13px; border-radius: 17px;
  }
  .composer-row #send,
  .composer-row .btn-primary { flex: none; min-height: 44px; padding: 0 16px; border-radius: 12px; }

  /* ---------- ③ 抽屉：去掉与四宫格重复的页签条 ----------
     抽屉「聊天信息」页顶部那条 [资料|课题文件|知识库|经验|成员]
     与下面四宫格（课题文件/知识库/经验/成员）功能完全重复 → 去掉。
     导航改为：四宫格进子页 → 顶部返回箭头回到「聊天信息」。
     返回箭头的行为由 JS 动态设置（home 时关抽屉，子页时回 home）。 */
  .side { display: flex; flex-direction: column; }
  .side-tabs { display: none; }
  .side-back { flex: none; }
  /* z95：恢复「手机专用 chrome」的显示（桌面端由上面的全局规则隐藏） */
  .rm-tools { display: flex; }
  .side-back { display: flex; }
  .side.pc-drawer { display: block; }
  .side-body { height: auto; flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .side-back .bk { font-size: calc(22px * var(--fs, 1)); padding: 0 6px; }
}

/* ============================================================
   z96：说明气泡（ⓘ）—— 把「解释性文案」收起来
   ------------------------------------------------------------
   用户要求：页面上一大段一大段的「怎么用 / 什么规则」说明太占地方，
   收进标题旁边一个圆圈 i 里；**电脑端鼠标移上去、手机端点一下**才展开
   （就是很多 App 的做法），页面立刻简洁。
   实现要点：
     ① 气泡是**全局单例**、position:fixed 挂在 <body> 上 ——
        说明文案常常出现在 overflow:hidden 的卡片 / 弹窗里，
        挂到元素内部会被裁掉（这是这类组件最常见的坑）。
     ② 位置由 JS 现算：贴着 ⓘ 下方居中，左右不越界、下方放不下就翻到上方。
     ③ 只在真正有指针悬停能力时（hover:hover + pointer:fine）才自动展开，
        触屏一律靠点击，避免「点一下又弹出又立刻消失」。
   ⚠️ 只用 :root 里真实定义过的变量（--ink/--panel/--line/--teal*）；
     --panel-soft 与 --ink-4 在 :root 里是自引用（无效），默认主题下取不到值。
   ============================================================ */
.info-dot {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 16px; height: 16px; margin-left: 6px; padding: 0;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--panel); color: var(--ink-3);
  font-family: var(--serif); font-size: calc(11px * var(--fs, 1)); font-weight: 700; font-style: italic;
  line-height: 1; vertical-align: 1px;
  cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.info-dot:hover, .info-dot:focus-visible, .info-dot.on {
  background: var(--teal-soft); color: var(--teal); border-color: var(--teal-2);
  outline: none;
}
.info-dot:active { transform: scale(.94); }

.infotip {
  position: fixed; left: 0; top: 0; z-index: 220;
  max-width: min(330px, calc(100vw - 28px));
  padding: 10px 12px; border-radius: 12px;
  background: var(--ink); color: var(--panel);
  font-size: calc(12.5px * var(--fs, 1)); line-height: 1.7; text-align: left;
  white-space: pre-wrap; word-break: break-word;
  box-shadow: 0 10px 28px rgba(20, 18, 14, .26);
  opacity: 0; pointer-events: none; transform: translateY(-4px);
  transition: opacity .15s ease, transform .15s ease;
}
.infotip.on { opacity: 1; transform: translateY(0); }
.infotip.on::after {                     /* 小尖角：跟着放置方向翻转 */
  content: ''; position: absolute; left: 50%; margin-left: -5px;
  width: 0; height: 0; border: 5px solid transparent;
}
.infotip.on.above::after { bottom: -9px; border-top-color: var(--ink); }
.infotip.on.below::after { top: -9px; border-bottom-color: var(--ink); }

/* 手机端：气泡稍大一点，好点也好读 */
@media (max-width: 620px) {
  .info-dot { width: 17px; height: 17px; margin-left: 5px; }
  .infotip { font-size: calc(13px * var(--fs, 1)); padding: 11px 13px; line-height: 1.72; }
}

/* ============================================================
   z108 改动5：文件区「按实验 / 按分类」双视图
   ------------------------------------------------------------
   配色沿用线上 catColor()（方案琥珀 / 数据蓝 / 结论苔绿），不引入新色系。
   ============================================================ */
.cat-filter button.mode { background: var(--violet-soft); color: var(--violet); border-color: #ddd0f5; font-weight: 650; }
.cat-filter button.mode.on { background: var(--violet); color: #fff; border-color: var(--violet); }

.run-card {
  border: 1px solid var(--line); border-radius: 11px; margin-bottom: 9px;
  overflow: hidden; background: var(--panel);
}
.run-head {
  display: flex; align-items: center; gap: 8px; padding: 9px 11px; cursor: pointer;
  background: linear-gradient(90deg, #f3f8f6, #fff);
}
.run-head:hover { background: var(--teal-soft); }
.run-id {
  font-size: calc(12px * var(--fs, 1)); font-weight: 700; color: var(--teal);
  background: var(--teal-soft); padding: 1px 8px; border-radius: 20px; flex-shrink: 0;
}
.run-date { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.run-badge { margin-left: auto; font-size: calc(10.5px * var(--fs, 1)); padding: 1px 8px; border-radius: 20px; flex-shrink: 0; }
.rb-done { background: #ecf4ea; color: #4a7c48; }
.rb-wip { background: #fdf3e2; color: #b8791a; }
.run-caret { color: var(--ink-4); font-size: calc(11px * var(--fs, 1)); flex-shrink: 0; }
.run-chips { display: flex; gap: 5px; flex-wrap: wrap; padding: 0 11px 8px; }
.chip {
  font-size: calc(10.5px * var(--fs, 1)); padding: 2px 8px; border-radius: 20px;
  border: 1px solid; display: inline-flex; align-items: center;
}
.c-plan { background: #fdf3e2; color: #b8791a; border-color: #f0dcb8; }
.c-data { background: #e8f1fa; color: #2b6cb0; border-color: #cfe0f4; }
.c-conc { background: #ecf4ea; color: #4a7c48; border-color: #c8dfc4; }
.run-body { padding: 0 11px 9px; }
.sec-h {
  font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); font-weight: 700;
  letter-spacing: .06em; margin: 12px 0 7px;
}

/* ============================================================
   z109 改动9/11：实验卡区块 + 未解决问题清单
   ============================================================ */
.run-card-box {
  margin: 8px 11px 0; padding: 9px 11px; border-radius: 9px;
  background: #fbfdfc; border: 1px solid var(--line-soft);
}
.rcb-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 5px; }
.rcb-head b { font-size: calc(12px * var(--fs, 1)); color: var(--teal); }
.rcb-ops { margin-left: auto; display: flex; gap: 4px; flex-wrap: wrap; }
.rcb-tag { font-size: calc(10px * var(--fs, 1)); padding: 1px 7px; border-radius: 20px; }
.rcb-tag.edited { background: var(--blue-soft); color: var(--blue); }
.rcb-tag.stale { background: var(--amber-soft); color: var(--amber); }
.rcb-line { font-size: calc(12px * var(--fs, 1)); line-height: 1.65; color: var(--ink-2); margin: 2px 0; }
.rcb-line .k {
  display: inline-block; min-width: 52px; color: var(--teal);
  font-weight: 650; margin-right: 4px;
}
.rcb-toggle { font-size: calc(11px * var(--fs, 1)); color: var(--ink-4); cursor: pointer; margin-top: 4px; }
.rcb-toggle:hover { color: var(--teal); }

.run-questions {
  border: 1px solid #f0dcb8; background: var(--amber-soft);
  border-radius: 10px; padding: 9px 11px; margin-bottom: 10px;
}
.run-questions .rq-head {
  font-size: calc(11.5px * var(--fs, 1)); font-weight: 700; color: var(--amber);
  letter-spacing: .04em; margin-bottom: 5px;
}
.run-questions ul { margin: 0; padding-left: 17px; font-size: calc(12px * var(--fs, 1)); color: #7a5410; line-height: 1.7; }
.run-questions .rq-tag {
  font-size: calc(10px * var(--fs, 1)); background: #fff; color: var(--amber);
  border-radius: 20px; padding: 0 6px; margin-right: 5px;
}

/* ============================================================
   z114 问题3：实验卡折叠式（收起态只占一行）+ 「⋯」菜单 + 文件快捷打开
   ============================================================ */
/* 收起态：一行摘要（目标 + 结论），点它展开 */
.run-card-box.rcb-brief { margin: 6px 11px 0; padding: 0; border-radius: 8px; }
.run-card-box.rcb-flag { border-color: #f0dcb8; background: #fffdf8; }
.rcb-sum {
  display: flex; align-items: center; gap: 6px; padding: 7px 9px;
  cursor: pointer; border-radius: 8px; min-height: 32px;
}
.rcb-sum:hover { background: var(--teal-soft); }
.rcb-caret { color: var(--ink-4); font-size: calc(10px * var(--fs, 1)); flex-shrink: 0; }
.rcb-brief {
  flex: 1; min-width: 0; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rcb-brief-goal { font-weight: 600; color: var(--ink); }
.rcb-brief-conc { color: var(--ink-2); }
.rcb-none { color: var(--ink-4); font-style: italic; }
.rcb-ops { display: flex; gap: 3px; flex-shrink: 0; align-items: center; }
.rcb-ops .btn-ghost.sm { padding: 2px 7px; min-height: 24px; }
/* 展开态：字段明细 + 文件快捷入口 */
.rcb-detail { padding: 2px 9px 8px; border-top: 1px dashed var(--line-soft); margin-top: 2px; }
.rcb-files-line { margin-top: 6px; display: flex; gap: 5px; flex-wrap: wrap; align-items: baseline; }
.rcb-files-line .k { display: inline-block; min-width: 40px; color: var(--teal); font-weight: 650; font-size: calc(12px * var(--fs, 1)); }
.rcb-file-cat {
  font-size: calc(10px * var(--fs, 1)); padding: 1px 6px; border-radius: 20px;
  border: 1px solid; flex-shrink: 0;
}
.rcb-file-cat.c-plan { background: #fdf3e2; color: #b8791a; border-color: #f0dcb8; }
.rcb-file-cat.c-data { background: #e8f1fa; color: #2b6cb0; border-color: #cfe0f4; }
.rcb-file-cat.c-conclusion { background: #ecf4ea; color: #4a7c48; border-color: #c8dfc4; }
.rcb-file {
  font-size: calc(11.5px * var(--fs, 1)); color: var(--teal); background: var(--teal-soft);
  border: 1px solid var(--teal-2); border-radius: 6px; padding: 1px 7px;
  cursor: pointer; max-width: 220px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; text-align: left;
}
.rcb-file:hover { background: var(--teal); color: #fff; }
.rcb-file.no-open { color: var(--ink-3); background: var(--panel); border-color: var(--line); cursor: default; }
/* 「⋯」菜单 */
.rcb-menu { display: flex; flex-direction: column; gap: 7px; }
.rcb-menu .btn-ghost { justify-content: flex-start; width: 100%; }

/* ============================================================
   z116 问题2/3：生成中转圈 + 长内容完整显示
   ============================================================ */
/* 问题3：重新生成后内容变长，`.rcb-detail` 必须能自然撑开并**完整换行**。
   原来的 `.rcb-line` 没有 `white-space` 规则，长中文串在窄屏会被压成一行溢出。
   这里显式允许换行 + 给 `.k` 标签做窄屏顶格，避免"标签占一行、内容被挤到屏幕外"。 */
.rcb-detail {
  height: auto !important; max-height: none; overflow: visible;
}
.rcb-line {
  white-space: normal; overflow-wrap: anywhere; word-break: break-word;
  display: block;
}
.rcb-line .k { vertical-align: top; }
/* 问题2：生成中的转圈标识 —— 与内容**并排**（不清空原内容） */
.rcb-busy {
  display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
  font-size: calc(11px * var(--fs, 1)); color: var(--teal);
}
.rcb-busy[hidden] { display: none; }
.rcb-spin {
  width: 11px; height: 11px; border-radius: 50%; flex-shrink: 0;
  border: 2px solid var(--teal-2); border-top-color: transparent;
  animation: rcb-spin .7s linear infinite;
}
@keyframes rcb-spin { to { transform: rotate(360deg); } }
/* 生成中：内容略降透明度，但**仍然可见**（用户原话：不要把原总结换掉） */
.run-card-box.is-busy .rcb-brief { opacity: .55; }
/* 窄屏：标签顶格，内容占满整行 */
@media (max-width: 420px) {
  .rcb-line .k { display: block; min-width: 0; margin-right: 0; margin-bottom: 1px; }
  .rcb-files-line .k { display: block; min-width: 0; }
}

/* ============================================================
   z118 问题2：文件行收成「类别标签 + 文件名 + 大小 + ⋯」
   —— 原先每行都有「预览 / 打开 / 下载 / 删」四个文字按钮，手机上又宽又挤。
   ============================================================ */
.file-row { align-items: center; }
.file-row .fr-cat {
  font-size: calc(10px * var(--fs, 1)); padding: 1px 7px; border-radius: 20px; border: 1px solid;
  flex-shrink: 0; white-space: nowrap;
}
.file-row .fr-cat.c-plan { background: #fdf3e2; color: #b8791a; border-color: #f0dcb8; }
.file-row .fr-cat.c-data { background: #e8f1fa; color: #2b6cb0; border-color: #cfe0f4; }
.file-row .fr-cat.c-conclusion { background: #ecf4ea; color: #4a7c48; border-color: #c8dfc4; }
.file-row .fr-cat.c-design { background: #f1ebfa; color: #6d4aa8; border-color: #ddd0f5; }
/* z220：新增「序列信息」类别徽标（青绿色，和总体设计的紫色区分开） */
.file-row .fr-cat.c-sequence { background: #e6f6f2; color: #1f7a63; border-color: #c4e8de; }
.file-row .fr-cat.c-history, .file-row .fr-cat.c-other { background: #eef1f0; color: #6b807e; border-color: #d8dedc; }
.file-row .fn {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.file-row .fr-size { font-size: calc(11px * var(--fs, 1)); color: var(--ink-4); flex-shrink: 0; }
/* 「⋯」按钮 */
.fr-more {
  flex-shrink: 0; min-width: 30px; height: 26px; padding: 0 6px;
  border: 1px solid var(--line); border-radius: 7px; background: var(--panel);
  color: var(--ink-3); font-size: calc(14px * var(--fs, 1)); line-height: 1; cursor: pointer;
}
.fr-more:hover { border-color: var(--teal-2); color: var(--teal); background: var(--teal-soft); }
/* 「⋯」弹出的菜单 */
.fr-menu { display: flex; flex-direction: column; gap: 7px; }
.fr-menu .btn-ghost, .fr-menu .btn-danger { justify-content: flex-start; width: 100%; }
/* 实验卡里的文件行（与上面同款） */
.rcb-file-row { display: flex; align-items: center; gap: 5px; width: 100%; padding: 2px 0; }
.rcb-file-name {
  flex: 1; min-width: 0; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rcb-file-row .rcb-file-cat { flex-shrink: 0; }

/* ============================================================
   z121 问题3：Run 卡片标题栏上的**文件图标**（替代原来那一整块文件行）
   —— 用户标注：红圈处放图标、绿圈处（文件行列表）全部去掉。
   ============================================================ */
.run-filechips { display: inline-flex; align-items: center; gap: 3px; margin-left: auto; flex-shrink: 0; }
.run-filechip {
  position: relative; display: inline-flex; align-items: center; gap: 1px;
  min-width: 24px; height: 22px; padding: 0 5px;
  border: 1px solid; border-radius: 6px; background: #fff;
  font-size: calc(12px * var(--fs, 1)); line-height: 1; cursor: pointer;
}
.run-filechip.c-plan { background: #fdf3e2; color: #b8791a; border-color: #f0dcb8; }
.run-filechip.c-data { background: #e8f1fa; color: #2b6cb0; border-color: #cfe0f4; }
.run-filechip.c-conc { background: #ecf4ea; color: #4a7c48; border-color: #c8dfc4; }
.run-filechip:hover { filter: brightness(.95); box-shadow: 0 1px 4px rgba(0,0,0,.10); }
.run-filechip .rf-more { font-size: calc(9.5px * var(--fs, 1)); opacity: .8; margin-left: 1px; }
/* 窄屏：标题栏元素太多时，日期先让位 */
@media (max-width: 400px) {
  .run-head .run-date { max-width: 84px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .run-filechip { min-width: 22px; height: 20px; padding: 0 4px; font-size: calc(11px * var(--fs, 1)); }
}

/* ============================================================
   z123：「生成实验卡」改为状态行 + 「不生成」按钮（不用复选框）
   —— 空复选框 + 文字说"默认会生成"是自相矛盾的，用户一定会误读。
   ============================================================ */
.arc-card-op {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border: 1px solid var(--teal-2); border-radius: 9px;
  background: var(--teal-soft);
}
.arc-card-op .aco-text { flex: 1; min-width: 0; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); line-height: 1.5; }
.arc-card-op .btn-ghost.sm { flex-shrink: 0; padding: 2px 9px; min-height: 26px; background: #fff; }
.arc-card-op.is-off { border-color: var(--line); background: var(--panel); }
.arc-card-op.is-off .aco-text { color: var(--ink-3); }

/* ============================================================
   第三批 · 改动 15：指标趋势图（纯前端 SVG，无图表库）
   —— 横轴 Run 编号、纵轴指标值；连续 3 次同向时下方给信号。
   ============================================================ */
.trend-panel {
  margin: 8px 0 10px; padding: 9px 10px 7px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
}
.tp-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.tp-title { font-size: calc(12px * var(--fs, 1)); font-weight: 650; color: var(--teal); flex-shrink: 0; }
.tp-head select {
  flex: 1; min-width: 0; font-size: calc(12px * var(--fs, 1)); padding: 3px 6px;
  border: 1px solid var(--line); border-radius: 7px; background: #fff; color: var(--ink);
}
.tp-chart { min-height: 40px; }
.trend-svg { width: 100%; height: auto; display: block; }
.tp-grid { stroke: var(--line); stroke-width: .7; stroke-dasharray: 2 3; }
.tp-line { fill: none; stroke: var(--teal); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.tp-dot { fill: var(--teal); }
.tp-ytick { font-size: calc(9px * var(--fs, 1)); fill: var(--ink-4); text-anchor: end; }
.tp-xtick { font-size: calc(9px * var(--fs, 1)); fill: var(--ink-3); text-anchor: middle; }
.tp-empty { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-4); padding: 6px 0; }
.tp-signal {
  margin-top: 3px; font-size: calc(11.5px * var(--fs, 1)); color: #b8791a;
  background: #fdf3e2; border: 1px solid #f0dcb8; border-radius: 7px; padding: 3px 7px;
}

/* ══════════════════════════════════════════════════════════════════
   z136（第四批）· 文献卡 + 知识库检索
   ------------------------------------------------------------------
   全部走主题变量（5 套主题自动适配），不使用写死的浅色背景。
   ══════════════════════════════════════════════════════════════════ */
/* 页头检索行：搜索框 + 批量补全 + 状态计数 */
.kb-search { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 16px 10px; border-bottom: 1px solid var(--line-soft); }
.kb-search-input { flex: 1; min-width: 180px; font-size: calc(12.5px * var(--fs, 1)); padding: 6px 10px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--bg); color: var(--ink); font-family: var(--sans); }
.kb-search-input:focus { outline: none; border-color: var(--violet); }
.kb-count { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); white-space: nowrap; }
.kb-batch-hint { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); background: none; border: none; padding: 0; text-align: left; cursor: default; }
.kb-batch-hint:empty { display: none; }
/* 侧栏里的“资料”小标题 */
.kb-sec-head { display: flex; align-items: center; gap: 4px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); font-weight: 600; margin: 12px 0 2px; }

/* 文献卡（折叠态 = 主旨 / 关键结果 / 与本课题关联；展开 = 全部字段） */
.kb-item.lit-card { border-left-color: var(--violet); background: var(--violet-soft); padding: 8px 10px; }
/* z139 改动 3：顶行改成三列 —— [▸] [标题区（可多行）] [顶行操作]
   ⚠️ 原来是 `display:flex; flex-wrap:wrap`：英文论文题目一长（占三四行），
      flex 换行就把 `▸` **单独甩在第一行**，用户截图里"那个小三角形占一整行非常浪费"就是它。
      用 grid 三列后，三角形永远贴住标题第一行左侧，操作永远在右上角，标题想折几行都不影响布局。 */
.lit-l1 { display: grid; grid-template-columns: 10px minmax(0, 1fr) auto; gap: 6px; align-items: start; }
.lit-l1 .lit-title { font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; color: var(--ink); }
.lit-n { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); margin-left: auto; white-space: nowrap; }
.kb-badge.lit { background: var(--violet); color: #fff; }
.kb-badge.edited { background: var(--blue-soft); color: var(--blue); }
.kb-badge.stale { background: var(--amber-soft); color: var(--amber); }
.lit-sum { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); margin-top: 4px; line-height: 1.6; word-break: break-word; }
.lit-empty { color: var(--ink-3); }
.lit-more { margin-top: 5px; }
.lit-more > summary { font-size: calc(11px * var(--fs, 1)); color: var(--violet); cursor: pointer; list-style: none; }
.lit-more > summary::-webkit-details-marker { display: none; }
.lit-more > summary::before { content: '▸ '; }
.lit-more[open] > summary::before { content: '▾ '; }
.lit-fields { margin-top: 5px; border-top: 1px dashed var(--line); padding-top: 5px; }
.lit-f { display: flex; gap: 7px; font-size: calc(11.5px * var(--fs, 1)); line-height: 1.65; margin-bottom: 3px; }
.lit-f .lit-k { flex: 0 0 74px; color: var(--ink-3); }
.lit-f .lit-v { flex: 1; min-width: 0; color: var(--ink); word-break: break-word; }
.lit-ops { margin-top: 6px; }
.lit-ops .kacts { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
.lit-ops .kacts .btn-ghost.sm { padding: 1px 7px; font-size: calc(10.5px * var(--fs, 1)); }
.lit-ops .kacts .btn-danger { padding: 1px 6px; font-size: calc(10.5px * var(--fs, 1)); }
.lit-ops .kacts .st { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); }
.lit-warn { font-size: calc(11px * var(--fs, 1)); color: var(--amber); background: var(--amber-soft); border: 1px solid #f0dcb8; border-radius: 7px; padding: 4px 7px; margin: 5px 0 0; line-height: 1.6; }
/* 预览弹窗顶部的卡片块 */
.lit-card.open { border-left: 3px solid var(--violet); background: var(--violet-soft); border-radius: 0 8px 8px 0; padding: 9px 11px; margin: 0 0 10px; }
.lit-card.open .lit-head { font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; color: var(--ink); display: flex; align-items: center; gap: 8px; }
.lit-card.open .lit-head .lit-n { margin-left: auto; }

/* ============================================================
   z138（用户报障 3 + 4）· 文献卡折叠 / 摘要分段 / 已修订·过期 / 正在生成
   ---------------------------------------------------------------------------
   ① 折叠：卡片默认只占「标题行 + 三行摘要 + 操作行」，点标题行才展开完整内容
      （与第二批实验卡 `.rcb-sum` 的交互语言一致 —— 平台只保留一种"卡片折叠"手势）。
   ② 摘要分段：主旨 / 关键结果 / 与本课题关联 **各成一行**，收起态单行省略、
      展开态完整成段。原来三段用 `　·　` 连成一坨，正文一长就折成一大段密字。
   ③ 状态角标：`.kb-badge.edited`（已修订）/ `.kb-badge.stale`（原文已变动，建议重算）
      —— 样式 z136 就有，z138 起才真正可达（修订按钮 + 过期推导）。
   ④ 正在生成：复用实验卡的 `.rcb-spin`（全平台一个转圈样式），**与内容并排**、不清空原内容。
   ============================================================ */
.lit-l1 { cursor: pointer; }
.lit-l1:focus-visible { outline: 2px solid var(--violet); outline-offset: 1px; border-radius: 6px; }
.lit-caret { flex: 0 0 auto; width: 10px; text-align: center; font-size: calc(10px * var(--fs, 1)); color: var(--ink-3); }
/* 收起态摘要：三段各占一行，每行单行省略（一眼扫完） */
.lit-sum .lit-p { display: flex; gap: 6px; font-size: calc(11.5px * var(--fs, 1)); line-height: 1.58; margin-top: 2px; color: var(--ink-2); }
.lit-sum .lit-p .lit-k2 { flex: 0 0 auto; color: var(--ink-3); }
.lit-sum .lit-p .lit-v2 { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* 展开态：同一批字段完整成段（不省略），后面跟「全部字段」 */
.lit-body { margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--line); }
.lit-body[hidden] { display: none; }
.lit-body .lit-s { font-size: calc(11.5px * var(--fs, 1)); line-height: 1.7; margin-bottom: 6px; }
.lit-body .lit-s .lit-sk { display: block; font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); margin-bottom: 1px; }
.lit-body .lit-s .lit-sv { display: block; color: var(--ink); word-break: break-word; }
.lit-body .lit-more { margin-top: 6px; }
/* 正在生成：与内容并排的转圈（不清空原内容 —— 与实验卡同一条约定） */
.lit-busy { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; font-size: calc(10.5px * var(--fs, 1)); color: var(--teal); white-space: nowrap; }
.lit-busy[hidden] { display: none; }
.lit-busy .rcb-spin { width: 10px; height: 10px; border-width: 1.5px; }
.kb-item.lit-card.is-busy .lit-sum { opacity: .6; }
.kb-row.is-busy .kt { color: var(--teal); }
/* 批量补全按钮的转圈态（z138 改动 D②） */
#kbBatchFill.is-busy { display: inline-flex; align-items: center; gap: 5px; }
.kb-batch-hint .rcb-spin { width: 10px; height: 10px; border-width: 1.5px; vertical-align: -1px; margin-right: 3px; }

/* ============================================================
   z139（用户报障 3 + 4）· 卡片顶行重排 + 论文题目 + ⋯ 菜单
   ---------------------------------------------------------------------------
   ① 顶行重排（用户原话）：「实验卡最上面那个小三角形占一整行非常浪费，
      可以把重新生成和修订放到那一行，预览、打开、下载、删折叠到点点点里面去，
      点点点也放在最上面那一行，这样更加整洁清爽。」
      → 顶行 = [▸] [标题区] [↻ 重新生成][✎ 修订][⋯]；四个低频动作进 ⋯ 菜单。
   ② 论文题目（用户原话）：「直接上传的文献卡连论文标题都没有」——
      条目标题在"直接上传"路径上就是文件名，题目改为从卡片 `title` 字段取，
      显示为主标题，原条目名降为副标题（仅当两者不同时出现）。
   ③ 版式统一：直接上传 / 手动生成**同一套**渲染（本来都是 `kbItemHtml`，
      以前看起来不一样是因为标题长度不同 + 三角形被 flex 换行挤走）。
   ============================================================ */
/* 标题区：题目（主）+ 条目名（副）+ 角标行 */
.lit-main { display: block; min-width: 0; }
.kb-item.lit-card .lit-title {
  display: block; font-size: calc(12.5px * var(--fs, 1)); font-weight: 650; line-height: 1.5;
  color: var(--ink); word-break: break-word;
}
.lit-sub {
  display: block; margin-top: 1px; font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.lit-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; margin-top: 4px; }
.lit-meta .lit-n { margin-left: auto; }
/* 三角形：与标题第一行的视觉中心对齐（不是与整块居中） */
.lit-l1 .lit-caret { margin-top: 3px; line-height: 1; }
/* 顶行操作：始终贴右上角，不被标题挤走 */
.lit-topops { display: flex; align-items: center; gap: 3px; flex-shrink: 0; }
.lit-topops .btn-ghost.sm { padding: 2px 6px; min-height: 22px; }
.lit-topops .lit-dots { padding: 2px 5px; letter-spacing: 1px; }
/* ⋯ 菜单（复用全平台 `.ctx-menu` 外观，只收紧宽度） */
.ctx-menu.kb-more-menu { min-width: 108px; }
.ctx-menu.kb-more-menu button { padding: 7px 12px; font-size: calc(12px * var(--fs, 1)); }
/* 隐藏容器：装"预览/打开/下载/删"四个真按钮（不参与布局，仅作转发源） */
.lit-more-src { display: none; }

/* ══════════════════════════════════════════════════════════════════
   z140（用户报障 1 + 2 + 3）
   ------------------------------------------------------------------
   ① 图片卡：知识库条目 / 聊天附件里的图片，一律**卡片化**（缩略图 + 名称 + 操作），
      不再把原图铺在气泡或列表里（用户原话：「图片上传后不要显示得那么大，
      跟其他文档类型一样，挂成图片卡，有预览、打开、下载、删除按钮。」）。
   ② 图片的应用内预览：`.kb-img-preview` 以 60vh 封顶 —— 点「预览」不再跳到浏览器
      看一张铺满整屏的图。
   ③ 文献卡「主旨出现两次」：展开后藏掉收起态那三行摘要（`.lit-card.is-open .lit-sum`）。
   ⚠️ 缩略图必须靠**固定宽高 + object-fit:cover** 压住：不给尺寸的话，
      一张竖拍照片会把卡片撑成一条长条 —— 那就等于换了个地方继续"显示得那么大"。
   ══════════════════════════════════════════════════════════════════ */

/* 图片条目（知识库列表）：左缩略图 + 右标题/角标，下方操作行 */
.kb-item.img-card { border-left-color: var(--blue); background: var(--panel-soft); }
.img-l1 { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: 9px; align-items: start; }
.img-thumb {
  width: 44px; height: 44px; border-radius: 8px; object-fit: cover;
  border: 1px solid var(--line); background: #fff; display: block;
}
.img-thumb-none {
  display: flex; align-items: center; justify-content: center;
  font-size: calc(9px * var(--fs, 1)); letter-spacing: .5px; color: var(--ink-3);
  background: var(--panel); border: 1px dashed var(--line);
}
.kb-item.img-card .lit-title { font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; color: var(--ink); word-break: break-word; }
.img-ops { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; margin-top: 6px; }
.img-ops .btn-ghost.sm { padding: 1px 7px; font-size: calc(10.5px * var(--fs, 1)); }
.img-ops .btn-danger { padding: 1px 6px; font-size: calc(10.5px * var(--fs, 1)); }
.img-ops .st { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); }
.kb-badge.img { background: #e8f4fb; color: var(--blue); }
.kb-item.img-card.is-busy .img-thumb { opacity: .55; }

/* 图片条目的应用内预览（60vh 封顶，多图纵向排列） */
.kb-img-preview { display: flex; flex-direction: column; gap: 10px; align-items: center; }
.kb-img-preview img {
  max-width: 100%; max-height: 60vh; width: auto; height: auto;
  border-radius: 8px; border: 1px solid var(--line); background: #fff;
}

/* 聊天附件卡里的图片缩略图（36px，与文件卡的 .word-ico 同高同宽） */
.fcard-thumb {
  width: 36px; height: 36px; border-radius: 8px; object-fit: cover;
  border: 1px solid var(--line); flex-shrink: 0; display: block; background: #fff;
}
.word-card.attach-fcard.img-fcard .word-name { font-weight: 600; }

/* 文献卡：展开后不再重复显示收起态的三行摘要（主旨只能出现一次） */
.lit-card.is-open .lit-sum { display: none; }

/* z212（用户需求 2）：资料卡上的「删除卡片」按钮 —— 悬停转成危险色，
   提醒它与「删除条目」不是一回事（只拆摘要，原文完整保留） */
.lit-topops .lit-carddel:hover { color: var(--danger); border-color: var(--danger); background: var(--danger-soft); }

/* ══════════════════════════════════════════════════════════════════
   z212（用户需求 2）：入库时「要不要生成资料卡」弹窗（三屏：问 / 生成 / 可编辑保存）
   ══════════════════════════════════════════════════════════════════ */
.lit-offer .lo-list {
  margin: 6px 0 10px; padding: 0; list-style: none;
  max-height: 168px; overflow: auto;
  border: 1px solid var(--line-soft); border-radius: var(--radius-sm); background: var(--panel);
}
.lit-offer .lo-list li {
  display: flex; gap: 10px; justify-content: space-between; align-items: baseline;
  padding: 6px 9px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2);
  border-bottom: 1px solid var(--line-soft);
}
.lit-offer .lo-list li:last-child { border-bottom: 0; }
.lit-offer .lo-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lit-offer .lo-n { flex: none; color: var(--ink-3); font-size: calc(11.5px * var(--fs, 1)); }
.lit-offer .lo-more { color: var(--ink-3); }
.lit-offer .lo-warn { color: var(--amber); }
/* z213：申请入库窗有**三个出口**（取消 / 仅入库 / 入库并生成），窄屏允许换行 —— 不要挤出弹窗 */
.lit-offer .modal-actions { flex-wrap: wrap; }
.lit-offer .lo-bar { height: 5px; border-radius: 5px; background: var(--line-soft); overflow: hidden; margin: 8px 0 8px; }
.lit-offer .lo-bar i { display: block; height: 100%; width: 0; background: var(--teal-2); transition: width .3s ease; }
.lit-offer .lo-cards { max-height: 46vh; overflow: auto; margin: 8px 0 4px; }
.lit-offer .lo-card {
  border: 1px solid var(--line-soft); border-radius: var(--radius-sm);
  background: var(--panel); padding: 8px 10px; margin-bottom: 8px;
}
.lit-offer .lo-card > summary { cursor: pointer; font-size: calc(13px * var(--fs, 1)); font-weight: 600; color: var(--ink); }
.lit-offer .lo-src { margin: 4px 0 8px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.lit-offer .lo-card .field { margin-bottom: 7px; }
.lit-offer .lo-card .field label { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.lit-offer .lo-card textarea, .lit-offer .lo-card input { font-size: calc(12.5px * var(--fs, 1)); }

/* ══════════════════════════════════════════════════════════════════
   z141（用户要求 2）：公共撰写区「房间文件」按**文件类型**分类的标签配色
   只影响 fileRowHtml(f, true) 这条路径（撰写区），实验房间的归档类别标签不变。
   ⚠️ 用 .c-word/.c-ppt/.c-excel/.c-pdf/.c-image 命名而不是 .c-doc2 之类：
      它们直接来自 FTYPE_DEFS 的 key，改 key 就必须改这里，语义是一一对应的。
   ══════════════════════════════════════════════════════════════════ */
.file-row .fr-cat.c-word  { background: #e8eefb; color: #2b4f96; border-color: #cdd9f2; }
.file-row .fr-cat.c-ppt   { background: #fbeee6; color: #a4551f; border-color: #f2d6c2; }
.file-row .fr-cat.c-excel { background: #e6f5ec; color: #24704a; border-color: #c6e5d7; }
.file-row .fr-cat.c-pdf   { background: #fbe9e9; color: #9b3232; border-color: #f2cccc; }
.file-row .fr-cat.c-image { background: #efe9fb; color: #5f45a2; border-color: #ddd0f5; }

/* ══════════════════════════════════════════════════════════════════
   z141（用户要求 3）：知识库「本次待入库」列表
   版式目标：一行看完 图标 / 文件名 / 大小 / 能不能做卡 / 状态，
   文件名过长时省略而不是换行（列表高度必须稳定，否则按钮会被挤走）。
   ══════════════════════════════════════════════════════════════════ */
.kb-queue { margin-top: 8px; }
.kb-queue-head {
  font-size: calc(12px * var(--fs, 1)); font-weight: 600; color: var(--ink-2);
  padding: 0 2px 6px; display: flex; align-items: center; gap: 6px;
}
.kb-q-list { display: flex; flex-direction: column; gap: 4px; }
.kb-q-row {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 7px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface, #fff); font-size: calc(12px * var(--fs, 1)); line-height: 1.5;
}
.kb-q-row .qk-ic { flex-shrink: 0; font-size: calc(12.5px * var(--fs, 1)); }
.kb-q-row .qk-name {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--ink);
}
.kb-q-row .qk-size { flex-shrink: 0; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.kb-q-row .qk-st { flex-shrink: 0; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.kb-q-row .qb-card {
  flex-shrink: 0; font-size: calc(10.5px * var(--fs, 1)); padding: 0 5px; border-radius: 6px;
  background: #e8f4fb; color: var(--blue); border: 1px solid #cfe6f5;
}
.kb-q-row .qb-nocard {
  flex-shrink: 0; font-size: calc(10.5px * var(--fs, 1)); padding: 0 5px; border-radius: 6px;
  background: #f1f2f1; color: var(--ink-3); border: 1px solid var(--line);
}
.kb-q-row .qk-x {
  flex-shrink: 0; border: none; background: none; cursor: pointer;
  color: var(--ink-3); font-size: calc(14px * var(--fs, 1)); line-height: 1; padding: 0 2px;
}
.kb-q-row .qk-x:hover { color: var(--danger); }
/* 四态：待入库（默认）/ 处理中 / 已入库 / 失败 —— 用左侧色条区分，不动文字颜色 */
.kb-q-row.q-run  { border-left: 3px solid var(--blue); background: #f7fbff; }
.kb-q-row.q-ok   { border-left: 3px solid #4a7c48; background: #f6faf5; opacity: .8; }
.kb-q-row.q-err  { border-left: 3px solid var(--danger); background: #fff8f8; }
.kb-q-row.q-err .qk-name { color: var(--danger); }
.kb-q-acts { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.kb-q-acts .btn-primary.sm, .kb-q-acts .btn-ghost.sm { padding: 3px 10px; font-size: calc(12px * var(--fs, 1)); }
.kb-q-acts button[disabled] { opacity: .5; cursor: not-allowed; }

/* ══════════════════════════════════════════════════════════════════
   z141（用户报障 1 的兜底）：用户要文件、模型只给了文字时的补救按钮
   低调但要看得见 —— 它是"这次没给文件"的唯一出口，不能藏进 ⋯ 菜单里。
   ══════════════════════════════════════════════════════════════════ */
.doc-rescue {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 8px; padding: 6px 8px; border: 1px dashed var(--line); border-radius: 8px;
  background: rgba(0, 0, 0, .015);
}
.doc-rescue .dr-hint { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.doc-rescue .btn-ghost.sm { padding: 2px 9px; font-size: calc(11.5px * var(--fs, 1)); }

/* ══════════════════════════════════════════════════════════════════════
   z150：借鉴库
   ---------------------------------------------------------------------
   · .bp-row  条目上的「编号 + 借鉴点」一行（编号 L-00003 是聊天里的引用坐标）
   · .bp-opt  上传时的借鉴点多选（四个 + 自由补充）
   ⚠️ 编号必须等宽、高对比：用户要在聊天里手打它，看不清就等于没有。
   ══════════════════════════════════════════════════════════════════════ */
.bp-row { display: flex; flex-wrap: nowrap; align-items: center; gap: 3px; margin-top: 4px; overflow: hidden; }
.bp-no {
  /* z218：原来是 font 简写 —— 简写里塞 calc() 兼容性差，拆成三个属性（值不变）。 */
  font-weight: 600;
  font-size: calc(10px * var(--fs, 1));
  line-height: 1.45;
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  letter-spacing: .3px; padding: 1px 6px; border-radius: 5px;
  background: #eef2ff; color: #3b4fa8; border: 1px solid #ccd6f5;
  flex: 0 0 auto; white-space: nowrap;
}
.bp-chip {
  /* z162（用户要求）：四个借鉴点尽量排一行，整体缩小一档 */
  font-size: calc(10px * var(--fs, 1)); padding: 0 5px; line-height: 16px; border-radius: 12px; flex: 0 0 auto;
  background: var(--teal-soft); color: var(--teal); white-space: nowrap;
}
.bp-chip.note { background: var(--amber-soft, #fdf1e3); color: #a4620a; max-width: 160px; overflow: hidden; text-overflow: ellipsis; }

/* 借鉴点选择弹窗 */
.bp-opts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
@media (max-width: 520px) { .bp-opts { grid-template-columns: 1fr; } }
.bp-opt {
  display: flex; flex-direction: column; gap: 2px; position: relative;
  padding: 9px 10px 9px 34px; border: 1px solid var(--line);
  border-radius: 9px; cursor: pointer; background: var(--panel);
  transition: border-color .12s, background .12s;
}
.bp-opt:hover { border-color: var(--teal); }
.bp-opt input { position: absolute; left: 11px; top: 11px; width: 15px; height: 15px; accent-color: var(--teal); }
.bp-opt input:checked ~ .bp-opt-n { color: var(--teal); }
.bp-opt:has(input:checked) { border-color: var(--teal); background: var(--teal-soft); }
.bp-opt-n { font-size: calc(13px * var(--fs, 1)); font-weight: 600; }
.bp-opt-t { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); line-height: 1.4; }

/* z190（用户要求）：三张入口卡在手机上也要**一行三个**（原来第三张被强制独占一行）。
   一行三个 = 一眼看全三个库，不用往下找。 */
@media (max-width: 620px) {
  .team-space { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .team-space > .team-space-card:nth-child(3) { grid-column: auto; }
}

/* z153：回收站列表（软删除恢复） */
.file-trash-entry { margin: 6px 0 4px; }
.trash-list { max-height: 46vh; overflow: auto; }
.trash-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 9px; border: 1px solid var(--line-soft); border-radius: 8px;
  background: var(--panel-soft); margin-bottom: 6px;
}
.trash-row .tr-l { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.trash-row .tr-l b { font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trash-row .tr-l span { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }

/* z154：更早对话的一行提示（不是压缩 —— 消息本身全部照常显示） */
.hist-line {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 6px 10px; margin: 2px 0 10px;
  border: 1px dashed var(--line); border-radius: 8px;
  background: var(--panel-soft);
}
.hist-line .hl-txt { flex: 1; min-width: 0; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.hist-line .hl-btn {
  font: inherit; font-size: calc(11.5px * var(--fs, 1)); padding: 3px 9px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--panel); color: var(--ink-2);
}
.hist-line .hl-btn:hover { border-color: var(--teal); color: var(--teal); }

/* z159：借鉴提交弹窗（一个弹窗走完 选点→生成→改→提交） */
.bp-hint { margin: 2px 0 10px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); line-height: 1.55; }
.bp-lbl { display: block; margin-bottom: 3px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); font-weight: 600; }
.bp-edit-note { width: 100%; box-sizing: border-box; font: inherit; font-size: calc(12.5px * var(--fs, 1)); line-height: 1.5; padding: 7px 9px; border: 1px solid var(--line); border-radius: 7px; background: var(--panel); color: var(--ink-1); resize: vertical; }
.bp-edit-note:focus { outline: none; border-color: var(--teal); }

/* z161：编号徽标 = 可点击复制（不再兼作展开入口） */
.bp-no.copy { cursor: pointer; user-select: none; }
.bp-no.copy:hover { background: var(--teal-weak, #e6f2f0); color: var(--teal); }
.bp-no.copy:active { transform: scale(0.97); }

/* z163（用户要求）：借鉴卡顶部信息整合成一行（资料卡 + 编号 + 借鉴点），节约空间 */
.lit-meta.bp-meta .kb-badge.lit { font-size: calc(10px * var(--fs, 1)); padding: 0 6px; line-height: 16px; }
.lit-meta.bp-meta .bp-no { font-size: calc(9.5px * var(--fs, 1)); padding: 0 5px; line-height: 16px; }

/* z164（用户截图）：nowrap 下子项默认会被压缩 —— 「资料卡」被挤成竖排单字、后面的借鉴点被裁掉。
   修法：每个子项 flex:0 0 auto（不压缩），只有最后一个借鉴点允许省略。 */
/* 一行放得下就一行；实在放不下**允许换行**（换行也比被裁掉/竖排好看） */
.lit-meta.bp-meta { flex-wrap: wrap; overflow: visible; gap: 4px 5px; align-items: center; }
.lit-meta.bp-meta > * { flex: 0 0 auto; white-space: nowrap; }
.lit-meta.bp-meta .bp-chip { font-size: calc(9.5px * var(--fs, 1)); padding: 0 5px; line-height: 16px; min-width: 0; overflow: hidden; text-overflow: ellipsis; flex: 0 0 auto; }

/* z176：输入框旁的「文件」开关（一次性：只管下一条消息，默认关） */
.gen-doc-btn {
  display: inline-flex; align-items: center; gap: 3px;
  height: 32px; padding: 0 8px; margin-right: 6px; flex: 0 0 auto;
  border: 1px solid var(--line, #dcdfe6); border-radius: 8px;
  background: #fff; color: var(--muted, #8a8f99);
  font-size: calc(12px * var(--fs, 1)); cursor: pointer; transition: all .15s;
}
.gen-doc-btn:hover { border-color: var(--teal); color: var(--teal); }
.gen-doc-btn.on { background: var(--teal); border-color: var(--teal); color: #fff; }
.gen-doc-btn svg { flex: 0 0 auto; }
@media (max-width: 560px) { .gen-doc-btn span { display: none; } .gen-doc-btn { padding: 0 7px; } }

/* ============================================================
   z202：成员头像墙上的「移出成员」× （全宽度生效）
   —— 上面那段在 @media(max-width:620px) 内，这里补一份不依赖断点的，
      保证任何宽度渲染头像墙时样式都不丢（红线：样式必须跟着组件走）。
   ============================================================ */
.mz-cell .mz-rm {
  position: absolute; top: -6px; right: -8px;
  width: 20px; height: 20px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: calc(13px * var(--fs, 1)); line-height: 1; font-weight: 700;
  background: var(--danger); color: #fff;
  border: 1.5px solid var(--panel);
  box-shadow: 0 1px 3px rgba(0,0,0,.18);
  cursor: pointer;
}
.mz-cell .mz-rm::before { content: ''; position: absolute; inset: -7px; }

/* ============================================================
   z218（用户需求 1）：聊天页截图
   ------------------------------------------------------------
   入口按钮长得跟 📎 一样（同一个视觉重量），手机端占「第 1 行第 2 格」
   （原来「实验数据」腾出来的那个格子），桌面端挤在 📎 右边。
   覆盖层：整屏深色 + 中间冻结的画面 + 拖拽框选 + 底部工具条。
   ============================================================ */
.shot-btn {
  width: 34px; height: 34px; flex-shrink: 0; padding: 0;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel);
  color: var(--ink-2); display: flex; align-items: center; justify-content: center;
  cursor: pointer;
}
.shot-btn:hover { border-color: var(--teal-2); color: var(--teal); background: var(--teal-soft); }
.shot-btn:active { background: var(--line-soft); }
.shot-btn svg { width: 17px; height: 17px; }

/* z221：截图前置选择（z219 删过，用户要求加回来）。
   两块并排、点谁截谁 —— **不要写解释段落**（z218 那版太长，用户当时嫌罗嗦）。 */
.shot-pick {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 6px 0 2px;
}
.shot-pick-item {
  display: flex; flex-direction: column; gap: 4px; align-items: flex-start;
  padding: 12px 13px 11px; text-align: left; cursor: pointer;
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel);
  font-family: inherit;
}
.shot-pick-item b {
  font-size: calc(14px * var(--fs, 1)); font-weight: 700; color: var(--ink);
}
.shot-pick-item span { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); line-height: 1.45; }
.shot-pick-item:hover { border-color: var(--teal-2); background: var(--teal-soft); }
.shot-pick-item:active { background: var(--line-soft); }
@media (max-width: 520px) { .shot-pick { grid-template-columns: 1fr; } }

/* 截图覆盖层 */
.shot-mask {
  position: fixed; inset: 0; z-index: 340;
  background: rgba(12, 16, 15, .88);
  display: flex; flex-direction: column;
  padding: 10px 10px calc(10px + env(safe-area-inset-bottom, 0px));
}
/* z219：提示条自带底衬（药丸形）—— 否则它会和背后被压暗的页面文字**叠在一起**，
   看着像两行字打架（截图里「工作台 / AI团队」和提示叠在一起）。 */
.shot-tip {
  color: #dfe7e4; text-align: center; line-height: 1.5;
  font-size: calc(12.5px * var(--fs, 1));
  align-self: center; max-width: min(92vw, 760px);
  padding: 7px 16px; margin: 2px 0 10px;
  background: rgba(14, 20, 18, .94); border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 999px;
}
.shot-tip b { color: #8fe0c0; }
/* z221：截错源（选了「去掉当前窗口」却分享到窗口/标签页）时的提示 —— 要显眼但不能像报错弹窗 */
.shot-tip.shot-warn {
  color: #ffe0a6; border-color: rgba(255, 190, 90, .5);
  background: rgba(58, 42, 12, .96);
}
.shot-tip.shot-warn b { color: #ffd27a; }
/* z224：「请把本应用最小化」等待层 —— 「去掉当前窗口」要的是最小化之后的整屏。
   网页没法自己最小化，只能请用户点一下「−」；所以这一步必须写得非常清楚、按钮要够大。 */
.shot-wait {
  position: fixed; inset: 0; z-index: 9998; display: flex; align-items: center;
  justify-content: center; padding: 20px; background: rgba(10, 14, 12, .58);
}
.shot-wait-card {
  max-width: min(92vw, 480px); background: var(--panel); color: var(--ink);
  border: 1px solid var(--line-soft); border-radius: 14px; padding: 20px 22px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, .32);
}
.shot-wait-card b { display: block; font-size: calc(16px * var(--fs, 1)); margin-bottom: 10px; }
.shot-wait-card p { margin: 0 0 16px; color: var(--ink-2); line-height: 1.7; font-size: calc(13px * var(--fs, 1)); }
.shot-wait-card kbd {
  display: inline-block; padding: 1px 6px; border-radius: 5px;
  border: 1px solid var(--line-soft); background: var(--panel-soft); font: inherit;
  font-size: calc(12px * var(--fs, 1));
}
.shot-wait-acts { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }
.shot-stage {
  position: relative; flex: 1 1 auto; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden; touch-action: none; cursor: crosshair;
}
.shot-stage img {
  max-width: 100%; max-height: 100%;
  user-select: none; -webkit-user-drag: none; pointer-events: none;
}
/* 选框：用超大 box-shadow 把「没框到的部分」压暗，一眼看出要发的是哪块 */
.shot-sel {
  position: absolute; display: none; pointer-events: none;
  border: 2px solid #56d6a6; border-radius: 2px;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, .42);
}
.shot-bar {
  flex: none; display: flex; gap: 8px; justify-content: center; align-items: center;
  padding: 10px 4px 2px; flex-wrap: wrap;
}
.shot-bar .btn-ghost { border-color: rgba(255, 255, 255, .35); color: #e7efec; background: transparent; }
.shot-info { color: #9fb3ad; font-size: calc(11.5px * var(--fs, 1)); margin-right: auto; padding-left: 6px; }

@media (max-width: 620px) {
  /* 手机端不再在输入框旁边挤按钮（一行里已经有「+ / 输入框 / 文件 / 发送」，
     再加一个「截图」会把输入框压到只剩几十像素）。截图入口改到「+」面板里
     —— 且只有浏览器真支持屏幕捕获时才出现（见 openAttachSheet）。 */
  .composer-row .shot-btn { display: none; }
  .shot-tip { font-size: calc(12px * var(--fs, 1)); }
}

/* z218（用户需求 2）：字号选择器里的「字」样本（固定 px，不跟 --fs 走 —— 它本身就是预览） */
.font-sample {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  background: var(--panel-soft); border: 1px solid var(--line); color: var(--ink-2);
}
