/* ============================================================
   科研课题管理平台 — 视觉系统
   深青绿主色 · 琥珀=进行中 · 苔绿=已归档 · 实验记录本质感
   ============================================================ */
:root {
  /* ★★ z341：换设计稿色板（深墨绿）。
     · --teal / --teal-2 / --teal-soft 直接指向 g800 / g600 / g050 ——
       全站凡是 var(--teal) 的地方**一起**换成新墨绿，不用逐条去改几百处。
     · --ink-3 按设计稿调到 #94a19d（辅助文字 / 占位符）。
     · --line **没动**：它同时被表单边框、输入框、表格分隔线用着，
       调成设计稿那档浅色会让全站控件"没有边界"。卡片内分隔线走 --line-soft。
     · 阴影改成带绿调的极淡阴影（黑色阴影在这套配色里显脏）。 */
  --g900: #0c4139;
  --g800: #0f4c44;
  --g700: #145a4f;
  --g600: #1d6d5e;
  --g050: #eef4f2;

  --ink: #1b2321;
  --ink-2: #5b6a66;
  --ink-3: #94a19d;
  --line: #dfe7e4;
  --line-soft: #eef3f1;
  --bg: #f2f5f4;
  --panel: #ffffff;
  --teal: #0f4c44;
  --teal-2: #1d6d5e;
  --teal-soft: #eef4f2;
  /* 设计稿的圆角 / 阴影令牌（四页用；老规则仍走 --radius / --shadow） */
  --r-card: 16px;
  --r-tile: 14px;
  --r-btn: 12px;
  --sh-card: 0 3px 12px rgba(30, 70, 60, .05);
  --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(30, 70, 60, .04), 0 6px 18px rgba(30, 70, 60, .05);
  /* ★ z315（用户要求）：整个应用**不再出现宋体** —— 原来标题走到这里会落到
     Songti SC / Noto Serif CJK（宋体/衬线），和正文的雅黑不是一套字，看着像两篇文章拼在一起。
     现在 --serif 直接指向 --sans（-apple-system / 微软雅黑 …），所有标题、品牌字、
     消息里的 h3/h4、弹窗标题**一次全改**，不用逐条去改 30 多处 `font-family: var(--serif)`。
     ⚠️ 变量名保留（改动面最小），但语义已经是"标题字体 = 正文字体"。 */
  --serif: var(--sans, -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-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;
  /* ★ z368：桌面骨架的"边框缩放"。字号倍率不能 1:1 套到左栏/右栏宽度上 ——
     125% 时侧栏会从 236 涨到 295、右栏从 322 涨到 402，一共吃掉 140px，
     1440 窗口下团队卡只剩 327，团队名会整条变成省略号。
     实测只需要补「标题 + 版本 chip」那 30px：取一半系数 ——
     125% → 侧栏 277 / 右栏 378（chip 不再被裁，团队区只让出 40px）；
     150% → 319 / 434（侧栏里那行字刚好还是完整的）。 */
  --fw: calc(1 + (var(--fs, 1) - 1) * .7);
  /* z349：首页整体缩到 92% 用的「用户字号快照」。
     ⚠️ 不能直接改 --fs：那是用户在「外观与账号」里选的字号（会被 JS 写到 <html> 上），
        覆盖它等于把用户的设置吃掉。这里在 :root 把 --fs 解析成一个字面值存下来，
        首页再写 `--fs: calc(var(--fs-user) * .92)` —— 快照在 :root 就算完了，
        子元素继承到的是数值，不会和 .hm 里的 --fs 形成循环引用。 */
  --fs-user: var(--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; }
/* 强制浅色：不加这条，手机深色模式会把页面反色成黑底（设计稿硬约束 3.1 的反面） */
html { color-scheme: light; }
/* z415：审批页切换页签时，内容长短不同会让根滚动条出现/消失，整块内容（含「采购」这种页头）
   横向抖一下 —— 用户报的「页面之间切换的时候页头会有点移动」就是它。
   常驻预留滚动条槽位即可；老浏览器用 overflow-y:scroll 兜底。 */
html { overflow-y: scroll; scrollbar-gutter: stable; }
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 里被隐藏。
   ============================================================ */
/* z271：⋮ 从「只在窄屏出现」改成**全端出现** —— 外观 / 字号 / 密聊 / 退出都收进它。
   ⚠️ 下面 ≤620px 的媒体查询里还有一条同名规则（只覆盖字号与内边距），不要删。 */
.topbar-more {
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1; padding: 3px 8px;
  font-size: calc(15px * var(--fs, 1));
}
/* 密聊有未读时的小红点（不带数字，免得在窄按钮上挤成一团；具体条数看菜单里那一行）。 */
.topbar-more.has-badge { position: relative; }
.topbar-more.has-badge::after {
  content: ''; position: absolute; top: 3px; right: 4px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--danger); box-shadow: 0 0 0 2px var(--panel);
}
/* 密聊页没有对应的导航项了，用 ⋮ 的高亮表示「当前就在密聊里」 */
.topbar-more.on { background: var(--teal-soft); color: var(--teal); }
.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); }
/* z338：桌面端 ⋮ 弹层（贴着按钮的小面板）。
   手机端不用它（走 #/more 整页），所以这组规则不进媒体查询。 */
.more-mask { position: fixed; inset: 0; z-index: 120; background: transparent; }
.more-pop {
  position: fixed; top: 52px; right: 16px; z-index: 121; width: 208px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 16px 40px rgba(20, 40, 35, .16); overflow: hidden;
  animation: morePopIn .13s ease-out;
}
@keyframes morePopIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
/* z366：面板改成跟着 ⋮ 的实际位置弹出（在底部就向上展开），动画方向也要跟着翻 */
.more-pop.up { animation-name: morePopUp; }
@keyframes morePopUp { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.more-pop .sheet-head { padding: 11px 14px 9px; margin: 0; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }
.more-pop .sheet-row { padding: 11px 14px; font-size: calc(13.5px * var(--fs, 1)); display: flex; align-items: center; gap: 8px; }
.more-pop .sheet-row .sub {
  margin-left: auto; font-style: normal; font-size: calc(10.5px * var(--fs, 1));
  font-family: var(--mono); color: var(--ink-4);
}
.more-pop .sheet-row .sub.warn {
  font-family: inherit; font-weight: 600; color: var(--amber);
  background: var(--amber-soft); border-radius: 6px; padding: 1px 5px;
}
.more-pop .sheet-row .sub.warn:empty { display: none; }
#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)); }
/* z234：统计行从「等宽网格」改为「一张主卡 + 三张窄卡」
   ------------------------------------------------------------
   用户要求：通知公告卡**和其他三个卡同一行**，位于**最左边**，
   宽度是「放了其他三个卡片后剩余的宽度」。
   所以三张窄卡给固定宽度，通知卡 flex:1 吃掉剩余空间 ——
   不需要算百分比，容器变宽变窄都自适应（红线 115 的教训：别用 1fr 硬算）。 */
.stat-row { display: flex; align-items: stretch; 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; }

/* 三张窄卡：固定宽度（够放「待我处理」四个字 + 两位数字），不参与伸缩 */
.stat-row > .stat:not(.stat-notice) { flex: 0 0 148px; min-width: 0; }

/* 通知·公告·动态主卡（z240 · 方案 B：浅底色带 + 左侧竖条 + 大字标题）
   ★ 卡片自身 padding 归零，左右留白交给 --sn-px：
     「表头」要整条铺满底纹，列表却要左右缩进，只有把 padding 提到子元素上才做得到。
     手机上只需改 --sn-px / --sn-py 两个变量，头部与列表不会一边宽一边窄。
   ★ overflow:hidden 是必须的：底纹带要贴着卡片圆角裁掉，否则四角会露出白方块。 */
.stat.stat-notice { --sn-px: 13px; --sn-py: 9px;
  flex: 1 1 auto; min-width: 0; cursor: pointer; padding: 0; overflow: hidden;
  transition: transform .12s ease, box-shadow .12s ease; }
.stat.stat-notice:hover { transform: translateY(-1px); box-shadow: 0 3px 10px rgba(0, 0, 0, .10); }
.sn-head { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: var(--sn-py) var(--sn-px);
  /* ★ 底纹必须用主题变量，不能写死颜色：站内有 warm / tea / sakura / bamboo 四套主题，
    写死一个冷调浅绿在「暖阳陶土」主题下会明显偏色（实测 #f4f8f6 在那边显得发灰）。
    选 --line-soft 而不是 --teal-soft：后者与列表里「通知」标签的底色撞色，
    而这条带子应该是中性的，不能跟彩色标签抢注意力。 */
  background: var(--line-soft); border-bottom: 1px solid var(--line); }
.sn-l { display: inline-flex; align-items: center; gap: 8px;
  font-size: calc(16px * var(--fs, 1)); font-weight: 700; color: var(--ink); }
/* z240：3px 青绿竖条 —— 纯装饰，用 flex 子元素而不是 border-left，
   这样它跟着标题字号走、也不会被 .sn-l 的 gap 吃掉位置 */
.sn-bar { flex: 0 0 auto; width: 3px; height: 15px; border-radius: 2px; background: var(--teal-2); }
.sn-body { padding: 2px var(--sn-px) 5px; }
.sn-dot { font-style: normal; font-size: calc(11px * var(--fs, 1)); line-height: 1;
  background: #d64541; color: #fff; border-radius: 999px; padding: 2px 7px; }
.sn-more { font-size: calc(11px * var(--fs, 1)); color: var(--teal-2); white-space: nowrap; }
.sn-row { display: flex; align-items: center; gap: 7px; padding: 3px 0; min-width: 0; }
.sn-row + .sn-row { border-top: 1px dashed var(--line-soft); }
.sn-tag { flex: 0 0 auto; font-size: calc(10.5px * var(--fs, 1)); line-height: 1.5;
  padding: 1px 6px; border-radius: 5px; border: 1px solid transparent; }
.sn-tag.k-notice { color: var(--teal); background: var(--teal-soft); border-color: #cfe4e0; }
.sn-tag.k-announcement { color: var(--amber); background: var(--amber-soft); border-color: #f0dfbe; }
.sn-tag.k-update { color: var(--violet); background: var(--violet-soft); border-color: #e3d8f7; }
.sn-t { flex: 1 1 auto; min-width: 0; font-size: calc(13px * var(--fs, 1)); color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sn-d { flex: 0 0 auto; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.sn-empty { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); padding: 8px 0 4px; }

.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; }
/* z338：去掉「房间名预览」那一行之后整体收一档（14/16 → 12/14，gap 8 → 6），
   统计压成两组两列，卡片比原来矮约 30px，同屏能多看一排团队。 */
.team-card { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px; 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; }
/* z236：团队卡的四个数字分**两行**（课题·Run / 手稿·发表论文）。
   z237：先试过并成一行，用户看后要求还是两行 —— 已回退；「进入团队 →」也随之从右上角
   挪回**右下角**（在 .tc-foot 里靠右、与第二行数字底部对齐）。 */
.team-card .tc-foot { display: flex; align-items: flex-end; justify-content: space-between; gap: 8px; margin-top: auto; }
.team-card .tc-lines { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* z338：四项统计（课题 / Run / 手稿 / 论文）要在 280~290px 的卡宽里**排成一行**，
   所以把项间距与字号各收一点（12 → 11.5px，gap 12 → 9px）。实测 289px 卡宽可一行放下。 */
.team-card .tc-row { display: flex; align-items: center; flex-wrap: wrap; gap: 3px 9px; }
/* z240：团队卡统计数字放大到与房间卡 `.room-stats .rs b` 同一口径（13px / 700 / --ink），
   量词仍是 12px 次要色 —— 只放大数字，四类目在 280px 窄卡上才排得下、不会挤成四行。 */
.team-card .tc-num { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); white-space: nowrap; }
.team-card .tc-num b { color: var(--ink); font-weight: 700; font-size: calc(13px * var(--fs, 1)); }
.team-card .team-count { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
/* 卡片右上角只放未读 / 待审徽标。
   ⚠️ 用 margin-left:auto 把徽标推到右边，而不是绝对定位 —— 卡片宽度随栅格变化，
      绝对定位会在窄卡上压住标题。 */
.team-card .tc-right { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.team-card .tc-top > b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* z237：「进入团队 →」回到右下角 —— 与第二行数字的底边对齐，不再占标题行的位置。 */
.team-card .team-enter { flex: 0 0 auto; font-size: calc(12px * var(--fs, 1)); color: var(--teal); font-weight: 600; white-space: nowrap; }
.team-room-wrap { display: flex; flex-direction: column; gap: 6px; }
/* z237：团队页同一行的卡片等高（栅格拉伸）——把底栏钉到底部，
   否则标题折成两行的卡会把「申请加入」按钮顶下去，一排按钮高低不齐。 */
#teamGrid .room-card { display: flex; flex-direction: column; }
#teamGrid .room-card .foot { margin-top: auto; }
.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; }
/* z356：hover 反馈只在**真鼠标**下生效（.has-mouse 由 app.js 按 pointerType 加）。
   原来靠 @media (hover: hover) 判断，但企业微信/微信内置 WebView 等会把它误报为 true，
   手机点按仍会套上 hover 样式 ⇒ 房间卡闪一下。pointerType 是浏览器自己派发的，判得准。 */
.has-mouse .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)); }
/* z272（版式 D）：房间卡左侧 3px 课题类型色条 —— 和「撰写」卡的琥珀左边条同一手法，
   远看就能按类型分堆。四条规则只改 border-left，:hover 的青色描边依然作用在其余三边，
   所以鼠标移上去是「青框 + 类型色左边条」，两边信息都在。
   ⚠️ 写房间（kind=writing）不会带 rt-* class，琥珀色条不会被顶掉。
   ⚠️ 颜色一律引用主题变量（--c-* 在文件末尾定义，全部转发到 --violet/--blue/…），
      否则换「暖阳陶土」等主题后这四个色条会跟主题打架。 */
.room-card.rt-science { border-left: 3px solid var(--c-science); }
.room-card.rt-major   { border-left: 3px solid var(--c-major); }
.room-card.rt-minor   { border-left: 3px solid var(--c-minor); }
.room-card.rt-extend  { border-left: 3px solid var(--c-extend); }
.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; }
/* z236：房间卡的统计行 —— 取代原来的「课题说明」（用户要求）。
   保留与原 desc 相同的最小高度，让「已加入 / 未加入」两种卡的底部仍然对齐。 */
.room-card .room-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; min-height: 34px; align-content: flex-start; margin: 0 0 6px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }
.room-card .room-stats .rs { white-space: nowrap; }
.room-card .room-stats .rs b { color: var(--ink); font-weight: 700; font-size: calc(13px * var(--fs, 1)); }
.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); }
/* z235：未加入房间的卡片（团队页 / 临时测试区）。
   它不能点进去（还没有权限），所以不给手型、不加悬停高亮 —— 否则用户会以为点了有反应。
   底部同样是「左成员名单 + 右申请按钮」，成员名过长时省略，全名在 title 里。
   z237：成员徽标**按内容自适应宽度**（原来是 `flex:1 1 auto`，在长卡片上被拉成一条横贯的色块，
        用户截图反馈「红框太长」）。改成 0 1 auto：自适应 + 空间不足时才收缩省略，
        与已加入的房间卡完全一致；按钮与提示包进 .jc-act，避免被 space-between 拆散。 */
.room-card.join-card { cursor: default; }
.room-card.join-card:hover { border-color: var(--line); box-shadow: none; }
.room-card.join-card .foot { gap: 8px; }
.room-card.join-card .foot .badge.exp { flex: 0 1 auto; min-width: 0; }
.jc-act { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.jc-hint { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); white-space: nowrap; }
.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; }

/* ---------- 房间 ---------- */
/* ★ z317（用户要求：「可以缩小房间名称的字体」+「整个页面的上下有一些空区域」）：
   房间头的标题 20px→17px、行距收紧、与聊天卡之间 14px→8px；
   页面上下留白由 body.in-room main 单独收（见文件末尾 z317 段），
   省出来的高度全部给聊天区（fitRoomViewport 按真实可用高度写死 .discuss 的 height）。 */
.room-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 8px; flex-wrap: wrap; }
.room-head h2 { font-family: var(--serif); font-size: calc(17px * var(--fs, 1)); line-height: 1.35; margin: 0; }
.room-head .sub { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); line-height: 1.5; }
/* 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; }
/* ══ z485：闲聊区（第四个分区）══
   闲聊区的三条硬约束在界面上必须**看得见**，否则用户会以为 AI "忘了资料"：
     · 顶部一条提示条（只在闲聊区出现）
     · 输入区不出现「文件」按钮（闲聊区一律不产出、不归档文件）
   两者都由 `.discuss.chat-mode` 一个类控制 —— 切分区时只 toggle 这一个类，
   不去改内联样式，避免重绘时闪一下。 */
.chat-scope-bar { display: none; }
.discuss.chat-mode .chat-scope-bar {
  flex: none; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 8px 12px; border-bottom: 1px solid var(--line);
  background: #fff8e6; color: #8a6212; font-size: calc(11.5px * var(--fs, 1)); line-height: 1.5;
}
.chat-scope-bar b { font-weight: 700; color: #7a5410; }
.chat-scope-bar span { opacity: .92; }
.discuss.chat-mode .gen-doc-btn { display: none !important; }
/* 闲聊区的消息气泡整体压暗一档，和三个课题分区在视觉上区分开 */
.discuss.chat-mode .scope-tabs button[data-scope="chat"].on { color: #8a6212; border-bottom-color: #d8a13a; }
/* 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); } }

/* ★★ z314（用户要求，附微信截图）：输入框外面也要白的，和聊天区融为一体。
   原来是 var(--panel-soft) 米色 —— 聊天区是白卡，输入区是米色带，
   两条色带之间还夹着一道边框，看着像"两个框拼在一起"。
   现在整块跟聊天区同一个白底，只靠输入框自己的那道细边做分隔（微信就是这个观感）。
   ⚠️ 不要把 --panel-soft 加回来。 */
.composer { border-top: 1px solid var(--line-soft); padding: 10px 12px; background: var(--panel); }
.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; }

/* ---------- z311：强调格式超限（阻断式，文件拒收） ---------- */
.mol-list { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; margin-bottom: 12px; }
.mol-row { display: flex; align-items: center; gap: 10px; padding: 9px 12px; }
.mol-row + .mol-row { border-top: 1px solid var(--line-soft); }
.mol-name { flex: 1; min-width: 0; font-size: calc(13px * var(--fs, 1)); color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mol-num { flex: none; font-size: calc(12.5px * var(--fs, 1)); color: var(--amber); font-variant-numeric: tabular-nums; }
.mol-pct { display: inline-block; margin-left: 6px; padding: 0 7px; border: 1px solid #f0dcb4; border-radius: 999px; background: var(--amber-soft); font-size: calc(11.5px * var(--fs, 1)); color: var(--amber); }
.mol-advice { padding: 10px 12px; border-radius: 10px; background: var(--amber-soft); }
.mol-advice-t { margin-bottom: 4px; font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; color: var(--amber); }
.mol-advice-b { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); line-height: 1.65; }

.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; }
/* z322（用户要求）：闪**两次**——一次太短，眼睛容易错过。
   2 次 × .95s ≈ 1.9s，节奏接近微信「定位到这条」的提示。 */
.msg.flash { animation: msgFlash .95s ease-in-out 2; }
@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); }
.tax-badge.rule.type { background: var(--violet-soft); color: var(--violet); border-color: var(--violet); }   /* z272：类型层 */

/* 文档卡上的「盖章」标识（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); }
.card-stamps .tax-badge.rule.type { background: var(--violet); color: #fff; border-color: var(--violet-soft); }   /* z272：类型层 */

/* 归档类别选择弹窗（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; }
/* z276：解不出来的内嵌图（EMF/WMF 矢量图、超大图）占位块。
   用户报障的"这一段下面是空的"就是它：老实现把标记直接删掉，什么都不留。
   现在明确告诉读者「这儿有图，只是渲染不了」，配色跟随主题。 */
.doc-img-miss {
  display: block; margin: 10px 0; padding: 10px 12px;
  border: 1px dashed var(--line); border-left: 3px solid var(--warn, #c8912b);
  border-radius: 8px; background: var(--line-soft);
  font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); line-height: 1.7;
}

/* 表格：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); }
.has-mouse .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); }
/* z272：第四层「课题类型层」。用紫色（--violet）与课题组(靛紫 #5B6ABF) / 团队(蓝) / 房间(青)
   在色相上拉开距离；具体属于哪一类课题，由行内说明文字写「课题组 · 科学问题型」。 */
.rtag.type { border-color: var(--violet); color: var(--violet); background: var(--violet-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;
}
.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);
  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-ico .ic { display: block; width: calc(17px * var(--fs, 1)); height: calc(17px * var(--fs, 1)); }
.team-space-card .tsc-ico .ic svg { display: block; width: 100%; height: 100%; }
.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; }

/* ============================================================
   z275：团队页「课题类型修改待批」横幅
   ------------------------------------------------------------
   为什么要有这条：团队卡上的「审 N」里含「课题类型修改申请」（roomApvOf 计了 rt），
   但点进团队页以前没有任何审批入口（那批数据只在总览经验页里读）——
   用户反馈「卡片上标着审 N，点进去却哪儿都批不了」。
   颜色一律引用主题变量（--amber / --amber-soft），四套主题会自动跟着换；
   字号走 calc(Npx * var(--fs))，跟着全站字号倍率缩放。
   ============================================================ */
.rt-pend-bar {
  display: flex; align-items: center; gap: 10px; width: 100%;
  margin: 0 0 14px; padding: 10px 14px; text-align: left; font: inherit;
  background: var(--amber-soft, #fdf3e2); color: var(--ink, #1c2b2a);
  border: 1px solid var(--amber, #b8791a); border-left-width: 3px;
  border-radius: var(--radius-sm, 8px); cursor: pointer;
  transition: box-shadow .12s, transform .12s;
}
.rt-pend-bar:hover { box-shadow: 0 2px 10px rgba(0, 0, 0, .07); transform: translateY(-1px); }
.rt-pend-bar .rpb-ico { font-size: calc(16px * var(--fs, 1)); line-height: 1; flex: 0 0 auto; }
.rt-pend-bar .rpb-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.rt-pend-bar .rpb-txt b { font-size: calc(13px * var(--fs, 1)); font-weight: 600; }
.rt-pend-bar .rpb-sub {
  font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3, #6b807e); line-height: 1.45;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rt-pend-bar .rpb-go { flex: 0 0 auto; font-size: calc(12px * var(--fs, 1)); font-weight: 600; color: var(--amber, #b8791a); }
/* 审批弹窗里的申请列表：每条一张卡，别挤在一起 */
.rt-req-list { max-height: min(58vh, 460px); overflow: auto; }
.rt-req-list .rule-row { margin-bottom: 8px; }

/* 批量录入弹窗：一行一条（标题 + 正文 + 删除） */
.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; }

/* z368：AI 私聊（AI 团队会话）用量 —— 用量与积分页里单独一块。
   共享积分池只占一行（大数字 + 进度条 + 一句明细），下面每个 Agent 一行，
   点「明细」就地展开「按成员」四列（姓名 / 次数 / tokens / 积分）。 */
.aip-pool { margin: 2px 0 12px; }
.aip-n { font-size: calc(14px * var(--fs, 1)); color: var(--ink); }
.aip-n b { font-size: calc(23px * var(--fs, 1)); font-weight: 680; letter-spacing: -.4px; }
.aip-n i { font-style: normal; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); margin-left: 5px; }
.aip-bar { height: 6px; border-radius: 999px; background: #e9efec; overflow: hidden; margin: 7px 0 5px; }
.aip-bar i { display: block; height: 100%; border-radius: 999px; background: var(--teal-2); transition: width .3s; }
.aip-bar.warn i { background: var(--amber); }
.aip-bar.over i { background: var(--danger); }
.aip-sub { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.aip-row { border-top: 1px solid var(--line-soft); }
.aip-row:first-of-type { border-top: 0; }
.aip-h { display: flex; align-items: center; gap: 9px; padding: 9px 2px; }
.aip-av {
  width: 26px; height: 26px; border-radius: 50%; flex: none; color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: calc(11.5px * var(--fs, 1)); font-weight: 600;
}
.aip-nm { font-size: calc(13px * var(--fs, 1)); font-weight: 550; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.aip-meta { margin-left: auto; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); white-space: nowrap; }
.aip-h b { font-size: calc(13px * var(--fs, 1)); color: var(--teal); font-variant-numeric: tabular-nums; }
.aip-h .btn-ghost { flex: none; }
.aip-body { padding: 0 2px 8px 35px; }
.aip-empty { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); padding: 10px 2px; margin: 0; }
.aip-mrow {
  display: flex; align-items: center; gap: 10px;
  padding: 5px 9px; border-radius: 8px;
  font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2);
}
.aip-mrow:nth-child(odd) { background: var(--panel-soft); }
.aip-mrow > span:first-child { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.aip-mrow em { font-style: normal; color: var(--ink-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
.aip-mrow b { color: var(--teal); font-variant-numeric: tabular-nums; white-space: nowrap; }

.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); }

/* z259：学生学历标签（博士 / 硕士 / 本科）
   —— 沿用 role-chip 的胶囊外观，但本身是个 select，导师/管理员点开即可改。
   只影响显示，不改变任何权限（权限仍看 app_role）。 */
.deg-sel {
  display: inline-block; padding: 1px 4px 1px 8px; border-radius: 20px;
  font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; font-family: inherit;
  background: var(--line-soft); color: var(--ink-2);
  border: 1px dashed var(--line); outline: none; cursor: pointer;
  vertical-align: middle; max-width: 104px; margin-left: 2px;
  -webkit-appearance: none; appearance: none;
}
.deg-sel.has { background: var(--teal-soft); color: var(--teal); border-style: solid; }
.deg-sel:hover, .deg-sel:focus { border-color: var(--teal); }
.deg-sel:disabled { opacity: .55; cursor: default; }
/* 只读态（普通成员看到的别人的学历）仍用 .role-chip，无需额外样式 */

/* ============================================================
   课题归档 / 删除（归档区、归档详情、打包进度）
   ============================================================ */
.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; }
/* z378：工具使用情况之外单独一行「已生成笔记」chip —— 墨绿系，与灰色工具行区分 */
.note-trace { margin-top: -1px; }
.nt-chip { color: var(--teal); background: var(--teal-soft); border-color: #cfe0da; max-width: min(100%, 320px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ══ z380（用户定稿）：工具 / 笔记 chip 点开看明细 ══
   · chip 从 <span> 改成 <button>：长回答的 .fold-wrap 排除名单里有 button，
     这样点 chip 只开面板，不会顺手把整条回答收起（用户报的就是这个）；
   · 面板 flex-basis:100% 独占一行（父级 .tool-trace 是 flex+wrap）；
   · 工具行与笔记行互斥：同一时刻只开一个（见 app.js 的 document click 捕获）。
   ⚠️ chip 的高度/圆角/字号沿用 .tt-chip，只补 button 复位与 caret。 */
.tt-chip.tt-btn { font-family: inherit; cursor: pointer; user-select: none; display: inline-flex; align-items: center; gap: 4px; -webkit-tap-highlight-color: transparent; }
.tt-chip.tt-btn:active { transform: scale(.97); }
.tt-chip.tt-btn[aria-expanded="true"] { border-color: var(--teal-2); color: var(--ink); background: var(--panel); }
.tt-caret { font-style: normal; font-size: 9px; opacity: .55; display: inline-block; transition: transform .15s; }
.tt-chip.tt-btn[aria-expanded="true"] .tt-caret { transform: rotate(180deg); opacity: .9; }
.tt-panel { flex: 0 0 100%; width: 100%; margin-top: 6px; padding: 9px 11px; background: var(--panel-soft); border: 1px solid var(--line-soft); border-radius: 9px; font-size: calc(12px * var(--fs, 1)); line-height: 1.65; color: var(--ink-2); }
.tt-panel[hidden] { display: none; }
.tt-panel-note { border-color: #cfe0da; }
.ttp-hd { font-size: calc(11.5px * var(--fs, 1)); font-weight: 600; color: var(--ink-2); margin-bottom: 6px; }
.ttp-hx { font-weight: 500; color: var(--ink-3); margin-left: 4px; }
.ttp-list { margin: 0; padding: 0; list-style: none; }
.ttp-row { display: flex; gap: 7px; padding: 4px 0; align-items: flex-start; }
.ttp-row + .ttp-row { border-top: 1px dashed var(--line-soft); }
.ttp-no { flex: 0 0 auto; min-width: 15px; text-align: right; color: var(--ink-3); font: 500 calc(10.5px * var(--fs, 1))/1.9 ui-monospace, SFMono-Regular, Consolas, monospace; }
.ttp-main { flex: 1 1 auto; min-width: 0; }
.ttp-t { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px; color: var(--ink-2); font-weight: 600; word-break: break-word; }
.ttp-sep { font-style: normal; color: var(--ink-3); font-weight: 400; }
.ttp-target { font-weight: 400; color: var(--ink-2); }
.ttp-d { color: var(--ink-3); font-size: calc(11px * var(--fs, 1)); word-break: break-word; }
/* z475：「已拦截·未取正文」—— 原文已在本轮上下文里，这次工具调用被系统拦回，没取到正文。
   旧版这里标的是「上下文注入 ✓」，但载荷里没有返回内容，被拦回的调用也会被误标（见 app.js）。 */
.ttp-block { font-style: normal; font-size: calc(10px * var(--fs, 1)); font-weight: 600; color: var(--ink-3); background: var(--line-soft); border-radius: 4px; padding: 0 5px; white-space: nowrap; }
.ttp-err { font-style: normal; font-size: calc(10px * var(--fs, 1)); font-weight: 600; color: var(--danger); background: var(--danger-soft); border-radius: 4px; padding: 0 5px; }
.ttp-row.is-err .ttp-t { color: var(--danger); }
.ttp-ft { margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--line-soft); color: var(--ink-3); font-size: calc(11px * var(--fs, 1)); }
/* 笔记面板 */
.ntp-item { padding: 5px 0; }
.ntp-item + .ntp-item { border-top: 1px dashed var(--line-soft); }
.ntp-name { font-weight: 600; color: var(--ink); word-break: break-word; }
.ntp-meta { color: var(--ink-3); font-size: calc(11px * var(--fs, 1)); }
.ntp-more { font-family: inherit; font-size: calc(11px * var(--fs, 1)); color: var(--teal-2); background: none; border: 0; padding: 2px 0; cursor: pointer; }
.ntp-more:hover { text-decoration: underline; }
.ntp-sum { margin-top: 4px; padding: 6px 9px; background: var(--panel); border: 1px solid var(--line-soft); border-radius: 7px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); line-height: 1.7; }
.ntp-sum[hidden] { display: none; }
.ntp-seg { padding: 3px 0; word-break: break-word; }
.ntp-seg + .ntp-seg { border-top: 1px dashed var(--line-soft); }
.ntp-seg em { font-style: normal; color: var(--ink-3); font-size: calc(10.5px * var(--fs, 1)); margin-right: 5px; }
.ntp-empty { color: var(--ink-3); }
/* z285：中间轮正文归档（模型决定去读文件之前写的那段过渡话）。
   默认收起，只留一行「过程 · N 段」；正文区只显示最终答案，避免"文字闪一下又消失"。 */
.proc-fold { margin: 2px 0 8px; font-size: calc(12.5px * var(--fs, 1)); }
.proc-fold > summary { cursor: pointer; color: var(--ink-3); list-style: none; padding: 2px 0; user-select: none; }
.proc-fold > summary::-webkit-details-marker { display: none; }
.proc-fold > summary::before { content: '▸ '; }
.proc-fold[open] > summary::before { content: '▾ '; }
.proc-fold > summary:hover { color: var(--ink-2); }

/* ══ z303（用户要求）：正文最前面的「本轮依据」引用块 → 默认折叠 ══
   它本来就是一段引用块，所以保留左侧竖线的"引用"语义，只把内容收起来。 */
.basis-fold {
  margin: 2px 0 10px;
  padding: 1px 10px 1px 11px;
  border-left: 3px solid var(--line);
  border-radius: 0 6px 6px 0;
}
.basis-fold > summary {
  /* ⚠️ z304（用户报「点击展开按钮有问题」）：**不能用 display:flex**。
     iOS WebKit（微信内置浏览器 / Safari）下，flex 化的 <summary> 会丢掉原生的
     展开/收起行为，点上去经常没反应；而且 flex 把「点击展开」推到最右，
     手指容易点不到。改回原生 list-item（最稳的 toggle 语义），
     右侧提示用绝对定位，箭头用 ::before 内联，整行都是热区。 */
  display: list-item;
  position: relative;
  list-style: none;
  cursor: pointer; user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  min-height: 30px;
  padding: 8px 64px 8px 0;
  line-height: 1.45;
  font-size: calc(12.5px * var(--fs, 1));
  color: var(--ink-3);
}
.basis-fold > summary::-webkit-details-marker { display: none; }
.basis-fold > summary::marker { content: ''; }
.basis-fold > summary::before { content: '▸ '; color: var(--ink-3); }
.basis-fold[open] > summary::before { content: '▾ '; }
.basis-fold > summary:hover { color: var(--ink-2); }
.basis-fold .bf-title { font-weight: 600; color: var(--ink-2); }
.basis-fold .bf-hint {
  position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); opacity: .75;
}
/* z304：展开后不再是「什么提示都没有」，明确写「收起」，用户知道能点回去 */
.basis-fold .bf-hint .bf-close { display: none; }
.basis-fold[open] .bf-hint .bf-open { display: none; }
.basis-fold[open] .bf-hint .bf-close { display: inline; }
.basis-fold .basis-body {
  margin: 2px 0 8px;
  padding-top: 6px;
  border-top: 1px dashed var(--line);
  font-size: calc(12.5px * var(--fs, 1));
  color: var(--ink-2);
}
.basis-fold .basis-body p { margin: 5px 0; }
.basis-fold .basis-body ul { margin: 5px 0; padding-left: 18px; }
.proc-seg { margin: 6px 0 6px 8px; padding-left: 10px; border-left: 2px solid var(--line); color: var(--ink-3); }
.proc-seg p { margin: .35em 0; }
.proc-seg .md-h, .proc-seg h3, .proc-seg h4 { font-size: calc(13px * var(--fs, 1)); }
/* z144「本轮依据」：刻意与 🔧 工具标签**长得不一样**（虚线边 + 前缀标题）——
   用户一眼要能分清「系统自动给的料」和「Agent 主动拉的料」，这是本需求的核心。 */
.src-trace { margin-bottom: 8px; }
/* z341（用户定稿）：整块默认折叠，只有一行「本轮系统注入 X 类 X 条，AI 工具读取 X 类 X 条」。
   · 整行都是按钮（手机上点得中），▸ 展开后转 90° 变 ▾；
   · 颜色从 --ink-3 提到 --ink-2 —— 用户原话「标题行的字体颜色太浅了」。 */
.src-fold-hd { flex: 0 0 100%; display: flex; align-items: center; gap: 6px; text-align: left;
  font-family: inherit; font-size: calc(11.5px * var(--fs, 1)); font-weight: 600; line-height: 1.6;
  color: var(--ink-2); background: var(--line-soft); border: 1px solid var(--line);
  border-radius: 8px; padding: 3px 10px; cursor: pointer; }
.src-fold-hd b { font-weight: 600; }
.src-fold-hd:hover { border-color: var(--teal-2); color: var(--teal-2); }
.src-fold-ic { font-style: normal; font-size: 10px; color: var(--ink-2); flex: 0 0 auto; transition: transform .16s; }
.src-trace:not(.is-folded) .src-fold-ic { transform: rotate(90deg); }
.src-trace.is-folded .src-body { display: none; }
.src-body { flex: 0 0 100%; margin-top: 9px; }
.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); }
/* z245：「N 项不全」—— 用琥珀色点出来，替代模型以前在正文开头写的"我读到的范围" */
.src-part { font-style: normal; color: var(--amber); font-weight: 600; }
/* z249（用户要求「太占地方了，改成点击后查看」）：
   原来一段白话明细**常驻**展开，手机上七八个文件名糊成一大块几乎占满屏幕。
   现在明细按类别折叠，chip 变成按钮，点它才展开「到底是哪几条」。
   ⚠️ .src-trace 同时带 .tool-trace（flex + wrap），所以面板必须 flex-basis:100% 独占一行，
      否则会被塞到 chip 右侧挤成一条。 */
.src-chip { font-family: inherit; cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent; }
.src-chip:active { transform: scale(.97); }
.src-chip[aria-expanded="true"] { border-style: solid; border-color: var(--teal-2); color: var(--ink); background: var(--panel); }
.src-caret { font-style: normal; margin-left: 4px; font-size: 9px; opacity: .55; display: inline-block; transition: transform .15s; }
.src-chip[aria-expanded="true"] .src-caret { transform: rotate(180deg); opacity: .9; }
.src-pop { flex: 0 0 100%; width: 100%; margin-top: 7px; padding: 8px 11px; background: var(--panel-soft); border: 1px solid var(--line-soft); border-radius: 9px; font-size: calc(12px * var(--fs, 1)); line-height: 1.7; color: var(--ink-2); }
.src-pop[hidden] { display: none; }
.src-pop-hd { color: var(--ink-3); font-size: calc(11.5px * var(--fs, 1)); font-weight: 600; margin-bottom: 4px; }
.src-pop-list { margin: 0; padding: 0; list-style: none; }
.src-pop-list li { padding: 3px 0; word-break: break-word; }
.src-pop-list li + li { border-top: 1px dashed var(--line-soft); }
.src-pop .src-dt { font-style: normal; color: var(--ink-3); font-size: calc(11px * var(--fs, 1)); }
/* ⚠️ z337：**不要再给 detail 套一层括号**。新口径的 detail 自带
   「（来源·时间 动词）（字段 ｜ 字段）」两个括号，再用伪元素套就是三层嵌套。
   旧消息（detail 没带括号）由 srcTraceHtml 渲染时补上，见那里的 dtWrap。 */
.src-pop .src-nf { color: var(--amber); font-weight: 600; margin-left: 5px; font-size: calc(11px * var(--fs, 1)); }
/* z288：明细里已经写出「只读了 N 字符」的，用橙色标这段明细本身（不再叠「没读全」角标）。
   ⚠️ 不能直接复用 .src-nf —— 它带 margin-left 与固定字号，会把括号里的明细挤歪。 */
.src-pop .src-dt.nf { color: var(--amber); font-weight: 600; }
.src-pop .src-more { color: var(--ink-3); font-size: calc(11.5px * var(--fs, 1)); }
.src-pop .src-eid { display: inline-block; margin-left: 6px; padding: 0 4px; border: 1px solid var(--line-soft); border-radius: 4px; color: var(--ink-3); background: var(--panel); font: 500 calc(10px * var(--fs, 1))/1.6 ui-monospace, SFMono-Regular, Consolas, monospace; word-break: break-all; }
.src-pop .src-eid.legacy { border-style: dashed; font-family: inherit; }
.src-pop .src-warn { color: var(--amber); font-weight: 600; margin-top: 5px; padding-top: 5px; border-top: 1px dashed var(--line-soft); }
/* chip 里的「N 项不全」前留一点空隙，否则和「3 条」黏在一起 */
.src-chip .src-part { margin-left: 4px; }
/* z332（用户要求「标签卡补一条灰条」）：说明"这轮没随附什么" —— 不是注入记录，所以
   做成一条浅灰说明条（不设虚线边、不可点），与上面那些"注入了几条"的 chip 明显区别。
   ⚠️ 独占一行：紧跟 chip 时挤在旁边会被误读成又一个分类。
   ⚠️ 底色用 --line-soft，不用 --panel-soft：后者在 :root 里是自引用（`var(--panel-soft)`），
      算不出来就是透明，灰条会变成一行裸字。 */
.src-gray { flex: 0 0 100%; display: block; margin-top: 4px; padding: 3px 9px;
  border-radius: 7px; background: var(--line-soft); color: var(--ink-3);
  font-size: calc(11.5px * var(--fs, 1)); line-height: 1.7; }
/* z334（用户要求）：把原来混装的一行 chip 拆成两段 —— 本轮系统注入 / 本轮 AI 工具读取。
   「谁给的」一眼可分，不再靠 kind 名去猜（截图里"聊天附件 2 条"混着一条工具读出来的就是这个问题）。
   ⚠️ .src-trace 同时带 .tool-trace（flex + wrap），所以每一段必须 flex-basis:100% 独占一行，
      段内再自己 flex 排 chip。 */
.src-grp { flex: 0 0 100%; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
/* z341：两段**并列**，段间距 14px，**不画贯通竖线** ——
   那条线从系统注入顶部一直拉到 AI 工具读取末尾，用户会读成「AI 工具读取是系统注入的子类目」。
   现在只靠各自的色块标题区分归属。 */
.src-grp + .src-grp { margin-top: 14px; }
.src-grp-hd { flex: 0 0 auto; font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-2);
  font-weight: 700; letter-spacing: .3px; background: #e2eae7; border-radius: 5px;
  padding: 1px 8px; margin-right: 2px; }
/* 工具读取那段用主色标一下 —— 它是 AI 自己发起的动作，和"系统给的料"性质不同 */
.src-grp-tool .src-grp-hd { background: #d9e9e4; color: var(--teal-2); }
.src-grp .src-pop { flex: 0 0 100%; }
.src-grp .src-gray { margin-top: 0; }
.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; }

/* z457：「‹ › ⟳ ⋮」四个键的容器。
   桌面端（≥900px）把它画成一条控制条；窄屏必须"透明"——
   display:contents 让子元素仍按 .me 的 flex 直接排布，与 z457 之前一模一样。 */
.nav-bar { display: contents; }
.nav-bar .nav-sep { display: none; }

/* ============================================================
   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` 藏掉了（用户反馈「以前一直有，调了顶部样式就没了」）。
     z338：使用说明 / 注读说明已在手机端搬进 ⋮ →「更多」整页，这里明确隐藏
     （桌面端不受影响：桌面恢复成顶栏上两个按钮，用户要求「电脑端首页恢复原来的样子」）。 */
  .brand-inline { background: transparent; border-color: transparent; padding: 2px 0; gap: 6px; }
  .brand-inline:hover { background: transparent; border-color: transparent; }
  .topbar .me #guideBtn, .topbar .me #noteBtn { display: none; }
  .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, border-color .12s ease; }
.stat-clickable:hover { transform: translateY(-1px); box-shadow: 0 3px 10px rgba(0, 0, 0, .10); border-color: var(--teal-2); }
/* z234：删掉原来的 `::after { content:'▸' }` —— .n 与 .l 都是块级元素，
   那个箭头会掉到第二行、孤零零挂在数字下方像一颗多余的污点（桌面截图实测）。
   现在改用 hover 变描边提示「能点」，更克制也不占位。 */
.pend-list { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 4px; }
/* z433：未读消息页里的「审批通知」分组标题 —— 与下面的房间行分开，
   否则用户分不清哪条是审批、哪条是房间消息。 */
.unread-sec-h {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 6px 0 2px; font-size: calc(14.5px * var(--fs, 1)); font-weight: 700; color: var(--ink-2);
}
.unread-sec-h span { font-weight: 400; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); }
/* z444：未读页按来源分区（审批通知 / 课题讨论区 / 公共撰写区）——
   段与段之间要拉开，否则三个标题挤在一起又混成一锅；第一段不留额外上边距。 */
.unread-sec-h.unread-sec-gap { margin-top: 20px; }
.unread-sec-h:first-child { margin-top: 2px; }
/* z435：未读页标题里的「· 审批通知 N」—— 两个数字分开显示，避免被误读成一个总数 */
.page-head h2 .unread-sec-n {
  font-style: normal; font-weight: 400; margin-left: 4px;
  font-size: calc(13px * var(--fs, 1)); color: var(--ink-3);
}
.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; }
/* z464：分类行动作下的「明细子行」—— 只是让人看清是哪几份，不给按钮 */
.pend-sub-note { margin-left: 18px; padding: 7px 10px; }
.pend-sub-note .pr-l b { font-size: calc(12.5px * var(--fs, 1)); font-weight: 500; color: var(--ink-2); }
.pend-sub-note .pr-l span { font-size: calc(11px * var(--fs, 1)); }
.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; }

/* ============================================================
   z234：我的房间 / 通知公告两页
   ------------------------------------------------------------
   版式上刻意与「待我处理」「未读消息」保持同一套骨架（.pend-row / .pend-list），
   用户在清单类页面之间切换时不用重新学一遍怎么读。
   ============================================================ */
.pend-row[data-room] { cursor: pointer; }
.pend-row[data-room]:hover { border-color: var(--teal-2); }
.mine-sec {
  font-size: calc(12px * var(--fs, 1)); font-weight: 700; letter-spacing: .04em;
  color: var(--ink-3); margin: 18px 0 2px; text-transform: none;
}
.mine-sec:first-of-type { margin-top: 8px; }
.page-head .ph-acts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }

.board-sec { margin-bottom: 22px; }
.board-sec-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.board-sec-head h3 { font-family: var(--serif); font-size: calc(15px * var(--fs, 1)); margin: 0; }
.board-sec-head span { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.notice-item {
  border: 1px solid var(--line); border-radius: 11px; background: var(--panel);
  padding: 11px 13px; margin-bottom: 7px;
}
.ni-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ni-head b { font-size: calc(14px * var(--fs, 1)); }
.ni-state { font-size: calc(10.5px * var(--fs, 1)); padding: 1px 6px; border-radius: 5px; }
.ni-state.pend { color: var(--amber); background: var(--amber-soft); }
.ni-state.rej { color: #c0392b; background: #fdecea; }
.ni-body {
  font-size: calc(13px * var(--fs, 1)); color: var(--ink-2); line-height: 1.7;
  white-space: pre-wrap; word-break: break-word; margin-top: 5px; cursor: pointer;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden;
}
.ni-body.open { display: block; -webkit-line-clamp: unset; overflow: visible; }
.ni-note {
  margin-top: 6px; padding: 6px 9px; border-radius: 8px;
  background: var(--amber-soft); color: var(--amber); font-size: calc(12px * var(--fs, 1));
}
.ni-meta { margin-top: 6px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.ni-acts { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.board-empty {
  border: 1px dashed var(--line); border-radius: 11px; padding: 18px 14px;
  text-align: center; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3);
}

/* ============================================================
   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);
  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）。 */
  /* z234 手机端版式（用户明确指定）：
       「通知公告动态卡片位于最顶端，我的房间 / 待我处理 / 未读消息三个卡片
         位于动态卡片下面且同一行。」
     → 通知卡 flex-basis 100% 独占第一行（order:-1 保证它在最前，不依赖 DOM 顺序），
       三张窄卡 flex:1 平分第二行。全部用 minmax(0,1fr) 那套思路（红线 115：
       绝不写 1fr，否则内容会把格子撑破）。 */
  .stat-row {
    display: flex; flex-wrap: wrap;
    gap: 7px; margin-bottom: 14px;
  }
  /* ⚠️ 必须写 `.stat.stat-notice`（两个类），不能只写 `.stat-notice`：
     外层那条 `flex: 1 1 auto` 是 0,2,0 特异性，单类选择器（0,1,0）压不过它 ——
     实测症状就是「手机上四张卡挤在同一行、通知卡只有 175px」。
     这条坑与红线 115 同类：CSS 特异性也是「谁更具体谁赢」，跟书写顺序无关。 */
  .stat.stat-notice { flex: 1 1 100%; order: -1; --sn-px: 10px; --sn-py: 7px; padding: 0; }
  .stat-row > .stat:not(.stat-notice) { flex: 1 1 0; min-width: 0; }
  .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; }
  .sn-tag { font-size: calc(10px * var(--fs, 1)); padding: 0 4px; }
  .sn-t { font-size: calc(12.5px * var(--fs, 1)); }
  .sn-d { font-size: calc(10.5px * var(--fs, 1)); }
  /* z233：原来这里靠 `nth-child(5) { display:none }` 把第 5 项（未读消息）藏掉，
     等于手机上永远看不到未读 —— 用户明确否掉了这个做法。
     z234：卡片集合改为「通知公告 + 我的房间 + 待我处理 + 未读消息」，
     1 + 3 的版式下没有任何一项被隐藏。 */
  .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); }
  /* ★ z324：底部内边距 4→10px。原来最后一条消息的行（尤其已读回执那行）
     与输入区只隔 4px，视觉上就是"被输入框压住"。 */
  .msgs { flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto; padding: 12px 12px 10px; }
  .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 {
    /* z314：四宫格 → 五宫格（最后一格是「聊天记录」）。手机 390 宽下每格约 73px，
       11px 的「课题文件」刚好一行放得下。
       z338（用户原话「已经有成员墙了，就不需要成员卡片了」）：去掉「成员」格 → 回到 4 格
       （课题文件 / 知识库 / 经验 / 聊天记录），每格宽回 90px，图标与文字都更松快。
       待审批数字已挪到成员墙标题行（见 drawerHomeHtml 的 .dw-todo）。 */
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    background: var(--panel); margin: 0 12px 10px; padding: 13px 2px;
    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); }
  /* z340：右侧说明必须单行 —— 中文在 flex 项里会被压到「一字一行」（用户报障"积分用量被挤成纵向排列"） */
  .dw-list .rw .rt { color: var(--ink-3); font-size: calc(12.5px * var(--fs, 1)); display: flex; align-items: center; gap: 4px; white-space: nowrap; }
  .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 顶到房间头正下方。 */
  /* z345：原来这里写 `background: var(--panel) !important` —— sticky 早在 z95 就按
     「手机端不要 sticky」去掉了（见后面 2765 行那段），这条 !important 只剩下副作用：
     它会把手机端页签的底色死死钉成白色，z345 的浅灰底槽永远盖不上（实测就是白条）。
     现在只留 z-index，底色交给 z345。 */
  .scope-tabs { z-index: 13; }
  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); }

/* z271：⋮ 全端可用后，桌面端不能再用「底部整宽抽屉」（2400px 宽的面板糊在屏幕底下）。
   桌面改成贴着 ⋮ 的右上角浮层；手机端仍是底部抽屉（下面的 ≤620px 规则不动）。
   ⚠️ 只限 #moreSheet —— `.sheet` 还被「+」附件面板复用，那个在桌面端本来就不出现，
      但用 id 限定可以保证将来桌面端开附件面板时不会跟着变形。 */
@media (min-width: 621px) {
  #moreSheet {
    left: auto; right: 18px; top: 58px; bottom: auto;
    width: 252px; border-radius: 14px;
    padding: 10px 8px;
    transform: translateY(-6px) scale(.98); opacity: 0;
    transition: transform .16s ease, opacity .16s ease;
    box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
  }
  #moreSheet.on { transform: translateY(0) scale(1); opacity: 1; }
  #moreSheet .sheet-head { padding: 4px 8px 10px; }
  #moreSheet .sheet-cancel { margin-top: 8px; }
}
/* 菜单里「密聊」那一行的未读数字 */
.sheet-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 6px; margin-left: 8px;
  border-radius: 9px; background: var(--danger); color: #fff;
  font-size: calc(11px * var(--fs, 1)); font-weight: 600; vertical-align: 1px;
}
.sheet-row .sheet-badge:empty { display: none; }
/* z307：菜单里「检查更新」右侧的当前版本号（只显示尾段 z307，与顶栏窄屏口径一致） */
.sheet-ver {
  float: right; margin-left: 8px;
  font-family: var(--mono); font-size: calc(11px * var(--fs, 1));
  color: var(--ink-4); background: var(--line-soft);
  padding: 2px 6px; border-radius: 5px; line-height: 1.2;
}

@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; }
/* z236：论文手稿（蓝灰，草稿感）/ 发表论文（正红，正式成果）—— 两类刻意用不同色阶，
   一眼能分清"还在写"和"已发表" */
.file-row .fr-cat.c-manuscript { background: #eef2f8; color: #40567a; border-color: #d3ddeb; }
.file-row .fr-cat.c-published { background: #fdecec; color: #9b3232; border-color: #f4cfcf; }
/* z236：课题文件面板顶部的「直接上传论文」两个按钮（手稿 / 发表论文） */
.paper-up-row { display: flex; gap: 8px; margin: 4px 0 8px; }
.paper-up-row .btn-ghost { flex: 1 1 0; min-width: 0; justify-content: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.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); }
/* z238：待确认卡片上方的一句说明（「提交人已附资料卡，可直接确认 / ✎ 改 / 不要资料卡」）
   —— 与 .lit-warn 的琥珀告警区分开，这是中性提示。 */
.lit-note { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); background: var(--panel-soft); border: 1px dashed var(--line); border-radius: 7px; padding: 4px 7px; margin: 5px 0 0; line-height: 1.6; }
.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;
}
/* z258：attach 不再存 base64，缩略图改从云端原件签名加载 —— 加载中/失败时的占位 */
.fcard-thumb.thumb-ph {
  background: rgba(127, 127, 127, .14);
  animation: thumbPulse 1.4s ease-in-out infinite;
}
@keyframes thumbPulse {
  0%, 100% { opacity: .55; }
  50% { opacity: 1; }
}
.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;
}
/* z315：知识库「归属课题类型」—— 上传区下拉 / 已入库分档 chips / 条目角标 / 队列里的归属提示 */
.kb-rt-line { display: flex; align-items: center; gap: 6px; margin: 0 0 8px; flex-wrap: wrap; }
.kb-rt-sel {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: calc(12px * var(--fs, 1)); color: var(--ink-2);
}
.kb-rt-sel > span { color: var(--ink-3); }
.kb-rt-sel select {
  font: inherit; font-size: calc(12px * var(--fs, 1));
  padding: 4px 8px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel); color: var(--ink); max-width: 190px;
}
.kb-rt-filter { margin: 0 0 10px; }
.kb-rt-filter button { font-size: calc(11.5px * var(--fs, 1)); }
.kb-badge.rt { background: var(--rts, var(--teal-soft)); color: var(--rtc, var(--teal)); }
.kb-badge.rt.science { --rtc: var(--c-science); --rts: var(--c-science-soft); }
.kb-badge.rt.major   { --rtc: var(--c-major);   --rts: var(--c-major-soft); }
.kb-badge.rt.minor   { --rtc: var(--c-minor);   --rts: var(--c-minor-soft); }
.kb-badge.rt.extend  { --rtc: var(--c-extend);  --rts: var(--c-extend-soft); }
.kb-q-row .qk-rt, .kb-queue-head .q-rt {
  flex-shrink: 0; font-weight: 400; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3);
  margin-left: auto;
}
.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); 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);
}

/* ============================================================
   z242：使用说明 / 注读说明 —— 页面级（#/guide、#/note）
   文档整份塞进 iframe.srcdoc，主应用与文档的样式完全隔离
   ============================================================ */
.docpage-wrap{max-width:1100px;margin:0 auto;padding:8px 14px 0}
.docpage-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:6px 2px 10px}
.docpage-ttl{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.docpage-ttl b{font-size:15px;letter-spacing:-.1px}
.docpage-ttl small{font-size:11.5px;color:var(--ink-3);font-weight:500}
.docpage-acts{margin-left:auto;display:flex;gap:6px;flex-wrap:wrap}
.doc-frame{width:100%;height:calc(100dvh - 172px);min-height:420px;display:block;
  border:1px solid var(--line);border-radius:var(--radius);background:#fff;
  box-shadow:0 1px 3px rgba(28,43,42,.05)}
.docpage-foot{text-align:center;font-size:11.5px;color:var(--ink-3);padding:9px 0 6px}
@media(max-width:620px){
  .docpage-wrap{padding:6px 8px 0}
  .docpage-ttl small{display:none}
  .doc-frame{height:calc(100dvh - 196px);min-height:360px;border-radius:var(--radius-sm)}
  .docpage-acts .btn-ghost{padding:4px 9px}
}

/* ============================================================
   z243：版本落后提示条
   ------------------------------------------------------------
   场景：标签页一直挂着不关（手机 PWA 后台、电脑上开一整天），
   跑的还是刚打开时加载进内存的旧 JS，用户会以为「改了没生效」。
   这里只出一条细条提示 + 一个「立即刷新」按钮，**绝不自动刷新**
   （z202 的教训：自动 reload 会把用户正在写的内容冲掉）。

   为什么放在 .topbar 内部：顶栏是 position:sticky，提示条作为它的
   一整行（flex-basis:100%）会自然地贴在顶栏下沿，滚动时始终可见。
   ============================================================ */
.update-bar {
  flex: 0 0 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 3px 0 0;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  background: var(--amber-soft);
  border: 1px solid rgba(184, 121, 26, .28);
  color: var(--amber);
  font-size: calc(12.5px * var(--fs, 1));
}
.update-bar.hidden { display: none; }
.update-bar .ub-text { flex: 1; min-width: 0; }
.update-bar .ub-go {
  flex: none;
  border: 0;
  border-radius: 6px;
  padding: 3px 10px;
  background: var(--amber);
  color: #fff;
  font-size: inherit;
  font-family: inherit;
  cursor: pointer;
}
.update-bar .ub-go:hover { filter: brightness(1.08); }
.update-bar .ub-x {
  flex: none;
  border: 0;
  background: transparent;
  color: inherit;
  opacity: .65;
  font-size: calc(16px * var(--fs, 1));
  line-height: 1;
  padding: 0 2px;
  cursor: pointer;
}
.update-bar .ub-x:hover { opacity: 1; }

/* z243：说明文档从服务端按需取，中间那一下的占位（约 100~200ms，避免闪一下空白 iframe） */
.doc-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 240px;
  color: var(--ink-3);
  font-size: calc(13px * var(--fs, 1));
}
.doc-loading.hidden { display: none; }

/* ============================================================
   z260：统计页（Run 考核）+ 豁免
   ------------------------------------------------------------
   全部用主题变量，不写死颜色 —— 四套外观主题（青瓷/陶土/杏茶/晚樱/暖竹）
   会自动跟着变，不用为每套主题各写一遍。
   ============================================================ */
.page-sub { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); margin-top: 2px; }

/* 周 / 月 / 年 切换 */
.stats-tabs { display: inline-flex; gap: 4px; padding: 4px; background: var(--teal-soft); border-radius: 10px; margin-bottom: 12px; }
.stats-tabs button { border: 0; background: transparent; padding: 6px 16px; border-radius: 8px;
  font-size: calc(13px * var(--fs, 1)); color: var(--teal); cursor: pointer; font-family: inherit; }
.stats-tabs button.on { background: var(--panel); color: var(--ink); font-weight: 600; box-shadow: 0 1px 3px rgba(15, 92, 84, .12); }

/* 周期条（含"进行中 / 已结束"与剩余时间） */
.stats-period { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }
.stats-period .sp-main { display: flex; align-items: baseline; gap: 10px; font-size: calc(13.5px * var(--fs, 1)); }
.stats-period .sp-main b { font-family: var(--mono); font-weight: 600; }
.stats-period .sp-done { color: var(--ink-3); font-size: calc(12px * var(--fs, 1)); }
.stats-period .sp-live { color: var(--amber); font-size: calc(12px * var(--fs, 1)); }

/* 统计卡一行会放 5 个，窄屏必须换行（.stat-row 默认不 wrap） */
.stat-row.wrap { flex-wrap: wrap; }

/* 学历卡 */
.deg-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
.deg-card { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 12px 14px; background: var(--panel); }
.deg-card .dc-h { display: flex; justify-content: space-between; align-items: baseline; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); }
.deg-card .dc-h b { color: var(--ink); font-size: calc(14px * var(--fs, 1)); }
.deg-card .dc-main { margin-top: 6px; display: flex; align-items: baseline; gap: 6px; }
.deg-card .dc-ok { font-family: var(--serif); font-size: calc(28px * var(--fs, 1)); line-height: 1; color: var(--teal); }
.deg-card .dc-of { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); }
.deg-card .dc-bar { height: 4px; background: var(--line-soft); border-radius: 3px; margin: 8px 0 6px; overflow: hidden; }
.deg-card .dc-bar i { display: block; height: 100%; background: var(--teal-2); border-radius: 3px; }
.deg-card .dc-sub { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.deg-card.dc-phd { border-left: 3px solid var(--violet); }
.deg-card.dc-master { border-left: 3px solid var(--blue); }
.deg-card.dc-bachelor { border-left: 3px solid var(--moss); }
.deg-card.dc-unknown { border-left: 3px solid var(--amber); }

/* 统计表 */
.stats-tbl { width: 100%; border-collapse: collapse; font-size: calc(12.5px * var(--fs, 1)); }
.stats-tbl th, .stats-tbl td { padding: 7px 8px; border-bottom: 1px solid var(--line-soft); text-align: left; vertical-align: middle; }
.stats-tbl th { color: var(--ink-3); font-weight: 500; font-size: calc(11.5px * var(--fs, 1)); white-space: nowrap; }
.stats-tbl tbody tr:hover { background: var(--line-soft); }
.stats-tbl .num { text-align: right; font-family: var(--mono); white-space: nowrap; }
.stats-tbl .dim { color: var(--ink-3); }
.stats-tbl .nm-cell { font-weight: 500; white-space: nowrap; }
.stats-tbl .act-cell { text-align: right; white-space: nowrap; }

/* 状态胶囊 */
.sg { display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: calc(11px * var(--fs, 1)); white-space: nowrap; }
.sg-ok { background: var(--teal-soft); color: var(--teal); }
.sg-bad { background: var(--danger-soft); color: var(--danger); }
.sg-exempt { background: var(--amber-soft); color: var(--amber); }
.sg-new { background: var(--blue-soft); color: var(--blue); }
.sg-leader { background: var(--violet-soft); color: var(--violet); }
.mini-tag { margin-left: 5px; padding: 0 5px; border-radius: 4px; background: var(--violet-soft); color: var(--violet); font-size: calc(10px * var(--fs, 1)); }

/* 行内小按钮（比 .sm 再小一档，用于名单里的「豁免」） */
.btn-ghost.xs, .btn-primary.xs { padding: 2px 8px; font-size: calc(11.5px * var(--fs, 1)); }
.field-row { display: flex; gap: 10px; }
.field-row .field { flex: 1; min-width: 0; }

@media (max-width: 720px) {
  .stats-tbl { display: block; overflow-x: auto; white-space: nowrap; }
  .deg-grid { grid-template-columns: repeat(2, 1fr); }
  .stats-period .sp-main { flex-direction: column; gap: 2px; align-items: flex-start; }
}

/* ============================================================
   z263 · 密聊
   ------------------------------------------------------------
   两个视觉要求：① 会话里**不出现**任何姓名/头像，只有代号；
   ② 画面上叠一层几乎看不见的水印（截图带出去也能对上人）。
   ============================================================ */
.sec-page { max-width: 860px; margin: 0 auto; }
.sec-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.sec-head h2 { margin: 0 0 4px; font-size: calc(19px * var(--fs, 1)); }
.sec-head .hint { margin: 0; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }
.sec-acts { display: flex; align-items: center; gap: 10px; }
.sec-tip { font-size: calc(12px * var(--fs, 1)); color: var(--amber); background: var(--amber-soft); padding: 3px 8px; border-radius: 999px; }

.sec-list { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.sec-row { display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 12px 14px; background: none; border: 0; border-bottom: 1px solid var(--line-soft); cursor: pointer; }
.sec-row:last-child { border-bottom: 0; }
.sec-row:hover { background: var(--bg); }
.sec-av { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto;
  background: repeating-linear-gradient(45deg, var(--line) 0 4px, transparent 4px 8px); border: 1px solid var(--line); }
.sec-row-main { flex: 1 1 auto; min-width: 0; }
.sec-row-nm { display: block; font-size: calc(14px * var(--fs, 1)); color: var(--ink); font-weight: 500; }
.sec-row-sub { display: block; margin-top: 2px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }

.sec-people { max-height: 46vh; overflow-y: auto; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.sec-p { display: flex; justify-content: space-between; gap: 10px; width: 100%; padding: 9px 12px;
  background: none; border: 0; border-bottom: 1px solid var(--line-soft); cursor: pointer; text-align: left; }
.sec-p:hover { background: var(--teal-soft); }
.sec-p span { font-size: calc(13px * var(--fs, 1)); color: var(--ink); }
.sec-p i { font-style: normal; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }

/* ---- 会话页 ---- */
.sec-chat { position: relative; display: flex; flex-direction: column;
  height: calc(100dvh - 190px); min-height: 420px; overflow: hidden;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); }
.sec-bar { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-bottom: 1px solid var(--line);
  background: var(--panel); position: relative; z-index: 2; }
.sec-title { font-size: calc(15px * var(--fs, 1)); font-weight: 500; color: var(--ink); }
.sec-sublabel { font-size: calc(11px * var(--fs, 1)); color: var(--violet); background: var(--violet-soft); padding: 2px 8px; border-radius: 999px; }
.sec-feed { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 14px 14px 6px; display: flex; flex-direction: column; gap: 10px;
  position: relative; z-index: 1; }
.sec-feed.sc-blur .sc-bubble { filter: blur(7px); }
.sc-msg { max-width: 78%; display: flex; flex-direction: column; gap: 3px; }
.sc-msg.me { align-self: flex-end; align-items: flex-end; }
.sc-msg.them { align-self: flex-start; align-items: flex-start; }
.sc-bubble { padding: 8px 11px; border-radius: 12px; font-size: calc(14px * var(--fs, 1)); line-height: 1.55;
  white-space: pre-wrap; word-break: break-word; user-select: none; }
.sc-msg.me .sc-bubble { background: var(--bubble-me-bg, #d8f3c4); border: 1px solid var(--bubble-me-bd, #bde3a3); }
.sc-msg.them .sc-bubble { background: var(--bg); border: 1px solid var(--line); }
.sc-bubble.sc-gone { color: var(--ink-3); font-style: italic; background: transparent; border-style: dashed; }
.sc-meta { display: flex; gap: 6px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.sc-left { color: var(--violet); }
.sc-msg.me .sc-left { color: var(--ink-3); }
.sc-sys { align-self: center; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3);
  background: var(--bg); border: 1px dashed var(--line); border-radius: 999px; padding: 2px 10px; }
.sc-sys-me { color: var(--amber); border-color: var(--amber); }

.sec-compose { display: flex; gap: 8px; align-items: flex-end; padding: 10px 12px; border-top: 1px solid var(--line);
  background: var(--panel); position: relative; z-index: 2; }
.sec-compose textarea { flex: 1 1 auto; resize: none; max-height: 120px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); font-family: var(--sans);
  font-size: calc(14px * var(--fs, 1)); line-height: 1.5; color: var(--ink); background: var(--panel); }
.sec-compose textarea:focus { outline: 2px solid var(--teal-soft); border-color: var(--teal-2); }

/* 水印：几乎看不见，但截图放大后能读出「谁 + 什么时候」。pointer-events:none 不挡操作 */
.sec-wm { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden;
  display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 74px; }
.sec-wm span { display: flex; align-items: center; justify-content: center; transform: rotate(-22deg);
  font-size: calc(10px * var(--fs, 1)); color: var(--ink); opacity: .045; white-space: nowrap; user-select: none; }

.sec-terms { margin: 6px 0 8px; padding-left: 18px; }
.sec-terms li { font-size: calc(13px * var(--fs, 1)); line-height: 1.7; color: var(--ink-2); margin-bottom: 6px; }

@media (max-width: 768px) {
  .sec-chat { height: calc(100dvh - 210px); border-radius: 0; border-left: 0; border-right: 0; }
  .sec-wm { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 70px; }
  .sc-msg { max-width: 86%; }
}

/* ============================================================
   z272 · 课题类型（用户需求）
   四类：科学问题型 / 大类性质型 / 小类性质型 / 拓展应用型
   房间卡版式 = C + D（用户 2026-10-26 选定）：
     · C —— 类型标签嵌在「领域 · 创建于」那一行的行首，彩色小字 + 色点，
            不加底色、不占高度，「领域」二字按用户要求删掉；
     · D —— 卡片左侧 3px 类型色条（上面 .room-card.rt-* 那四条），
            **不要** D 的右上三角角标（用户明确否掉）。
   配色大部分转发到现有主题变量（--blue/--moss/--amber），
   换 warm / tea / sakura / bamboo 主题时类型色自动跟着走，不会跳出来跟主题打架：
     绛红 = 科学问题型（#a8456a，z344 由 --violet 改来 —— 紫与蓝在 3px 细条上分不清）
     蓝 = 大类性质型（--blue）
     绿 = 小类性质型（--moss，原设计稿用的青绿在暖色主题里会跳，改苔绿更稳）
     琥珀 = 拓展应用型（--amber，与「撰写」房间的琥珀左边条同色系）
   ============================================================ */
:root {
  /* z344：科学问题型由紫 → 绛红 #a8456a。
     原因：紫 #6d4aa8 与蓝 #2b6cb0 只差 30° 左右色相，在 3px 的卡片色条上几乎分不出来；
     绛红落在蓝的对面（相差 ~120°），又不像朱砂那样跟「拓展应用型」的琥珀挤在暖橙区。
     青瓷绿底 + 绛红是这套主题里最稳的一对对比色，四色凑齐是 绛红 / 蓝 / 苔绿 / 琥珀。 */
  --c-science: #a8456a; --c-science-soft: #fbeef2;
  --c-major:   var(--blue);   --c-major-soft:   var(--blue-soft);
  --c-minor:   var(--moss);   --c-minor-soft:   var(--moss-soft);
  --c-extend:  var(--amber);  --c-extend-soft:  var(--amber-soft);
}

/* ---- 类型标签（C 版式）：平时只是「带色点的小字」，hover 才浮出胶囊底 ---- */
/* 这样既不加视觉重量，又能让人察觉到「这里可以点」。 */
/* ⚠️ z274（用户报障「课题性质和创建时间没有对齐」）：这里**不能**用 inline-flex。
   inline-flex 的基线取自「第一个 flex 子项」的基线，而第一个子项是那个空的 <i> 色点 ——
   空盒子没有基线，浏览器就拿它的**底边**当基线，于是标签里的文字比旁边那截「创建于」
   低 1px 左右，12px 的小字上肉眼就能看出来。
   改成 inline-block + 行内色点：基线取自最后一行文字的基线，与周围文字天然对齐。 */
.rt-pill {
  --rtc: var(--ink-3); --rts: #eef1f0;
  display: inline-block;
  font-size: calc(11px * var(--fs, 1)); font-weight: 600; line-height: 1.6;
  color: var(--rtc); white-space: nowrap;
  border: 1px solid transparent; border-radius: 20px;
  padding: 1px 7px 1px 6px; margin: 0 -2px;
  cursor: pointer; user-select: none;
  transition: background .14s ease, border-color .14s ease, box-shadow .14s ease;
}
/* 色点：原来是 flex 居中，现在用 vertical-align 抬 1px，落点与原来一致
   （11px 字的基线上方约 1px 处是 5px 圆点的视觉中心） */
.rt-pill i { display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--rtc); margin-right: 5px; vertical-align: 1px; }
.rt-pill.science { --rtc: var(--c-science); --rts: var(--c-science-soft); }
.rt-pill.major   { --rtc: var(--c-major);   --rts: var(--c-major-soft); }
.rt-pill.minor   { --rtc: var(--c-minor);   --rts: var(--c-minor-soft); }
.rt-pill.extend  { --rtc: var(--c-extend);  --rts: var(--c-extend-soft); }
.rt-pill.none    { --rtc: var(--ink-3);     --rts: #eef1f0; border-style: dashed; border-color: var(--line); }
.rt-pill.none i  { display: none; }
/* z356：hover 底只在真鼠标下给（触屏点按也会命中 :hover，chip 会闪一下底色）。
   .on 是「菜单开着」的状态标记，两种设备都要保留。 */
.rt-pill.on { background: var(--rts); border-color: var(--rtc); }
.has-mouse .rt-pill:hover { background: var(--rts); border-color: var(--rtc); }
.rt-pill.on { box-shadow: 0 0 0 3px var(--rts); }
/* 只读身份（学生）：不给 hover 反馈，别误导成能改 */
.rt-pill.ro { cursor: default; }
.rt-pill.ro:hover { background: transparent; border-color: transparent; }

/* 卡片「创建于」前面的分隔点（原来那里是「领域 ·」，领域已按用户要求删掉） */
.meta-dot { color: var(--ink-3); opacity: .6; margin: 0 5px; }

/* 「已提交修改申请、等管理员审批」的小黄标，跟在类型标签后面。
   比 ⏳ emoji 明确：不同手机的 emoji 字体差异很大，而且看不出是「待审」还是「加载中」。 */
.rt-clock {
  display: inline-block; margin-left: 4px; vertical-align: 1px;
  font-size: calc(9.5px * var(--fs, 1)); font-weight: 600; line-height: 1.7;
  color: var(--amber); background: var(--amber-soft); border: 1px solid #f0dfbe;
  border-radius: 20px; padding: 0 5px; white-space: nowrap;
}

/* 房间页头部那一行：标签嵌在 12px 的说明文字里，稍大一点更好点 */
.room-head .sub .rt-pill { font-size: calc(11.5px * var(--fs, 1)); }

/* ---- 点标签弹出的类型菜单 ---- */
/* ⚠️ z274（用户报障「选项溢出屏幕选不了」）：两处关键改动，别改回去
   ① position 从 absolute 改成 **fixed** —— 定位一律算视口坐标，页面滚到哪儿都不会偏；
   ② 宽度用 min()/calc() 夹在视口内，极窄屏（320/360）也不会溢到右边外面；
   ③ 高度由 openRoomTypeMenu 现算（下方放不下就翻到上方，两边都放不下就内部滚动），
      开滚动时才加 .sc —— 和 z220 修的 .ctx-menu 是同一套做法。 */
.rt-menu {
  position: fixed; z-index: 80;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: var(--shadow); padding: 6px;
  min-width: min(268px, calc(100vw - 16px));
  max-width: min(320px, calc(100vw - 16px));
}
.rt-menu.sc { overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.rt-menu-t { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); padding: 4px 8px 6px; letter-spacing: .03em; }
.rt-menu button {
  display: flex; align-items: flex-start; gap: 8px; width: 100%;
  background: none; border: 0; text-align: left; color: var(--ink); font: inherit;
  font-size: calc(13px * var(--fs, 1)); padding: 7px 8px; border-radius: 7px;
  transition: background .12s;
}
.rt-menu button:hover { background: var(--bg); }
.rt-menu button.cur { background: var(--teal-soft); }
.rt-menu button.cur .rt-mi b { color: var(--teal); }
.rt-menu button:disabled { opacity: .55; cursor: default; }
.rt-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; margin-top: 6px; }
.rt-dot.science { background: var(--c-science); }
.rt-dot.major   { background: var(--c-major); }
.rt-dot.minor   { background: var(--c-minor); }
.rt-dot.extend  { background: var(--c-extend); }
.rt-mi { flex: 1 1 auto; min-width: 0; }
.rt-mi b { display: block; font-size: calc(13px * var(--fs, 1)); font-weight: 600; line-height: 1.5; }
.rt-mi em { display: block; font-style: normal; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); line-height: 1.45; margin-top: 1px; }
.rt-cur {
  flex: none; margin-top: 4px; font-size: calc(10px * var(--fs, 1)); font-weight: 600;
  color: var(--teal); background: var(--panel); border: 1px solid var(--teal-soft);
  border-radius: 10px; padding: 0 6px;
}
.rt-menu-note {
  border-top: 1px solid var(--line-soft); margin-top: 4px; padding: 7px 8px 3px;
  font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); line-height: 1.55;
}
/* 管理员在菜单里直接跳到审批 */
.rt-menu button.rt-menu-pend {
  margin-top: 3px; border-top: 1px solid var(--line-soft); border-radius: 0 0 7px 7px;
  color: var(--amber); font-weight: 600; justify-content: center;
}
.rt-menu button.rt-menu-pend:hover { background: var(--amber-soft); }

/* ---- 类型选择器（新建房间 / 录入类型经验时用）---- */
.rt-picker { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 6px; }
.rt-picker.sm { grid-template-columns: 1fr; gap: 7px; }
.rt-opt { --rtc: var(--ink-3); display: flex; align-items: flex-start; gap: 8px; cursor: pointer; min-width: 0; }
.rt-opt.science { --rtc: var(--c-science); }
.rt-opt.major   { --rtc: var(--c-major); }
.rt-opt.minor   { --rtc: var(--c-minor); }
.rt-opt.extend  { --rtc: var(--c-extend); }
/* 单选框藏起来但不移出可访问树 —— app.js 用 input[name="nrRt"]:checked 取值 */
.rt-opt input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.rt-opt-b {
  flex: 1 1 auto; min-width: 0;
  border: 1px solid var(--line); border-radius: 9px; padding: 8px 10px;
  background: var(--panel); transition: border-color .14s, background .14s, box-shadow .14s;
}
.rt-opt-b b { display: block; font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; color: var(--ink); line-height: 1.5; }
.rt-opt-b b::before {
  content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--rtc); margin-right: 6px; vertical-align: 1px;
}
.rt-opt-b em { display: block; font-style: normal; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); line-height: 1.45; margin-top: 2px; }
.rt-opt:hover .rt-opt-b { border-color: var(--teal-2); }
/* 选中态：按类型上色（无 :has 依赖，走「兄弟选择器」，老浏览器也不错） */
.rt-opt input:checked ~ .rt-opt-b { border-color: var(--teal); background: var(--teal-soft); box-shadow: 0 0 0 2px var(--teal-soft); }
.rt-opt.science input:checked ~ .rt-opt-b { border-color: var(--c-science); background: var(--c-science-soft); box-shadow: 0 0 0 2px var(--c-science-soft); }
.rt-opt.major   input:checked ~ .rt-opt-b { border-color: var(--c-major);   background: var(--c-major-soft);   box-shadow: 0 0 0 2px var(--c-major-soft); }
.rt-opt.minor   input:checked ~ .rt-opt-b { border-color: var(--c-minor);   background: var(--c-minor-soft);   box-shadow: 0 0 0 2px var(--c-minor-soft); }
.rt-opt.extend  input:checked ~ .rt-opt-b { border-color: var(--c-extend);  background: var(--c-extend-soft);  box-shadow: 0 0 0 2px var(--c-extend-soft); }
.rt-opt.all     input:checked ~ .rt-opt-b { border-color: var(--teal);      background: var(--teal-soft);      box-shadow: 0 0 0 2px var(--teal-soft); }
.rt-opt input:focus-visible ~ .rt-opt-b { outline: 2px solid var(--teal-2); outline-offset: 1px; }
/* z460（用户报「新建房间页面上课题类型的渲染有问题」）：
   根因是 `.field label`（权重 0,1,1）把 `.rt-opt`（0,1,0）的 display:flex 压成 block，
   并塞了 margin-bottom:5px —— 四个选项不再按 flex 排版，卡片高度不一致、边框互相压住、
   说明文字溢出卡片（探针实测 optDisplay=block / optMargin=5px）。
   这里显式提权重修回来，并让同一行的两张卡等高。 */
.field .rt-picker > label.rt-opt {
  display: flex; align-items: stretch; gap: 0; margin-bottom: 0;
  font-size: inherit; color: inherit; font-weight: inherit; min-width: 0;
}
.rt-picker { align-items: stretch; }
.rt-opt-b { display: flex; flex-direction: column; width: 100%; height: 100%; box-sizing: border-box; }
.rt-opt-b b { flex: 0 0 auto; }
.rt-opt-b em { flex: 1 1 auto; }
/* 弹窗里「必选」小标记 */
.field label .req {
  font-style: normal; font-size: calc(10.5px * var(--fs, 1)); font-weight: 600;
  color: var(--amber); background: var(--amber-soft); border: 1px solid #f0dfbe;
  border-radius: 20px; padding: 0 6px; margin-left: 6px; vertical-align: 1px;
}
@media (max-width: 560px) {
  .rt-picker { grid-template-columns: 1fr; }
}

/* ---- 经验页：「课题类型修改待审批」的条目 ---- */
.rule-row.rt-req { border-left-color: var(--amber); background: #fffdf7; }
.rule-row.rt-req .rt b { color: var(--ink); font-weight: 600; }


/* ══════════════════════════════════════════════════════════
   z277：长消息折叠（首块 + 末块常驻，只收中间那段）
   · 按行数折（桌面 14 行 / 手机 12 行），不按字数 —— 两端体验一致，也不切断表格/代码块
   · 折叠区本身可点（看不见内容，不会误伤选中文字）；刷新后重新折叠（不记忆）
   ══════════════════════════════════════════════════════════ */
.fold-wrap { overflow: hidden; position: relative; cursor: pointer; }
.fold-wrap::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 32px;
  background: linear-gradient(to bottom, transparent, var(--panel, #fff));
  pointer-events: none;
}
.fold-wrap.open { cursor: auto; }
.fold-wrap.open::after { display: none; }
.fold-btn {
  display: block; width: 100%; margin: 6px 0 4px; padding: 6px 10px;
  border: 0; border-radius: 8px; background: var(--line-soft); color: var(--ink-2);
  font-size: calc(12px * var(--fs, 1)); font-family: inherit; cursor: pointer; text-align: center;
}
.fold-btn:hover { color: var(--ink); }

/* z277 的「本轮读图」浮动脚注在 z288 **整体删除**（用户：「这两个地方重复了，下面的删掉」）。
   图账本现在只有一个出口：随消息落库的标签卡《随附图片》。
   ⚠️ 别凭样式名去别处找 .img-note —— CSS 与 DOM 都已无引用，留个墓碑免得后人重复造。 */

/* z280：密聊「未就绪投递」提示条（对方还没登录过） */
.sc-pending {
  margin: 8px 10px 4px; padding: 8px 12px; border-radius: 10px;
  background: var(--amber-soft); color: var(--amber);
  font-size: calc(12px * var(--fs, 1)); line-height: 1.7;
}

/* ============================================================
   z314：电脑端聊天区改版（用户选定方案 B）
   ------------------------------------------------------------
   需求原文：① 所有注释文字取消 ② 输入框放大 ③ 上传/截图放输入框左下角，
   生成文件/发送放右下角 ④ 右侧课题文件/知识库等边栏收成一条图标条，
   腾出的空间全给聊天区 ⑤ 增加聊天记录搜索（侧栏第六个图标）
   另有：抽屉三种收起方式（点外部 / 收起按钮 / Esc）、消息多选。
   ============================================================ */

/* ---------- ① 房间头：按钮收进 ⋯ ---------- */
.room-acts { position: relative; }
.room-more-btn {
  font-size: calc(17px * var(--fs, 1)); line-height: 1;
  padding: 2px 11px 4px; letter-spacing: 1px;
}
.room-menu {
  position: absolute; right: 0; top: calc(100% + 7px); z-index: 45;
  display: flex; flex-direction: column; align-items: stretch; gap: 3px;
  min-width: 132px; padding: 6px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 30px rgba(20, 18, 14, .16);
}
.room-menu button { text-align: left; justify-content: flex-start; }
.room-menu button[disabled] { opacity: .45; }

/* ---------- ② 侧栏图标条（60px，六项同构） ---------- */
.rail {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  width: 60px; padding: 8px 0 10px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); position: sticky; top: 68px; align-self: start;
}
.rail-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: 3px; width: 52px; min-height: 46px; padding: 7px 2px 5px;
  border: 0; border-radius: 9px; background: transparent;
  color: var(--ink-3); cursor: pointer; font-family: inherit;
  transition: background .13s ease, color .13s ease;
}
.rail-btn svg { width: 19px; height: 19px; flex: none; }
/* 四个字的标签（课题文件 / 聊天记录）在 52px 里放得下；
   字级放大到「特大」时会自动折成两行，**不要**加 nowrap。 */
.rail-btn span {
  font-size: calc(11px * var(--fs, 1)); line-height: 1.2;
  text-align: center; max-width: 48px; word-break: break-word;
}
.rail-btn:hover { background: var(--teal-soft); color: var(--teal); }
.rail-btn.on { background: var(--teal-soft); color: var(--teal); font-weight: 500; }
.rail-sep { width: 30px; height: 1px; background: var(--line); margin: 6px 0; }
.rail-rec { border: 1px solid var(--line); background: #fff; }
.rail-rec:hover { border-color: var(--teal-2); }
.rail-rec.on { border-color: var(--teal-2); }

/* ---------- ③ 宽屏：右侧栏变 380px 抽屉（点外部 / 收起按钮 / Esc 都能关） ---------- */
@media (min-width: 621px) {
  .room-layout { grid-template-columns: minmax(0, 1fr) 60px; align-items: start; }
  .side {
    position: fixed; top: 0; right: 0; bottom: 0; z-index: 70;
    width: 380px; max-width: 92vw; height: 100dvh;
    display: flex; flex-direction: column;
    background: var(--panel); border: 0; border-left: 1px solid var(--line);
    border-radius: 0; overflow: hidden;
    transform: translateX(100%); transition: transform .22s cubic-bezier(.22,.61,.36,1);
    box-shadow: -10px 0 30px rgba(20, 18, 14, .10);
  }
  .side.on { transform: translateX(0); }
  .side-tabs { flex: none; }
  .side-body { flex: 1 1 auto; min-height: 0; max-height: none; overflow-y: auto; }
  .side-mask { display: none; }
  .side.on + .side-mask {
    display: block; position: fixed; inset: 0; z-index: 69;
    background: rgba(20, 18, 14, .16);
  }
  /* 抽屉里给「收起」按钮留一个固定的右上角位（只宽屏有） */
  .side-close {
    position: absolute; top: 9px; right: 10px; z-index: 3;
    display: inline-flex; align-items: center; gap: 3px;
    height: 26px; padding: 0 9px; border-radius: 7px;
    border: 1px solid var(--line); background: var(--panel); color: var(--ink-3);
    font-size: calc(11.5px * var(--fs, 1)); font-family: inherit; cursor: pointer;
  }
  .side-close:hover { border-color: var(--teal-2); color: var(--teal); background: var(--teal-soft); }
  /* 页签条右侧留出收起按钮的位置，避免最后一项被压住 */
  .side-tabs { padding-right: 74px; }
}

/* ---------- ④ 输入框：一个大框，文字在上、按钮在下 ---------- */
/* ⚠️ 不改 DOM 结构：.composer-row 仍是 flex，用 flex-wrap + order 把
   textarea 顶到第一行独占，图标与按钮落到第二行两端。这样手机端
   （下面的 ≤620 覆盖）与所有既有 JS（#attachInput / #shotBtn / #genDocBtn）
   都不用动，风险最小。 */
@media (min-width: 621px) {
  .composer {
    /* z314：与聊天区同一白底（用户：输入框外面也要白的，融为一体），
       分隔只靠 composer-row 自己的那道细边，不再用米色底 + 上边框做"两条带"。 */
    border-top: 0; background: var(--panel);
    /* z317：底边再收 2px（配合下面把整个输入区压到 ~118px，见 composer-row 的注释） */
    padding: 6px 12px 8px;
  }
  .composer-row {
    display: flex; flex-wrap: wrap; align-content: space-between;
    align-items: center; gap: 4px 6px;
    /* ★ z317（用户要求：「聊天窗口有点矮，输入窗口有点高」「输入框和微信的输入框高度保持一致」）：
       原来是 min-height:140px（+ 外层 16px 内边距 ≈ 156px），比微信的输入区高出约 40px。
       现在 104px（≈ 两行文字 + 一排图标），多出来的高度全部还给聊天区。
       ⚠️ 输入框本身仍然会随内容长高（paintComposer 里按 scrollHeight 设 height，上限 260px），
          这里只压"空着的时候"有多高。 */
    min-height: 104px; padding: 8px 11px 7px;
    background: var(--panel); border: 1px solid var(--line); border-radius: 13px;
    transition: border-color .15s ease, box-shadow .15s ease;
  }
  .composer-row:focus-within { border-color: var(--teal-2); box-shadow: 0 0 0 3px var(--teal-soft); }
  .composer .composer-row textarea {
    order: 1; flex: 1 1 100%; width: 100%;
    min-height: 62px; max-height: 260px;
    border: 0; border-radius: 0; box-shadow: none !important;
    padding: 2px 3px 4px; background: transparent;
    font-size: calc(14px * var(--fs, 1)); line-height: 1.72;
  }
  .composer .composer-row textarea:focus { outline: 0; border: 0; box-shadow: none !important; }
  /* 左下角两个小图标：去掉原来的方块边框，做成"裸图标" */
  .composer-row .attach-btn,
  .composer-row .shot-btn {
    order: 2; width: 30px; height: 30px; padding: 0; flex: none;
    border: 0; border-radius: 7px; background: transparent; color: var(--ink-3);
  }
  .composer-row .attach-btn:hover,
  .composer-row .shot-btn:hover { background: var(--line-soft); color: var(--teal); }
  .composer-row .attach-btn svg, .composer-row .shot-btn svg { width: 18px; height: 18px; }
  /* 右下角：生成文件开关 + 发送 */
  .composer-row .gen-doc-btn {
    order: 3; margin-left: auto; margin-right: 4px;
    height: 32px; padding: 0 10px; border-radius: 8px;
    font-size: calc(12.5px * var(--fs, 1));
  }
  .composer-row #send {
    order: 3; min-height: 32px; height: 32px; padding: 0 18px;
    border-radius: 8px; font-size: calc(13px * var(--fs, 1));
  }
  .composer .attach-list { margin-bottom: 7px; }
}

/* ---------- ⑤ 手机端：图标条隐藏、输入框保持「+ 输入框 发送」一行 ---------- */
@media (max-width: 620px) {
  .rail { display: none; }
  .side-mask { display: none !important; }
  .room-more-btn, .room-menu { display: none; }
  /* ★ z314：桌面端抽屉的「收起」按钮在手机端没有定位样式（它只在 ≥621px 那一段里定位），
     不隐藏就会以默认按钮的样子落到抽屉最底部，撑出一条横贯全屏的"收起"栏。
     手机端收抽屉用左上角「‹」，这个按钮直接不要。 */
  .side-close { display: none; }
}

/* ---------- ⑥ 对话区撑满视口（用户要求：腾出来的空间全部给聊天区） ----------
   高度由 JS 的 fitRoomViewport() 写在内联 style 上（窗口尺寸、字体倍率、
   顶栏换行都会影响可用高度，写死 calc 一定错）。
   这里只负责让 .msgs 在固定高度的 .discuss 里自己滚。 */
@media (min-width: 621px) {
  .discuss { min-height: 420px; }
  .discuss > .msgs { flex: 1 1 auto; min-height: 0; max-height: none; }
}

/* ============================================================
   z314：聊天记录搜索弹窗（微信式居中窗）
   ============================================================ */
.cs-modal {
  position: fixed; inset: 0; z-index: 200; padding: 24px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(20, 18, 14, .34);
}
.cs-card {
  width: min(660px, 100%); max-height: min(78vh, 640px);
  display: flex; flex-direction: column;
  background: var(--panel); border-radius: 14px; overflow: hidden;
  box-shadow: 0 18px 50px rgba(20, 18, 14, .24);
  /* ★ z319：手机端左滑关窗的**真根因**补丁 ——
     不写 touch-action 时浏览器会把横向手势当成自己的 pan/返回手势接管，
     随后丢一个 touchcancel 过来，JS 的 touchmove 就此中断（真机才复现）。
     pan-y = 竖向滚动照旧交给浏览器，横向留给 JS。 */
  touch-action: pan-y;
  overscroll-behavior: contain;
  /* z322：左滑跟手位移（松手回弹）；拖动中关掉过渡，否则手指会有"橡皮筋"滞后 */
  transition: transform .2s ease;
}
.cs-card.cs-drag { transition: none; }
.cs-head {
  display: flex; align-items: center; gap: 6px;
  padding: 12px 16px; border-bottom: 1px solid var(--line-soft);
  font-size: calc(13px * var(--fs, 1)); font-weight: 500; color: var(--ink);
}
/* z319：手机端左上角「‹ 返回」（与抽屉/子页的返回键同一位置），小屏才显示 */
.cs-back {
  display: none; flex: none; border: 0; background: transparent;
  color: var(--teal); font-size: calc(22px * var(--fs, 1)); line-height: 1;
  padding: 0 6px 0 0; margin-top: -2px; cursor: pointer;
}
.cs-ttl { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-x {
  border: 0; background: transparent; color: var(--ink-3);
  font-size: calc(20px * var(--fs, 1)); line-height: 1; cursor: pointer; padding: 0 4px;
}
.cs-x:hover { color: var(--ink); }
.cs-search {
  display: flex; align-items: center; gap: 8px;
  margin: 12px 16px 0; padding: 7px 12px;
  border: 1px solid var(--teal-2); border-radius: 9px; background: var(--panel);
}
.cs-search svg { flex: none; color: var(--ink-3); }
.cs-search input {
  flex: 1 1 auto; min-width: 0; border: 0; outline: 0; background: transparent;
  font-family: inherit; font-size: calc(13px * var(--fs, 1)); color: var(--ink);
}
.cs-count { flex: none; font-size: calc(11px * var(--fs, 1)); color: var(--ink-4); }
.cs-tabs {
  display: flex; gap: 18px; padding: 10px 16px 0;
  border-bottom: 1px solid var(--line-soft);
}
.cs-tabs button {
  border: 0; background: transparent; padding: 0 0 7px; cursor: pointer;
  font-family: inherit; font-size: calc(12.5px * var(--fs, 1));
  color: var(--ink-3); border-bottom: 2px solid transparent;
}
.cs-tabs button.on { color: var(--teal); border-bottom-color: var(--teal); font-weight: 500; }
.cs-datebar { padding: 9px 16px 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cs-datebar input {
  font-family: inherit; font-size: calc(12.5px * var(--fs, 1));
  padding: 4px 8px; border: 1px solid var(--line); border-radius: 7px;
  background: var(--panel); color: var(--ink); min-width: 0;
}
/* z317：区间分隔与快捷段 */
.cs-dsep { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-4); flex: none; }
.cs-dquick { display: inline-flex; gap: 4px; flex: none; }
.cs-dquick button {
  border: 1px solid var(--line); background: var(--panel); color: var(--ink-2);
  border-radius: 20px; padding: 3px 9px; cursor: pointer; font-family: inherit;
  font-size: calc(11.5px * var(--fs, 1)); white-space: nowrap;
}
.cs-dquick button:hover { border-color: var(--teal-2); color: var(--teal); }
.cs-datebar .cs-dclear {
  border: 0; background: transparent; color: var(--teal); cursor: pointer;
  font-family: inherit; font-size: calc(12px * var(--fs, 1)); padding: 0;
}
/* ============================================================
   z317：文件 / 图片两个分类改成**微信式平铺**
   · 图片 → 九宫格缩略图（点开看大图）
   · 文件 → 一行一个文件（类型方块 + 名称 + 类型/大小/时间/上传人 + 「打开」）
   ============================================================ */
.cs-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 5px; padding: 10px 12px 12px; }
.cs-cell {
  position: relative; padding: 0; border: 0; cursor: pointer; overflow: hidden;
  aspect-ratio: 1 / 1; border-radius: 8px; background: var(--line-soft);
  display: block; width: 100%;
}
.cs-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cs-cell-ph { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: calc(11px * var(--fs, 1)); color: var(--ink-4); }
.cs-cell-d {
  position: absolute; left: 0; right: 0; bottom: 0; text-align: left;
  padding: 12px 5px 3px; font-size: calc(10px * var(--fs, 1)); color: #fff;
  background: linear-gradient(to top, rgba(0, 0, 0, .48), rgba(0, 0, 0, 0));
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cs-frow {
  display: flex; gap: 10px; width: 100%; align-items: center;
  padding: 9px 16px; cursor: pointer; text-align: left;
}
.cs-frow:hover { background: var(--line-soft); }
.cs-fico {
  width: 32px; height: 32px; flex: none; border-radius: 8px;
  display: flex; align-items: center; justify-content: center;
  background: var(--line-soft); color: var(--ink-3);
  font-size: calc(12.5px * var(--fs, 1)); font-weight: 700;
}
.cs-fico.xls { background: #e8f3ec; color: #1f8a54; }
.cs-fico.pdf { background: #fdeceb; color: #c0392b; }
.cs-fico.ppt { background: #fdf0e4; color: #b26a12; }
.cs-fico.img { background: #eaf0fb; color: #2f5fa8; }
.cs-fico.video { background: #f0ecfa; color: #5a45a8; }
.cs-fico.zip { background: #f2f0e6; color: #7a6a2a; }
.cs-fmeta { flex: 1 1 auto; min-width: 0; }
.cs-fname {
  display: block; font-size: calc(13px * var(--fs, 1)); color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cs-fname mark { background: #fdf0d2; color: #854f0b; padding: 0 1px; border-radius: 2px; }
.cs-fsub { display: block; margin-top: 2px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-4); }
.cs-fopen {
  flex: none; border: 1px solid var(--line); background: var(--panel); color: var(--teal);
  border-radius: 7px; padding: 4px 10px; cursor: pointer; font-family: inherit;
  font-size: calc(11.5px * var(--fs, 1));
}
.cs-fopen:hover { border-color: var(--teal-2); background: var(--teal-soft); }
/* 点图片看大图 */
.cs-imgview { text-align: center; background: var(--line-soft); border-radius: 10px; padding: 8px; }
.cs-imgview img { max-width: 100%; max-height: 62vh; border-radius: 6px; display: block; margin: 0 auto; }
.cs-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 5px 0 8px; }
.cs-list:has(.cs-grid) { padding: 0; }
.cs-row {
  display: flex; gap: 10px; width: 100%; text-align: left;
  padding: 8px 16px; border: 0; background: transparent;
  cursor: pointer; font-family: inherit; align-items: flex-start;
}
.cs-row:hover { background: var(--line-soft); }
.cs-av {
  width: 26px; height: 26px; flex: none; border-radius: 7px;
  display: flex; align-items: center; justify-content: center;
  background: var(--teal-soft); color: var(--teal);
  font-size: calc(11px * var(--fs, 1)); font-weight: 500;
}
.cs-av.f {
  background: var(--line-soft); color: var(--ink-3); border: 1px solid var(--line);
}
.cs-main { flex: 1 1 auto; min-width: 0; }
.cs-txt {
  display: block; font-size: calc(13px * var(--fs, 1)); color: var(--ink);
  line-height: 1.55; overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  word-break: break-word;
}
.cs-txt mark { background: #fdf0d2; color: #854f0b; padding: 0 1px; border-radius: 2px; }
.cs-sub {
  display: block; margin-top: 2px;
  font-size: calc(11px * var(--fs, 1)); color: var(--ink-4);
}
.cs-empty {
  padding: 44px 16px; text-align: center;
  font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-4); line-height: 1.8;
}
.cs-foot {
  flex: none; padding: 7px 16px; border-top: 1px solid var(--line-soft);
  font-size: calc(11px * var(--fs, 1)); color: var(--ink-4);
}
@media (max-width: 620px) {
  .cs-modal { padding: 0; align-items: stretch; }
  .cs-card { width: 100%; max-height: 100%; border-radius: 0; height: 100dvh; }
  /* z319：手机端显示左上角「‹ 返回」，标题左右都留出按钮位 */
  .cs-back { display: inline-flex; align-items: center; }
  .cs-head { padding-top: calc(12px + env(safe-area-inset-top, 0px)); }
  /* z317：手机端图片固定 3 列（微信也是 3 列）；文件行左右内边距收一点 */
  .cs-grid { grid-template-columns: repeat(3, 1fr); gap: 4px; padding: 8px 10px 10px; }
  .cs-datebar { padding: 8px 12px 0; gap: 6px; }
  .cs-datebar input { flex: 1 1 38%; }
  .cs-frow { padding: 10px 12px; }
  .cs-head, .cs-search, .cs-tabs, .cs-foot { padding-left: 12px; padding-right: 12px; }
  .cs-search { margin-left: 12px; margin-right: 12px; padding-left: 10px; padding-right: 10px; }
}

/* ============================================================
   z314：消息多选（右键 →「多选」）
   ============================================================ */
.multi-bar {
  flex: none; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 8px 14px; background: var(--teal-soft);
  border-bottom: 1px solid #cfe4e0;
  font-size: calc(12.5px * var(--fs, 1)); color: var(--teal);
}
.multi-bar .mb-btn {
  border: 0; background: transparent; color: var(--teal); cursor: pointer;
  font-family: inherit; font-size: calc(12.5px * var(--fs, 1)); padding: 2px 4px;
}
.multi-bar .mb-btn:hover { text-decoration: underline; }
.multi-bar .mb-count { color: var(--ink-2); font-weight: 500; }
.multi-acts {
  position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 130;
  display: flex; gap: 8px; padding: 8px 10px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 12px 30px rgba(20, 18, 14, .16);
}
.multi-acts button {
  min-width: 82px; height: 34px; padding: 0 14px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
  color: var(--ink-2); font-family: inherit; font-size: calc(13px * var(--fs, 1)); cursor: pointer;
}
.multi-acts button:hover:not(:disabled) { border-color: var(--teal-2); color: var(--teal); background: var(--teal-soft); }
.multi-acts button.danger { color: var(--danger); border-color: #e9c8c4; }
.multi-acts button.danger:hover:not(:disabled) { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.multi-acts button:disabled { opacity: .42; cursor: default; }

body.multi-mode .composer { display: none; }
body.multi-mode .msgs > .msg { position: relative; padding-left: 30px; cursor: pointer; }
body.multi-mode .msgs > .msg::before {
  content: ''; position: absolute; left: 2px; top: 50%; transform: translateY(-50%);
  width: 17px; height: 17px; border-radius: 50%;
  border: 1px solid #cfdcd9; background: var(--panel);
}
body.multi-mode .msgs > .msg.sel::before { background: var(--teal); border-color: var(--teal); }
body.multi-mode .msgs > .msg.sel::after {
  content: ''; position: absolute; left: 7px; top: 50%;
  width: 7px; height: 4px; margin-top: -2px;
  border-left: 1.6px solid #fff; border-bottom: 1.6px solid #fff;
  transform: rotate(-45deg);
}
body.multi-mode .msgs > .msg.sel { background: rgba(230, 242, 240, .55); border-radius: 8px; }
@media (max-width: 620px) {
  .multi-acts { left: 12px; right: 12px; transform: none; bottom: 14px; justify-content: space-between; }
  .multi-acts button { flex: 1 1 0; min-width: 0; }
}

/* z314：弹窗必须盖在「手机端全屏抽屉」（z-index:60）与「宽屏 380px 抽屉」（70）之上。
   原来 .modal 是 60，与抽屉同级、只靠 DOM 顺序压住 —— 用户报
   「点分组/房间信息，弹窗出来了但背景回到聊天页」，一半原因就是这一处太脆。
   现在抽屉不再自动关（见 bindDrawerBody），层级必须硬性高过它。 */
.modal { z-index: 210; }

/* ============================================================
   z317：房间页「把上下空白收掉、把高度还给聊天区」（用户附微信截图提的要求）
   ------------------------------------------------------------
   用户原话：「聊天窗口有点矮，输入窗口有点高……目前整个页面的上下有一些空区域，
   可以稍微减少这个空区域的高度，上面这片区域也占得太多了」。
   量法：桌面端 main 上内边距 24px、下内边距 60px，房间头还有 14px 外边距 +
   20px 标题 —— 加起来吃掉了 90px 左右的可用高度，而这些位置没有任何内容。
   这里只对**房间页**生效（body.in-room 由 route() 打标记），别的页面留白不变。
   ⚠️ fitRoomViewport 读的是 main 的真实 padding-bottom 来计算 .discuss 高度，
      所以改这里就等于直接加高聊天区，不需要再改 JS 里的数字。
   ============================================================ */
body.in-room main { padding-top: 10px; padding-bottom: 16px; }
@media (min-width: 621px) {
  body.in-room main { padding-top: 10px; padding-bottom: 16px; }
  /* 房间头右侧那排（用量 chip + ⋯）与标题同一行居中，别再撑高整行 */
  body.in-room .room-head { margin-bottom: 8px; }
  body.in-room .room-head h2 { max-width: 62vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* ============================================================
   z326：手机端三页改版（团队页 A1 / 房间分区描边 / 抽屉 C1）
   ------------------------------------------------------------
   用户定稿：团队页 A1、房间页 B1、房间抽屉 C1。三处**都只是界面**：
     · 团队页 KPI 跟着入口分叉（课题 / 撰写两套文案），房间行写出实验成员
     · 房间页三分区文案压成「日常实验 / 课题设计 / 撰写」，改用描边卡
     · 抽屉图标从 emoji 换成线性 SVG，加成员头像墙，设置行分两组
   ⚠️ 全部限定在 620px 以内；桌面端一行不动（.tm-* 默认 display:none）。
   ⚠️ 实验成员的「排除组长/导师/管理员」沿用 EXP_ROLE_EXCLUDE，没有另起一套规则。
   ============================================================ */

/* 桌面端：手机专用结构一律不参与布局 */
.tm-head, .tm-kpi, .tm-list, .tm-sec { display: none; }
/* z363：以下三样只给桌面用（手机上是「全部 N ›」那套，不需要计数 b 和面包屑） */
.tm-bread, .tm-matsec, .tm-cnt { display: none; }

/* ============================================================
   z343：手机端团队页 —— 按设计稿整体重做
   ------------------------------------------------------------
   用户报障：「跟这个团队页完全对不上」。查实上一轮（z342）只调了尺寸：
   头部还是白底、KPI 是「四格 + 竖分隔线」、三个入口是横排药丸、房间是
   一条条独立卡 —— 与稿子（绿头 + 浮起白卡 + 竖排入口卡 + 整卡列表）不是一个东西。
   本轮**只动样式 + 三处标记**（KPI 卡挪出绿块 / 小标题「全部 N ›」/「进行中」包一层），
   数据、徽标、点击路由、申请按钮、归档状态一律沿用，桌面端一行不动。
   ============================================================ */
@media (max-width: 620px) {
  #teamHeadDesk, .team-page-head { display: none; }
  body.in-team { background: var(--bg); }
  body.in-team main { padding: 0 0 40px; }
  body.in-team .home-sec { margin-top: 0; }

  /* ---------- 绿色头部（左右 6px 留白 + 下圆角 + 右上角淡色圆） ----------
     圆的画法：radial-gradient 硬停止 —— 用伪元素还得再写一层裁剪，
     而背景图天然按 border-radius 裁切，一行就够。 */
  .tm-head {
    display: block; position: relative;
    /* z344：下内边距从 17 → 30。KPI 卡 -15 压上来，两者之间就留出 15px 呼吸
       （原来只剩 2px，副标题几乎贴着白卡）。 */
    margin: 0 6px; padding: 10px 20px 30px;
    background-color: var(--teal);
    background-image: radial-gradient(circle at 112% 0%, rgba(255,255,255,.13) 0 190px, rgba(255,255,255,0) 191px);
    border-radius: 0 0 22px 22px;
    color: #fff;
  }
  .tm-r1 { display: flex; align-items: center; gap: 8px; height: 38px; }
  .tm-bk {
    width: 24px; height: 24px; margin-left: -9px; padding: 0;
    border: 0; background: transparent; color: #fff;
    font-size: 26px; line-height: 1; cursor: pointer; flex: none;
  }
  .tm-r1 h2 {
    margin: 0; flex: 0 1 auto; min-width: 0;
    font-family: inherit;
    font-size: calc(22px * var(--fs, 1)); font-weight: 700; line-height: 1.2;
    color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* 「课题 / 撰写」胶囊：稿子里是半透明白底 + 白字（不是浅青底深青字） */
  .tm-kind {
    flex: none; margin-left: 1px;
    font-size: calc(12px * var(--fs, 1)); font-weight: 600;
    color: #fff; background: rgba(255, 255, 255, .17);
    border-radius: 7px; padding: 3px 8px;
  }
  .tm-kind.w { color: #fff; background: rgba(255, 255, 255, .17); }
  .tm-gap { flex: 1 1 auto; }
  .tm-r2 {
    display: flex; align-items: center; gap: 6px;
    margin-top: 5px;
    /* z344：与上面的大标题左对齐。返回键 24 宽但左移了 9（视觉对齐），再加 gap 8，
       标题就从容器左侧 23px 起 —— 副标题补同样的 23，两行文字左边缘落在同一条线上。 */
    padding-left: 23px;
    font-size: calc(13px * var(--fs, 1)); color: rgba(255, 255, 255, .74);
  }
  .tm-todo {
    margin-left: auto; flex: none;
    font-size: 11px; font-weight: 600;
    color: #ffe0b8; background: rgba(255, 255, 255, .16);
    border-radius: 6px; padding: 2px 7px;
  }

  /* ---------- 白色 KPI 卡：负 margin 压住绿块下沿 ---------- */
  .tm-kpi {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    margin: -15px 18px 0; padding: 16px 2px 15px;
    background: #fff; border-radius: 20px;
    box-shadow: 0 8px 22px rgba(26, 61, 54, .07);
    position: relative; z-index: 2;
  }
  .tm-kpi > div { min-width: 0; text-align: center; padding: 0 3px; }
  /* z344：用户要求「统计卡里面用灰色竖线分隔」—— 第 2~4 格左侧一道 1px 浅灰线 */
  .tm-kpi > div + div { border-left: 1px solid #edf1f0; }
  .tm-kpi b {
    display: block;
    /* z349：用户「团队页上面的统计数字太大了，调小 20%」⇒ 30 → 24 */
    font-size: calc(24px * var(--fs, 1)); font-weight: 700; line-height: 1;
    letter-spacing: -.02em; font-variant-numeric: tabular-nums; color: var(--ink);
  }
  .tm-kpi i {
    display: block; margin-top: 9px; font-style: normal;
    font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); white-space: nowrap;
  }
  /* Run 那一列是金色（#a87418，直接从设计稿取色） */
  /* z363：类名跟随模板改成 tm-run / tm-warn，避免与桌面右栏未读角标 .run 撞名 */
  .tm-kpi .tm-run b { color: #a87418; }
  .tm-kpi .tm-warn b { color: var(--amber); }

  /* ---------- 三个入口：竖排白卡（图标在上、文字在下） ---------- */
  .team-space {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 18px; margin: 14px 22px 0;
  }
  .team-space-card {
    flex-direction: column; align-items: center; justify-content: center; gap: 9px;
    height: 90px; min-height: 0; padding: 0;
    border: 0; border-radius: 20px; background: #fff;
    box-shadow: none; text-align: center;
  }
  .team-space-card:hover { transform: none; box-shadow: none; border-color: transparent; }
  .team-space-card .tsc-ico {
    display: grid; place-items: center;
    width: 33px; height: 33px; border-radius: 10px;
    background: #edf3f1; color: #1d4f48;
  }
  .team-space-card .tsc-ico .ic { width: 17px; height: 17px; }
  .team-space-card .tsc-txt { display: block; }
  .team-space-card .tsc-txt b { font-size: calc(15px * var(--fs, 1)); font-weight: 600; color: var(--ink); }
  /* z409：说明行只给桌面的「资料架」用；手机上入口卡固定 90 高、只放图标 + 名称，
     多一行字会把卡撑破（≥900 段才显示）。 */
  .team-space-card .tsc-txt i { display: none; }

  /* ---------- 「课题房间」+「全部 N ›」 ---------- */
  .tm-sec {
    display: flex; align-items: baseline; justify-content: space-between;
    padding: 18px 20px 10px;
    font-size: calc(18px * var(--fs, 1)); font-weight: 700; color: var(--ink);
    letter-spacing: 0;
  }
  .tm-sec i {
    font-style: normal; font-family: inherit;
    font-size: calc(13px * var(--fs, 1)); font-weight: 400; color: var(--ink-3);
  }
  .tm-sec i u { text-decoration: none; margin-left: 3px; color: var(--ink-4, #b3bfbd); }

  /* ---------- 房间列表：一整张白卡 + 行间 1px 线 ---------- */
  #teamGrid { display: none; }
  .tm-list {
    display: block; margin: 0 18px;
    background: #fff; border-radius: 16px;
    overflow: hidden;
  }
  .tm-list .room-card.tr {
    display: flex; flex-direction: column;
    padding: 15px 16px 15px 21px;
    background: #fff; border: 0; border-radius: 0; box-shadow: none;
    border-top: 1px solid #f1f4f3;
    position: relative;
    /* z344：色条默认值 —— 没有课题类型的房间给一道中性浅灰（不冒充任何类型色）。
       有类型的由下面 .rt-science/.rt-major/.rt-minor/.rt-extend 覆盖。 */
    --rtbar: #dbe3e1;
  }
  .tm-list .room-card.tr:first-child { border-top: 0; }
  .tm-list .room-card.tr:hover { box-shadow: none; }
  /* 类型色条：稿子里是**贴左边、整行高**的 3px（不是内缩圆头）；
     首尾的直角由 .tm-list 的 overflow:hidden 顺着卡片圆角削掉 */
  .tm-list .room-card.tr::before {
    content: ''; position: absolute; left: 0; top: 0; bottom: 0;
    width: 3px; background: var(--rtbar, transparent);
  }
  .tm-list .room-card.tr.rt-science { --rtbar: var(--c-science); }
  .tm-list .room-card.tr.rt-major { --rtbar: var(--c-major); }
  .tm-list .room-card.tr.rt-minor { --rtbar: var(--c-minor); }
  .tm-list .room-card.tr.rt-extend { --rtbar: var(--c-extend); }
  .tm-list .room-card.tr.writing { --rtbar: var(--amber); }

  .tm-list .tr-top { display: flex; align-items: center; gap: 8px; min-width: 0; }
  .tm-list .tr-name {
    font-size: calc(18px * var(--fs, 1)); font-weight: 700; line-height: 1.25; color: var(--ink);
    min-width: 0; flex: 0 1 auto;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* 类型胶囊：有类型 = 实心浅色底；未设置 = 虚线框（稿子里两种都画了）
     z344：用户要求**靠右对齐**（原来紧跟标题，标题短的时候胶囊就浮在中间）。
     用 margin-left:auto 顶到右边；徽标排在它右侧，只有真有未读/待办时才占位。 */
  .tm-list .tr-top .rt-pill {
    flex: none; margin-left: auto;
    font-size: calc(11.5px * var(--fs, 1)); font-weight: 500;
    line-height: 1.5; padding: 2px 8px; border-radius: 7px;
    background: var(--rts); border: 1px solid transparent;
  }
  .tm-list .tr-top .rt-pill.none {
    background: transparent; border: 1px dashed #d3dcd9; color: var(--ink-3);
  }
  .tm-list .tr-top .room-tag { flex: none; margin-left: auto; }
  .tm-list .tr-badges { margin-left: 6px; flex: none; display: flex; align-items: center; gap: 4px; }
  .tm-list .tr-badges:empty { display: none; }
  .tm-list .tr-mem { margin-top: 6px; min-width: 0; line-height: 1.25; }
  /* expMembersHtml 返回的是胶囊 badge，行内把它还原成一行灰字 */
  .tm-list .tr-mem .badge.exp,
  .tm-list .tr-mem .badge.exp.none,
  .tm-list .tr-mem .badge.exp.loading {
    display: inline-block; max-width: 100%;
    background: none; border: 0; padding: 0; border-radius: 0;
    font-size: calc(14px * var(--fs, 1)); font-weight: 400; line-height: 1.35; color: #7f8c88;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .tm-list .tr-foot { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
  .tm-list .tr-stats {
    font-size: calc(14px * var(--fs, 1)); line-height: 1.35; color: #8d9a96;
    font-variant-numeric: tabular-nums;
  }
  .tm-list .tr-stats b { font-weight: 400; color: #8d9a96; }
  .tm-list .tr-act {
    margin-left: auto; flex: none; display: flex; align-items: center; gap: 6px;
    font-size: calc(12px * var(--fs, 1)); line-height: 1.35; color: var(--ink-3);
  }
  /* 「进行中 / 已归档」浅绿胶囊 */
  .tm-list .tr-st {
    font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; line-height: 1.4;
    color: #1a564c; background: #e8f3ee; border-radius: 9px; padding: 5px 12px;
  }
  /* 未加入时的「申请加入 / 直接加入」按钮 */
  .tm-list .tr-act .btn-primary.sm,
  .tm-list .tr-act .btn-ghost.sm,
  .tm-list .tr-act .btn-danger.sm {
    height: 30px; min-height: 0; padding: 0 13px; border-radius: 9px;
    font-size: 12px; font-weight: 600; line-height: 1; box-shadow: none;
  }
  .tm-list .tr-act .btn-primary.sm {
    background: var(--teal-soft); color: var(--teal); border: 1px solid transparent;
  }
  .tm-list .tr-act .jc-hint { display: none; }

  /* ---------- 房间页：三分区改描边卡（C2 那套 border） ---------- */
  .scope-tabs {
    display: flex; gap: 6px; align-items: center;
    margin: 0 0 8px; padding: 0;
    background: transparent; border-bottom: 0; overflow: visible;
  }
  .scope-tabs button {
    flex: 1 1 0; min-width: 0; height: 33px; padding: 0 4px;
    display: flex; align-items: center; justify-content: center; gap: 4px;
    border: 1px solid var(--line); border-radius: 9px;
    background: var(--panel); color: var(--ink-2);
    font-size: calc(12px * var(--fs, 1));
    white-space: nowrap; overflow: hidden;
  }
  .scope-tabs button.on {
    background: var(--teal-soft); border-color: var(--teal-2);
    color: var(--teal); font-weight: 700;
  }
  .scope-tabs button.on::after { display: none; }

  /* ---------- 抽屉：线性图标 + 成员头像墙 + 分组设置 ---------- */
  .dw-grid > div { display: flex; flex-direction: column; align-items: center; }
  .dw-grid i {
    width: 38px; height: 38px; margin: 0 auto 6px; border-radius: 11px;
    background: var(--teal-soft); color: var(--teal);
  }
  .dw-grid i svg { width: 19px; height: 19px; }
  .dw-grid > div > span { font-size: calc(10.5px * var(--fs, 1)); line-height: 1.2; }

  .dw-mem {
    background: var(--panel); margin: 0 12px 10px; padding: 11px 13px 13px;
    border-radius: 12px; border: 1px solid var(--line-soft);
  }
  /* z338：成员子页顶部的「有数字但没入口」说明条（加入申请无权限 / 技能在别处） */
  .dw-hint {
    margin: 0 12px 10px; padding: 10px 12px; border-radius: 12px;
    background: var(--amber-soft); border: 1px solid var(--amber-soft);
    font-size: calc(11.5px * var(--fs, 1)); line-height: 1.6; color: var(--ink-2);
  }
  .dw-hint p { margin: 0; }
  .dw-hint p + p { margin-top: 4px; }
  .dw-hint .btn-ghost { margin-top: 8px; }
  .dw-mem-h { display: flex; align-items: center; justify-content: space-between; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); cursor: pointer; }
  .dw-mem-h i { font-style: normal; font-size: 9.5px; font-family: var(--mono, ui-monospace, monospace); }
  /* z338：成员墙标题行右侧 —— 人数 + 待处理徽标 + 箭头（整行可点 → 成员子页） */
  .dw-mem-r { display: flex; align-items: center; gap: 6px; }
  .dw-mem-r .dw-todo {
    font-style: normal; font-size: 9.5px; font-weight: 600; letter-spacing: .01em;
    color: var(--amber); background: var(--amber-soft); border-radius: 5px; padding: 2px 6px;
  }
  .dw-mem-r u { text-decoration: none; font-size: calc(12px * var(--fs, 1)); color: var(--ink-4, #b3bfbd); }
  .dw-mem-wall { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 9px; }
  .dwm-av {
    width: 34px; height: 34px; border-radius: 50%; border: 0; padding: 0;
    background: var(--teal); color: #fff; font-size: 12px; font-weight: 600;
    display: flex; align-items: center; justify-content: center; cursor: pointer;
    font-family: inherit;
  }
  .dwm-av:active { opacity: .72; }
  .dwm-more { background: var(--line-soft); color: var(--ink-3); border: 1px dashed var(--line); font-weight: 500; }

  .dw-list .rw { justify-content: flex-start; gap: 9px; }
  .dw-list .rw .rw-ic { width: 17px; height: 17px; flex: none; color: var(--ink-2); display: flex; }
  .dw-list .rw .rw-ic svg { width: 100%; height: 100%; }
  .dw-list .rw .rw-t { flex: 1 1 auto; min-width: 0; }
  .dw-list .rw .rt { flex: none; }
  .dw-list .rw.danger .rw-ic { color: var(--danger, #b4443a); }
}


/* z326：桌面专用块（手机隐藏）—— 用于团队页空状态，避免两端各渲染一次 */
@media (max-width: 620px) { .tm-desk-only { display: none; } }

/* ══════════════════════════════════════════════════════════════════
   z327：手机端首页（K1 定稿）+「更多」独立整页
   ------------------------------------------------------------
   · `.hm-*` 只在 ≤620px 出现 —— 桌面端首页结构一字未改（沿用 z326 的做法：
     新增结构默认 display:none，只有手机才打开，回滚只需删掉这一段）。
   · `.mr-*` 是**真实页面**（#/more），全端可用；但所有规则都挂在 `.mr` 之下 ——
     `.member-row` 已有一族 `.mr-main / .mr-acts`，不加作用域会串到成员列表上。
   · 颜色一律走主题变量：首页是每天第一眼看的页面，硬编码青绿在暖色主题下必翻车。
   ══════════════════════════════════════════════════════════════════ */
.hm { display: none; }

/* z328：临时测试区（「【自动化】…」房间）不再展示给任何人 —— 手机首页末尾那段、
   以及桌面「临时测试区」折叠区，一律看不见。
   但节点照旧渲染在 DOM 里：自动化脚本仍能 `.click()` 进房间（display:none 不影响程序化点击），
   所以回滚只需删掉这三条规则。 */
.hm-sub-test, .hm-row.test, .home-sec.test-sec { display: none !important; }

/* ─── 图标：只定盒子，尺寸由各处的 width/height 决定 ─── */
.mr .ic, .mr .s, .hm .ic, .hm .s, .hm .bx { display: block; flex: 0 0 auto; }
.mr .ic svg, .mr .s svg, .hm .ic svg, .hm .s svg, .hm .bx svg { display: block; width: 100%; height: 100%; }

/* ═══════════════ 更多（#/more · 全端） ═══════════════ */
/* z328：更多页（#/more）也按 K1 定稿的固定色落地 —— 只重定义这里的变量，
   规则一条不改；否则在暖阳陶土主题下整页会变成陶土橙，跟设计稿差一截。 */
.mr {
  max-width: 640px; margin: 0 auto;
  --ink: #17211f; --ink-2: #4a5c59; --ink-3: #93a3a1;
  --line: #e9efed; --line-soft: #f2f6f4;
  --panel: #fff; --bg: #f5f7f6;
  --teal: #0f5c54; --teal-2: #178277; --teal-soft: #eef3f1;
  --danger: #b42318;
}
.mr .mr-row .ic { color: #37655e; }
.mr .mr-top { display: flex; align-items: center; gap: 6px; height: 48px; }
.mr .mr-bk {
  width: 32px; height: 32px; margin-left: -8px; border: 0; background: none; border-radius: 10px;
  display: grid; place-items: center; color: var(--ink); cursor: pointer;
  font-size: calc(25px * var(--fs, 1)); line-height: 1;
}
.mr .mr-bk:hover { background: var(--line-soft); }
.mr .mr-top h1 { margin: 0; flex: 1 1 auto; min-width: 0; font-size: calc(25px * var(--fs, 1)); font-weight: 700; }
.mr .mr-ava {
  width: 32px; height: 32px; border-radius: 10px; color: #fff; display: grid; place-items: center;
  font-size: calc(15px * var(--fs, 1)); font-weight: 600; flex: 0 0 auto;
}
.mr .mr-sea {
  display: flex; align-items: center; gap: 8px; height: 40px; margin-top: 6px; padding: 0 11px;
  border-radius: 11px; background: var(--panel); border: 1px solid var(--line); color: var(--ink-3);
}
.mr .mr-sea .s { width: 15px; height: 15px; }
.mr .mr-sea input {
  flex: 1 1 auto; min-width: 0; height: 100%; border: 0; background: none; color: var(--ink);
  font-size: calc(15.63px * var(--fs, 1)); font-family: inherit;
}
.mr .mr-sea input:focus { outline: 0; }
.mr .mr-g { margin: 0; }
.mr .mr-gh { padding: 17px 2px 8px; font-size: calc(11.88px * var(--fs, 1)); color: var(--ink-3); letter-spacing: .1em; }
.mr .mr-g4 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.mr .mr-cell {
  display: flex; align-items: center; gap: 9px; padding: 13px 12px; text-align: left;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  cursor: pointer; font: inherit; color: var(--ink); min-width: 0;
}
.mr .mr-cell:hover { border-color: var(--teal-2); }
.mr .mr-cell .u {
  width: 30px; height: 30px; border-radius: 9px; background: var(--teal-soft); color: var(--teal);
  display: grid; place-items: center; flex: 0 0 auto;
}
.mr .mr-cell .u .ic { width: 16px; height: 16px; }
.mr .mr-cell .tx { flex: 1 1 auto; min-width: 0; }
.mr .mr-cell b { display: block; font-size: calc(15.63px * var(--fs, 1)); font-weight: 600; }
.mr .mr-cell i {
  display: block; font-style: normal; font-size: calc(11.88px * var(--fs, 1)); color: var(--ink-3);
  margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mr .mr-cell .bd {
  flex: 0 0 auto; font-style: normal; font-size: calc(11.88px * var(--fs, 1)); font-weight: 600;
  border-radius: 8px; padding: 1px 5px; color: #fff; background: var(--danger);
}
.mr .mr-rws { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.mr .mr-row {
  display: flex; align-items: center; gap: 10px; width: 100%; height: 50px; padding: 0 12px;
  text-align: left; background: none; border: 0; border-bottom: 1px solid var(--line-soft);
  cursor: pointer; font: inherit; color: var(--ink);
}
.mr .mr-row:last-child { border-bottom: 0; }
.mr .mr-row:hover { background: var(--line-soft); }
.mr .mr-row .ic { width: 17px; height: 17px; color: var(--teal); }
.mr .mr-row .t { flex: 1 1 auto; min-width: 0; font-size: calc(15.63px * var(--fs, 1)); }
.mr .mr-row .sub { font-style: normal; font-size: calc(13.13px * var(--fs, 1)); color: var(--ink-3); }
.mr .mr-row .bd {
  font-style: normal; font-size: calc(12.5px * var(--fs, 1)); font-weight: 600;
  color: #fff; background: var(--danger); border-radius: 8px; padding: 1px 5px;
}
.mr .mr-row .ar { font-size: calc(16.25px * var(--fs, 1)); color: var(--ink-3); opacity: .45; }
.mr .mr-row.danger, .mr .mr-row.danger .ic { color: var(--danger); }
.mr .mr-ft { padding: 18px 0 10px; text-align: center; font-size: calc(11.88px * var(--fs, 1)); color: var(--ink-3); opacity: .85; }
.mr .hidden { display: none !important; }

/* ═══════════════ 手机端首页（K1） ═══════════════ */
@media (max-width: 620px) {
  /* 首页自带页内顶栏（标题 + 搜索 + ⋮），全局顶栏整条让位；
     其它页面不动 —— 它们仍然靠顶栏导航（体例见 route() 里的 body.in-home）。 */
  body.in-home .topbar { display: none; }
  body.in-home { background: var(--bg); }
  body.in-home main { padding: 0 0 44px; }
  /* 更多页同理：它自带 ‹ 更多 + 头像那一行，再叠全局顶栏就是三层标题 */
  body.in-more .topbar { display: none; }
  body.in-more { background: var(--bg); }
  body.in-more main { padding: 0 13px 40px; }
  /* 团队页（A1）：页内头自带 ‹ 返回 + 团队名 + 类型胶囊 + ⋯，全局顶栏让位 */
  body.in-team .topbar { display: none; }
  body.in-team { background: var(--bg); }
  body.in-team main { padding: 0 0 40px; }
  body.in-more .mr .mr-top { height: 52px; }
  /* z353（用户要求）：标题字号缩小 20%（20 → 16px）；文字「更多」→「返回」在
     renderMorePage() 里按视口宽度切换，桌面端仍是 20px「更多」。 */
  body.in-more .mr .mr-top h1 { font-size: calc(20px * var(--fs, 1)); }
  /* 桌面端那套（统计卡 + 两块分区 + 折叠区）在手机上不画 —— 数据仍在 DOM 里，轮询照旧更新 */
  body.in-home .stat-row, body.in-home .home-sec { display: none; }

  /* ============================================================
     z341：手机端首页（设计稿 v1）
     · 绿渐变卡：顶部贴边、左右内缩 6px、只圆下面两角
     · 白色导航卡上浮 20px，压住绿卡底边
     · 公告条 / 下划线分段 / 团队卡列表
     ⚠️ 只改外观：id 与 data-* 全没动（hmSearchBtn / hmMoreBtn / hmSea / hmUpd /
        hmNotice / hmList / data-hmstat / data-hmseg / data-hmgo / data-hmroom）
     ============================================================ */
  .hm {
    display: block; background: var(--bg); min-height: 100vh; color: var(--ink);
    /* z349：用户「首页文字太大了，整体调成现在的 92%」。
       只压字号（--fs 只被 font-size 用），图标 / 头像 / 卡片 / 间距一个像素都不动。 */
    --fs: calc(var(--fs-user, 1) * 0.92);
  }
  /* 绿渐变卡（设计稿：margin:0 6px；radius 0 0 26px 26px；padding 14 20 40） */
  .hm-head {
    margin: 0 6px;
    padding: 14px 20px 40px;
    border-radius: 0 0 26px 26px;
    background: linear-gradient(158deg, #0c4139 0%, #145a4f 58%, #1d6d5e 100%);
  }
  .hm-r1 { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; padding-top: 8px; }
  .hm-hello { flex: 1 1 auto; min-width: 0; }
  .hm-hello b {
    /* z451：用户要求「欢迎回来四个字增大 12%」—— 22px × 1.12 = 24.64px */
    display: block; font-size: calc(24.64px * var(--fs, 1)); font-weight: 700;
    letter-spacing: .3px; line-height: 1.24; color: #fff;
    /* 名字长的时候单行省略，不许折成两行把绿卡顶高（设计稿是一行） */
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .hm-hello i {
    display: block; font-style: normal; margin-top: 7px;
    font-size: calc(15px * var(--fs, 1)); color: rgba(255, 255, 255, .72);
  }
  /* z450：用户要求「圆圈保留，图标缩小 10%，三个图标之间间距也缩小 10%」。
     圆圈仍是 40px（点击热区不变，手指好点），只把里面的线条从 20px 收到 18px、
     间距从 8px 收到 7.2px —— 省出约 1.6px 宽度给左边问候语，视觉上也更透气。 */
  .hm-ics { display: flex; align-items: center; gap: 7.2px; flex: 0 0 auto; }
  .hm-ic {
    position: relative; width: 40px; height: 40px; display: grid; place-items: center;
    border: 0; border-radius: 50%; background: rgba(255, 255, 255, .14);
    color: #fff; cursor: pointer;
  }
  .hm-ic .ic { width: 18px; height: 18px; }
  .hm-ic.on { background: rgba(255, 255, 255, .28); }
  .hm-ic.has-badge::after {
    content: ''; position: absolute; top: 8px; right: 9px; width: 6px; height: 6px;
    border-radius: 50%; background: #ff6b5e; box-shadow: 0 0 0 1.5px #145a4f;
  }
  .hm-sea { display: none; }
  .hm-sea.on {
    display: flex; align-items: center; gap: 8px; margin: 12px 18px 0; padding: 0 12px;
    border-radius: 14px; background: #fff; box-shadow: var(--sh-card);
  }
  .hm-sea .s { width: 16px; height: 16px; color: var(--ink-3); }
  .hm-sea input {
    flex: 1 1 auto; min-width: 0; height: 42px; border: 0; background: none; color: var(--ink);
    font-size: calc(14px * var(--fs, 1)); font-family: inherit;
  }
  .hm-sea input:focus { outline: 0; }
  .hm-upd { display: none; }
  .hm-upd.on {
    display: flex; align-items: center; gap: 8px; margin: 12px 18px 0; padding: 0 8px 0 12px; height: 40px;
    border-radius: 12px; background: #fdf4e3; border: 1px solid #f2e6cf;
    font-size: calc(12px * var(--fs, 1)); color: #4a3d28;
  }
  .hm-upd .txt { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .hm-upd .go {
    flex: 0 0 auto; border: 0; border-radius: 8px; padding: 5px 10px; cursor: pointer;
    background: #a8791f; color: #fff; font-size: calc(11.5px * var(--fs, 1)); font-family: inherit;
  }
  /* 三格统计：绿卡里的三块半透明玻璃格（设计稿 32px / 800，标签 13.5px） */
  .hm-stat {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 18px;
  }
  .hm-stat > div {
    padding: 13px 10px 12px 14px; border-radius: 14px; cursor: pointer;
    background: rgba(255, 255, 255, .10);
  }
  .hm-stat > div:active { background: rgba(255, 255, 255, .18); }
  .hm-stat .n {
    /* z349：用户「52 0 0 这几个统计数字还是太大了，再调小 10%」
       ⇒ 32 → 28.8（首页整体 92% 之后再乘 0.9，实际 26.5） */
    display: block; font-size: calc(28.8px * var(--fs, 1)); line-height: 1; font-weight: 800;
    letter-spacing: -.03em; font-variant-numeric: tabular-nums; color: #fff;
  }
  .hm-stat i {
    display: block; font-style: normal; font-size: calc(13.5px * var(--fs, 1)); margin-top: 8px;
    color: rgba(255, 255, 255, .72);
  }
  /* 待我处理的数字用琥珀色（有 0 条时也是暖色，与「我的房间 / 未读消息」区分开） */
  .hm-stat .d2 .n { color: #f0cd94; }

  /* 五个入口：白色导航卡上浮 20px，压住绿卡底边（设计稿 margin:-20px 18px 0） */
  .hm-five {
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
    margin: -20px 18px 0; padding: 14px 4px 12px;
    background: #fff; border-radius: 18px;
    box-shadow: 0 6px 20px rgba(6, 40, 34, .08);
  }
  .hm-five a {
    position: relative; display: flex; flex-direction: column; align-items: center; gap: 8px;
    text-decoration: none; color: var(--ink-2); font-size: calc(14px * var(--fs, 1));
  }
  .hm-five .bx {
    width: 44px; height: 44px; border-radius: 14px; display: grid; place-items: center;
    background: var(--g050); color: var(--g700);
  }
  /* ⚠️ 方块本身就是 .bx（图标 span），里面的 svg 必须定死 22px ——
     不加这条会被上面的 `.hm .bx svg{width:100%;height:100%}` 撑满整个方块（实测 65×42），
     图标放大三倍、笔画跟着变粗，这就是 z328「图标跟 K1 对不上」的真凶。 */
  .hm .hm-five .bx > svg { width: 22px; height: 22px; }
  .hm-five a.on { color: var(--g800); font-weight: 700; }
  .hm-five a.on .bx {
    background: var(--g800); color: #fff;
    box-shadow: 0 6px 14px rgba(15, 76, 68, .26);
  }
  .hm-five .nav-badge {
    position: absolute; top: -4px; right: calc(50% - 26px); margin: 0; height: 17px; min-width: 17px;
    font-size: calc(10px * var(--fs, 1)); padding: 0 4px; z-index: 2;
    box-shadow: 0 0 0 1.5px #fff;
  }
  /* 公告条（设计稿：白卡 + 琥珀标签 + 18.5px 正文，没有左侧竖条、没有铃铛） */
  .hm-ntc {
    position: relative; display: flex; align-items: center; gap: 9px; margin: 14px 18px 0;
    padding: 12px 13px; border-radius: 14px; cursor: pointer;
    background: #fff; box-shadow: var(--sh-card);
  }
  .hm-ntc .b { display: none; }
  .hm-ntc .tag {
    flex: 0 0 auto; font-size: calc(14px * var(--fs, 1)); font-weight: 700; color: #a8791f;
    background: #fdf4e3; border-radius: 8px; padding: 3px 7px; white-space: nowrap;
  }
  .hm-ntc .txt {
    flex: 1 1 auto; min-width: 0; font-size: calc(18.5px * var(--fs, 1)); color: #26302c;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .hm-ntc .ago { flex: 0 0 auto; font-size: calc(13.5px * var(--fs, 1)); color: var(--ink-3); }
  .hm-ntc .go { flex: 0 0 auto; font-size: calc(16px * var(--fs, 1)); color: #c8d2ce; }
  /* 分段：下划线式（不是胶囊），17px / 700，激活项下面一条 3px 圆头短线 */
  .hm-seg { display: flex; gap: 10px; padding: 16px 18px 0; }
  .hm-seg button {
    flex: 1 1 0; position: relative; border: 0; background: none; cursor: pointer; font-family: inherit;
    padding: 0 0 11px; text-align: left;
    font-size: calc(17px * var(--fs, 1)); font-weight: 700; color: var(--ink-3);
  }
  .hm-seg button u {
    text-decoration: none; font-family: inherit; font-weight: 400;
    font-size: calc(15px * var(--fs, 1)); margin-left: 6px; color: var(--ink-3);
  }
  .hm-seg button.on { color: var(--g800); }
  .hm-seg button.on u { color: var(--g800); opacity: .66; }
  .hm-seg button.on::after {
    content: ''; position: absolute; left: 0; bottom: 0;
    width: 34px; height: 3px; border-radius: 2px; background: var(--g800);
  }
  /* 列表：整块透明，每行是一张独立白卡 */
  .hm-list { background: transparent; border-top: 0; padding: 12px 13px 0; }
  .hm-row {
    display: flex; align-items: center; gap: 12px; margin: 0 0 10px; padding: 14px;
    background: #fff; border: 0; border-radius: 16px; box-shadow: var(--sh-card); cursor: pointer;
  }
  .hm-row:active { background: #fbfdfc; }
  .hm-row .av {
    width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center;
    color: #fff; font-size: calc(20px * var(--fs, 1)); font-weight: 700; flex: 0 0 auto;
  }
  /* z338（用户要求「团队图标统一青绿」）：浅青底 + 深青字，与首页五个入口同一色系。
     ⚠️ 只作用于团队行（.tint）——归档行/测试行仍是中性灰，不能一锅端。 */
  .hm-row .av.tint { color: var(--g800); font-weight: 700; }
  .hm-row .mid { flex: 1 1 auto; min-width: 0; display: block; }
  /* z349：团队名在 92% 基础上再 -8% ⇒ 22 → 20.24（实际 18.62），字重 400 → 500 */
  .hm-row .t { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: calc(20.24px * var(--fs, 1)); font-weight: 500; color: var(--ink); }
  .hm-row .t .nm { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .hm-row .t .rl {
    flex: 0 0 auto; font-size: calc(12.5px * var(--fs, 1)); font-weight: 700; white-space: nowrap;
    color: var(--g800); background: #e8f4f1; border-radius: 6px; padding: 2px 7px;
  }
  .hm-row .t .rl.tu { color: #a8791f; background: #fdf4e3; }
  .hm-row .s {
    display: block; margin-top: 7px; font-size: calc(15px * var(--fs, 1)); color: var(--ink-2);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .hm-row .s em { font-style: normal; color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
  .hm-row .bd { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
  .hm-row .bd i { font-style: normal; font-size: calc(11px * var(--fs, 1)); font-weight: 700; border-radius: 9px; padding: 1px 6px; }
  .hm-row .bd i.u { color: #fff; background: #d9534f; }
  .hm-row .bd i.a { color: #a8791f; background: #fdf4e3; }
  .hm-row .ch { flex: 0 0 auto; font-size: calc(18px * var(--fs, 1)); color: #c8d2ce; }
  .hm-row.test .av { color: #4a5c59; }
  .hm-sub {
    display: flex; align-items: center; justify-content: space-between;
    padding: 4px 4px 9px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); letter-spacing: .04em;
  }
  .hm-sub u { text-decoration: none; font-family: var(--mono); }
  .hm-new {
    display: flex; align-items: center; gap: 9px; height: 52px; padding: 0 16px; margin: 0 0 10px;
    cursor: pointer; border-radius: 16px; background: #fff; box-shadow: var(--sh-card);
    color: var(--ink-3); font-size: calc(14px * var(--fs, 1));
  }
  .hm-new s {
    text-decoration: none; width: 24px; height: 24px; border-radius: 8px; flex: 0 0 auto;
    border: 1px dashed #c6d3d1; display: grid; place-items: center;
    font-size: calc(13px * var(--fs, 1)); color: var(--ink-3);
  }
  .hm-new:active { background: #fbfdfc; }
  .hm-empty {
    padding: 42px 20px; margin: 0 0 10px; border-radius: 16px; text-align: center;
    background: #fff; box-shadow: var(--sh-card); color: var(--ink-3);
    font-size: calc(13.5px * var(--fs, 1));
  }
}

/* ============================================================
   z345：手机端房间页 —— 按设计稿重做（房间头 / 分段 / 消息 / 输入区）
   ------------------------------------------------------------
   稿子（390×844 @2x）拆成四块：
     ① 房间头：‹ + 20px 粗体房间名 + 右侧一个白色圆角 ☰
        （用户明确要求：**删掉 ⋯，只留 ☰**；按钮 DOM 保留，抽屉里的
         「房间信息 / 分组 / 归档」是转交给它的 .click()）
     ② 分段：浅灰底槽（#e6eae8，r14）+ 三段等分 + 选中白药丸（r11）
     ③ 消息：38px 圆头像、16px 名字、无尾巴气泡（白 / 浅青绿）、r16、17px 正文
     ④ 输入区：44 圆「+」、胶囊输入框（#f1f3f2）、文档图标、深青「发送」
   全部限定 ≤620px，桌面端一行不动；文案/DOM/路由/回执一律沿用。
   ============================================================ */
@media (max-width: 620px) {
  /* ---------- ① 房间头 ---------- */
  /* z353（方案 A）：输入条以下不再留 16px 页面底距 —— 此前「输入条 → 视口底」实测 16px
     （body.in-room main 的 padding-bottom:16px，4576 行那条在手机端仍然生效），
     加上 composer 自身的 11px，就是截图里输入条下面那条空白带。
     现在只留 composer 自己的 padding + 系统安全区，输入条贴到屏幕底（微信行为）。
     只在手机端生效，桌面端 body.in-room main 的 16px 不动。 */
  body.in-room main { padding-bottom: 0; }
  /* 左右内边距 15px = 下方页签槽的左右外边距。
     ⇒ 「返回箭头墨迹左缘」与「页签槽左边框」落在同一条竖线上（用户 2026-11-03 明确要求），
        ☰ 的右缘同理与槽右缘对齐。改这两个值必须成对改，否则会出现 1~2px 的错位。 */
  .room-head {
    padding: 6px 15px 10px; margin: 0; gap: 6px;
    background: transparent; border-bottom: 0;
  }
  /* z347b：三件东西（箭头 / 房间名 / ☰ chip）的**视觉中心必须在同一条横线上**
     （用户 2026-11-03：「箭头的纵向位置、房间名称那一行字、右边三条杠 chip
      这三个的中心位置的纵向高度应该一样的」）。
     ① 改 line-height（1.35→1.2→24px）**没用**：对「flex 居中」的单行文字，
        行盒变小后盒子上移 Δ/2、行内文字相对盒子上移 Δ/2，正好抵消，墨迹一动不动。
     ② 只有盒子位移能挪墨迹，而 align-items:center 下 margin-top 负值的位移 = 值的一半，
        且**按 1px 吸附**（-1px→墨迹 66.5、-2px→64.5，中间值不存在）。
     ③ 所以先 margin-top:-1px 落到 66.5，再用 `transform: translateY(-.5px)` 补最后半像素
        （transform 走渲染层，不参与吸附）⇒ 名字墨迹中心 65.5。
     2x 截图同框实测（基准 = ☰ 白 chip 的几何中心，避开整页 1px 抖动）：
       chip 65.5 / 箭头墨迹 65.5 / 房间名墨迹 65.5 —— 三者严格同高（改前名字 68.5，低 1.5px）。 */
  .room-head h2 {
    /* z349：用户「房间页顶部的房间名称字体调小 10%」⇒ 21 → 18.9 */
    font-size: calc(18.9px * var(--fs, 1)); font-weight: 700; line-height: 1.35;
    /* 字号从 21 缩到 18.9 后，行盒与字面一起变小 ⇒ 原来给 21px 调好的 -1px 反而把名字顶高了
       2.5 个设备像素（实测 63.0 vs chip 中心 65.5）。margin 归 0 回落 ~2px，
       translateY 用 -0.25px 补最后 0.5px。 */
    margin: 0; transform: translateY(-0.5px);
  }
  /* z347：返回箭头**去掉白色底框**（用户：箭头没有白框），也不再靠负 margin 往左顶 ——
     盒子左缘就是 15px 那条线，箭头墨迹（SVG 描边）紧贴盒子左缘起笔。 */
  .room-head .rm-back {
    width: 9px; height: 32px; padding: 0; margin: 1px 2px 0 0;
    background: transparent; border: 0; box-shadow: none;
    color: var(--ink); font-size: 0;
    justify-content: flex-start;
  }
  .room-head .rm-back svg { display: block; width: 9px; height: 16px; }
  .rm-tools { gap: 0; }
  .rm-tools #rmMore { display: none; }        /* 用户要求：⋯ 删掉 */
  /* ⚠️ 白底圆角只给 .rm-tools 里的图标（☰），**不能**写裸 `.rm-ico` ——
     .rm-back 也带 .rm-ico，裸选择器会把白框一起发给箭头（z347 前就是这样）。 */
  .rm-tools .rm-ico {
    width: 32px; height: 32px; border-radius: 10px;
    background: #fff; color: var(--ink);
    font-size: calc(16px * var(--fs, 1));
    box-shadow: 0 1px 3px rgba(24, 60, 52, .07);
  }
  /* 注：☰ 字形（三条杠）本身在字体 em 框里偏下 0.5px（实测 66.5 vs chip 中心 65.5，设备像素）。
     试过 padding-bottom 1 / 1.25 / 1.5 / 2px —— 字形基线按 1 逻辑像素（2 设备像素）吸附，
     只能在「低 1 设备像素」和「高 1 设备像素」之间二选一，拿不到正好居中，故不加 hack：
     白色 chip 的几何中心已与箭头、房间名严格齐平，肉眼不可分辨的是字形本身。 */
  .rm-tools .rm-ico:active { background: #f4f7f6; }

  /* ---------- ② 分段槽 ---------- */
  .scope-tabs {
    margin: 0 15px 10px; padding: 3px; gap: 0;
    height: 47px; box-sizing: border-box;
    background: #e6eae8; border-bottom: 0; border-radius: 14px;
  }
  .scope-tabs button {
    height: 41px; padding: 0 4px;
    display: flex; align-items: center; justify-content: center;
    border: 0; border-radius: 13px; background: transparent;
    color: #5b6a66; font-size: calc(15px * var(--fs, 1)); font-weight: 600;
  }
  .scope-tabs button.on {
    /* z347：选中页签的字改成青绿色（用户：「日常实验四个字是青绿色」），
       原来跟正文一样的近黑色 #16211f。取全站主色 --teal，和 ☰ / 发送键同一色相。 */
    color: var(--teal); font-weight: 700;
    background: #fff; box-shadow: 0 1px 3px rgba(24, 60, 52, .09);
  }
  .scope-tabs button.on::after { display: none; }   /* 去掉旧的下划线 */

  /* ---------- ③ 消息 ---------- */
  /* 稿子里聊天区就是页面底色，没有白色卡片壳 */
  .discuss { background: transparent; border: 0; border-radius: 0; }
  .msgs { padding: 4px 14px 6px; gap: 18px; }
  /* z353（用户选定方案 A·微信式贴底）：消息少时不再顶在页签下、下方留出大空白
     （实测空白可达 265px——#msgs 被 flex 拉伸占满，子项却从顶部开始排）。
     实现：给第一个子元素 margin-top:auto 吃掉全部富余空间 ⇒ 消息整体贴住输入条上缘；
     内容超一屏时 auto 解析为 0，贴底滚动（pinBottomSoon）与 z324 机制完全不受影响。
     排除 .empty：空房间的引导卡仍从顶部开始排。 */
  #msgs > :first-child:not(.empty) { margin-top: auto; }
  /* z356（用户两次报障：点课题类型 chip 时「整个房间卡闪一下，像点了卡片」）：
     排查结论——手机点按会同时命中三样东西，逐一压掉：
     ① 浏览器默认的 tap highlight（灰蒙层，Android WebView 会盖在被点区块上）；
     ② :hover 反馈（441/1085 已改成只有 .has-mouse 才生效；这里再兜底关阴影与过渡，
        但绝不写 border-color —— 会连左缘类型色条一起染色，那是第一版修复自伤）；
     ③ chip 自己的 hover 底色（.rt-pill 已拆成只在 .has-mouse 下给）。 */
  .room-card, .room-card *, .rt-pill, .tm-list { -webkit-tap-highlight-color: transparent; }
  .room-card:hover { box-shadow: none; transition: none; }
  .msg { gap: 10px; }
  .msg .av {
    width: 38px; height: 38px; border-radius: 50%;
    font-size: calc(17px * var(--fs, 1)); font-weight: 500;
  }
  .msg.other .av { background: #1c5a4d; color: #fff; }
  .msg .who { gap: 8px; margin-bottom: 4px; }
  .msg .who b { font-size: calc(17px * var(--fs, 1)); font-weight: 600; }
  .msg .who .time { font-size: calc(15px * var(--fs, 1)); color: #93a09c; }
  /* 角色胶囊：稿子里统一浅绿底深青字（原来按角色分红/紫/琥珀，
     手机上那一小串彩色胶囊比气泡本身还抢眼） */
  .msg .who .badge {
    font-size: calc(12px * var(--fs, 1)); font-weight: 500;
    padding: 2px 7px; border-radius: 6px;
    background: #e6f1ec; color: #1a564c;
  }
  /* z350：气泡宽度 76% → 92%（用户选 B2），内边距 17 → 11，圆角 16 → 12。
     文字占气泡比例：76%/17px 时 82.4% → 92%/11px 时 92.4%。

     ⚠️⚠️ z370：z350 当年顺手加的 `text-align: justify` **已撤销，不要再加回来**。
     原因（用户实测反馈「稀疏不一致」，逐字量过）：
       中文 justify 拉的是**字间距**（inter-ideograph），不是英文的**词间距**，所以它没法"悄悄"补空。
       气泡一行才 15 字，为补满一行要把几十像素摊到 15 个字上 —— 每字被撑开 0.85~3.8px。
       同一段文本实测（17px 字号、内容宽 266.9）：
         · 15 字行 → 字距 17.85px（正常 17，撑开 5%）
         · 13 字行 → 字距 20.82px（撑开 22%，肉眼一眼看出"这行好稀"）
         · 中英混排行 → 词距被拉到 4.11~17.25px 不等，"和"两侧空出巨大间隙
       即：**行末空白没有了，代价是每行的字距各不相同**，用户看到的正是这种"疏密不一致"。
     微信 / QQ / Telegram / iMessage 的聊天正文**全部是左对齐**，从不做两端对齐；
     中文两端对齐只适合一行 30 字以上的宽栏正文（书刊、A4 文档），窄气泡里必然翻车。
     行末留白本来也不是 padding 问题：气泡是 inline-block 的 fit-content，右缘永远贴着最长行，
     实测 left 下 15 字行行末只空 11.9px（0.7 个字），和左边 11px 一样。
     唯一会留白多一些的情况是中文里夹了长英文词（`standard deviation` 整体换行，留 45.9px），
     微信同样如此处理，属于中文排版固有的代价，不要用 justify 去"抹平"。 */
  .msg.other .txt, .msg.ai .txt, .msg.me .txt {
    border: 0; border-radius: 12px; padding: 11px;
    max-width: 92%;
    font-size: calc(17px * var(--fs, 1)); line-height: 1.5;
    box-shadow: none;
    text-align: left;
  }
  .msg.other .txt { background: #fff; }
  .msg.ai .txt { background: #fff; }
  .msg.me .txt { background: #dbefe6; }
  /* 稿子里气泡没有小尾巴，四角一样圆 */
  .msg.other .txt::before, .msg.ai .txt::before, .msg.me .txt::before { display: none; }
  .msg .txt p { margin: 0 0 6px; }
  .msg .txt h3, .msg .txt h4 { font-size: calc(16px * var(--fs, 1)); }
  /* 已读回执 */
  .rr-rcpt { font-size: calc(13px * var(--fs, 1)); color: #93a09c; }
  .rr-rcpt-btn, .rr-rcpt-quick, .rr-rcpt-li { font-size: calc(13px * var(--fs, 1)); color: #93a09c; }

  /* ---------- ④ 输入区 ----------
     z346：照用户截图重画（灰圆「+」→ 灰胶囊输入框 → 灰方「文档+星」→ 深青「发送」）
     四个控件同高 44，间距 10，左右边距 14；三块灰底一律 #f4f6f5、无描边。 */
  .composer {
    background: #fff; border-top: 1px solid #ebefee;
    /* z354：上内距 11 → 6px（用户：输入条上面的白带太高，「至少收到和下面一样」）。
       现在「最后消息 → 输入行」= msgs padB 6 + composer padT 6 = 12px，
       输入条下面 = 11px + 安全区 ⇒ 上 ≤ 下。 */
    padding: 6px 13px calc(11px + env(safe-area-inset-bottom, 0px));
  }
  .composer-row { gap: 9px; }
  .plus-btn {
    width: 40px; height: 40px; border-radius: 50%;
    border: 0; background: #f4f6f5; color: #46534f;
    box-shadow: none;
  }
  .plus-btn:active { background: #e9edec; }
  .plus-btn svg { width: 18px; height: 18px; }
  .composer textarea {
    height: 40px; min-height: 40px; padding: 10px 16px;
    border: 0; border-radius: 20px; background: #f4f6f5;
    font-size: calc(16px * var(--fs, 1));
    /* 稿子里输入框是干净的一枚胶囊：把 textarea 的滚动条彻底藏掉
       （单行时它只会在右边露一条 3px 的灰线，很像渲染残渣；
        多行时高度由 JS 的 auto-grow 撑开，用不到滚动条）。 */
    scrollbar-width: none;
  }
  .composer textarea::-webkit-scrollbar { width: 0; height: 0; }
  /* 占位只用 14px 且不换行：16px 时「发消息，@ 可唤起 Agent」在 44 高的框里
     会折成两行、多出一条竖滚动条（实测 sw>clientWidth）。文本框本身仍是 16px
     —— iOS 聚焦缩放的判定看的是 input 自身字号，不受 ::placeholder 影响。 */
  .composer textarea::placeholder {
    font-size: calc(14px * var(--fs, 1)); color: #a3a9a7;
    white-space: nowrap; overflow: hidden;
  }
  .composer textarea:focus { border: 0; box-shadow: none; }
  /* 稿子里「文件」是一枚 44×44 的浅灰方块，里面只有「文档 + 星」图标，不带文字框。
     注意：4268 行有一条 `.composer-row .gen-doc-btn`（order/margin-left:auto/32 高/圆角 8），
     选择器比我这里多一级，光写 `.gen-doc-btn` 盖不住 —— 必须同样带 `.composer-row`。 */
  .composer-row .gen-doc-btn {
    order: 0; flex: 0 0 auto; margin: 0; padding: 0;
    width: 40px; height: 40px; min-width: 40px;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; border-radius: 12px; background: #f4f6f5; color: #3d4b46;
  }
  .composer-row .gen-doc-btn:active { background: #e9edec; }
  .composer-row .gen-doc-btn.on { background: var(--teal); color: #fff; }
  .composer-row .gen-doc-btn svg { width: 21px; height: 21px; }
  .composer-row .gen-doc-btn span { display: none; }
  .composer-row #send, .composer-row .btn-primary {
    order: 0; flex: 0 0 auto;
    width: 66px; height: 40px; min-height: 40px; padding: 0;
    border: 0; border-radius: 12px;
    background: var(--teal); color: #fff;
    font-size: calc(16px * var(--fs, 1)); font-weight: 600;
    letter-spacing: .5px;
  }
  .composer-row #send:active { background: #0b3d38; }
}

/* z346：窄屏（≤384，iPhone SE / 13 mini 等）占位句会差几个像素被截断，
   单独缩 1px —— 只在窄屏生效，390 及以上仍是设计稿的 14px。 */
@media (max-width: 384px) {
  .composer textarea::placeholder { font-size: calc(13px * var(--fs, 1)); }
}

/* ══════════════════════════════════════════════════════════════════
   z348：手机房间抽屉 —— 按任务书 5.4 重做（2026-11-03）
   ------------------------------------------------------------
   结构（稿子）：浅色顶栏（‹ 聊天信息）→ 成员卡 → 四入口卡 → 设置卡 → 操作卡。
   数值全部照抄任务书 5.4 的表格；本段只在 ≤620px 生效，桌面端右侧栏一行不动。
   ⚠️ 稿子里**没有房间信息卡**（.dw-card）⇒ 这里 display:none。
      DOM 保留：桌面端右侧栏还在用它，且回滚只需删掉这一行。
   ══════════════════════════════════════════════════════════════════ */
@media (max-width: 620px) {
  /* ---------- 顶栏：浅色，标题 20/700 ---------- */
  .side-back {
    height: 52px; padding: 0 14px; gap: 4px;
    background: #fff; border-bottom: 1px solid var(--line-soft);
    font-weight: 400;
  }
  .side-back .bk { font-size: calc(23px * var(--fs, 1)); color: var(--ink); padding: 0 6px 0 0; }
  .side-back #sideBackTitle { font-size: calc(20px * var(--fs, 1)); font-weight: 700; color: var(--ink); }

  /* ---------- 抽屉体：16px 卡间距（这页留白要宽松） ---------- */
  #sideBody { padding: 12px 16px 28px; }

  /* 稿子里没有房间信息卡（桌面端仍在用，别删 DOM） */
  .dw-card { display: none; }

  .dw-mem, .dw-grid, .dw-list {
    margin: 0 0 16px; border: 0; border-radius: 16px;
    background: #fff; box-shadow: var(--sh-card);
  }
  .dw-mem:last-child, .dw-grid:last-child, .dw-list:last-child { margin-bottom: 0; }

  /* ---------- 成员卡：16/700 标题 + 3 个 40px 头像 + 「N ›」 ---------- */
  .dw-mem { padding: 14px 14px 15px; }
  .dw-mem-h { font-size: calc(16px * var(--fs, 1)); font-weight: 700; color: #3f4d49; }
  .dw-mem-h i { font-size: calc(16px * var(--fs, 1)); font-weight: 700; font-family: inherit; color: #3f4d49; }
  .dw-mem-r { gap: 5px; font-size: calc(14px * var(--fs, 1)); color: var(--ink-3); }
  .dw-mem-r u { font-size: calc(14px * var(--fs, 1)); color: var(--ink-3); }
  .dw-mem-r .dw-todo { font-size: calc(11px * var(--fs, 1)); padding: 2px 6px; border-radius: 6px; }
  .dw-mem-wall { gap: 10px; margin-top: 12px; }
  .dwm-av {
    width: 40px; height: 40px; font-size: calc(14px * var(--fs, 1)); font-weight: 700;
    background: linear-gradient(150deg, #2c6f5f, #14544a);
  }
  .dwm-av:active { opacity: .72; }

  /* ---------- 四入口卡：44×44 图标底 + 15px 标签 + 右上角红点角标 ---------- */
  .dw-grid { padding: 16px 6px 14px; }
  .dw-grid > div { gap: 0; }
  .dw-grid i {
    width: 44px; height: 44px; margin: 0 auto 9px; border-radius: 14px;
    background: var(--g050); color: var(--g700);
    position: relative;   /* 角标的定位基准 = 这颗图标底，不是整个格子 */
  }
  .dw-grid i svg { width: 21px; height: 21px; }
  .dw-grid > div > span { font-size: calc(15px * var(--fs, 1)); font-weight: 500; color: #5b6a66; line-height: 1.25; }
  /* 角标：任务书 5.4 —— 右上角 / 17px 高 / 白 1.5px 描边（压在图标上才不糊） */
  .dw-grid .cnt-badge {
    position: absolute; top: -5px; right: -6px; margin: 0;
    height: 17px; min-width: 17px; padding: 0 4px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 9px; border: 1.5px solid #fff;
    background: #d9534f; color: #fff;
    font-size: calc(10.5px * var(--fs, 1)); font-weight: 700; line-height: 1;
    box-shadow: none; vertical-align: baseline;
  }
  /* 待处理徽标（琥珀）留在标签下方，它和「条目数」是两件事，别合并 */
  .dw-grid .apv-badge {
    margin: 5px 0 0; font-size: calc(10.5px * var(--fs, 1));
    padding: 1px 6px; border-radius: 8px;
  }

  /* ---------- 设置卡：行 13/14、名称 17.2/500、右侧值 14.6、箭头 15 ---------- */
  .dw-list { padding: 2px 0; }
  .dw-list .rw {
    padding: 13px 14px; gap: 12px; min-height: 0;
    border-bottom: 1px solid var(--line);
  }
  .dw-list .rw:last-child { border-bottom: 0; }
  .dw-list .rw .rw-ic { width: 20px; height: 20px; color: #6a7874; }
  .dw-list .rw .rw-ic svg { width: 18px; height: 18px; }
  .dw-list .rw .rw-t { font-size: calc(17.2px * var(--fs, 1)); font-weight: 500; color: #22302c; }
  .dw-list .rw .rt { font-size: calc(14.6px * var(--fs, 1)); font-weight: 400; color: var(--ink-3); }
  .dw-list .rw .rt i {
    font-style: normal; font-size: calc(15px * var(--fs, 1)); color: #c8d2ce;
    margin-left: 4px; line-height: 1;
  }
  /* 危险项：图标与文字都 #c0504a */
  .dw-list .rw.danger .rw-ic, .dw-list .rw.danger .rw-t, .dw-list .rw.danger .rt { color: #c0504a; }
}

/* ══════════════════════════════════════════════════════════════════
   z354：工作台四页 + 首页四入口 · 手机端改版（版式候选定稿）
   ------------------------------------------------------------
   · `.wp-*` 默认 display:none，只在 ≤620px 打开 —— 桌面端结构一字不改
     （体例同 z327 .hm / .mr）。
   · 页面自带顶栏：body.in-wp 时全局顶栏让位，页内画 ‹ 返回 + 标题。
   · 配色走 .mr 同款 K1 定稿固定变量（z328 教训：不锁死就在暖色主题翻车）。
   · 字号一律 calc(Npx * var(--fs,1))；数字一律 tabular-nums。
   组件清单：
     .wp        页根        .wp-top   页内顶栏（‹ + 标题 + 右侧动作）
     .wp-ov     数字概览条  .wp-ov .c/.n/.l
     .wp-seg    分段页签（可多行 .wp-seg+.wp-seg）
     .wp-card   白卡        .wp-grp   团队/分档分组条
     .wp-row    行式列表行  （.av 头像方块 / .mn 主区 / .rv 右值 / .chev）
     .wp-pill   状态胶囊    ok/warn/bad/mut
     .wp-alert  需处理警示条（.bad 红 / .warn 琥珀）
     .wp-bar    进度条（.warn/.over）
     .wp-chip   小标签      .wp-empty 空态
   ══════════════════════════════════════════════════════════════════ */
.wp { display: none; }
@media (max-width: 620px) {
  /* 页面级：顶栏让位（各 render 里给 body 挂 in-wp） */
  body.in-wp .topbar { display: none; }
  body.in-wp { background: var(--bg); }
  body.in-wp main { padding: 0 13px 40px; }

  .wp {
    display: block; max-width: 640px; margin: 0 auto;
    --ink: #17211f; --ink-2: #4a5c59; --ink-3: #93a3a1;
    --line: #e9efed; --line-soft: #f2f6f4;
    --panel: #fff; --bg: #f5f7f6;
    --teal: #0f5c54; --teal-2: #178277; --teal-soft: #eef3f1;
    --danger: #b42318;
  }
  /* ─── 页内顶栏（同 .mr-top 体例：z353 标题 16px） ─── */
  .wp .wp-top { display: flex; align-items: center; gap: 6px; height: 52px; }
  .wp .wp-bk {
    width: 32px; height: 32px; margin-left: -8px; border: 0; background: none; border-radius: 10px;
    display: grid; place-items: center; color: var(--ink); cursor: pointer;
    font-size: calc(20px * var(--fs, 1)); line-height: 1;
  }
  .wp .wp-bk:hover { background: var(--line-soft); }
  .wp .wp-top h1 { margin: 0; flex: 1 1 auto; min-width: 0; font-size: calc(16px * var(--fs, 1)); font-weight: 700; }
  .wp .wp-act {
    flex: 0 0 auto; border: 0; background: none; cursor: pointer; font: inherit;
    font-size: calc(12px * var(--fs, 1)); font-weight: 600; color: var(--teal); padding: 4px 2px;
  }
  /* ─── 数字概览条 ─── */
  .wp .wp-ov {
    display: grid; grid-template-columns: repeat(4, 1fr); padding: 14px 4px;
    background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
  }
  .wp .wp-ov .c { text-align: center; position: relative; min-width: 0; }
  .wp .wp-ov .c + .c::before { content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 1px; background: var(--line-soft); }
  .wp .wp-ov .c.tappable { cursor: pointer; }
  .wp .wp-ov .n { font-size: calc(20px * var(--fs, 1)); font-weight: 700; line-height: 1.25; color: var(--ink); font-variant-numeric: tabular-nums; }
  .wp .wp-ov .n.green { color: var(--teal); }
  .wp .wp-ov .n.red { color: var(--danger); }
  .wp .wp-ov .n u { text-decoration: none; font-size: calc(11.5px * var(--fs, 1)); font-weight: 600; }
  .wp .wp-ov .l { font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); margin-top: 3px; }
  /* ─── 分段页签 ─── */
  .wp .wp-seg {
    display: flex; gap: 4px; margin: 12px 0 0; padding: 3px;
    background: #e8edeb; border-radius: 11px; overflow-x: auto; scrollbar-width: none;
  }
  .wp .wp-seg::-webkit-scrollbar { display: none; }
  .wp .wp-seg + .wp-seg { margin-top: 6px; }
  .wp .wp-seg button {
    flex: 1 1 0; min-width: max-content; border: 0; background: none; cursor: pointer; font: inherit;
    font-size: calc(12.5px * var(--fs, 1)); font-weight: 500; color: var(--ink-2);
    padding: 6px 10px; border-radius: 8.5px; white-space: nowrap;
  }
  .wp .wp-seg button.on { background: var(--panel); color: var(--ink); font-weight: 600; box-shadow: 0 1px 4px rgba(30,70,60,.08); }
  .wp .wp-seg button u { text-decoration: none; font-variant-numeric: tabular-nums; }
  /* ─── 卡片 / 分组条 ─── */
  .wp .wp-card {
    margin-top: 12px; background: var(--panel); border: 1px solid var(--line);
    border-radius: 14px; overflow: hidden;
  }
  .wp .wp-sechead { display: flex; align-items: baseline; justify-content: space-between; padding: 13px 14px 4px; }
  .wp .wp-sechead .t { font-size: calc(15px * var(--fs, 1)); font-weight: 700; }
  .wp .wp-sechead .m { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); font-variant-numeric: tabular-nums; }
  .wp .wp-grp {
    display: flex; align-items: center; gap: 8px; padding: 10px 14px;
    background: #f7faf9; border-bottom: 1px solid var(--line-soft);
    font-size: calc(12.5px * var(--fs, 1)); font-weight: 700; color: var(--teal);
  }
  .wp .wp-grp .n { margin-left: auto; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); font-weight: 500; font-variant-numeric: tabular-nums; }
  /* ─── 行式列表 ─── */
  .wp .wp-row {
    display: flex; align-items: center; gap: 11px; padding: 11px 14px; width: 100%;
    border: 0; border-bottom: 1px solid var(--line-soft); background: none;
    text-align: left; font: inherit; color: var(--ink); cursor: pointer; min-width: 0;
  }
  .wp .wp-row:last-child { border-bottom: 0; }
  .wp .wp-row.static { cursor: default; }
  .wp .wp-row .av {
    width: 38px; height: 38px; border-radius: 11px; background: var(--teal-soft); color: var(--teal);
    display: grid; place-items: center; font-size: calc(12px * var(--fs, 1)); font-weight: 700; flex: 0 0 auto;
  }
  .wp .wp-row .av.round { border-radius: 50%; color: #fff; }
  .wp .wp-row .mn { flex: 1 1 auto; min-width: 0; }
  .wp .wp-row .mn .r1 { font-size: calc(14.5px * var(--fs, 1)); font-weight: 600; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .wp .wp-row .mn .r1.multiline { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  .wp .wp-row .mn .r2 { font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); margin-top: 2px; line-height: 1.45; font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .wp .wp-row .mn .r2.multiline { white-space: normal; }
  .wp .wp-row .rv { flex: 0 0 auto; font-size: calc(13px * var(--fs, 1)); color: var(--ink-2); font-variant-numeric: tabular-nums; }
  .wp .wp-row .rv b { color: var(--ink); font-weight: 700; }
  .wp .wp-row .chev { flex: 0 0 auto; color: var(--ink-3); opacity: .45; font-size: calc(14px * var(--fs, 1)); }
  .wp .wp-acts { display: flex; align-items: center; justify-content: center; gap: 14px; padding: 11px 14px; }
  /* ─── 胶囊 / 小标签 ─── */
  .wp .wp-pill { flex: 0 0 auto; font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; border-radius: 999px; padding: 2.5px 9px; }
  .wp .wp-pill.ok { background: #e5f3ec; color: #1d7a52; }
  .wp .wp-pill.warn { background: #f6f0dc; color: #8a6d1f; }
  .wp .wp-pill.bad { background: #fdeeec; color: var(--danger); }
  .wp .wp-pill.mut { background: #eef1f0; color: #8a968f; }
  .wp .wp-pill.g { background: var(--teal-soft); color: var(--teal); }
  .wp .wp-chip { display: inline-block; font-size: calc(10px * var(--fs, 1)); font-weight: 600; border-radius: 6px; padding: 1.5px 6px; vertical-align: 1px; margin-left: 5px; }
  .wp .wp-chip.g { background: var(--teal-soft); color: var(--teal); }
  .wp .wp-chip.deg { background: #f4f1e8; color: #8a7a3f; }
  .wp .wp-chip.mut { background: #f0f1f2; color: #6b7774; }
  /* ─── 需处理警示条 ─── */
  .wp .wp-alert {
    display: flex; align-items: center; gap: 8px; margin: 4px 14px 11px; padding: 8.5px 12px;
    border-radius: 10px; font-size: calc(12px * var(--fs, 1)); font-weight: 600; cursor: pointer;
    background: #fdeeec; color: var(--danger); border: 0; width: calc(100% - 28px); text-align: left; font-family: inherit;
  }
  .wp .wp-alert.warn { background: #f6f0dc; color: #8a6d1f; }
  .wp .wp-alert .num { font-variant-numeric: tabular-nums; }
  .wp .wp-alert .go { margin-left: auto; opacity: .6; }
  /* ─── 进度条 ─── */
  .wp .wp-bar { height: 6px; border-radius: 3px; background: #edf1f0; overflow: hidden; flex: 1 1 auto; }
  .wp .wp-bar i { display: block; height: 100%; border-radius: 3px; background: linear-gradient(90deg, var(--teal), var(--teal-2)); }
  .wp .wp-bar i.warn { background: #d9a13c; }
  .wp .wp-bar i.over { background: #c0504a; }
  /* ─── 上传区 / 空态 ─── */
  .wp .wp-upzone {
    margin: 10px 14px 14px; border: 1.5px dashed #c4d4cf; border-radius: 12px; background: #f7faf9;
    padding: 15px 12px; text-align: center; font-size: calc(13px * var(--fs, 1)); color: var(--teal); font-weight: 600;
    cursor: pointer; display: block; width: calc(100% - 28px); font-family: inherit;
  }
  .wp .wp-upzone i { display: block; font-style: normal; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); font-weight: 400; margin-top: 4px; }
  .wp .wp-empty { padding: 16px 14px; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); line-height: 1.6; }
  /* ─── 行内展开（点行展开操作区：改角色/学历/删除等，方案一定稿交互） ─── */
  .wp .wp-xitem { border-bottom: 1px solid var(--line-soft); }
  .wp .wp-xitem:last-child { border-bottom: 0; }
  .wp .wp-xitem .wp-row { border-bottom: 0; }
  .wp .wp-xitem.open .chev { transform: rotate(90deg); }
  .wp .wp-x-body { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 2px 14px 12px 63px; }
  .wp .wp-x-body[hidden] { display: none; }
  .wp .wp-x-body .wp-sel {
    height: 30px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
    color: var(--ink); font: inherit; font-size: calc(12px * var(--fs, 1)); padding: 0 6px; max-width: 130px;
  }
  .wp .wp-x-body .btn-primary.sm, .wp .wp-x-body .btn-ghost.sm, .wp .wp-x-body .btn-danger {
    font-size: calc(11.5px * var(--fs, 1)); padding: 5px 10px; border-radius: 8px;
  }
  .wp .wp-x-body .deg-sel { height: 30px; max-width: 130px; }
  /* ─── z356 修复：截图反馈的版式问题 ─── */
  /* .r1/.r2 是 span（inline），overflow/ellipsis 根本不生效 —— 长标题钻到胶囊/箭头底下 */
  .wp .wp-row .mn .r1, .wp .wp-row .mn .r2 { display: block; }
  /* 行展开体被 inline style display:block 压过 hidden，导致所有房间默认展开 */
  .wp .wp-x-body[hidden] { display: none !important; }
  /* 概览条：标签不折行（积分（2026-11）两行难看）；数字不折行（599.8万 拆两行） */
  .wp .wp-ov .n, .wp .wp-ov .l { white-space: nowrap; }
  /* 行内按钮不被压缩成竖排（「会话」两字叠起来） */
  .wp .wp-row .btn-primary, .wp .wp-row .btn-ghost, .wp .wp-row .btn-danger { flex: 0 0 auto; white-space: nowrap; }
  /* 加载等待：页内顶栏 + 转圈（wpLoadingPaint 同步画壳） */
  .wp .wp-loading { display: grid; place-items: center; padding: 90px 0; }
  .wp .wp-spin { width: 26px; height: 26px; border: 3px solid #dbe4e1; border-top-color: #0f5c54; border-radius: 50%; animation: wpspin .8s linear infinite; }
  /* 团队子页标题较长（XX团队 · 知识库），顶栏标题单行省略，别把返回键/右侧动作挤走 */
  .wp .wp-top h1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* 桌面结构在手机上隐藏（数据还在 DOM，逻辑照旧） */
  .wp-desk-hide { display: none !important; }
}

/* z354：知识库「类型档」筛选条在 .wp 里重排成分段页签样式（复用 data-kbrt 绑定） */
@media (max-width: 620px) {
  .wp .kb-rt-filter {
    display: flex; gap: 4px; margin: 0; padding: 3px;
    background: #e8edeb; border-radius: 11px; overflow-x: auto; scrollbar-width: none;
  }
  .wp .kb-rt-filter::-webkit-scrollbar { display: none; }
  .wp .kb-rt-filter button {
    flex: 1 1 0; min-width: max-content; border: 0; background: none; cursor: pointer; font: inherit;
    font-size: calc(12px * var(--fs, 1)); font-weight: 500; color: #4a5c59;
    padding: 6px 10px; border-radius: 8.5px; white-space: nowrap;
  }
  .wp .kb-rt-filter button.on { background: #fff; color: #17211f; font-weight: 600; box-shadow: 0 1px 4px rgba(30,70,60,.08); }
}

@keyframes wpspin { to { transform: rotate(360deg); } }

/* ============================================================
   ★★ z359：电脑版独立骨架（≥1024px）
   ------------------------------------------------------------
   用户要求（原话）：「现在开始改造电脑版的视觉设计，要跟手机版相呼应」，
   选定方案 = B 系：左侧栏 236 + 内容区，首页用「两列团队网格 + 右栏 4 块」。

   为什么这样做而不是"把手机页拉宽"：
     · 手机端是单列长页 + 五宫格底栏；拉宽后每行 1600px 读不过来，
       而且没有"我在哪一页"的位置感。桌面要的是三栏/两栏的稳定骨架。
     · 侧栏复用现有的 .topnav（一个 <a> 都没新增路由），只是把横排改竖排、
       加了三个分组标题（.sd-grp，窄屏 display:none）和一个「待我处理」入口。
     · ⚠️ 手机端一条规则都没动：本段全部在 min-width:1024px 内，
       窄屏媒体查询（max-width:620px）与它不重叠。
   ============================================================ */
.sd-grp, #sdPendNav, .dw, .me-id, .brand-ver { display: none; }

@media (min-width: 900px) {
  /* ---------- 骨架 ---------- */
  /* z366：用户要求「鼠标拉窄窗口时左边栏固定不动，并且给一个最低宽度」。
     两条一起做：
       ① 桌面断点 1024 → 900：再窄一点也仍是「左栏 + 主区」，不会突然塌回手机顶栏；
       ② #shell 设 min-width:1000px：比这更窄就出横向滚动条，左栏仍是 236 固定不动，
          而不是把内容挤成一条。低于 900 才交回手机版式。 */
  /* z368：用户用「特大字」时侧栏版本号被裁、右栏与团队区底边错开 38px。
     根因是 z218 只放大了字号，**骨架尺寸全写死 px**（侧栏 236 / 右栏 322 / 格子 107），
     文字一大就溢出、两栏比例失衡。这里让骨架跟着字号走：
       · 侧栏 / 右栏宽度 → 走 --fw（半系数，见 :root 注释）；
       · 团队格子高度、右栏标题行与图标 → 走 --fs（全系数，底边才对得齐）。
     ⚠️ 格子里"装字"的横向尺寸（图标 44、左右 padding、间隙）**不缩放** ——
        缩放了等于把刚争来的宽度又还回去，团队名会被整条省略。 */
  #shell { display: flex; align-items: stretch; min-height: 100vh; min-width: calc(1000px + 360px * (var(--fs, 1) - 1)); }
  .topbar {
    position: sticky; top: 0; left: 0;
    width: calc(236px * var(--fw)); flex: 0 0 calc(236px * var(--fw));
    height: 100vh; min-height: 0;
    flex-direction: column; flex-wrap: nowrap; align-items: stretch;
    gap: 0; row-gap: 0; padding: 16px 12px 14px;
    background: var(--panel); border-bottom: 0; border-right: 1px solid var(--line);
    backdrop-filter: none; overflow-y: auto; overflow-x: hidden;
  }
  /* z368：品牌行必须能"让位"——字号放大后「平台名 + 版本 chip」总宽超过侧栏，
     原来整行溢出、chip 被侧栏的 overflow-x:hidden 裁成「z36」。现在标题可省略、
     chip 永不被压缩，任何字号倍率下版本号都是完整的。 */
  .brand-inline { background: none; border: 0; padding: 4px 8px 14px; gap: 8px; border-radius: 0; min-width: 0; }
  .brand-inline:hover { background: none; border-color: transparent; }
  .brand-inline strong {
    font-size: calc(15px * var(--fs, 1)); font-weight: 650; letter-spacing: -.2px;
    flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* z360：设计稿 logo 是 22×22 圆角块 + 内层浅色芯（原来只是一个 9×9 小色点） */
  .brand-dot {
    width: 22px; height: 22px; border-radius: 7px; flex: none;
    background: var(--g800); box-shadow: inset 0 0 0 6px #9ecfbe;
  }
  .brand-user { display: none; }               /* 身份块在侧栏底部（.me） */
  .topnav { flex: 1 1 auto; flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: 1px; margin: 2px 0 0; }
  .topnav a {
    position: relative; display: flex; align-items: center; gap: 10px;
    padding: 9px 11px; border-radius: 10px;
    font-size: calc(13.5px * var(--fs, 1)); font-weight: 500;
    /* z366：用户反馈「左边栏字体太浅」。原来是 --ink-2（#5b6a66），在白底上对比度约 4.1:1，
       侧栏是常驻导航，用正文色 --ink 才够看。 */
    color: var(--ink);
  }
  /* z360：每个菜单项前一个 17×17 圆角方块图标（设计稿用一个伪元素画两层：
     外层 var(--g050) 底、内层 9×9 的 #b7cfc6 芯，选中时反色）。 */
  .topnav a::before {
    content: ""; width: 17px; height: 17px; border-radius: 5px; flex: none;
    background: linear-gradient(#b7cfc6, #b7cfc6) center / 9px 9px no-repeat, var(--g050);
  }
  .topnav a:hover { background: var(--line-soft); }
  .topnav a.on { background: var(--teal-soft); color: var(--g800); font-weight: 600; }
  .topnav a.on::before { background: linear-gradient(#bfe0d3, #bfe0d3) center / 9px 9px no-repeat, var(--g800); }
  /* 选中项左侧 3px 竖条（设计稿 .sitem.on::before） */
  .topnav a.on::after {
    content: ""; position: absolute; left: -12px; top: 9px; bottom: 9px; width: 3px;
    border-radius: 0 3px 3px 0; background: var(--g800);
  }
  .sd-grp {
    display: block; padding: 14px 10px 6px;
    font-size: calc(10.5px * var(--fs, 1)); font-weight: 600;
    color: var(--ink-3); letter-spacing: .8px;
  }
  #sdPendNav { display: flex; }
  .topnav .sbadge {
    margin-left: auto; min-width: 17px; height: 17px; padding: 0 5px; border-radius: 999px;
    background: var(--danger); color: #fff; font-size: calc(10px * var(--fs, 1)); font-weight: 600;
    display: flex; align-items: center; justify-content: center;
  }
  /* 角标为 0 时只靠 hidden 属性隐藏不够：上面的 display:flex 会盖掉浏览器默认的 [hidden]{display:none}，
     于是「待我处理」会挂出一个红色的 0。必须显式再压一次。 */
  .topnav .sbadge[hidden], .topnav .nav-badge[hidden] { display: none; }
  /* 侧栏底部：用户块 + ⋮
     z366：构建版本 / 使用说明 / 注读说明从侧栏底部**移走**了
       · 版本号 → 标题右侧的 .brand-ver（侧栏顶部）；
       · 使用说明 / 注读说明 → ⋮ 菜单里两行。
     这样侧栏竖向高度刚好，900 高的窗口也不出滚动条。 */
  .topbar .me {
    position: relative;
    margin-top: auto; padding-top: 10px; border-top: 1px solid var(--line-soft);
    gap: 6px; flex-wrap: wrap; align-items: center;
  }
  .topbar .me .build-tag { display: none; }
  .topbar .me #guideBtn, .topbar .me #noteBtn { display: none; }
  /* z360：设计稿底部是「30px 圆头像 + 名字 + 角色副行」，⋮ 贴在它右侧 */
  .topbar .me .me-id {
    display: flex; align-items: center; gap: 9px;
    flex: 1 1 100%; order: 2; padding: 2px 32px 0 0;
  }
  /* z366：标题右侧的版本 chip（桌面专用；手机端仍用顶栏右侧那个） */
  .brand-ver {
    display: inline-flex; align-items: center; margin-left: auto; flex: none;
    padding: 2px 7px; border-radius: 6px;
    font-family: var(--mono); font-size: calc(10px * var(--fs, 1)); letter-spacing: .2px;
    color: var(--ink-3); background: var(--line-soft); white-space: nowrap; cursor: pointer;
  }
  .brand-ver:hover { color: var(--g800); background: #e7eeeb; }
  .me-av {
    width: 30px; height: 30px; border-radius: 50%; flex: none;
    background: var(--g600); color: #fff; font-size: calc(12px * var(--fs, 1)); font-weight: 600;
    display: flex; align-items: center; justify-content: center;
  }
  .me-tx { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
  .me-tx b {
    font-size: calc(12.5px * var(--fs, 1)); font-weight: 600; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;
  }
  /* z366：#meName 在 z91 被全局 display:none（名字搬去品牌块了），
     但桌面侧栏的品牌块本身是隐藏的 → 底部用户块只剩角色、没有名字。
     这里按 id 覆盖回来（手机端不受影响，那一段在媒体查询外）。 */
  .topbar .me .me-tx #meName { display: block; }
  .me-tx i {
    font-style: normal; font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* z457（用户 2026-11-06 选版式 C「一体控制条」）：
     「‹ › ⟳」三个键 + 1px 分隔线 + ⋮ 合成一条 24px 高的圆角控制条。
     为什么这么改：
       · 原来三个键是裸字符，字重和视觉大小天生不同，⋮ 又单独套方框、绝对定位在右下角，
         四者不在一条线上 —— 用户原话「这里好丑」；
       · ⋮ 收进条里后，不用再为它预留右侧 30px，这段宽度还给姓名（长名字更不容易被截）。
     ⚠️ ⋮ 仍是 index.html 里那个 #moreBtn（只是父节点变了），
        菜单定位读的是 getBoundingClientRect()，所以照样贴着按钮向上弹。 */
  .topbar .me .nav-bar {
    order: 3; flex: none;
    display: flex; align-items: center;
    height: 24px; margin: 0;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--panel);
    overflow: hidden;            /* 让两端 hover 底色跟着圆角切 */
  }
  .topbar .me .nav-bar #pwaNav {
    order: 0; flex: none;
    display: flex; align-items: center;
    margin: 0; padding: 0; height: auto; gap: 0;
  }
  .topbar .me .nav-bar #pwaNav button {
    width: 24px; height: 22px; padding: 0; margin: 0;
    display: flex; align-items: center; justify-content: center;
    border: 0; border-radius: 0;
    color: var(--ink-2);
    background: transparent;
  }
  .topbar .me .nav-bar #pwaNav button:hover { background: var(--panel-soft); border: 0; }
  .topbar .me .nav-bar #pwaNav button + button { border-left: 1px solid var(--line-soft); }
  .topbar .me .nav-bar #pwaNav svg { width: 13px; height: 13px; display: block; }
  .topbar .me .nav-bar .nav-sep { display: block; width: 1px; height: 12px; background: var(--line); flex: none; }
  .topbar .me .nav-bar .topbar-more {
    position: static; width: 24px; height: 22px; margin: 0; padding: 0;
    display: flex; align-items: center; justify-content: center;
    border: 0; border-radius: 0;
    font-size: calc(15px * var(--fs, 1)); color: var(--ink-2);
    line-height: 1;
  }
  .topbar .me .nav-bar .topbar-more:hover { background: var(--panel-soft); }
  /* 用户块要能被压缩，否则多出来的导航会被挤到下一行（长名字靠省略号收） */
  .topbar .me .me-id { flex: 1 1 0%; min-width: 0; padding-right: 0; }
  /* 角色行与姓名左对齐：姓名胶囊有 8px 左内边距、角色没有，原来看着「管理员」缩进不齐 */
  .topbar .me .me-tx i { padding-left: 8px; }
  /* z366：版本落后提示条 —— 原来留在侧栏里（order:-1 把整条塞进 236px 宽的左栏顶部，
     用户看到的是「整个左边栏变成一条通知」）。现在钉在内容区最上方，横跨整页宽度。 */
  #updateBar {
    position: fixed; top: 0; left: calc(236px * var(--fw)); right: 0; z-index: 60;
    margin: 0; border-radius: 0; padding: 8px 18px;
  }
  /* 提示条出现时把内容区整体下移，避免盖住页头（.hidden 时这条不生效） */
  body:has(#updateBar:not(.hidden)) main#view { padding-top: 78px; }
  /* 1440 以上不再无脑拉宽：两列团队格被撑到 800+ 会显得空，内容居中留白更好看 */
  main#view { max-width: 1440px; flex: 1 1 auto; min-width: 0; margin: 0 auto; padding: 26px 32px 48px; }
  /* 首页：宽屏走下面这套，手机 K1 那套整块让位（数据仍在 DOM 里） */
  .hm { display: none; }
  body.in-home .stat-row, body.in-home .home-sec { display: none; }

  /* ---------- 工作台首页 ---------- */
  /* z360：页头横跨整页（按钮才贴得到页面右缘），右栏从第一行起、与页头同高。
     三行布局：row1 = 页头（跨两列），row2 = 左栏，右栏跨 1~2 行。 */
  .dw {
    display: grid; grid-template-columns: minmax(0, 1fr) calc(322px * var(--fw));
    column-gap: 20px;
    align-items: start;
  }
  /* z407：左栏（团队区）与右栏（四个分组）同处一行、互相拉伸 ——
     行高取两者内容高度的较大值：右栏条目变多时把行撑高，团队区白卡跟着变高；
     反过来右栏永远不会高过团队区（两栏底边始终齐平）。 */
  .dw-l {
    grid-column: 1; grid-row: 2; min-width: 0;
    align-self: stretch; display: flex; flex-direction: column;
  }
  .dw-l > .gcard { flex: 1 1 auto; display: flex; flex-direction: column; }
  /* 格子保持固定 107 高，不随白卡被拉高而变形；多出的空间留在卡片底部 */
  .dw-l > .gcard > .ggrid { flex: 1 1 auto; align-content: start; }
  .dw-r {
    grid-column: 2; grid-row: 2; align-self: stretch;
    min-width: 0; display: flex; flex-direction: column;
    /* z407：四个分组之间严格等距（用户定稿：等距离纵向排列） */
    gap: calc(13px * var(--fs, 1));
  }
  /* 一个分组 = 标题行 + 卡片；块内间距固定，块间间距只由 .dw-r 的 gap 决定 */
  .dgrp { display: flex; flex-direction: column; min-width: 0; }
  .dhead {
    grid-column: 1 / -1; grid-row: 1;
    display: flex; align-items: flex-start;
    justify-content: space-between; gap: 16px; margin-bottom: 16px;
  }
  .dhead h2 {
    margin: 0; font-size: calc(23px * var(--fs, 1)); font-weight: 680;
    letter-spacing: -.4px; line-height: 1.35; color: var(--ink);
  }
  .dhead p { margin: 2px 0 0; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); }
  .dhead .dact { display: flex; gap: 8px; align-items: center; padding-top: 6px; }
  .dhead .dact .btn-ghost {
    background: #fff; border: 1px solid var(--line); color: var(--ink-2);
    border-radius: 11px; padding: 9px 15px; font-size: calc(13px * var(--fs, 1)); font-weight: 500;
  }
  .dhead .dact .btn-ghost:hover { background: var(--line-soft); }
  .dhead .dact .btn-primary {
    border-radius: 11px; padding: 9px 15px; font-size: calc(13px * var(--fs, 1)); font-weight: 600;
    background: var(--g800);
  }

  /* 分段（课题讨论区 / 公共撰写区）—— 与右栏标题同一套语汇，放在容器外面 */
  .dsec { display: flex; align-items: center; gap: 12px; height: calc(32px * var(--fs, 1)); margin-bottom: 5px; }
  /* z360：设计稿是一个 #e7eeeb 凹槽 + 白色凸起片（原来只是裸文字按钮 + 浅绿底） */
  .dseg {
    display: inline-flex; align-items: center; gap: 2px;
    background: #e7eeeb; border-radius: 10px; padding: 3px;
  }
  .dseg button {
    border: 0; background: none; font: inherit; cursor: pointer;
    font-size: calc(12.5px * var(--fs, 1)); font-weight: 500; color: #6b7a76;
    padding: 4px 12px; border-radius: 8px; line-height: 1.4;
    transition: background .12s ease, color .12s ease, box-shadow .12s ease;
  }
  .dseg button:hover { color: var(--g800); }
  .dseg button.on {
    background: #fff; color: #0f4c44; font-weight: 600;
    box-shadow: 0 1px 3px rgba(30, 70, 60, .09);
  }
  .dseg button u {
    text-decoration: none; margin-left: 5px;
    font-size: calc(11px * var(--fs, 1)); font-weight: 500; color: #a2b0ac;
  }
  .dseg button.on u { color: #7d9c94; }
  .dmore { margin-left: auto; font-size: calc(12px * var(--fs, 1)); color: #7d8d88; font-weight: 500; cursor: pointer; }
  .dmore:hover { color: var(--g700); }

  /* 团队网格：两列、格子之间只有 1px 细线 */
  .gcard { background: var(--panel); border-radius: 16px; box-shadow: var(--sh-card); overflow: hidden; }
  .ggrid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .gcell {
    position: relative; display: flex; align-items: center; gap: 12px;
    height: calc(107px * var(--fs, 1)); min-width: 0; padding: 0 20px;
    border-right: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft);
    cursor: pointer; transition: background .12s ease;
  }
  .gcell:nth-child(2n) { border-right: 0; }
  /* z360：设计稿最后一行没有下边线，否则卡片底部会多出一条「没缝上」的线 */
  .gcell:nth-last-child(-n+2) { border-bottom: 0; }
  .gcell:hover { background: #f9fbfa; }
  .gcell.blank, .gcell.blank:hover { background: none; cursor: default; }
  .gic {
    width: 44px; height: 44px; border-radius: 13px; flex: none;
    background: var(--g050); color: #3f6159;
    display: flex; align-items: center; justify-content: center;
  }
  .gic svg { width: 23px; height: 23px; }
  .gic.off { background: #f5f8f7; color: #c3cecb; }
  .gnm { min-width: 0; flex: 1 1 auto; }
  .gn1 { display: flex; align-items: center; gap: 6px; min-width: 0; }
  .gname {
    font-size: calc(16px * var(--fs, 1)); font-weight: 600; color: var(--ink);
    letter-spacing: -.2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .gname.mute { color: var(--ink-3); font-weight: 500; }
  /* 角色胶囊（设计稿：全圆角、lead 灰绿 / mentor 琥珀 / admin 绛红） */
  .grole {
    flex: none; font-size: calc(10.5px * var(--fs, 1)); font-weight: 500;
    color: #6d847d; background: #f0f5f3;
    border-radius: 999px; padding: 0 6px; line-height: 17px;
  }
  .grole.ment { color: #b8791a; background: #fdf3e2; }
  .grole.man { color: #a8433f; background: #fbecea; }
  .gstats { display: flex; align-items: center; margin-top: 7px; font-size: calc(12.5px * var(--fs, 1)); color: #95a39f; white-space: nowrap; }
  .gstats span { flex: 1 1 0; min-width: 0; padding-left: 12px; border-left: 1px solid #e9efec; line-height: 1.15; overflow: hidden; text-overflow: ellipsis; }
  .gstats span:first-child { padding-left: 0; border-left: 0; }
  /* 最后一项右对齐：右上角的「头像 + 角标」是 margin-left:auto 顶到 .gnm 右缘的，
     只有让第 4 项文字也贴到格子右缘，两者才在同一条竖线上（左对齐时文字右缘会内缩约 45px，
     看起来就是头像块孤零零地飘在右边）。数字宽度变化时对齐依然成立，不用写死像素。 */
  .gstats span:last-child { text-align: right; }
  .gstats b { color: var(--ink-2); font-weight: 600; font-variant-numeric: tabular-nums; margin-right: 1px; }
  .gsub { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* 成员头像并排 + 未读/未审（有数＝实心色块，0＝灰点灰 0）
     z362：原来 margin-left:-9px 叠成一小坨，只认得出两个人；改成完整并排 3 个。
     因此头像从 22 缩到 20、白边 1.5→1、组间距 3，整段宽度基本没变（不至于把团队名挤成省略号）。 */
  .gavrow { display: flex; align-items: center; gap: 6px; margin-left: auto; padding-left: 4px; flex: none; }
  .gav { display: flex; align-items: center; gap: 3px; flex: none; }
  .gav i {
    width: 20px; height: 20px; border-radius: 50%; background: #e2eee9; color: #456b61;
    font-size: calc(9.5px * var(--fs, 1)); font-style: normal;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--panel); margin-left: 0; flex: none;
  }
  .gav .plus { font-size: calc(10px * var(--fs, 1)); color: var(--ink-3); margin-left: 4px; letter-spacing: -.2px; }
  .gsep { width: 1px; height: 13px; background: #e4ebe8; flex: none; }
  .gtags { display: flex; align-items: center; gap: 7px; flex: none; }
  .gnum {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 19px; height: 19px; padding: 0 5px; border-radius: 6px;
    font-size: calc(10.5px * var(--fs, 1)); font-weight: 650; color: #fff;
    font-variant-numeric: tabular-nums;
  }
  .gnum.u { background: #b04a5c; }
  .gnum.p { background: #c08a2a; }
  .gtzag { display: inline-flex; align-items: center; gap: 5px; font-size: calc(11.5px * var(--fs, 1)); font-weight: 600; color: #b7c6c1; font-variant-numeric: tabular-nums; }
  .gtzag i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; flex: none; }
  .gchev { color: #ccd8d4; font-size: calc(19px * var(--fs, 1)); line-height: 1; flex: none; margin-left: 10px; }
  .gcell:hover .gchev { color: var(--g700); }

  /* ---------- 右栏 ---------- */
  .where { position: relative; display: flex; align-items: center; height: calc(32px * var(--fs, 1)); margin-bottom: 5px; padding-left: 10px; }
  .where::before {
    content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 3px; height: 14px; border-radius: 2px; background: var(--g800); opacity: .85;
  }
  .where h3 { margin: 0; font-size: calc(14px * var(--fs, 1)); font-weight: 600; color: var(--ink); letter-spacing: -.2px; }
  .where .cnt { margin-left: 7px; font-size: calc(11px * var(--fs, 1)); font-weight: 500; color: var(--ink-3); }
  .where .more { margin-left: auto; font-size: calc(11px * var(--fs, 1)); color: #a3b1ad; cursor: pointer; }
  .where .more:hover { color: var(--g700); }
  /* 没有点击目标的那两块（未读 / 最近）指针不变成手型 */
  .where .more:not([data-dgo]):not([data-dpend]) { cursor: default; }
  .where .more:not([data-dgo]):not([data-dpend]):hover { color: #a3b1ad; }
  .rcard { background: var(--panel); border-radius: 14px; box-shadow: var(--sh-card); padding: calc(3px * var(--fs, 1)); }
  .rrow { display: flex; align-items: center; gap: calc(9px * var(--fs, 1)); padding: calc(2px * var(--fs, 1)) calc(8px * var(--fs, 1)); border-radius: 10px; cursor: pointer; }
  .rrow:hover { background: #f9fbfa; }
  .ric {
    width: calc(28px * var(--fs, 1)); height: calc(28px * var(--fs, 1));
    border-radius: calc(8px * var(--fs, 1)); flex: none;
    background: #f1f6f4; color: #5f8279;
    display: flex; align-items: center; justify-content: center;
    font-size: calc(11px * var(--fs, 1)); font-weight: 500;
  }
  .ric.c { background: #fbecef; color: #a83f52; }
  .ric.a { background: #fbf1e0; color: #96681a; }
  .rm { min-width: 0; flex: 1 1 auto; }
  .rt { font-size: calc(12.5px * var(--fs, 1)); font-weight: 500; line-height: 1.4; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .rs { margin-top: 1px; font-size: calc(10.5px * var(--fs, 1)); line-height: 1.35; color: var(--ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .rar { color: #c9d5d1; font-size: calc(15px * var(--fs, 1)); flex: none; }
  .run {
    min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px; flex: none;
    background: var(--danger); color: #fff; font-size: calc(10px * var(--fs, 1)); font-weight: 600;
    display: flex; align-items: center; justify-content: center;
  }
  /* z407：原来的 .dgap 占位块已删除 —— 右栏改为 .dw-r 的 gap 统一控制块间距 */
  .dempty { padding: 14px 10px; text-align: center; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
  /* 空态面板（z361 定稿 C）：条目为 0 时用「已完成 + 下一步」替代一句灰字。
     尺寸对齐设计稿 322 宽下的 97 高，和有 3 行的卡片同量级，右栏不会塌出一大块。 */
  .rcard.ep {
    flex: none; padding: 8px 12px 7px; text-align: center;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    background: #fbfdfc; box-shadow: none; border: 1px solid #e8f0ed;
  }
  .epic {
    width: 24px; height: 24px; border-radius: 50%; flex: none;
    background: #e9f4ef; color: #2f7a66; margin-bottom: 3px;
    display: flex; align-items: center; justify-content: center;
    font-size: calc(12px * var(--fs, 1));
  }
  .epic.mute { background: #f1f4f3; color: #b3c0bc; }
  .ept { font-size: calc(12px * var(--fs, 1)); font-weight: 600; color: #38504a; line-height: 1.4; }
  .eps { font-size: calc(10.5px * var(--fs, 1)); color: #9daba7; line-height: 1.5; }
  .epw { margin-top: 4px; }
  .epb {
    display: inline-flex; align-items: center; gap: 2px; cursor: pointer;
    font-size: calc(10.5px * var(--fs, 1)); font-weight: 600; color: #2f6b5b;
    line-height: 16px; padding: 1px 6px; margin: -1px -6px; border-radius: 7px;
  }
  .epb i { font-style: normal; color: #8fb3a9; font-size: calc(12.5px * var(--fs, 1)); line-height: 1; }
  .epb:hover { background: #eef6f2; color: var(--g700); }

  /* ============================================================
     z363：团队页（仅桌面）—— 按设计稿 4.3 重排
     ------------------------------------------------------------
     顺序改成：面包屑 → 页头（标题+副标题+两按钮）→ 4 格 KPI →
     房间标题 + 房间卡 → 团队资料（三个入口收成小列表行）。
     实现方式：模板外面包一层 .tm-page，桌面用 flex + order 重排；
     手机端 .tm-page 是普通块，DOM 顺序与改前完全一致，一个字没动。
     ============================================================ */
  .tm-page { display: flex; flex-direction: column; }
  .tm-bread {
    display: block; order: 1; margin: 0 0 10px;
    font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3);
  }
  .tm-bread u { text-decoration: none; margin: 0 5px; color: #c5d1cd; }
  .tm-page > .tm-head { display: none; }          /* 桌面用 #teamHeadDesk，不用手机绿头 */
  .tm-page > #teamHeadDesk { order: 2; align-items: flex-start; margin-bottom: 16px; }
  .page-head .ph-sub { margin: 4px 0 0; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); }
  /* 4 格 KPI：手机那套是「负 margin 压住绿块下沿」的一整张卡，
     桌面没有绿块，改成 4 张独立白卡（与首页团队卡同一套令牌）。 */
  .tm-page > .tm-kpi {
    order: 3; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px; margin: 0 0 18px; padding: 0;
    background: none; border-radius: 0; box-shadow: none;
  }
  .tm-page > .tm-kpi > div {
    min-width: 0; text-align: left; padding: 13px 16px;
    background: var(--panel); border-radius: 15px; box-shadow: var(--sh-card);
  }
  .tm-page > .tm-kpi > div + div { border-left: 0; }
  /* KPI 文字：手机段那套 .tm-kpi b/i 只写在 ≤620 里，桌面拿不到，
     所以这里必须把 display:block / 字号 / 颜色写全，否则数字和标签会挤在同一行。 */
  .tm-page > .tm-kpi b {
    display: block; font-size: calc(24px * var(--fs, 1)); font-weight: 680;
    line-height: 1.05; letter-spacing: -.02em; font-variant-numeric: tabular-nums; color: var(--ink);
  }
  .tm-page > .tm-kpi i {
    display: block; margin-top: 7px; font-style: normal;
    font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); white-space: nowrap;
  }
  .tm-page > .tm-kpi .tm-run b { color: #a87418; }
  .tm-page > .tm-kpi .tm-warn b { color: var(--amber); }
  .tm-page > .rt-pend-bar { order: 4; }
  /* z407：团队资料（知识库 / 经验 / 借鉴库）整块提到课题房间**上面**（用户要求）。
     原来在房间列表下面，用户要「先看到资料入口，再看到房间」。 */
  .tm-page > .tm-matsec { order: 5; margin-top: 0; }
  .tm-page > .team-space { order: 6; margin-bottom: 22px; }
  /* 房间区块：DOM 里 #teamGrid 在 .tm-sec 之前（手机端是先列表后「全部 N ›」），
     桌面要「标题在卡片上面」，所以这一层也改成 flex + order 单独重排。 */
  .tm-page > .home-sec { order: 7; display: flex; flex-direction: column; margin-top: 0; }
  .tm-page > .home-sec > .tm-sec { order: 1; }
  .tm-page > .home-sec > #teamGrid { order: 2; }
  .tm-page > .home-sec > #teamListM { order: 3; }
  /* 区块标题：与首页右栏 .where 同一套语汇（左侧 3px 竖线 + 标题 + 计数） */
  .tm-page .tm-sec {
    display: flex; align-items: center; gap: 8px; position: relative;
    height: 26px; margin: 0 0 10px; padding-left: 10px;
  }
  .tm-page .tm-sec::before {
    content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 3px; height: 13px; border-radius: 2px; background: var(--g600);
  }
  .tm-page .tm-sec span { font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; color: var(--ink); }
  .tm-page .tm-sec .tm-cnt { display: inline; font-size: calc(11px * var(--fs, 1)); font-weight: 500; color: var(--ink-3); }
  .tm-page .tm-sec i { display: none; }           /* 手机那行「全部 N ›」桌面不要（已有计数） */
  .tm-matsec {
    display: flex; align-items: center; position: relative;
    height: 26px; margin: 20px 0 10px; padding-left: 10px;
    font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; color: var(--ink);
  }
  .tm-matsec::before {
    content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 3px; height: 13px; border-radius: 2px; background: var(--g600);
  }
  /* 三个入口：竖排大卡 → 「图标 + 标题 + ›」小列表行，让房间卡成为主角
     ★ z409（用户选定方案 A「资料架」）：原来三条横条各 38px 高、右端一大段空白，
       看着像三条分隔线而"不像入口"。现在合成**一张白卡**、卡内分三段，
       段间 1px 竖线；段内 38px 图标 + 标题 + 一行说明，整条 76px 高。
       —— 只动 ≥900 段；手机（≤620 那套 90px 竖排入口卡）一个字没动。 */
  .tm-page > .team-space {
    gap: 0; margin: 0 0 22px; padding: 6px;
    background: var(--panel); border-radius: 16px; box-shadow: var(--sh-card);
  }
  .tm-page .team-space-card {
    flex-direction: row; align-items: center; justify-content: flex-start; gap: 12px;
    height: auto; min-height: 64px; padding: 13px 14px 13px 12px;
    border: 0; border-radius: 12px; background: transparent; box-shadow: none;
    position: relative; transition: background .14s ease;
  }
  .tm-page .team-space-card:hover {
    border: 0; background: #f4f9f7; box-shadow: none; transform: none;
  }
  /* 段间竖线画在第 2、3 段的左边缘（grid gap 为 0，正好落在两段交界） */
  .tm-page .team-space-card + .team-space-card::before {
    content: ''; position: absolute; left: 0; top: 15px; bottom: 15px;
    width: 1px; background: var(--line-soft);
  }
  .tm-page .team-space-card .tsc-ico {
    width: 38px; height: 38px; border-radius: 11px; flex: none;
    display: flex; align-items: center; justify-content: center;
    background: #eef4f2; color: #4a7d6f;
  }
  .tm-page .team-space-card .tsc-ico .ic { width: 18px; height: 18px; }
  .tm-page .team-space-card .tsc-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
  .tm-page .team-space-card .tsc-txt b { font-size: calc(13px * var(--fs, 1)); font-weight: 600; }
  .tm-page .team-space-card .tsc-txt i {
    display: block; font-style: normal; line-height: 1.3;
    font-size: calc(11px * var(--fs, 1)); color: var(--ink-3);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .tm-page .team-space-card::after {
    content: '›'; margin-left: auto; color: #c8d4d0;
    font-size: calc(15px * var(--fs, 1)); line-height: 1;
  }
  /* 房间卡：跟首页团队卡同一套令牌（无边框、16 圆角、轻阴影） */
  .tm-page #teamGrid .room-card { border: 0; border-radius: 16px; box-shadow: var(--sh-card); }
  .tm-page #teamGrid .room-card:hover { border: 0; box-shadow: 0 4px 16px rgba(30, 70, 60, .09); }
  .tm-page #teamGrid .room-card h3 {
    font-family: inherit; font-size: calc(14.5px * var(--fs, 1)); font-weight: 600; line-height: 1.45;
  }

  /* ============================================================
     z363：统计页（仅桌面）—— 表格化收尾
     设计稿要求「行高 44、hover 整行浅青、数字右对齐等宽」。
     原样只是 7px 内边距（约 31 高），表头字号也偏小。数字右对齐和 hover
     在基础样式里本来就有，这里只补行高与表头，不动任何数据口径。
     ============================================================ */
  .stats-tbl th, .stats-tbl td { padding: 13px 10px; }
  .stats-tbl th { font-size: calc(11.5px * var(--fs, 1)); font-weight: 500; color: var(--ink-3); }
  .stats-tbl tbody tr { transition: background .12s ease; }
  .stats-tbl tbody tr:hover { background: #f4f9f7; }
  .stats-tbl .nm-cell { font-weight: 600; }
  /* 「按团队」表：团队名吃掉剩余宽度，四个数字列各自固定 130 靠右聚集。
     不这样写的话 5 列会在 1100 宽里均分，数字之间隔出一大段空白，读起来像断裂的表格。 */
  .stats-tbl.tbl-team th.num, .stats-tbl.tbl-team td.num { width: 92px; }

  /* ============================================================
     z363：房间页（仅桌面）—— 按设计稿 4.4
     ------------------------------------------------------------
     ① 右栏 60px 裸图标条 → 264px 文字侧栏（图标 + 文字 + ›，横向排列）；
     ② 页签从「下划线 tabs」换成首页那套「灰凹槽 + 白色凸起片」；
     ③ 消息列限宽 820 居中，1440 屏上一句话不再横跨整行。
     手机端仍走 ☰ 全屏抽屉，以上三条全在 ≥1024 段内，手机一个字没动。
     ============================================================ */
  .room-layout { grid-template-columns: minmax(0, 1fr) 264px; gap: 18px; }
  .rail {
    width: auto; align-items: stretch; gap: 2px; padding: 10px 8px;
    border: 0; border-radius: 16px; box-shadow: var(--sh-card);
  }
  .rail-btn {
    flex-direction: row; align-items: center; justify-content: flex-start; gap: 10px;
    width: auto; min-height: 0; padding: 9px 10px; border-radius: 10px;
  }
  .rail-btn svg { width: 17px; height: 17px; }
  .rail-btn span {
    font-size: calc(12.5px * var(--fs, 1)); line-height: 1.3;
    text-align: left; max-width: none; word-break: keep-all;
  }
  .rail-btn::after {
    content: '›'; margin-left: auto; color: #ccd8d4;
    font-size: calc(14px * var(--fs, 1)); line-height: 1;
  }
  .rail-btn.on::after { color: var(--teal); }
  .rail-sep { width: auto; margin: 8px 6px; background: #e9efec; }
  .rail-rec { border: 0; background: transparent; }
  .rail-rec:hover { border-color: transparent; }
  /* 右栏下半部分（仅桌面） */
  .rail-extra { display: block; }
  .rail-sec { padding: 2px 10px 7px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
  .rail-mems { display: flex; flex-wrap: wrap; gap: 5px; padding: 0 10px 12px; }
  .rail-mems i {
    width: 26px; height: 26px; border-radius: 50%; flex: none;
    background: #e2eee9; color: #456b61; font-style: normal;
    font-size: calc(11px * var(--fs, 1));
    display: flex; align-items: center; justify-content: center;
  }
  .rail-mems i.more { background: #f1f4f3; color: var(--ink-3); }
  .rail-type { padding: 0 10px 12px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); }

  /* 页签：灰凹槽 + 白色凸起片（与首页分段 chip 同一语汇） */
  .discuss .scope-tabs {
    flex: none; display: inline-flex; align-self: flex-start; gap: 2px;
    width: auto; margin: 12px 14px 4px; padding: 3px;
    background: #e9efec; border: 0; border-radius: 10px;
  }
  .scope-tabs button {
    flex: 0 0 auto; padding: 5px 14px; border: 0; border-radius: 8px;
    background: transparent; color: #6b7a76;
    font-size: calc(12.5px * var(--fs, 1)); font-weight: 500; line-height: 1.45;
  }
  .scope-tabs button.on {
    background: #fff; color: var(--g800); font-weight: 600;
    box-shadow: 0 1px 3px rgba(30, 70, 60, .12);
  }
  /* 消息列限宽 820 居中 */
  /* ★★ z366：聊天区按「微信 / QQ 桌面版」的屏占比重排。
     原来消息列被限宽 820 居中，宽屏（1440/1920）两侧各空 200~280px，
     而下面的输入框却是通栏 —— 一眼就看出「内容没占满、还错位」。
     现在：消息铺满 .discuss 的可用宽度（只留 26px 内边距），
     气泡自己限宽到 78%（长文不横跨整屏、短句仍是左右对齐的小气泡），
     头像 30 → 34px，正文 13.5 → 14px，输入框内边距与消息列对齐。 */
  .discuss .msgs { padding: 16px 26px 20px; }
  .discuss .msgs > * { width: 100%; max-width: 100%; margin-left: 0; margin-right: 0; }
  .discuss .msg { gap: 12px; }
  .discuss .msg .av {
    width: 34px; height: 34px; border-radius: 10px;
    font-size: calc(13px * var(--fs, 1));
  }
  .discuss .msg .who b { font-size: calc(13px * var(--fs, 1)); }
  .discuss .msg .txt {
    max-width: min(78%, 880px);
    font-size: calc(14px * var(--fs, 1));
    line-height: 1.62;
    padding: 10px 14px;
  }
  .discuss .composer { padding: 12px 26px 14px; }
  .discuss .composer textarea { font-size: calc(14px * var(--fs, 1)); }

  /* ============================================================
     z364：成员管理 / 用量与积分 / AI 团队（仅桌面）
     设计稿第六章「表格化收尾」：数字统一 24、列表行高 56 + 头像 32、
     面板改无边框卡片、AI 团队 5 列 → 4 列、卡内按钮收到右下角。
     全部在 ≥1024 段内，手机端现有样式一条没动。
     ============================================================ */

  /* 四格数字卡：原来是 flex 固定 148 宽（右侧留一片空白），改等分网格；
     数字 26 → 24，与统计页表格的数字节奏统一。 */
  .stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
  .stat-row > .stat:not(.stat-notice) { flex: none; }
  .stat { padding: 13px 16px; border: 0; border-radius: 14px; box-shadow: var(--sh-card); }
  .stat .n { font-size: calc(24px * var(--fs, 1)); letter-spacing: -.4px; }
  .stat .l { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin-top: 2px; }

  /* 折叠面板：边框 → 卡片阴影；标题行拉通卡片、下面一条细线 */
  .panel.fold { border: 0; border-radius: 16px; box-shadow: var(--sh-card); padding: 4px 18px 14px; }
  .fold > .fold-head { margin: 0 -18px; padding: 13px 18px; border-bottom: 1px solid var(--line-soft); }
  .fold.collapsed > .fold-head { border-bottom: 0; }

  /* 成员行：行高 56 / 头像 32 / 右侧按钮改幽灵（原来四个实心小方片挤成一排很吵） */
  .member-row {
    min-height: 56px; padding: 8px 0; gap: 12px; flex-wrap: nowrap;
    border-bottom: 1px solid var(--line-soft);
  }
  .member-row:last-child { border-bottom: 0; }
  .member-row:hover { background: #f6faf8; }
  .member-row .av { width: 32px; height: 32px; flex: none; font-size: calc(12.5px * var(--fs, 1)); }
  .member-row .mr-name { font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; line-height: 1.4; }
  .member-row .mr-meta { font-size: calc(11.5px * var(--fs, 1)); margin-top: 2px; }
  .member-row .mr-acts { flex-wrap: nowrap; align-items: center; }
  .member-row .mr-acts .btn-ghost,
  .member-row .mr-acts .btn-danger {
    padding: 4px 9px; border-radius: 8px; box-shadow: none;
    border: 1px solid transparent; background: transparent;
    font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2);
  }
  .member-row .mr-acts .btn-ghost:hover { background: #eef4f2; color: var(--g800); }
  .member-row .mr-acts .btn-danger { color: #b4443a; }
  .member-row .mr-acts .btn-danger:hover { background: var(--danger-soft); }
  .member-row .deg-sel { max-width: 96px; }

  /* 用量页：每个房间两栏 —— 左边房间信息，右边「本月 x 积分 + 进度条」窄列，
     扫一眼就能对比各房间消耗；上限输入仍占整行。 */
  .room-usage-row {
    display: grid; grid-template-columns: minmax(0, 1fr) 190px;
    column-gap: 22px; align-items: center; padding: 13px 4px;
  }
  .room-usage-row > .ru-head { grid-column: 1; grid-row: 1; }
  .room-usage-row > .ru-head > div:first-child { min-width: 0; }
  .room-usage-row > .ru-bar { grid-column: 2; grid-row: 1; margin: 0; }
  .room-usage-row > .ru-meta { grid-column: 2; grid-row: 2; text-align: right; margin-top: 5px; }
  .room-usage-row > .ua-limit { grid-column: 1 / -1; margin-top: 11px; }
  .room-usage-row > .usage-table { grid-column: 1 / -1; }
  .room-usage-row:hover { background: #f6faf8; }
  .team-fold { border: 0; border-radius: 14px; box-shadow: var(--sh-card); margin-top: 12px; }
  .team-fold .tf-head { min-height: 46px; padding: 0 16px; }
  .team-fold .tf-roles { font-size: calc(11.5px * var(--fs, 1)); }

  /* AI 团队：5 列 → 4 列；卡片同首页令牌；「开始会话」不再拉满整卡，收到右下角 40 高 */
  .agent-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
  .agent-card {
    display: flex; flex-direction: column; padding: 18px;
    border: 0; border-radius: 16px; box-shadow: var(--sh-card);
  }
  .agent-card .hd { margin-bottom: 10px; }
  .agent-card .av { width: 40px; height: 40px; border-radius: 12px; font-size: calc(14.5px * var(--fs, 1)); }
  .agent-card h3 { font-family: inherit; font-size: calc(15px * var(--fs, 1)); font-weight: 650; }
  .agent-card .duty {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; min-height: 0; margin: 0 0 14px;
  }
  .agent-card .btn-primary {
    width: auto !important; align-self: flex-end; margin-top: auto;
    height: 40px; padding: 0 18px; border-radius: 10px;
  }
  .agent-card .kb-sub { text-align: right !important; margin-top: auto !important; }
  .agent-card .m { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line-soft); }

  /* 通用面板（知识库 / 模型设置 / 统计 / 成员管理共用）：细边框 → 卡片阴影，
     标题从「宋体 16」改「无衬线 14.5 / 650」——和首页团队卡、右栏小卡同一语汇。 */
  .panel, .kb-panel { border: 0; border-radius: 16px; box-shadow: var(--sh-card); }
  .panel h3, .kb-panel-head h3 {
    font-family: inherit; font-size: calc(14.5px * var(--fs, 1)); font-weight: 650;
  }
  .kb-panel { margin-top: 16px; }
  /* 知识库的「归属课题类型」筛选：深绿实心胶囊 → 灰凹槽 + 白色凸起片（和手机端、首页 chip 同一套） */
  .kb-rt-filter {
    display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px; margin: 0 0 12px;
    width: auto; background: #e9efec; border-radius: 10px;
  }
  .kb-rt-filter button {
    padding: 5px 12px; border: 0; border-radius: 8px; background: transparent;
    font-size: calc(12px * var(--fs, 1)); font-weight: 500; color: #6b7a76; line-height: 1.45;
  }
  .kb-rt-filter button.on {
    background: #fff; color: var(--g800); font-weight: 600;
    box-shadow: 0 1px 3px rgba(30, 70, 60, .12);
  }
  .kb-rt-filter button:not(.on):hover { color: var(--g800); }

  /* z364：`#/home` 路由的兜底 —— body.in-home 只在 `#/` 上打（见 route()），
     从「团队页返回」等深链进 `#/home` 时电脑版仪表盘下面会连同旧版首页分区一起渲染：
     通知·公告·动态 + 三张数字卡 + 课题讨论区 / 公共撰写区整段重复一遍。
     这里用 :has(.dw) 兜住：只要页面上有电脑版仪表盘，旧分区一律不显示。
     （.dw 只在首页出现，不会误伤统计/成员/用量页的 .stat-row。） */
  body:has(.dw) .stat-row,
  body:has(.dw) .home-sec { display: none; }
}

/* 900~1279（z366 起桌面断点下移到 900）：右栏收窄、团队网格改单列
   （两列会把团队名挤成省略号） */
@media (min-width: 900px) and (max-width: 1279px) {
  .dw { grid-template-columns: minmax(0, 1fr) calc(300px * var(--fw)); column-gap: 18px; }
  .ggrid { grid-template-columns: 1fr; }
  /* AI 专家卡：4 列在这个宽度只剩 180 左右，「模型：…」会折成两行 */
  .agent-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .gcell { border-right: 0; }
  /* 单列时「最后两格」不再同一行：底边线要保留，只去掉最后一格 */
  .gcell:nth-last-child(-n+2) { border-bottom: 1px solid var(--line-soft); }
  .gcell:last-child { border-bottom: 0; }
}


/* ══════════════════════════════════════════════════════════════
   z410：知识库 / 借鉴库 / 经验（桌面 ≥900）「工具条 + 单列列表」
   · 页头：标题 + 一行副标题 + 右侧主按钮
   · 一张卡：工具条（筛选 chips + 搜索）+ 单列紧凑行（标题单行省略）
   · 手机端不动：仍走 .wp / .wp-seg 那套
   ══════════════════════════════════════════════════════════════ */
.libv { background: var(--panel); border: 1px solid var(--line-soft); border-radius: var(--r-card); box-shadow: var(--sh-card); overflow: hidden; }
.libv-bar { display: flex; align-items: center; gap: 14px; padding: 10px 16px; border-bottom: 1px solid var(--line-soft); }
.libv-bar .cat-filter { margin: 0; flex: 1 1 auto; min-width: 0; gap: 5px; }
.libv-tools { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.libv-search { width: 236px; height: 30px; padding: 0 11px; border: 1px solid var(--line); border-radius: 9px; background: var(--panel); font: inherit; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink); }
.libv-search::placeholder { color: var(--ink-3); }
.libv-search:focus { outline: none; border-color: #bcd6cd; box-shadow: 0 0 0 3px rgba(29, 109, 94, .07); }
.libv-count { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); white-space: nowrap; }
.libv-note { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); padding: 9px 16px; border-bottom: 1px solid var(--line-soft); background: #fbfcfc; }
.libv-empty { padding: 24px 18px; text-align: center; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); }

/* 知识库 / 借鉴库：单列紧凑行 */
.kbr { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-bottom: 1px solid var(--line-soft); }
.kbr:last-child { border-bottom: 0; }
.kbr:hover { background: #f8fbfa; }
.kbr.pend { background: #fffdf6; }
.kbr.pend:hover { background: #fffbea; }
.kbr-ic { flex: none; width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 9.5px; font-weight: 700; letter-spacing: .02em; }
.kbr-ic.pdf { background: #fdecea; color: #b4443a; }
.kbr-ic.doc { background: #eaf1fb; color: #2b6cb0; }
.kbr-ic.xls { background: #eaf6ee; color: #3f7d4f; }
.kbr-ic.ppt { background: #fdf1e6; color: #b8791a; }
.kbr-ic.img { background: #f3effb; color: #6d4aa8; }
.kbr-ic.txt { background: #f1f4f3; color: #5b6a66; }
.kbr-ic.card { background: #eef4f2; color: #0f4c44; }
.kbr-main { flex: 1 1 auto; min-width: 0; }
.kbr-t { font-size: calc(13px * var(--fs, 1)); font-weight: 550; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kbr-m { display: flex; align-items: center; gap: 7px; margin-top: 4px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); min-width: 0; overflow: hidden; }
.kbr-m > * { flex: none; }
.kbr-dim { white-space: nowrap; }
.kbr-act { flex: none; display: flex; align-items: center; gap: 6px; opacity: .6; transition: opacity .12s; }
.kbr:hover .kbr-act, .kbr.pend .kbr-act { opacity: 1; }
.kbr .lit-more-src { display: none; }

/* 经验：同一张卡里的单列列表（去掉每行独立边框，只留分隔线） */
.rlv .rule-row { margin: 0; border: 0; border-bottom: 1px solid var(--line-soft); border-radius: 0; padding: 12px 16px; background: transparent; }
.rlv .rule-row:last-child { border-bottom: 0; }
.rlv .rule-row:hover { background: #f8fbfa; box-shadow: none; }
.rlv .rule-row.pending { background: #fffdf6; }
.rlv .rule-row.rt-req { background: #fffdf6; }
.rlv .rule-row .rmain { flex: 1 1 auto; min-width: 0; }
.rlv .rule-row .racts { flex: none; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; opacity: .6; transition: opacity .12s; }
.rlv .rule-row:hover .racts, .rlv .rule-row.pending .racts { opacity: 1; }
.rlv-sub { margin: 0; padding: 9px 16px 5px; font-size: calc(11px * var(--fs, 1)); font-weight: 600; color: var(--ink-3); letter-spacing: .02em; background: #fbfcfc; border-bottom: 1px solid var(--line-soft); }

/* 经验行文字溢出修复（z410 的根因：flex 子项没有 min-width:0，长句把卡片顶破） */
.rule-row .rmain { min-width: 0; }
.rule-row .rt { overflow-wrap: anywhere; }

/* 上传弹层（知识库 / 借鉴库：上传收进页头按钮） */
.upmodal-mask { position: fixed; inset: 0; z-index: 130; display: flex; align-items: center; justify-content: center; padding: 24px; background: rgba(16, 34, 30, .34); }
.upmodal-mask.hidden { display: none; }
.upmodal { width: min(560px, 100%); max-height: 88vh; overflow: auto; background: var(--panel); border-radius: 18px; box-shadow: 0 24px 64px rgba(12, 36, 31, .26); padding: 20px 22px 18px; }
.upmodal h3 { margin: 0 0 3px; font-size: calc(15px * var(--fs, 1)); }
.upmodal .upmodal-sub { margin: 0 0 14px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }
.upmodal .upmodal-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }

/* z410 修正：桌面段有一条通用 `.rt { white-space:nowrap; text-overflow:ellipsis }`（约 6146 行），
   会把经验正文也截成一行省略号 —— 列表里必须完整换行。优先级靠 .rlv 前缀压过去。 */
.rlv .rule-row .rt { white-space: normal; overflow: visible; text-overflow: clip; }
/* 层级标签定宽：否则「课题组」/「房间」宽度不同，正文起始位置一行一个样 */
.rlv .rule-row .rtag { min-width: 58px; text-align: center; }
/* 工具条 chips 轻一点：实心墨绿在列表页太抢视线（方案稿是浅底深字） */
.libv-bar .cat-filter button { height: 30px; padding: 0 11px; border-radius: 9px; font-size: calc(12px * var(--fs, 1)); }
.libv-bar .cat-filter button.on { background: var(--teal-soft); color: var(--teal); border-color: #cfe2db; font-weight: 600; }
.kbr-date { flex: none; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); white-space: nowrap; }
.upmodal .upload-zone { padding: 22px 16px; }

/* ============================================================
   z412：审批（仪器预约 / 采购 / 请假 / 值日）
   ============================================================ */
.hm-five.hm-four { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.ap-sub { margin: 4px 0 0; font-size: 16.25px; color: var(--ink-2); }

.ap-cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px; margin: 14px 0 10px;
}
.ap-card {
  position: relative; display: flex; flex-direction: column; gap: 4px;
  padding: 16px 16px 14px; border: 1px solid var(--line); border-radius: 16px;
  background: var(--card, #fff); text-decoration: none; color: inherit;
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
}
.ap-card:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(6, 40, 34, .08); border-color: var(--g700, #1d6a5f); }
.ap-card b { font-size: 18.75px; color: var(--ink, #123); }
.ap-card i { font-size: 15px; color: var(--ink-2); font-style: normal; }
.ap-card .ar { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); color: var(--ink-2); }
.ap-cico {
  width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center;
  background: var(--g050); color: var(--g800); margin-bottom: 6px;
}
.ap-cico svg { width: 18px; height: 18px; }
.ap-note { margin-top: 8px; font-size: 16.25px; color: var(--ink-2); }
.ap-note b { color: var(--g800); }

.ap-chips { display: flex; gap: 8px; margin: 12px 0 14px; }
.ap-chips button {
  border: 1px solid var(--line); background: var(--card, #fff); color: var(--ink-2);
  border-radius: 999px; padding: 7px 16px; font-size: 16.25px; cursor: pointer;
}
.ap-chips button.on { background: var(--g800); border-color: var(--g800); color: #fff; font-weight: 600; }

.ap-tools { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 12px; }
.ap-tools input[type="search"] {
  flex: 1 1 220px; min-width: 180px; padding: 9px 12px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--card, #fff); font-size: 16.25px; color: inherit;
}
.ap-filters { display: flex; gap: 6px; }
.ap-filters button {
  border: 1px solid var(--line); background: transparent; color: var(--ink-2);
  border-radius: 999px; padding: 5px 12px; font-size: 15px; cursor: pointer;
}
.ap-filters button.on { color: var(--g800); border-color: var(--g700); font-weight: 600; }

.ap-list { display: flex; flex-direction: column; gap: 8px; }
.ap-row {
  display: flex; flex-direction: column; gap: 4px; width: 100%; text-align: left;
  padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--card, #fff); cursor: pointer; color: inherit;
}
.ap-row:hover { border-color: var(--g700, #1d6a5f); }
.ap-r1 { display: flex; align-items: center; gap: 8px; }
.ap-r1 b { font-size: 17.5px; color: var(--ink, #123); font-weight: 600; }
.ap-r2 { font-size: 15.63px; color: var(--ink-2); }
.ap-r3 { font-size: 15px; color: var(--ink-2); opacity: .85; }

.ap-st {
  font-style: normal; font-size: 13.75px; padding: 2px 8px; border-radius: 999px;
  border: 1px solid transparent; white-space: nowrap;
}
.ap-wait { color: #8a5a00; background: #fff4e0; border-color: #ffe0ad; }
.ap-wait2 { color: #7a3d00; background: #ffeede; border-color: #ffd3b0; }
.ap-ok { color: #0f6b3f; background: #e8f7ee; border-color: #c6ecd6; }
.ap-bad { color: #a02b2b; background: #fdeceb; border-color: #f6cfcc; }
.ap-mute { color: #6b7772; background: #f1f4f3; border-color: #e2e8e5; }

.ap-loading { padding: 26px 0; display: flex; justify-content: center; }
.ap-empty { padding: 26px 6px; text-align: center; color: var(--ink-2); font-size: 16.25px; }

/* 详情弹层 */
.ap-mask {
  position: fixed; inset: 0; z-index: 9000; background: rgba(8, 26, 22, .38);
  display: flex; align-items: center; justify-content: center; padding: 18px;
}
.ap-sheet {
  width: min(560px, 100%); max-height: 86vh; display: flex; flex-direction: column;
  background: var(--card, #fff); border-radius: 16px; overflow: hidden;
  box-shadow: 0 18px 48px rgba(6, 30, 25, .25);
}
.ap-sheet-top {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px; border-bottom: 1px solid var(--line-soft);
}
.ap-sheet-top b { font-size: 18.75px; }
.ap-x { border: 0; background: transparent; font-size: 25px; line-height: 1; color: var(--ink-2); cursor: pointer; }
/* z437：详情弹层右上角从 × 换成「返回」文字按钮（叠开相似单据后点它回上一层） */
.ap-x-txt { font-size: 15.63px; padding: 6px 10px; border-radius: 9px; color: var(--ink-2); }
.ap-x-txt:active { background: var(--g100, #eef2f0); }
.ap-sheet-body { padding: 14px 16px 18px; overflow: auto; }
.ap-dh { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.ap-dh b { font-size: 20px; color: var(--ink, #123); }
.ap-kv { display: flex; flex-direction: column; gap: 8px; }
.ap-kv > div { display: flex; gap: 10px; font-size: 16.25px; }
.ap-kv i { flex: 0 0 78px; color: var(--ink-2); font-style: normal; }
.ap-kv span { flex: 1; color: var(--ink, #123); word-break: break-all; }
.ap-acts-h { margin: 16px 0 8px; font-size: 15px; color: var(--ink-2); }
.ap-acts { display: flex; flex-direction: column; gap: 6px; }
.ap-act { display: flex; gap: 8px; align-items: baseline; font-size: 15.63px; color: var(--ink-2); }
.ap-act b { color: var(--ink, #123); }
.ap-act i { margin-left: auto; font-style: normal; font-size: 13.75px; opacity: .8; }
.ap-rev { margin-top: 16px; }
.ap-rev textarea {
  width: 100%; box-sizing: border-box; padding: 9px 11px; border: 1px solid var(--line);
  border-radius: 10px; font-size: 16.25px; resize: vertical; color: inherit; background: var(--card, #fff);
}
.ap-rev-btns { display: flex; gap: 10px; justify-content: flex-end; margin-top: 10px; }

/* z446：值日评分（审批面板内，0–100 + 快捷档位） */
.ap-score {
  margin-bottom: 10px; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--teal-soft);
}
.ap-score-h { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; }
.ap-score-h b { font-size: 16.25px; color: var(--ink); }
.ap-score-h span { font-size: 13.75px; color: var(--ink-3); }
.ap-score-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.ap-score-row input {
  width: 96px; box-sizing: border-box; padding: 8px 10px; border: 1px solid var(--line);
  border-radius: 9px; font-size: 16.25px; color: inherit; background: var(--panel);
}
.ap-score-qs { display: flex; gap: 6px; flex-wrap: wrap; }
.ap-score-qs button {
  padding: 6px 11px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--panel); color: var(--ink-2); font-size: 14.38px; cursor: pointer;
}
.ap-score-qs button:hover { border-color: var(--teal); color: var(--teal); }

/* 表单 */
.ap-form { display: flex; flex-direction: column; gap: 12px; max-width: 560px; }
.ap-form > label, .ap-two label {
  display: flex; flex-direction: column; gap: 6px; font-size: 16.25px; color: var(--ink-2);
}
.ap-form input, .ap-form select, .ap-form textarea {
  padding: 9px 11px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--card, #fff); font-size: 17.5px; color: var(--ink, #123); font-family: inherit;
}
.ap-two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ap-checks { display: flex; flex-direction: column; gap: 8px; }
.ap-chk { flex-direction: row !important; align-items: center; gap: 8px !important; font-size: 16.88px; color: var(--ink, #123); }
.ap-chk input { width: 16px; height: 16px; }
.ap-tip { font-size: 15px; color: var(--ink-2); }
/* z415：值日表整张没排人时的提示条（amber，别用红，不是错误） */
.ap-tip-warn {
  font-size: 15.63px; color: #7a5300; background: #fff8e8; border: 1px solid #f3e0b4;
  border-radius: 10px; padding: 9px 12px; margin: 0 0 10px; line-height: 1.6;
}
.ap-submit { align-self: flex-start; }
.ap-submit[disabled] { opacity: .5; cursor: not-allowed; }

.ap-clash { border: 1px solid #f6cfcc; background: #fdf3f2; border-radius: 12px; padding: 10px 12px; }
.ap-clash-h { font-size: 16.25px; color: #a02b2b; font-weight: 600; margin-bottom: 6px; }
.ap-clash-row { font-size: 15.63px; color: #7d3a36; }

.ap-similar { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; background: var(--g050); }
.ap-sim-h { font-size: 15.63px; color: var(--g800); font-weight: 600; margin-bottom: 8px; }
.ap-sim-h span { font-weight: 400; font-size: 13.75px; color: var(--ink-3); margin-left: 6px; }
.ap-sim-btns { display: flex; gap: 6px; flex: none; flex-wrap: wrap; justify-content: flex-end; align-items: center; }
/* z438：相似程度标签 —— 品牌 + 货号都一样叫「完全相同」，其余叫「高度相似」 */
.ap-sim-tag {
  font-style: normal; font-size: 12.5px; font-weight: 600; line-height: 1.5;
  border-radius: 999px; padding: 1px 8px; margin-left: 6px; white-space: nowrap;
}
.ap-sim-tag.same { color: #a02b2b; background: #fdeceb; }
.ap-sim-tag.high { color: #8a5a12; background: #fdf3e2; }
.ap-sim-tag.ing { color: #1a5e8a; background: #e8f2f9; }
/* 「点开这单」做成胶囊 + 右箭头，比普通按钮轻，跟标签同一套圆角语言 */
.ap-sim-open {
  display: inline-flex; align-items: center; gap: 3px; flex: none;
  border: 1px solid var(--line); background: #fff; border-radius: 999px;
  padding: 5px 12px; font-size: 14.38px; color: var(--ink); cursor: pointer;
}
.ap-sim-open i { font-style: normal; color: var(--ink-3, #93a3a1); margin-left: 1px; }
.ap-sim-open:active { background: var(--g050, #f3f8f6); }
.ap-sim-btns .btn-ghost.sm { border-radius: 999px; }
.ap-sim-row { display: flex; flex-direction: column; gap: 2px; padding: 6px 0; border-top: 1px dashed var(--line); }
.ap-sim-row:first-of-type { border-top: 0; }
.ap-sim-row b { font-size: 16.25px; color: var(--ink, #123); }
.ap-sim-row span { font-size: 14.38px; color: var(--ink-2); }
/* z437：重复提示统一用 <i class="ap-sim-dup"> 标签，不再用 ::after 叠一层 */
.ap-sim-row.strong b::after { content: none; }

@media (max-width: 620px) {
  .ap-cards { grid-template-columns: 1fr 1fr; gap: 10px; }
  .ap-card { padding: 13px 12px 11px; }
  .ap-card .ar { display: none; }
  .ap-two { grid-template-columns: 1fr; }
  .ap-tools input[type="search"] { flex-basis: 100%; }
  .ap-sheet { width: 100%; max-height: 92vh; }
  .ap-kv i { flex-basis: 68px; }
}

/* z413：手机端收起顶部那条横排导航（复用桌面 .topnav 的那一份）。
   理由：z412 起手机主入口是首页四 chip（首页/AI团队/审批/统计板），
   资料三库进「更多 → 资料」、成员/用量/模型进「更多 → 管理」；
   顶部再横排一遍侧栏全表，既是两套导航，右侧还会被截断（「用量与积…」）。
   手机上去这几处的通道：首页顶部「待我处理」数字 → #/pending；⋮ → #/more。
   ⚠️ 只限 ≤620；桌面侧栏一行都不能少。 */
/* z413：审批页自带的「‹ 首页」按钮，桌面端隐藏（桌面走侧栏） */
.ap-bk { display: none; }
@media (max-width: 620px) {
  .topnav { display: none; }
  .ap-bk { display: inline-flex; align-items: center; gap: 4px; }
}

/* ============================================================
   z416：审批第二轮修补（页内顶栏 / 卡片角标 / 时间滚轮 / 灰字时长 / 完整流程）
   ============================================================ */
/* ① 手机页内顶栏（仿 AI 团队页 .wp-top）；桌面端不显示，桌面仍用 .page-head */
.apm { display: none; }
@media (max-width: 620px) {
  body.in-wp .apm { display: flex; align-items: center; gap: 6px; height: 52px; }
  .apm-bk {
    width: 32px; height: 32px; margin-left: -8px; border: 0; background: none; border-radius: 10px;
    display: grid; place-items: center; color: var(--ink, #17211f); cursor: pointer;
    font-size: calc(25px * var(--fs, 1)); line-height: 1;
  }
  .apm-bk:active { background: #eef3f1; }
  .apm h1 {
    margin: 0; flex: 1 1 auto; min-width: 0; font-size: calc(20px * var(--fs, 1)); font-weight: 700;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .apm-act {
    flex: 0 0 auto; border: 0; background: none; cursor: pointer; font: inherit;
    font-size: calc(15px * var(--fs, 1)); font-weight: 600; color: var(--g800, #0f5c54); padding: 4px 2px;
  }
  /* 页内顶栏顶替原来的 .page-head —— 否则「审批」两行标题叠着 */
  body.in-wp .apm + .page-head { display: none; }
  body.in-wp .ap-cards { margin-top: 2px; }
  body.in-wp .ap-chips { margin-top: 8px; }
  body.in-wp .ap-form { max-width: 100%; }
}

/* ② 审批首页四张卡的「待我审 N」角标（顶栏那颗红点的同源数字） */
.ap-cbadge {
  position: absolute; top: 10px; right: 12px; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 999px; background: #d92d20; color: #fff; font-size: 13.75px; font-weight: 700;
  font-style: normal; line-height: 18px; text-align: center; box-shadow: 0 1px 4px rgba(0, 0, 0, .18);
}
.ap-cbadge[hidden] { display: none; }
@media (max-width: 620px) {
  .ap-card .ap-cbadge { top: 8px; right: 8px; }
  .ap-card .ar { display: none; }
}

/* ③ 时间选择：滚轮（两列吸附） */
.ap-timebtn {
  display: block; padding: 9px 11px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--card, #fff); font-size: 17.5px; font-weight: 500; color: var(--ink, #123);
  cursor: pointer; font-variant-numeric: tabular-nums; text-align: center; user-select: none;
}
.ap-timebtn::after { content: '⌄'; float: right; opacity: .4; font-weight: 400; }
.ap-timebtn:hover { border-color: var(--g700, #1d6a5f); }
.ap-wheel-hint { font-size: 15px; color: var(--ink-2); text-align: center; margin: 0 0 6px; }
.ap-wheel { position: relative; display: flex; align-items: center; justify-content: center; gap: 8px; height: 220px; }
.ap-wheel-band {
  position: absolute; left: 12px; right: 12px; top: 50%; height: 44px; margin-top: -22px;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  background: rgba(15, 92, 84, .05); border-radius: 10px; pointer-events: none;
}
.ap-wheel-col {
  position: relative; height: 220px; width: 86px; overflow-y: auto;
  scroll-snap-type: y mandatory; scrollbar-width: none; -ms-overflow-style: none;
}
.ap-wheel-col::-webkit-scrollbar { display: none; }
.ap-wheel-pad { height: 88px; }
.ap-wheel-i {
  height: 44px; line-height: 44px; text-align: center; font-size: 23.75px; font-weight: 600;
  color: var(--ink-2); scroll-snap-align: center; cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.ap-wheel-sep { font-size: 23.75px; font-weight: 700; color: var(--ink-3, #93a3a1); padding-bottom: 2px; }

/* ④ 预约时长：灰字说明（不可输入） */
.ap-dur {
  margin: -4px 0 0; font-size: 15.63px; color: var(--ink-3, #93a3a1);
  font-variant-numeric: tabular-nums;
}

/* ⑤ 流程节点：本单不需要 / 终点 */
.ap-node.is-skip .ap-dot { background: #f1f4f3; color: #9aa8a4; box-shadow: inset 0 0 0 1px #e2e8e5; }
.ap-node.is-skip .ap-node-t { color: var(--ink-2); }
.ap-node.is-skip .ap-node-s { color: #93a3a1; }
.ap-dot-tick { font-size: 16.25px; font-weight: 700; line-height: 1; }
.ap-node.is-done .ap-dot-end { background: var(--g700, #2f6b57); color: #fff; }

/* ⑥ 采购渠道说明（保留企微原文提示词） */
.ap-tip-note {
  display: flex; flex-direction: column; gap: 4px; font-size: 15.63px; line-height: 1.65;
  background: var(--g050, #f3f8f6); border: 1px solid var(--line); border-radius: 12px;
  padding: 10px 12px; color: var(--ink-2);
}
.ap-tip-note b { color: var(--g800, #0f5c54); font-weight: 600; }

/* ============================================================
   z414：申请页补字段 · 审批流程图 · 值日照片 · 借用
   ============================================================ */

/* --- 审批流程图（截图版式：圆点 + 竖线 + 人物卡） --- */
.ap-flow { margin-top: 26px; padding-top: 18px; border-top: 1px dashed var(--line); }
.ap-flow-h { font-size: 17.5px; font-weight: 700; color: var(--ink); margin-bottom: 14px; }
.ap-flow-h i { font-weight: 400; font-size: 15px; color: var(--ink-2); font-style: normal; margin-left: 6px; }
.ap-node { position: relative; display: flex; gap: 10px; padding-bottom: 20px; }
.ap-node:last-child { padding-bottom: 0; }
.ap-node::before {
  content: ''; position: absolute; left: 11px; top: 26px; bottom: 0; width: 2px;
  /* z414：原用 --line-soft(#eef3f1)，白底上几乎不可见，流程看起来是散的；改 --line 才串得起来 */
  background: var(--line, #dfe7e4);
}
.ap-node:last-child::before { display: none; }
.ap-node.off { display: none; }
.ap-dot {
  position: relative; z-index: 1; width: 24px; height: 24px; border-radius: 50%; flex: none;
  display: grid; place-items: center; background: var(--g050); color: var(--g800);
}
.ap-dot .ic, .ap-dot svg { width: 13px; height: 13px; }
.ap-dot .ic { display: grid; place-items: center; }
.ap-dot-cc { background: #eef1f0; color: #6b7772; }
.ap-node-b { display: flex; flex-direction: column; gap: 7px; padding-top: 1px; }
.ap-node-t { font-size: 16.25px; color: var(--ink); font-weight: 600; }
/* z438：抄送节点 —— 只通知不审批，整体压淡一档，别和审批环节抢注意力 */
.ap-node-cc .ap-node-t { color: var(--ink-2); font-weight: 500; }
.ap-node-cc .ap-person b { color: var(--ink-2); font-weight: 500; }
.ap-node-cc .ap-node-st { color: var(--ink-3, #93a3a1); }
.ap-node-s { font-size: 15px; color: var(--ink-2); }
.ap-person {
  display: inline-flex; align-items: center; gap: 8px; padding: 5px 12px 5px 5px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--card, #fff);
}
.ap-person b { font-size: 16.25px; font-weight: 500; color: var(--ink); }
.ap-ava {
  width: 26px; height: 26px; border-radius: 7px; background: var(--g050); color: var(--g800);
  display: grid; place-items: center; font-size: 15px; font-weight: 600;
}
.ap-people { display: flex; flex-wrap: wrap; gap: 8px; }

/* ===== z415：审批流程四态（已通过实线+✓ / 待处理实线 / 未到达虚线 / 驳回×）=====
   用户原话「没到达的流程用虚线，通过的流程就变成实线并标识√」。
   连线状态看「下一站到没到」（line-dash / line-solid 由 JS 按后一节点算好）。 */
.ap-node.line-dash::before {
  background: repeating-linear-gradient(to bottom, var(--line, #dfe7e4) 0 4px, transparent 4px 9px);
}
.ap-node.line-solid::before { background: var(--line, #dfe7e4); }
.ap-node.line-last::before { display: none; }
.ap-node.is-done .ap-dot { background: var(--g700, #2f6b57); color: #fff; }
.ap-node.is-bad .ap-dot { background: #b93b3b; color: #fff; }
.ap-node.is-now .ap-dot { background: #fff; color: var(--g800, #1d6a5f); box-shadow: 0 0 0 2px var(--g700, #2f6b57) inset; }
.ap-node.is-done .ap-dot::after {
  content: '✓'; position: absolute; right: -3px; bottom: -3px; width: 13px; height: 13px;
  border-radius: 50%; background: var(--g800, #1d6a5f); color: #fff; font-size: 10.63px;
  display: grid; place-items: center; line-height: 1; font-weight: 700;
  box-shadow: 0 0 0 1.5px var(--card, #fff);
}
.ap-node.is-bad .ap-dot::after {
  content: '×'; position: absolute; right: -3px; bottom: -3px; width: 13px; height: 13px;
  border-radius: 50%; background: #b93b3b; color: #fff; font-size: 11.25px;
  display: grid; place-items: center; line-height: 1; font-weight: 700;
  box-shadow: 0 0 0 1.5px var(--card, #fff);
}
.ap-node-st {
  font-style: normal; font-size: 13.75px; font-weight: 500; margin-left: 7px;
  color: var(--ink-2); padding: 1px 6px; border-radius: 999px; background: var(--g050, #eef6f2);
}
.ap-node.is-done .ap-node-st { color: #0f6b3f; background: #e8f7ee; }
.ap-node.is-now .ap-node-st { color: #8a5a00; background: #fff4e0; }
.ap-node.is-bad .ap-node-st { color: #a02b2b; background: #fdeceb; }

/* 四页签：手机上别挤成两行，也别把第 4 个挤出屏幕
   用 grid + minmax(0,1fr) 而不是 flex —— flex 子项 min-width:auto 会被内容撑住，
   390 宽下「申请页面 / 审批中 / 本人记录 / 全组记录」第 4 个会被推到视口外。 */
.ap-chips-4 button { padding: 6px 14px; font-size: 16.25px; }
@media (max-width: 620px) {
  .ap-chips-4 {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 5px; overflow-x: auto;
  }
  .ap-chips-4 button {
    padding: 6px 2px; font-size: 15px; white-space: nowrap;
    text-align: center; min-width: 0; letter-spacing: -.2px;
  }
}

/* 列表行里的撤销按钮（行本身已改成 div，按钮浮在右侧） */
.ap-row { position: relative; }
.ap-rowrev {
  position: absolute; right: 10px; top: 10px; z-index: 2;
  border: 1px solid var(--line); background: var(--card, #fff); color: #a02b2b;
  font-size: 14.38px; padding: 3px 9px; border-radius: 999px; cursor: pointer;
}
.ap-rowrev:hover { border-color: #e0b4b4; background: #fdf4f3; }

/* 撤销确认弹层 */
.ap-sheet-sm { width: min(420px, 100%); }
.ap-rev-tip { font-size: 16.25px; color: var(--ink-2); line-height: 1.65; margin: 0 0 12px; }
.ap-rev-l { display: block; font-size: 15.63px; color: var(--ink-2); margin-bottom: 10px; }
.ap-rev-l textarea, .ap-rev-l select {
  display: block; width: 100%; margin-top: 5px; padding: 8px 10px; border: 1px solid var(--line);
  border-radius: 9px; font-size: 16.25px; font-family: inherit; color: var(--ink); background: var(--card, #fff);
}
.ap-rev-btns-x { justify-content: flex-start; border-top: 1px dashed var(--line); padding-top: 12px; }

/* ===== 值日表页 ===== */
.ap-tools-roster { justify-content: space-between; }
.ap-roster-h { font-size: 16.88px; font-weight: 600; color: var(--ink); }
.ap-roster-btns { display: flex; gap: 8px; }
.ap-roster-btns .sm { padding: 6px 12px; font-size: 15.63px; }
.ap-roster { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.ap-rd {
  display: flex; align-items: center; gap: 12px; padding: 10px 13px;
  border: 1px solid var(--line); border-radius: 11px; background: var(--card, #fff);
}
.ap-rd.today { border-color: var(--g700, #2f6b57); box-shadow: 0 0 0 1px var(--g700, #2f6b57) inset; }
.ap-rd.pen { background: #fdf7f5; border-color: #f0d9d3; }
.ap-rd-d { flex: none; width: 96px; font-size: 15.63px; color: var(--ink-2); }
.ap-rd-d i {
  font-style: normal; margin-left: 5px; font-size: 13.13px; color: #fff;
  background: var(--g700, #2f6b57); padding: 1px 5px; border-radius: 999px;
}
.ap-rd-n { flex: 1; font-size: 16.88px; color: var(--ink); font-weight: 500; }
.ap-rd-t { display: flex; gap: 6px; flex: none; }
.ap-src {
  font-style: normal; font-size: 13.13px; padding: 2px 7px; border-radius: 999px;
  background: #f1f4f3; color: #6b7772;
}
.ap-src-ok { background: #e8f7ee; color: #0f6b3f; }
.ap-src-bad { background: #fdeceb; color: #a02b2b; }
.ap-rsel {
  width: 100%; padding: 6px 9px; border: 1px solid var(--line); border-radius: 8px;
  font-size: 16.25px; font-family: inherit; color: var(--ink); background: var(--card, #fff);
}
.ap-swap-say {
  font-size: 15.63px; line-height: 1.7; color: var(--ink); background: var(--g050, #eef6f2);
  border-radius: 9px; padding: 9px 11px; margin: 4px 0 12px;
}
.ap-swap-say b { color: var(--g800, #1d6a5f); }
.ap-swap-warn { color: #a02b2b; }
@media (max-width: 620px) {
  .ap-rd { flex-wrap: wrap; gap: 6px 10px; }
  .ap-rd-d { width: auto; }
  .ap-rd-t { margin-left: auto; }
}

/* --- 抄送人多选 --- */
.ap-cclabel { font-size: 16.25px; color: var(--ink-2); }
.ap-ccbox { display: flex; flex-wrap: wrap; gap: 8px; }
.ap-ccchk {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px;
  border: 1px solid var(--line); border-radius: 999px; font-size: 15.63px;
  color: var(--ink); cursor: pointer; user-select: none;
}
.ap-ccchk input { width: 14px; height: 14px; }
.ap-ccchk:has(input:checked) { border-color: var(--g700); background: var(--g050); }

/* --- 值日照片 / 附图机位 --- */
.ap-phh { font-size: 16.25px; color: var(--ink-2); }
.ap-phgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.ap-ph {
  position: relative; aspect-ratio: 1 / 1; border: 1px dashed var(--line); border-radius: 12px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  cursor: pointer; overflow: hidden; background: var(--card, #fff); text-align: center;
}
.ap-ph img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ap-ph i {
  font-size: 13.75px; color: var(--ink-2); font-style: normal; line-height: 1.25;
  padding: 0 4px; z-index: 1;
}
.ap-ph.done { border-style: solid; border-color: var(--g700); }
/* z414：原来只有一层透明渐变 + 白字，实拍缩略图亮的时候文件名基本看不清；改实底 + 右上角对勾 */
.ap-ph.done i {
  position: absolute; bottom: 0; left: 0; right: 0; color: #fff; padding: 3px 5px;
  font-size: 13.13px; letter-spacing: .2px; background: rgba(18, 34, 29, .62);
  backdrop-filter: blur(2px); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ap-ph.done::after {
  content: '✓'; position: absolute; top: 5px; right: 5px; z-index: 2;
  width: 18px; height: 18px; border-radius: 50%; display: grid; place-items: center;
  font-size: 13.75px; font-weight: 700; color: #fff; background: var(--g700, #2f6b57);
  box-shadow: 0 1px 4px rgba(0, 0, 0, .28);
}
.ap-ph.busy { opacity: .55; }
.ap-ph-ph { font-size: 25px; color: var(--ink-2); line-height: 1; }

/* --- 详情里的照片 --- */
.ap-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 8px; }
.ap-photo { display: flex; flex-direction: column; gap: 4px; text-decoration: none; color: inherit; }
.ap-photo img {
  width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 10px; border: 1px solid var(--line);
}
.ap-photo i { font-size: 13.13px; color: var(--ink-2); font-style: normal; line-height: 1.2; }
.ap-ph-miss {
  display: grid; place-items: center; aspect-ratio: 1 / 1; border-radius: 10px;
  background: var(--g050); font-size: 13.75px; color: var(--ink-2);
}

/* --- 相似采购：加「申请借用」按钮后改成左右两栏 --- */
.ap-sim-row { flex-direction: row !important; align-items: center; justify-content: space-between; gap: 10px; }
.ap-sim-l { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ap-sim-l b { font-size: 16.25px; color: var(--ink); }
.ap-sim-l span { font-size: 14.38px; color: var(--ink-2); }
.ap-borrow {
  flex: none; white-space: nowrap; border-color: var(--g700) !important; color: var(--g800) !important;
}

/* --- 待我审批页 --- */
.pend-sec-h { font-size: 13px; font-weight: 600; color: var(--ink-2); margin: 18px 0 8px; }
.pend-sec-h:first-child { margin-top: 0; }
.ap-app-tag {
  font-style: normal; font-size: 13.75px; color: var(--ink-2);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px;
}
.ap-borrow-list { display: flex; flex-direction: column; gap: 8px; }
.ap-borrow-row {
  display: flex; gap: 10px; align-items: center; justify-content: space-between;
  padding: 12px 14px; border: 1px solid #ffe0ad; background: #fff8ec; border-radius: 12px;
}
.ap-borrow-btns { display: flex; gap: 8px; flex: none; }

@media (max-width: 620px) {
  .ap-phgrid { gap: 8px; }
  .ap-borrow-row { flex-direction: column; align-items: flex-start; }
  .ap-borrow-btns { width: 100%; justify-content: flex-end; }
  .ap-flow { margin-top: 20px; }
}

/* ============================================================
   z420：待我审批页签 · 值日顺序（循环队列）· 相似采购展开
   ============================================================ */

/* ① 页签里的「待我审批」条数 —— 与导航红点同源 */
.ap-chips button { display: inline-flex; align-items: center; gap: 5px; }
.ap-chips-n {
  font-style: normal; font-size: 13.75px; font-weight: 700; line-height: 15px;
  min-width: 15px; height: 15px; padding: 0 4px; border-radius: 999px;
  background: #d92d20; color: #fff; text-align: center;
}
.ap-chips button.on .ap-chips-n { background: #fff; color: var(--g800, #0f5c54); }

@media (max-width: 620px) {
  /* flex 子项 min-width:auto 会把最后一颗挤出屏幕（z415 踩过），统一用 grid */
  .ap-chips-3, .ap-chips-4, .ap-chips-5 {
    display: grid; gap: 5px; overflow-x: auto;
  }
  .ap-chips-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ap-chips-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ap-chips-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  /* z433：这里原来漏了 .ap-chips-4 —— 值日页正好是 4 个页签（申请页面/待我审批/
     值日表/历史记录），别处都居中了，只有它还是默认的 7px 16px 内边距 + 左对齐，
     手机上一眼就能看出文字偏左（用户报「值日上面四个卡片的文字没有居中」）。
     四个页签统一：等宽 grid + 居中 + 收紧内边距。 */
  .ap-chips-3 button, .ap-chips-4 button, .ap-chips-5 button {
    padding: 6px 2px; font-size: 15px; white-space: nowrap;
    text-align: center; min-width: 0; justify-content: center; letter-spacing: -.2px;
  }
  .ap-chips-n { min-width: 13px; height: 13px; line-height: 13px; font-size: 12.5px; padding: 0 3px; }
}

/* ② 值日顺序面板 */
.ap-qbox {
  border: 1px solid var(--line); border-radius: 14px; background: var(--card, #fff);
  padding: 14px 16px; margin-bottom: 14px;
}
.ap-q-h { font-size: 16.25px; font-weight: 600; color: var(--ink); margin-bottom: 10px; }
.ap-q-h b { color: var(--g800, #0f5c54); font-size: 18.75px; margin: 0 2px; }
.ap-q-h span { display: block; font-weight: 400; font-size: 15px; color: var(--ink-3, #93a3a1); margin-top: 3px; }
/* z438：名单改成等宽两列 —— 原来是 flex-wrap，名字长短不同就东一个西一个，看着乱 */
.ap-q-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px; }
.ap-qi {
  display: flex; align-items: center; gap: 6px; min-width: 0;
  border: 1px solid var(--line); border-radius: 10px; padding: 5px 6px 5px 5px;
  background: var(--g050, #f3f8f6);
}
.ap-qi-n {
  font-style: normal; width: 20px; height: 20px; border-radius: 6px; background: var(--g800, #0f5c54);
  color: #fff; font-size: 13.75px; font-weight: 700; display: grid; place-items: center;
}
.ap-qi b { flex: 1 1 auto; min-width: 0; font-size: 15.63px; font-weight: 500; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ap-qi-off { font-style: normal; font-size: 13.75px; color: #a02b2b; }
.ap-qi-o { display: inline-flex; gap: 2px; margin-left: auto; flex: none; }
.ap-qi-o button {
  width: 19px; height: 19px; border: 1px solid var(--line); background: #fff;
  border-radius: 6px; font-size: 13.13px; line-height: 1; color: var(--ink-2); cursor: pointer; padding: 0;
}
.ap-qi-o button:disabled { opacity: .3; cursor: default; }
.ap-qi-o .ap-qi-x { color: #a02b2b; }
.ap-qb { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.ap-q-empty { font-size: 15.63px; color: var(--ink-2); line-height: 1.7; }
/* z438：设置页「调整某一天」弹层 */
.ap-dlist { display: flex; flex-direction: column; gap: 6px; max-height: 54vh; overflow: auto; margin: 4px 0 10px; }
.ap-dl { display: flex; align-items: center; gap: 10px; }
.ap-dl span { flex: 0 0 92px; font-size: 15px; color: var(--ink-2); }
.ap-dl select { flex: 1 1 auto; min-width: 0; }

/* ③ 批量输入弹层 */
.ap-qprev { margin-top: 10px; font-size: 15.63px; }
.ap-qok { color: var(--ink-2); margin-bottom: 8px; }
.ap-qok b { color: var(--g800, #0f5c54); font-size: 17.5px; }
.ap-qnames { display: flex; flex-wrap: wrap; gap: 6px; max-height: 190px; overflow-y: auto; }
.ap-qnames span {
  display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--g050, #f3f8f6); font-size: 15px;
}
.ap-qnames i {
  font-style: normal; font-size: 12.5px; color: #fff; background: var(--g700, #2f6b57);
  border-radius: 999px; min-width: 15px; height: 15px; line-height: 15px; text-align: center;
}
.ap-qmiss { margin-top: 8px; color: #a02b2b; line-height: 1.7; }
.ap-qw { display: block; color: #8a5a00; font-style: normal; }

/* ④ 添加值日人 */
.ap-qfind {
  width: 100%; padding: 9px 12px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--card, #fff); font-size: 16.25px; color: inherit; margin-bottom: 10px;
}
.ap-qpick { display: flex; flex-wrap: wrap; gap: 8px; max-height: 300px; overflow-y: auto; }
.ap-qp {
  border: 1px solid var(--line); background: var(--card, #fff); border-radius: 9px;
  padding: 8px 14px; font-size: 16.25px; color: var(--ink); cursor: pointer;
}
.ap-qp:hover { background: var(--line-soft, #f1f5f3); }
.ap-q-empty2 { font-size: 15.63px; color: var(--ink-3, #93a3a1); }

/* ⑤ 详情里的相似采购：每条可展开 */
.ap-simlist { display: flex; flex-direction: column; gap: 8px; }
.ap-sim2 {
  border: 1px solid var(--line); border-radius: 11px; background: var(--card, #fff);
  padding: 0 12px; overflow: hidden;
}
.ap-sim2 > summary {
  list-style: none; cursor: pointer; padding: 10px 0;
  display: flex; flex-direction: column; gap: 2px; position: relative; padding-right: 54px;
}
.ap-sim2 > summary::-webkit-details-marker { display: none; }
.ap-sim2 > summary b { font-size: 16.25px; font-weight: 600; color: var(--ink); }
.ap-sim2 > summary span { font-size: 15px; color: var(--ink-2); }
.ap-sim2 > summary em {
  position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  font-style: normal; font-size: 15px; color: var(--g800, #0f5c54); font-weight: 600;
}
.ap-sim2[open] > summary em::after { content: ''; }
.ap-sim2[open] > summary { border-bottom: 1px dashed var(--line); }
.ap-sim2-b { padding: 8px 0 12px; display: flex; flex-direction: column; gap: 5px; }
.ap-sim2-b div { display: flex; gap: 10px; font-size: 15.63px; }
.ap-sim2-b div i { font-style: normal; color: var(--ink-3, #93a3a1); flex: 0 0 74px; }
.ap-sim2-b div span { color: var(--ink); word-break: break-all; }
.ap-sim2-b .btn-ghost { align-self: flex-start; margin-top: 4px; }
.ap-sim-dup {
  font-style: normal; font-size: 13.13px; font-weight: 600; color: #a02b2b;
  background: #fdeceb; border-radius: 999px; padding: 1px 7px; margin-left: 6px;
}
/* z438：相似里命中「还没审完」的单据时要标出来 —— 防止两个人同时买同一样东西 */
.ap-sim-ing {
  font-style: normal; font-size: 13.13px; font-weight: 600; color: #1a5e8a;
  background: #e8f2f9; border-radius: 999px; padding: 1px 7px; margin-left: 6px;
}

/* 审批首页的「去处理」入口 */
.ap-note { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.ap-note-s { font-size: 15.63px; color: var(--ink-3, #93a3a1); }

/* ===== z429（用户要求）：审批板块 + 「更多」页整体字号放大 25% =====
   各板块自有样式（.ap-* / .apm* / .mr-*）已在原处把 px 值 ×1.25。
   这里只补被**通用类**压住的几处：.ap-sub 会被 .page-head p（特异性更高）覆盖，
   审批页头那几个按钮也走通用 .btn-ghost。用 :has() 限定容器，不影响别的板块。 */
#view:has(> .apm) .page-head h2 { font-size: calc(27.5px * var(--fs, 1)); }
#view:has(> .apm) .page-head p { font-size: calc(16.25px * var(--fs, 1)); }
#view:has(> .apm) .page-head .btn-ghost { font-size: calc(16.25px * var(--fs, 1)); }
#view:has(> .apm) .page-head .btn-ghost.sm { font-size: calc(15px * var(--fs, 1)); }

/* ═══════════════════════════════════════════════════════════════════
   z449：下载提示（A 版式）—— 顶部细进度条 + 底部胶囊
   用户原话：「下载过程不要静默，都不知道在不在下载」。
   两段式：网络下载走真实百分比；本地生成（Word / ZIP）走不确定态来回流动。
   z-index：盖住 toast(130)，压在 modal(210) 之下 —— 弹窗里的下载也要看得见。
   ═══════════════════════════════════════════════════════════════════ */
.dl-top {
  position: fixed; left: 0; right: 0; top: 0; height: 3px; z-index: 146;
  pointer-events: none; overflow: hidden; background: rgba(12, 40, 34, .10);
}
/* 琥珀色而不是品牌绿：首页顶部是深绿卡，绿条压上去等于隐形（截图验证过） */
.dl-top i {
  display: block; height: 3px; width: 0;
  background: #ef9f27;
  transition: width .18s linear;
}
/* 不确定态：一小段来回流动（没有总长时绝不假装 0%→100%） */
.dl-top.indet i {
  width: 36%; animation: dlSweep 1.15s cubic-bezier(.45,.05,.55,.95) infinite;
}
@keyframes dlSweep {
  0% { transform: translateX(-110%); }
  100% { transform: translateX(300%); }
}

.dl-pill {
  position: fixed; left: 10px; right: 10px;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  z-index: 147;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: 14px;
  background: var(--g900, #0c4139); color: #fff;
  box-shadow: 0 6px 22px rgba(12, 40, 34, .22);
  transform: translateY(150%); opacity: 0;
  transition: transform .26s cubic-bezier(.22,.61,.36,1), opacity .2s ease;
}
.dl-pill.on { transform: translateY(0); opacity: 1; }
.dl-pill.hidden { display: none; }

.dl-ic {
  flex: 0 0 auto; width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .26); border-top-color: #fff;
  animation: dlSpin .9s linear infinite;
}
@keyframes dlSpin { to { transform: rotate(360deg); } }

/* 完成态：转圈换成对勾，1.5 秒后自己滑走 */
.dl-pill.ok .dl-ic {
  border: 0; animation: none; background: #4ade80; position: relative;
}
.dl-pill.ok .dl-ic::after {
  content: ''; position: absolute; left: 4px; top: 4.5px;
  width: 6px; height: 3px; border-left: 1.6px solid #0c4139; border-bottom: 1.6px solid #0c4139;
  transform: rotate(-45deg);
}

.dl-tx { flex: 1 1 auto; min-width: 0; }
.dl-tx .dl-nm {
  display: block; font-size: calc(12.5px * var(--fs, 1)); font-weight: 500; color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dl-tx .dl-st {
  display: block; margin-top: 2px;
  font-size: calc(11.5px * var(--fs, 1)); font-style: normal; color: rgba(255, 255, 255, .68);
}
.dl-pill.ok .dl-tx .dl-st { color: #9ff0bf; }
.dl-x {
  flex: 0 0 auto; border: 0; border-radius: 8px; cursor: pointer;
  padding: 5px 9px; background: rgba(255, 255, 255, .12); color: #fff;
  font-family: inherit; font-size: calc(11.5px * var(--fs, 1));
}
.dl-x:active { background: rgba(255, 255, 255, .22); }

/* ═══════════════════════════════════════════════════════════════════
   z449：首页时钟 →「最近进入的房间」抽屉（只列房间）
   ═══════════════════════════════════════════════════════════════════ */
.rc-mask {
  position: fixed; inset: 0; z-index: 205;
  background: rgba(16, 34, 30, .36);
  opacity: 0; transition: opacity .22s ease;
}
.rc-mask.on { opacity: 1; }

.rc-sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 206;
  display: flex; flex-direction: column; max-height: 72vh;
  background: var(--panel); border-radius: 16px 16px 0 0;
  padding: 14px 0 calc(8px + env(safe-area-inset-bottom, 0px));
  box-shadow: 0 -6px 24px rgba(0, 0, 0, .14);
  transform: translateY(110%);
  transition: transform .26s cubic-bezier(.22,.61,.36,1);
}
.rc-sheet.on { transform: translateY(0); }

.rc-hd { display: flex; align-items: center; padding: 0 14px 2px 16px; }
.rc-hd b { font-size: calc(15.5px * var(--fs, 1)); font-weight: 500; color: var(--ink); }
.rc-x {
  margin-left: auto; width: 30px; height: 30px; border: 0; border-radius: 50%;
  background: transparent; color: var(--ink-3); cursor: pointer;
  font-size: 20px; line-height: 1; font-family: inherit;
}
.rc-x:active { background: var(--line-soft); }
.rc-sub { padding: 2px 16px 10px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.rc-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; }

.rc-row {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px 16px; border-top: 0.5px solid var(--line-soft);
  cursor: pointer;
}
.rc-row:active { background: var(--line-soft); }
.rc-main { flex: 1 1 auto; min-width: 0; }
.rc-nm {
  font-size: calc(13.5px * var(--fs, 1)); font-weight: 500; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rc-tag {
  display: inline-block; margin-left: 6px; padding: 1px 5px; border-radius: 4px;
  background: var(--teal-soft); color: var(--teal-2);
  font-size: calc(10.5px * var(--fs, 1)); font-weight: 400; vertical-align: 1px;
}
.rc-meta {
  margin-top: 3px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rc-mem { margin-top: 5px; overflow: hidden; }
.rc-mem .badge.exp { max-width: 100%; }
.rc-ago {
  flex: 0 0 auto; padding-top: 2px;
  font-size: calc(11px * var(--fs, 1)); color: var(--ink-3);
}
.rc-empty {
  padding: 30px 18px 34px; text-align: center;
  font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); line-height: 1.75;
}

/* z481：电脑端（≥900px）同一个抽屉改成**居中面板**。
   手机那套是「从底部升起的全宽抽屉」，在桌面全宽铺开就成了一条横贯整屏的窄条 ——
   房间名在左、时间被甩到屏幕右缘，中间一大片空。
   内容一个字不改（还是 openRecentRooms 生成的那份 DOM），只换外壳。
   ⚠️ 隐藏态用 opacity+pointer-events，不能用 translateY 移出屏幕：
      居中面板"移出屏幕"会闪一下再出现，而且隐藏时仍能接住点击。 */
@media (min-width: 900px) {
  .rc-sheet {
    left: 50%; right: auto; bottom: auto; top: 50%;
    width: calc(520px * var(--fw, 1)); max-height: 72vh;
    border-radius: 16px; padding-bottom: 8px;
    transform: translate(-50%, -46%) scale(.985);
    opacity: 0; pointer-events: none;
    box-shadow: 0 18px 48px rgba(0, 0, 0, .22);
    transition: opacity .2s ease, transform .22s cubic-bezier(.22, .61, .36, 1);
  }
  .rc-sheet.on { transform: translate(-50%, -50%) scale(1); opacity: 1; pointer-events: auto; }
}

/* ══════════════════════════════════════════════════════════════════════
   z454 统计板（#/stats）
   ----------------------------------------------------------------------
   版式：时间窗 → 总览八卡 → 八页签 → 视角工具条 → 内容区
   视角按钮必须在页签条**下面**：总览卡是全部数据的固定数字，不随视角变化，
   放上面会让人以为切视角卡也会变（用户明确提过这一点）。
   ══════════════════════════════════════════════════════════════════════ */
.bdb { max-width: 1180px; margin: 0 auto; padding: 18px 18px 40px; }
.bdb-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.bdb-hl h2 { margin: 0; font-size: calc(20px * var(--fs, 1)); color: var(--g900); letter-spacing: .2px; }
.bdb-sub { margin-top: 4px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }
.bdb-hr { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bdb-wins { display: inline-flex; background: var(--panel); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.bdb-wb { border: 0; background: transparent; padding: 6px 12px; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; }
.bdb-wb + .bdb-wb { border-left: 1px solid var(--line-soft); }
.bdb-wb.on { background: var(--teal-soft); color: var(--g800); font-weight: 600; }
.bdb-custom { display: flex; align-items: center; gap: 8px; margin: -4px 0 14px; font-size: calc(12.5px * var(--fs, 1)); }
.bdb-custom input { border: 1px solid var(--line); border-radius: 8px; padding: 5px 8px; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink); background: var(--panel); }

/* ---- 总览八卡 ---- */
.bdb-ov { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 16px; }
.bdb-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-tile); padding: 12px 14px; box-shadow: var(--sh-card); }
.bdb-ck { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.bdb-cv { margin-top: 6px; font-size: calc(21px * var(--fs, 1)); font-weight: 650; color: var(--g900); letter-spacing: .3px; }
.bdb-cs { margin-top: 4px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); }
.bdb-card.warn .bdb-cv { color: var(--amber); }
.bdb-card.bad .bdb-cv { color: var(--danger); }
.bdb-card.ok .bdb-cv { color: var(--g600); }
.bdb-card.mute .bdb-cv { color: var(--ink-3); }

/* ---- 页签：**统一定义在下方「页签条」一节**（z472 合并，别在这里再写一份，否则改样式会改了不生效）---- */

/* ---- 视角工具条（在页签下面，不在总览区）---- */
.bdb-tools { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 0 12px; flex-wrap: wrap; }
.bdb-views { display: inline-flex; background: var(--panel); border: 1px solid var(--line); border-radius: 9px; overflow: hidden; }
.bdb-vb { border: 0; background: transparent; padding: 5px 11px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; }
.bdb-vb + .bdb-vb { border-left: 1px solid var(--line-soft); }
.bdb-vb.on { background: var(--g600); color: #fff; }
.bdb-tip { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }

/* ---- 面板 ---- */
.bdb-panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-card); padding: 14px 16px 16px; margin-bottom: 12px; box-shadow: var(--sh-card); }
.bdb-panel > h3 { margin: 0 0 10px; font-size: calc(13.5px * var(--fs, 1)); color: var(--g800); font-weight: 600; }
.bdb-note { margin: 8px 0 0; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); line-height: 1.65; }
.bdb-empty { padding: 22px 0; text-align: center; color: var(--ink-3); font-size: calc(12.5px * var(--fs, 1)); }
.bdb-soon { background: var(--panel); border: 1px dashed var(--line); border-radius: var(--r-card); padding: 34px 20px; text-align: center; color: var(--ink-2); }
.bdb-soon b { font-size: calc(14px * var(--fs, 1)); color: var(--g800); }
.bdb-soon p { margin: 8px 0 0; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); }
.bdb-more a { font-size: calc(13px * var(--fs, 1)); color: var(--g600); text-decoration: none; font-weight: 600; }

/* ---- 数字行 ---- */
.bdb-kv { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.bdb-kv > div { background: var(--g050); border-radius: 10px; padding: 10px 12px; }
.bdb-kv b { display: block; font-size: calc(18px * var(--fs, 1)); color: var(--g900); }
.bdb-kv span { display: block; margin-top: 3px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }

/* ---- SVG ---- */
.bdb-svg { width: 100%; height: auto; display: block; overflow: visible; }
.bdb-svg .bdb-t { font-size: 9px; fill: var(--ink-2); }
.bdb-svg .bdb-tv { font-weight: 600; fill: var(--ink); }
.bdb-svg .bdb-calnum { font-size: 9.5px; fill: #fff; font-weight: 600; }
.bdb-facets { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.bdb-facet { border: 1px solid var(--line-soft); border-radius: 10px; padding: 8px; }
.bdb-fh { font-size: calc(12px * var(--fs, 1)); color: var(--g800); margin-bottom: 4px; }
.bdb-legend { display: flex; align-items: center; gap: 12px; margin-top: 10px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); flex-wrap: wrap; }
.bdb-legend i { display: inline-block; width: 11px; height: 11px; border-radius: 3px; margin-right: 4px; vertical-align: -1px; }

/* ---- 表格 ---- */
.bdb-tbl { width: 100%; border-collapse: collapse; font-size: calc(12px * var(--fs, 1)); }
.bdb-tbl th, .bdb-tbl td { padding: 7px 8px; border-bottom: 1px solid var(--line-soft); text-align: left; }
.bdb-tbl th { color: var(--ink-3); font-weight: 500; font-size: calc(11.5px * var(--fs, 1)); background: var(--g050); }
.bdb-tbl td.num, .bdb-tbl th.num { text-align: right; font-variant-numeric: tabular-nums; }
.bdb-tbl tbody tr:hover { background: #fafcfb; }
.bdb-sg { display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: calc(11px * var(--fs, 1)); }
.bdb-sg.ok { background: var(--moss-soft); color: var(--moss); }
.bdb-sg.bad { background: var(--danger-soft); color: var(--danger); }

@media (max-width: 860px) {
  .bdb { padding: 12px 12px 32px; }
  .bdb-tabbar { margin: 0 -12px; padding: 0 12px; }
  .bdb-ov { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .bdb-cv { font-size: calc(18px * var(--fs, 1)); }
  .bdb-kv { grid-template-columns: repeat(2, 1fr); }
  .bdb-facets { grid-template-columns: 1fr; }
  .bdb-tbl { font-size: calc(11.5px * var(--fs, 1)); }
  .bdb-tbl th, .bdb-tbl td { padding: 6px 5px; }
}
/* 值日日历：7 个格子固定尺寸，别被 SVG 的 width:100% 拉满整行 */
.bdb-calwrap { max-width: 420px; }
@media (max-width: 860px) { .bdb-calwrap { max-width: 100%; } }

/* ══════════════════════════════════════════════════════════════════════
   z456 统计板改版
   ----------------------------------------------------------------------
   版式：总览（含独立时间窗）→ 页签条（右端另一套时间窗）→ 视角条 → 内容 + 右侧抽屉
   ⚠️ 前缀必须用 bdb-：`.bd` 已被首页徽标占用（z454 撞过一次）。
   ⚠️ 抽屉有两套布局：≥1100px 停靠双栏（不遮罩）、<1100px 全屏抽屉 + 遮罩。
   ══════════════════════════════════════════════════════════════════════ */
.bdb { max-width: 1240px; margin: 0 auto; padding: 16px 18px 40px; }
.bdb-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.bdb-hl h2 { margin: 0; font-size: calc(20px * var(--fs, 1)); color: var(--g900); letter-spacing: .2px; }
.bdb-hr { display: flex; align-items: center; gap: 8px; }

/* ---- 总览区 ---- */
/* z472：总览不再套「灰底大板 + 14px 内边距」——用户原话「总览区的卡片占据的宽度比底下
   分类目的卡片要小一些，调大到和下面一致」。版式上把总览当成第 0 个页签：
   标题行 → 时间条（与各分类同一个组件）→ 卡片，卡片与下面完全同宽。 */
.bdb-ovsec { margin: 0; }
.bdb-ohead { display: flex; align-items: center; gap: 7px; margin: 0 0 9px; }
.bdb-obar { flex: 0 0 auto; width: 3px; height: 13px; border-radius: 2px; background: var(--g600); }
.bdb-ohead h3 { margin: 0; font-size: calc(15px * var(--fs, 1)); color: var(--g900); font-weight: 650; letter-spacing: .4px; }
.bdb-osub { margin-left: 1px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.bdb-ov { display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px; margin-bottom: 18px; }
.bdb-card { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; }
.bdb-ck { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.bdb-cv { margin-top: 5px; font-size: calc(20px * var(--fs, 1)); font-weight: 650; color: var(--g900); letter-spacing: .3px; }
.bdb-cs { margin-top: 3px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-2); line-height: 1.5; }
.bdb-card.warn .bdb-cv { color: #b8791a; }
.bdb-card.bad .bdb-cv { color: #b4443a; }
.bdb-card.ok .bdb-cv { color: #2f7d5d; }
.bdb-card.mute .bdb-cv { color: var(--ink-3); }

/* z472：时间口径 = 「灰底轨道 + 白底浮起」的分段控件（与全站 .tabs 同一套语言）。
   总览和页签下方都用它 —— 用户要的就是"上下两处一样"。 */
.bdb-wins { display: inline-flex; gap: 2px; background: #e7eeeb; border: 0; border-radius: 10px; padding: 3px; }
.bdb-wb { border: 0; background: transparent; padding: 5px 10px; border-radius: 7px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; white-space: nowrap; transition: background .12s, color .12s; }
.bdb-wb + .bdb-wb { border-left: 0; }
.bdb-wb:hover { color: var(--g800); }
.bdb-wb.on { background: var(--panel); color: var(--g800); font-weight: 600; box-shadow: 0 1px 2px rgba(30, 70, 60, .12); }
.bdb-custom { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: calc(12.5px * var(--fs, 1)); flex-wrap: wrap; }
.bdb-custom input { border: 1px solid var(--line); border-radius: 8px; padding: 4px 8px; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink); background: var(--panel); }

/* ---- 页签条 ---- */
/* z472：类目区 = ①「下划线导航」（用户 2026-11-07 从三套方案里选定）：
     页签只有「文字 + 选中下划线」，与总览那套「灰轨道 + 白底浮起」分清上下两级；
     时间口径仍用灰轨道胶囊（用户给的搭配图正是这个组合），另起一行。
   吸顶：滚到长表格下面时页签还挂在顶栏下沿。
   ⚠️ top 由 stats-board.js 写进 --bdb-top（实测 .topbar 高度，顶栏换行也能自适应）；
      **不要写死 52/54** —— z94 那次 sticky 错位就是写死偏移量踩的坑（父级 overflow 同理）。 */
.bdb-tabbar { display: flex; align-items: flex-end; gap: 10px;
  position: sticky; top: var(--bdb-top, 0px); z-index: 12; background: var(--bg);
  /* 负 margin + 等量 padding：吸顶时底色横向铺满，左右不留两条缝让表格从边上穿过去 */
  margin: 0 -18px; padding: 0 18px; }
.bdb-tabs { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; flex: 0 1 auto; max-width: 100%;
  border-bottom: 1px solid var(--line); }
.bdb-tabs::-webkit-scrollbar { display: none; }
.bdb-tab { flex: 0 0 auto; border: 0; background: transparent; padding: 8px 13px; margin-bottom: -1px;
  font-size: calc(13.5px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; white-space: nowrap;
  border-bottom: 2.5px solid transparent; transition: color .12s, border-color .12s; }
.bdb-tab:hover { color: var(--g700); }
.bdb-tab.off { color: var(--ink-4); }
.bdb-tab.off i { font-style: normal; font-size: calc(9.5px * var(--fs, 1)); margin-left: 4px;
  padding: 1px 4px; border-radius: 4px; background: var(--line-soft); color: var(--ink-3); vertical-align: 1px; }
/* .on 必须写在这组最后：P2 页签点开后也走 .on，得压掉 .off 的灰字 */
.bdb-tab.on { color: var(--g800); font-weight: 600; border-bottom-color: var(--g700); }

.bdb-tabwin { margin-left: auto; margin-bottom: 6px; flex: 0 0 auto; }
/* z465：时间窗从页签条右端挪到下方，每个类目单独一行（各存各的选择） */
.bdb-tabwinrow, .bdb-ovrow { display: flex; align-items: center; gap: 10px; padding: 11px 0 12px; flex-wrap: wrap; }
/* z472：总览那行紧贴标题，上面不留空 */
.bdb-ovrow { padding-top: 0; padding-bottom: 10px; }
.bdb-twrange { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); font-variant-numeric: tabular-nums; }
/* z466：日期区间两侧加 ‹ › 翻页 + 「回到本周/本月/本年」；
   z472：箭头/回到按钮统一成「白底小圆角块 + 极淡描边」，跟时间胶囊同一张桌子上的东西。 */
.bdb-twranger { display: inline-flex; align-items: center; gap: 5px; }
.bdb-navb { width: 26px; height: 26px; padding: 0; border: 1px solid var(--line-soft); background: var(--panel); border-radius: 8px;
  color: var(--ink-2); cursor: pointer; font-size: 15px; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
  box-shadow: 0 1px 2px rgba(30, 70, 60, .05); }
.bdb-navb:hover { color: var(--g800); }
.bdb-navb:active { transform: scale(.94); }
.bdb-backb { margin-left: 6px; height: 26px; padding: 0 11px; border: 1px solid var(--line-soft); background: var(--panel); border-radius: 8px;
  color: var(--g700); font-size: calc(11.5px * var(--fs, 1)); cursor: pointer; white-space: nowrap;
  box-shadow: 0 1px 2px rgba(30, 70, 60, .05); }
.bdb-backb:hover { color: var(--g800); }

.bdb-tools { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 0 12px; flex-wrap: wrap; }
.bdb-views { display: inline-flex; background: var(--panel); border: 1px solid var(--line); border-radius: 9px; overflow: hidden; }
.bdb-vb { border: 0; background: transparent; padding: 5px 12px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); cursor: pointer; }
.bdb-vb + .bdb-vb { border-left: 1px solid var(--line-soft); }
.bdb-vb.on { background: var(--g600); color: #fff; }
.bdb-tip { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); }

/* ---- 双栏 / 抽屉 ---- */
/* ---- z464：三区分块（全组 / 各团队 / 个人）---- */
.bdb-sec { margin: 0; }
.bdb-sech { display: flex; align-items: center; gap: 8px; margin: 20px 0 9px; }
.bdb-sec:first-child .bdb-sech { margin-top: 4px; }
.bdb-sech i { flex: 0 0 auto; width: 3px; height: 14px; border-radius: 2px; background: var(--g600); }
.bdb-sech h3 { margin: 0; font-size: calc(14px * var(--fs, 1)); color: var(--g900); font-weight: 650; letter-spacing: .3px; }
.bdb-sech span { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }

.bdb-split { display: block; }
.bdb-split.has-dr { display: grid; grid-template-columns: minmax(0, 1fr) 470px; gap: 16px; align-items: start; }
.bdb-main { min-width: 0; }
/* z466：抽屉卡片顶部要与左边「第一个分区的卡片顶部」齐平（用户原话「右边的详情弹窗往下移动，
   与左边的顶部对齐」）。z467：分区头高度会随文案换行变化，硬编码 33px 对不齐 —— 改成
   渲染后由 JS 实测「首张卡片顶 - 分栏顶」写进 --bdb-dr-top，33px 只是兜底。 */
.bdb-aside { position: sticky; top: 12px; margin-top: var(--bdb-dr-top, 33px); max-height: calc(100vh - 90px); overflow: auto; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-card); box-shadow: var(--sh-card); }
.bdb-drhead { position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 8px; padding: 9px 12px; background: var(--panel); border-bottom: 1px solid var(--line-soft); }
.bdb-crumbs { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; font-size: calc(12px * var(--fs, 1)); min-width: 0; }
.bdb-crumb { border: 0; background: transparent; padding: 2px 4px; color: var(--ink-2); cursor: pointer; font-size: calc(12px * var(--fs, 1)); border-radius: 5px; }
.bdb-crumb:hover { background: var(--g050); }
.bdb-crumb.on { color: var(--g800); font-weight: 600; }
.bdb-crumbsep { color: var(--ink-3); }
.bdb-x { margin-left: auto; border: 0; background: transparent; font-size: 15px; line-height: 1; color: var(--ink-3); cursor: pointer; padding: 4px 6px; border-radius: 6px; }
.bdb-x:hover { background: var(--g050); color: var(--ink); }
.bdb-drbody { padding: 12px 14px 18px; }
.bdb-drbody h3 { margin: 0 0 6px; font-size: calc(13.5px * var(--fs, 1)); color: var(--g800); }
/* z464：抽屉头只留标题 + ✕（面包屑已删）；sub 用独立类，手机端不跟着 .bdb-note 一起隐藏 */
.bdb-drt { flex: 1; min-width: 0; font-size: calc(13.5px * var(--fs, 1)); font-weight: 600; color: var(--g900); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* z471：抽屉第 2 级起的「‹ 返回上一级」。与 .bdb-x 同尺寸，左右各一个，标题仍居中占满 */
.bdb-drback { flex: none; margin-left: -4px; border: 0; background: transparent; font-size: calc(19px * var(--fs, 1)); line-height: 1; color: var(--ink-2); cursor: pointer; padding: 0 7px 2px; border-radius: 6px; }
.bdb-drback:hover { background: var(--g050); color: var(--ink); }
.bdb-drsub { margin: 0 0 8px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-2); line-height: 1.5; }
/* z464：抽屉每次打开都重播一次滑入（点新详情 = 关掉旧的再弹出新的） */
@keyframes bdbAsideIn { from { transform: translateX(22px); opacity: .55; } to { transform: none; opacity: 1; } }
/* z465：只有首次打开才滑入；换条目时只换内容、不重播动画（用户嫌「每点一下就弹一下」） */
.bdb-aside.anim { animation: bdbAsideIn .19s ease-out; }
.bdb-backdrop { display: none; }
@media (max-width: 1100px) {
  .bdb-split.has-dr { display: block; }
  .bdb-aside { position: fixed; top: 0; right: 0; bottom: 0; width: min(94vw, 560px); max-height: none; margin-top: 0; border-radius: 0; z-index: 70; border-left: 1px solid var(--line); }
  .bdb-backdrop { display: block; position: fixed; inset: 0; background: rgba(16, 24, 22, .38); z-index: 60; }
}

/* ---- 面板 ---- */
.bdb-panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-card); padding: 13px 15px 15px; margin-bottom: 12px; box-shadow: var(--sh-card); }
.bdb-panel > h3 { margin: 0 0 10px; font-size: calc(13.5px * var(--fs, 1)); color: var(--g800); font-weight: 600; }
.bdb-sub2 { margin: 16px 0 8px; font-size: calc(12.5px * var(--fs, 1)); color: var(--g800); }
.bdb-note { margin: 8px 0 0; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); line-height: 1.7; }
.bdb-badnote { margin: 0 0 8px; padding: 7px 10px; border-radius: 8px; background: #fbeeec; color: #8f352c; font-size: calc(12px * var(--fs, 1)); }
.bdb-empty { padding: 20px 0; text-align: center; color: var(--ink-3); font-size: calc(12.5px * var(--fs, 1)); }
.bdb-soon { background: var(--panel); border: 1px dashed var(--line); border-radius: var(--r-card); padding: 34px 20px; text-align: center; color: var(--ink-2); }
.bdb-soon b { font-size: calc(14px * var(--fs, 1)); color: var(--g800); }
.bdb-soon p { margin: 8px 0 0; font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-3); }
.bdb-more { margin-top: 4px; }
.bdb-more a { font-size: calc(12.5px * var(--fs, 1)); color: var(--g600); text-decoration: none; font-weight: 600; }

/* ---- 数字行 ---- */
.bdb-kv { display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px; }
.bdb-kv > div { background: var(--g050); border-radius: 9px; padding: 9px 11px; }
.bdb-kv b { display: block; font-size: calc(17px * var(--fs, 1)); color: var(--g900); }
.bdb-kv span { display: block; margin-top: 2px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }

/* ---- 横条排行（HTML，可点）---- */
.bdb-bars { display: flex; flex-direction: column; gap: 2px; }
.bdb-bar-row { display: grid; grid-template-columns: 118px 1fr 96px; align-items: center; gap: 10px; padding: 3px 4px; border-radius: 7px; }
.bdb-bar-row.click { cursor: pointer; }
.bdb-bar-row.click:hover { background: #f4f8f7; }
.bdb-bar-l { font-size: calc(12px * var(--fs, 1)); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bdb-bar-l .dim { font-size: calc(10.5px * var(--fs, 1)); }
.bdb-bar-t { position: relative; height: 12px; background: var(--g050); border-radius: 4px; overflow: hidden; }
.bdb-bar-t i { display: block; height: 100%; background: var(--g600); border-radius: 4px; }
.bdb-bar-v { text-align: right; font-size: calc(12px * var(--fs, 1)); color: var(--ink); font-variant-numeric: tabular-nums; }

/* ---- 学历卡（考核全组）---- */
.bdb-degs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.bdb-degcard { display: flex; flex-direction: column; gap: 4px; text-align: left; border: 1px solid var(--line); background: var(--panel); border-radius: 11px; padding: 11px 13px; cursor: pointer; transition: border-color .15s, box-shadow .15s; }
.bdb-degcard:hover { border-color: var(--g600); box-shadow: 0 2px 10px rgba(29, 109, 94, .12); }
.bdb-degcard .dc-k { font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); }
.bdb-degcard .dc-v { font-size: calc(24px * var(--fs, 1)); font-weight: 650; color: var(--g900); line-height: 1.15; }
.bdb-degcard .dc-v em { font-style: normal; font-size: calc(13px * var(--fs, 1)); color: var(--ink-3); margin-left: 2px; }
.bdb-degcard .dc-b { height: 5px; border-radius: 3px; background: var(--g050); overflow: hidden; }
.bdb-degcard .dc-b i { display: block; height: 100%; border-radius: 3px; background: var(--g600); }
.bdb-degcard .dc-s { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }
.bdb-degcard.ok .dc-v { color: #2f7d5d; } .bdb-degcard.ok .dc-b i { background: #2f7d5d; }
.bdb-degcard.warn .dc-v { color: #b8791a; } .bdb-degcard.warn .dc-b i { background: #d69a2f; }
.bdb-degcard.bad .dc-v { color: #b4443a; } .bdb-degcard.bad .dc-b i { background: #b4443a; }

/* ---- 考核周格子 ---- */
/* z467：本月/上周个人表的「各周」必须横排 —— 原来 flex-wrap，列一窄就折成两层，
   每行行高被顶得特别高（用户明确要求改横向）。 */
.bdb-wks { display: inline-flex; gap: 3px; flex-wrap: nowrap; }
/* 表格里的居中列（除姓名外都居中的通用类；.bdb-runp 里已有的规则保留） */
.bdb-tbl .bdb-ctr { text-align: center; }
.bdb-wk { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 5px; font-style: normal; font-size: 10.5px; color: #fff; font-weight: 600; }
.bdb-wk.ok { background: #2f7d5d; }
.bdb-wk.bad { background: #b4443a; }
.bdb-wk.ex { background: #d69a2f; }
/* z468：格子里改成写 Run 数之后，future 不能再是 color:transparent（数字会消失） */
.bdb-wk.fu { background: #e3e9e7; color: #9fadaa; }
.bdb-wk.og { background: #fff; border: 1px dashed #b9c7c3; color: #8a9995; }
.bdb-ycal { display: flex; flex-direction: column; gap: 5px; margin-top: 4px; }
/* z464 ⚠️ 这里原来是 grid-template-columns: 42px 1fr —— 而行里是「月份 + 一串 <i>」，
   grid 的自动布局会把**每个 <i> 都当成一个单元格**，于是每个格都占掉 42px 列宽，
   渲染成「每行只有 2 个格子、竖着往下掉」（用户截图报的就是这个）。
   月份名 + 一串格子是「一行文字流」，必须用 flex。 */
.bdb-yrow { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; min-height: 22px; }
.bdb-ym { flex: 0 0 42px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.bdb-yrow .bdb-wk { width: 22px; height: 22px; }

/* ---- 热力 / 日历 ---- */
.bdb-svg { width: 100%; height: auto; display: block; overflow: visible; }
.bdb-svg .bdb-t { font-size: 9px; fill: var(--ink-2); }
.bdb-calnum { font-size: 9.5px; fill: #fff; font-weight: 600; }
.bdb-click { cursor: pointer; }
.bdb-click:hover { opacity: .82; }
.bdb-calrow { display: flex; gap: 16px; flex-wrap: wrap; }
.bdb-calbox { flex: 0 0 auto; min-width: 0; }
.bdb-calbox .bdb-fh { font-size: calc(12px * var(--fs, 1)); color: var(--g800); margin-bottom: 4px; }
.bdb-cal3 { width: 150px; }
/* z464：值日年历改成「全年所有月份铺开」—— 桌面每行 4 个、手机每行 2 个，不再翻页 */
.bdb-calgrid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px 18px; }
.bdb-calgrid .bdb-calbox { flex: 1 1 auto; }
.bdb-cal { width: 100%; height: auto; }

/* z466：采购「全组」改成上下结构 —— 4 个指标横排一行，趋势图通栏放下面。
   原来「左边 272px 的 2×2 + 右边趋势图」会让指标下面空一大块。 */
/* z466 方案 B：左边 2×2 指标大卡、右边趋势图。
   两列用 align-items:stretch + 行高 1fr，让指标卡自动长到与右侧图等高 —— 否则左边
   又会出现「四个 chip 下面一大片空白」（用户一开始就是嫌这个）。 */
.bdb-buyhead { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 16px; align-items: stretch; }
.bdb-buykv { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr)); gap: 10px; }
.bdb-buykv > div { background: var(--g050); border-radius: 10px; padding: 12px 14px 13px; min-width: 0;
  display: flex; flex-direction: column; justify-content: center; }
.bdb-buykv b { display: block; font-size: calc(23px * var(--fs, 1)); color: var(--g900); letter-spacing: .2px; line-height: 1.2; }
.bdb-buykv span { display: block; margin-top: 4px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-3); }
.bdb-buytrend { min-width: 0; }
/* z467：趋势图按容器真实宽度 1:1 布图（viewBox 单位 = CSS px），所以这里只要
   宽度 100%、高度 auto 就能正好铺满右列；max-height 反而会把图缩窄、右边留白，
   已经去掉（原来「电脑端图片又太大」是靠它压的，现在由 JS 按 px 布图控制）。 */
.bdb-buytrend svg.bdb-trend { width: 100%; height: auto; display: block; }
/* z466：采购趋势的坐标字号（用户要求放大到原来的 2.5 倍）。
   ⚠️ SVG 里的文字字号是 CSS 控制的（.bdb-svg .bdb-t 为准），所以必须用专属类覆盖，
   不能改全局 —— 值日年历的星期头也用 .bdb-t，跟着放大会糊成一片。
   ⚠️ z467 起 viewBox 单位 == CSS px，这个值必须和 JS 里 opts.fontSize 保持一致。 */
.bdb-svg.bdb-trend .bdb-t { font-size: 22px; }
/* 右纵轴（累计线）的数字用线的颜色，和左轴（柱）区分开 */
.bdb-svg.bdb-trend .bdb-t.bdb-tr { fill: #6d4aa8; }
.bdb-tlegend { flex: 0 0 auto; margin-left: auto; display: inline-flex; align-items: center; gap: 5px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); font-weight: 400; white-space: nowrap; }
.bdb-tlegend i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.bdb-tlegend i + i { margin-left: 6px; }
/* z467：标题行不换行 —— 窄屏（360 手机 = 284 可用）挤不下时让副标题省略，
   而不是把图例顶到第二行、贴右边界被裁掉（.bdb-buytrend 是 overflow 裁剪的）。 */
.bdb-trendh { display: flex; align-items: baseline; gap: 8px; flex-wrap: nowrap; font-size: calc(13px * var(--fs, 1)); color: var(--g800); font-weight: 600; margin-bottom: 4px; padding-right: 2px; }
.bdb-trendt { font-weight: 600; white-space: nowrap; flex: 0 0 auto; }
.bdb-trendh > span:not(.bdb-tlegend) { min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); font-weight: 400; }
.bdb-calnav { display: flex; gap: 8px; margin-top: 10px; }
.bdb-calnav button[disabled] { opacity: .45; cursor: default; }
.bdb-days { display: flex; gap: 5px; flex-wrap: wrap; }
.bdb-day { width: 34px; border: 1px solid var(--line-soft); border-radius: 8px; padding: 5px 0 4px; text-align: center; }
.bdb-day .dd-d { display: block; font-size: calc(11px * var(--fs, 1)); color: var(--ink-2); }
.bdb-day i { display: block; width: 16px; height: 16px; margin: 3px auto 0; border-radius: 4px; }
.bdb-legend { display: flex; align-items: center; gap: 10px; margin-top: 10px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-2); flex-wrap: wrap; }
.bdb-legend i { display: inline-block; width: 11px; height: 11px; border-radius: 3px; }

/* ---- 表格 ---- */
.bdb-scroll { overflow-x: auto; }
.bdb-tbl { width: 100%; border-collapse: collapse; font-size: calc(12px * var(--fs, 1)); }
.bdb-tbl th, .bdb-tbl td { padding: 6px 7px; border-bottom: 1px solid var(--line-soft); text-align: left; white-space: nowrap; }
.bdb-tbl th { color: var(--ink-3); font-weight: 500; font-size: calc(11px * var(--fs, 1)); background: var(--g050); }
/* z464：抽屉里的详情表允许换行 —— 不用左右滑动就能看全（用户明确要求「尽量不需要滑动」） */
.bdb-tbl-fit th, .bdb-tbl-fit td { white-space: normal; }
.bdb-tbl-fit td.it { line-height: 1.35; word-break: break-word; }
.bdb-tbl td.num, .bdb-tbl th.num { text-align: right; font-variant-numeric: tabular-nums; }
.bdb-tbl tbody tr:hover { background: #fafcfb; }
.bdb-rowclick { cursor: pointer; }
.bdb-rowclick:hover { background: #f2f8f6 !important; }
.bdb-hasrun { color: #1d6d5e; font-weight: 600; }
.bdb-okt { color: #2f7d5d; font-weight: 600; }
.bdb-badt { color: #b4443a; font-weight: 600; }
.bdb-runp th.num { font-size: calc(10px * var(--fs, 1)); line-height: 1.35; }
/* z466：学历 / 合计 / 标准 三列内容居中（用户明确要求），并给固定列宽 ——
   否则宽屏下姓名、团队两列把七格挤到最右边，中间拉出一条大空白（就是「太空旷」的根因）。
   做法：table-layout: fixed + 姓名/学历/团队/合计/标准 固定宽，**七格列不设宽**（平分剩余），
   方块再撑满整列 —— 宽屏下七格会自然变宽，表格不再「左边一溜、右边全空」。 */
.bdb-runp { table-layout: fixed; }
.bdb-runp .bdb-ctr { text-align: center; }
.bdb-runp th.bdb-ctr { font-size: calc(11px * var(--fs, 1)); }
/* ⚠️ fixed 布局下必须用百分比凑满 100%，否则多余宽度会按原宽比例摊回各列，
   「团队」列又会被撑到 400px（第一版就踩了这个，七格依旧挤成一条）。 */
.bdb-runp th.bdb-c-name, .bdb-runp td.bdb-c-name { width: 9.5%; }
.bdb-runp th.bdb-c-deg, .bdb-runp td.bdb-c-deg { width: 6%; }
.bdb-runp th.bdb-c-team, .bdb-runp td.bdb-c-team { width: 21.5%; overflow: hidden; text-overflow: ellipsis; }
.bdb-runp th.bdb-dc-h, .bdb-runp td.bdb-dc { width: 6.7%; }
.bdb-runp th.bdb-c-sum, .bdb-runp td.bdb-c-sum { width: 8%; }
/* 桌面端把七格做大，让表格的视觉重心落在「哪天有 Run」上 */
@media (min-width: 861px) {
  .bdb-runp th, .bdb-runp td { padding: 7px 8px; }
  .bdb-runp th.bdb-dc-h, .bdb-runp td.bdb-dc { padding: 5px 4px; }
  .bdb-runp .bdb-dcb { width: 100%; max-width: 46px; height: 26px; font-size: calc(11.5px * var(--fs, 1)); border-radius: 5px; }
}
/* z465：七天 = 七个紧挨的小方块（原来 7 列文字表头太占宽，手机要横滑） */
.bdb-runp th.bdb-dc-h { width: 24px; min-width: 24px; padding: 6px 0; text-align: center; }
.bdb-runp td.bdb-dc { width: 24px; min-width: 24px; padding: 4px 0; text-align: center; }
.bdb-dcb { display: inline-flex; align-items: center; justify-content: center; width: 19px; height: 19px;
  border-radius: 4px; background: #f1f5f4; color: var(--ink-3); font-size: calc(10.5px * var(--fs, 1));
  font-variant-numeric: tabular-nums; }
.bdb-dcb.on { background: #dcefe7; color: #1d6d5e; font-weight: 650; }
/* z468：本月个人区格子里改成写周 Run 数，桌面端把方块放大到能舒服读两位数字
   （手机端由后面的媒体查询覆盖成 13px，否则 6 周 × 24px 会把 360 宽的表格撑爆）。 */
.bdb-monthp .bdb-wk { width: 24px; height: 24px; font-size: calc(11.5px * var(--fs, 1)); }
/* 桌面端本月表也固定列宽：auto 布局会把多出来的宽度全塞给最后一列，
   结果「达标比」右边拖出一大片空白（就是之前说的「太空旷」）。 */
@media (min-width: 861px) {
  .bdb-monthp { table-layout: fixed; }
  .bdb-monthp th:nth-child(1) { width: 20%; }
  .bdb-monthp th:nth-child(2) { width: 9%; }
  .bdb-monthp th:nth-child(3) { width: 9%; }
  .bdb-monthp th:nth-child(4) { width: 9%; }
  .bdb-monthp th:nth-child(5) { width: 34%; }
  .bdb-monthp .bdb-wks { gap: 6px; }
  .bdb-monthp .bdb-wk { width: 28px; height: 28px; font-size: calc(12px * var(--fs, 1)); }
}
.bdb-sg { display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: calc(11px * var(--fs, 1)); }
.bdb-sg.ok { background: #e6f2ec; color: #2f7d5d; }
.bdb-sg.bad { background: #fbeeec; color: #b4443a; }

@media (max-width: 860px) {
  .bdb { padding: 12px 12px 32px; }
  .bdb-tabbar { margin: 0 -12px; padding: 0 12px; }
  .bdb-ov { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .bdb-cv { font-size: calc(17px * var(--fs, 1)); }
  .bdb-kv { grid-template-columns: repeat(2, 1fr); }
  /* z464：手机端一律不显示解释性长句（用户原话「不然严重影响格式和美观」）。
     ⚠️ 抽屉头那句汇总量用的是 .bdb-drsub，不受这条影响。 */
  .bdb-note, .bdb-tip { display: none; }
  .bdb-sech { margin: 16px 0 8px; }
  .bdb-sech h3 { font-size: calc(13px * var(--fs, 1)); }
  /* 全组学历卡：原来一行一张卡、三张占掉半屏 → 压缩成三列小卡 */
  .bdb-degs { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .bdb-degcard { padding: 8px 9px; gap: 2px; border-radius: 9px; }
  .bdb-degcard .dc-k { font-size: calc(11px * var(--fs, 1)); }
  .bdb-degcard .dc-v { font-size: calc(17px * var(--fs, 1)); }
  .bdb-degcard .dc-v em { font-size: calc(10px * var(--fs, 1)); }
  .bdb-degcard .dc-b { height: 4px; }
  .bdb-degcard .dc-s { font-size: calc(10px * var(--fs, 1)); line-height: 1.3; }
  .bdb-bar-row { grid-template-columns: 84px 1fr 76px; gap: 6px; }
  /* z465：团队排行在手机上换成两行 —— 名字和数值占第一行（名字不再被 ellipsis 吃掉），
     进度条独占第二行、满宽更直观。个人区条目多，不套这条。 */
  .bdb-bars.wrap .bdb-bar-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "l v" "t t";
    row-gap: 4px; gap: 4px 8px; padding: 5px 4px;
  }
  .bdb-bars.wrap .bdb-bar-l { grid-area: l; white-space: normal; overflow: visible; }
  .bdb-bars.wrap .bdb-bar-v { grid-area: v; white-space: nowrap; }
  .bdb-bars.wrap .bdb-bar-t { grid-area: t; }
  /* z465：考核个人表在手机上必须整表放得下（用户「不用滑动」）。
     省的三处：团队列（分区已经说明是「个人」，团队信息在团队区能看到）、
     七格宽度、单元格内边距。 */
  .bdb-runp .bdb-c-team { display: none; }
  /* z466：桌面端给了固定列宽（table-layout:fixed），手机上必须一起收紧，否则 412px 装不下。
     z467：固定 px 在 360 宽的手机上会超过整表可用宽（实测 335 > 284 → 要横滑），
     所以手机端一律改成百分比：姓名/学历/合计/标准 占 58%，剩下 42% 由七个日格均分，
     永远凑满 100%，任何宽度都不用滑动。 */
  .bdb-runp th.bdb-c-name, .bdb-runp td.bdb-c-name { width: 20%; }
  .bdb-runp th.bdb-c-deg, .bdb-runp td.bdb-c-deg { width: 11%; }
  .bdb-runp th.bdb-c-sum, .bdb-runp td.bdb-c-sum { width: 13.5%; }
  .bdb-runp th, .bdb-runp td { padding: 5px 3px; }
  /* ⚠️ 日格不能再给 min-width —— 那会把表撑出容器 */
  .bdb-runp th.bdb-dc-h, .bdb-runp td.bdb-dc { width: auto; min-width: 0; padding: 3px 0; }
  .bdb-dcb { width: 16px; height: 16px; font-size: calc(9.5px * var(--fs, 1)); border-radius: 3px; }
  /* z467：本月/上周个人表（姓名 / 学历 / Run / 标准 / 各周 / 达标比）。
     百分比列宽 + fixed 布局，保证 360 宽的手机也整表放得下；
     各周那列吃掉剩余空间，一周一个、横着排。
     z468：格子里改成写 Run 数（原来是勾叉），13px 太小看不清 →
     方框放到 15px、字号 10px，同时把「达标比」和姓名列各让出一点宽度。 */
  .bdb-monthp { table-layout: fixed; }
  .bdb-monthp th, .bdb-monthp td { padding: 5px 3px; white-space: nowrap; }
  .bdb-monthp th:nth-child(1), .bdb-monthp td:nth-child(1) { width: 19%; }
  .bdb-monthp th:nth-child(2), .bdb-monthp td:nth-child(2) { width: 9%; }
  .bdb-monthp th:nth-child(3), .bdb-monthp td:nth-child(3) { width: 10%; }
  .bdb-monthp th:nth-child(4), .bdb-monthp td:nth-child(4) { width: 10%; }
  .bdb-monthp th:nth-child(6), .bdb-monthp td:nth-child(6) { width: 15%; }
  .bdb-monthp .bdb-wks { gap: 2px; }
  /* 15px 会让整表超出容器 2px（360 宽实测 286 > 284）→ 取 14px */
  .bdb-monthp .bdb-wk { width: 14px; height: 14px; border-radius: 3px; font-size: 9.5px; }
  .bdb-bar-v { font-size: calc(11.5px * var(--fs, 1)); }
  .bdb-tabbar { flex-wrap: wrap; }
  .bdb-tabwin { margin-left: 0; margin-bottom: 6px; }
  .bdb-calrow { gap: 10px; }
  .bdb-cal3 { width: 132px; }
  /* 值日年历：手机每行 2 个（原来每行放两个后右边还空一大块，是固定宽度导致的） */
  .bdb-calgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 10px; }
  /* 采购：手机上保持上下（概况在上、趋势在下） */
  /* z466：手机上 4 个指标 2×2，趋势图接在下面 */
  .bdb-buyhead { grid-template-columns: 1fr; gap: 12px; }
  .bdb-buykv { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: auto auto; margin-bottom: 0; }
  .bdb-buykv > div { justify-content: flex-start; padding: 10px 12px 11px; }
  .bdb-svg.bdb-trend .bdb-t { font-size: 16px; }
  .bdb-panel { padding: 12px 12px 13px; }
}
/* 「参与考核」开关的关闭态：看上去是「关着的」，不要和普通按钮混在一起 */
.btn-ghost.off { color: var(--ink-3); border-style: dashed; opacity: .9; }

/* ══════════════════════════════════════════════════════════════════
   z474：方案摘要 + 数据模板 确认卡片
   —— 用户 2026-11-07 定的流程：归档方案时 AI 出「摘要 + 数据表头」，
      学生当场确认/修改。所以这块首先是**可编辑表单**，不是展示页。
   宽度放宽到 640：模板列名很长（荧光值[A10](a.u.)），470 会挤成两行看不清。
   ══════════════════════════════════════════════════════════════════ */
#modalCard.dg-card { max-width: 640px; }
.dg-note { font-size: calc(12.5px * var(--fs, 1)); color: var(--ink-2); line-height: 1.65;
  background: var(--panel-soft); border-left: 3px solid var(--teal-2); border-radius: 6px;
  padding: 9px 11px; margin: 0 0 14px; }
.dg-warn { font-size: calc(12px * var(--fs, 1)); color: var(--amber); line-height: 1.6;
  background: var(--amber-soft); border-left: 3px solid var(--amber); border-radius: 6px;
  padding: 8px 11px; margin: 0 0 12px; }
.dg-sec { border: 1px solid var(--line); border-radius: 10px; padding: 12px 13px 6px; margin-bottom: 12px; }
.dg-h { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  font-size: calc(13.5px * var(--fs, 1)); font-weight: 500; color: var(--ink); margin-bottom: 10px; }
.dg-sub { font-size: calc(11.5px * var(--fs, 1)); font-weight: 400; color: var(--ink-3); }
.dg-l { display: block; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); margin: 0 0 3px; }
.dg-sec input, .dg-sec textarea { width: 100%; padding: 6px 9px; border: 1px solid var(--line);
  border-radius: 7px; font-size: calc(12.5px * var(--fs, 1)); font-family: inherit; color: var(--ink);
  background: var(--panel-soft); margin-bottom: 9px; line-height: 1.55; resize: vertical; }
.dg-sec input:focus, .dg-sec textarea:focus { outline: 0; border-color: var(--teal-2); box-shadow: 0 0 0 3px var(--teal-soft); }
.dg-t { width: 100%; border-collapse: collapse; margin-bottom: 4px; }
.dg-t th { text-align: left; font-weight: 500; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3);
  padding: 0 4px 5px; border-bottom: 1px solid var(--line); }
.dg-t td { padding: 2px 4px; font-size: calc(12px * var(--fs, 1)); color: var(--ink-2); vertical-align: middle; }
.dg-t td input { margin-bottom: 2px; }

/* ══ C1（z477）：结论摘要 / 终审卡片的字段行 ══
   终审卡片要把「结论摘要 + 公式 + 文档」放在一屏里，字段必须紧凑可扫读：
   左标签定宽（对齐）、右内容换行（长结论不截断，学生/组长要能读全）。 */
.cr-kv { display: flex; gap: 10px; padding: 5px 0; border-bottom: 1px dashed var(--line);
  font-size: calc(12.5px * var(--fs, 1)); line-height: 1.65; }
.cr-kv:last-child { border-bottom: 0; }
.cr-kv .k { flex: 0 0 88px; color: var(--ink-3); }
.cr-kv .v { flex: 1; color: var(--ink); white-space: pre-wrap; word-break: break-word; }

/* ══════════════════════════════════════════════════════════════════
   z484：方案摘要确认卡片改版（用户 2026-11-07：界面太丑、每格太小）
   版式 B：单列 820px，字段各自成块、输入框给足高度；数据模板画成二维网格。
   ⚠️ .dg-card 由 #modalCard 承载，原来写死 640px + 单列 10 个字段，
      表单挤成清单；这里改成「固定头/脚 + 中间滚动」，底部按钮永远可见。
   ══════════════════════════════════════════════════════════════════ */
#modalCard.dg-plan { max-width: 820px; padding: 0; display: flex; flex-direction: column; overflow: hidden; }
.dg-head { flex: none; padding: 15px 20px 11px; border-bottom: 1px solid var(--line); }
.dg-head h3 { margin: 0 0 3px; }
.dg-head .dg-meta { font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); word-break: break-all; }
.dg-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 14px 20px 6px; }
.dg-foot { flex: none; display: flex; gap: 9px; justify-content: flex-end; align-items: center;
  padding: 11px 20px; border-top: 1px solid var(--line); background: var(--panel); }
.dg-blk { margin-bottom: 18px; }
.dg-blk-h { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin: 0 0 10px;
  font-size: calc(13.5px * var(--fs, 1)); font-weight: 500; color: var(--ink); }
.dg-blk-h::before { content: ''; align-self: center; width: 3px; height: 13px; border-radius: 2px; background: var(--teal-2); }
.dg-f { display: block; margin-bottom: 12px; }
.dg-f > span { display: block; margin-bottom: 5px; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); }
.dg-f > span i { font-style: normal; opacity: .85; }
.dg-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.dg-blk input, .dg-blk textarea { width: 100%; padding: 8px 10px; border: 1px solid var(--line);
  border-radius: 8px; font-size: calc(13px * var(--fs, 1)); font-family: inherit; color: var(--ink);
  background: var(--panel-soft); line-height: 1.62; resize: vertical; }
.dg-blk textarea { min-height: 76px; }
.dg-blk input:focus, .dg-blk textarea:focus { outline: 0; border-color: var(--teal-2); box-shadow: 0 0 0 3px var(--teal-soft); }
.dg-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important; font-size: calc(12.5px * var(--fs, 1)) !important; }

/* 数据模板二维网格：表头 = 真实列名，上一行按组别合并成组带，下面空格子等人填数 */
.dg-grid-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: 9px; background: #fff; }
.dg-grid { border-collapse: separate; border-spacing: 0; font-size: calc(11.5px * var(--fs, 1)); }
.dg-grid th, .dg-grid td { padding: 4px 7px; border-right: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft); white-space: nowrap; }
.dg-grid tr:last-child th, .dg-grid tr:last-child td { border-bottom: 0; }
.dg-grid th:last-child, .dg-grid td:last-child { border-right: 0; }
.dg-grid .dg-ix { position: sticky; left: 0; z-index: 2; background: var(--panel-soft);
  color: var(--ink-3); text-align: right; font-weight: 500; }
.dg-grid .dg-hd th { background: #fbfdfc; color: var(--ink-2); font-weight: 500; text-align: left;
  border-bottom: 1px solid var(--line); }
/* z484-1：组带原来靠「--teal-soft / --line-soft 交替」区分，但两者是
   #eef4f2 与 #eef3f1（RGB 只差 1），肉眼完全一样 → 交替是噪音，整条带糊成一片。
   改成：所有组带同底色，靠**组间竖线**分隔，底边加深一档与表头行分层。 */
.dg-grid .dg-gr th { background: var(--teal-soft); color: var(--teal); font-weight: 500;
  text-align: center; font-size: calc(11px * var(--fs, 1)); letter-spacing: .02em;
  padding: 6px 8px; border-right: 1px solid #cfe1da; border-bottom: 1px solid var(--line); }
.dg-grid .dg-gr th:last-child { border-right: 0; }
.dg-grid .dg-gr th.corner { background: var(--panel); border-right: 1px solid var(--line); }
.dg-grid .corner { background: var(--panel); }
.dg-grid tbody td { min-width: 46px; height: 22px; background: #fff; }
.dg-grid-note { margin: 6px 0 0; font-size: calc(11.5px * var(--fs, 1)); color: var(--ink-3); line-height: 1.6; }
.dg-fold { margin-top: 9px; }
@media (max-width: 760px) {
  .dg-2col { grid-template-columns: 1fr; }
  #modalCard.dg-plan { max-width: 100%; }
}
.dg-t td input { width: 100%; padding: 5px 8px; border: 1px solid var(--line); border-radius: 7px;
  font-size: calc(12.5px * var(--fs, 1)); font-family: inherit; color: var(--ink); background: var(--panel-soft); }

/* z485：批量改表头（一次粘一行，替换全部列名；单列仍可逐个改） */
.dg-bulk { margin: 0 0 12px; padding: 10px 12px 11px; border: 1px solid var(--line);
  border-radius: 9px; background: var(--panel-soft); }
.dg-bulk .dg-f { margin-bottom: 8px; }
.dg-bulk textarea { min-height: 62px; background: #fff; }
.dg-bulk-bar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.dg-bulk-hint { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); }

/* z486：逐列 / 批量改成两个折叠按钮，点开才出各自区域 */
.dg-fold-bar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.dg-fold-btn { display: inline-flex; align-items: center; gap: 6px; }
.dg-fold-btn .dg-caret { font-style: normal; font-size: 10px; line-height: 1; color: var(--ink-3); }
.dg-fold-btn[data-open="1"] { border-color: var(--teal); color: var(--teal); }
.dg-fold-btn[data-open="1"] .dg-caret { color: var(--teal); }
.dg-fold-n { font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); font-weight: 400; }
.dg-fold-btn[data-open="1"] .dg-fold-n { color: var(--teal); opacity: .75; }

/* z487（用户 2026-11-07）：摘要卡改成「一个类目占一整行」后，字段竖向叠在一起，
   原来并排时的 76px 最小高 + 12px 间距会显得松 —— 单列下略收紧，别把卡片拉得更长。 */
.dg-plan .dg-blk textarea { min-height: 62px; }
.dg-plan .dg-f { margin-bottom: 10px; }
#dgBulkBox .dg-bulk { margin: 0; }

/* ══════════════════════════════════════════════════════════════════
   z489（用户 2026-11-07）：确认卡三件事
   ① 任何形态都有明确的关闭（右上角 ×）；② 已确认 → 只读（不再出现
      「重新生成 / 稍后再说 / 确认」）；③ 数据画像每行改成卡片行 + 注入内容预览，
      解决手机端 5 列表格横向溢出。
   ══════════════════════════════════════════════════════════════════ */
#modalCard.dg-plan { position: relative; }
.dg-x { position: absolute; top: 9px; right: 11px; width: 28px; height: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 8px; background: var(--panel);
  color: var(--ink-3); font-size: 17px; line-height: 1; cursor: pointer; z-index: 3; }
.dg-x:hover { color: var(--ink); border-color: var(--teal-2); background: var(--panel-soft); }
/* 标题右侧给 × 让出位置，长文件名也别压到按钮底下 */
.dg-head { padding-right: 48px; }

/* 只读视图：输入框变成「正文块」，一眼看出这不是在编辑 */
.dg-read .dg-blk input, .dg-read .dg-blk textarea,
.dg-read .dg-bulk textarea, .dg-read .dg-t td input {
  border-color: transparent; background: transparent; padding-left: 0; padding-right: 0;
  resize: none; cursor: default; color: var(--ink); box-shadow: none;
}
.dg-read .dg-blk input:focus, .dg-read .dg-blk textarea:focus { border-color: transparent; box-shadow: none; }
/* 只读时高度由 JS 按 scrollHeight 收紧，这里只保证不再被最小高/滚动条撑住 */
.dg-read .dg-blk textarea, .dg-read .dg-bulk textarea { min-height: 0; overflow: hidden; resize: none; }
.dg-read .dg-f > span i { display: none; }         /* 提示性小字对只读没意义 */
/* ⚠️ 只藏「逐列 / 批量改表头」那一类折叠区；画像卡的「注入内容预览」必须留着
   （只读时更需要看这份原文，别一起藏了 —— z489 第一版就是这么错的）。 */
.dg-read .dg-fold:not(.dg-fold-inject) { display: none; }
.dg-read .dg-grid tbody { display: none; }          /* 空行是「学生填数的位置」，归档后不必再画 */

/* ── 数据画像：一行一个卡片行（名称 / 数值 / 命中行 / 删） ── */
.dp-list { display: flex; flex-direction: column; gap: 8px; margin: 0 0 12px; }
.dp-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 4px 12px; align-items: start; padding: 9px 11px 8px;
  border: 1px solid var(--line); border-radius: 9px; background: #fff; }
.dp-main { min-width: 0; }
.dp-row-warn { border-color: var(--red-line, #f09595); background: var(--red-soft, #fcebeb); }
.dp-warn { margin-top: 3px; font-size: calc(11px * var(--fs, 1)); color: var(--red-ink, #a32d2d); line-height: 1.5; }
.dp-name { font-size: calc(13px * var(--fs, 1)); color: var(--ink); line-height: 1.5; word-break: break-word; }
.dp-sub { margin-top: 2px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3); line-height: 1.55; word-break: break-word; }
.dp-val { text-align: right; white-space: nowrap; font-size: calc(13px * var(--fs, 1)); color: var(--ink); }
.dp-val b { font-size: calc(15px * var(--fs, 1)); font-weight: 600; }
.dp-n { display: block; font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3); }
.dp-del { align-self: center; }
.dp-del-ph { width: 0; }
.dp-rows { grid-column: 1 / -1; font-size: calc(10.5px * var(--fs, 1)); color: var(--ink-3);
  word-break: break-all; line-height: 1.5; }
/* 注入内容预览 */
.dp-pre { margin: 0 0 6px; padding: 10px 12px; max-height: 220px; overflow: auto;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel-soft);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: calc(11.5px * var(--fs, 1)); line-height: 1.62; color: var(--ink-2);
  white-space: pre-wrap; word-break: break-word; }
@media (max-width: 620px) {
  /* 手机上固定头/脚的 padding 收一档，横向空间全给内容 */
  .dg-head { padding: 13px 14px 10px; padding-right: 44px; }
  .dg-body { padding: 12px 14px 4px; }
  .dg-foot { padding: 10px 14px; gap: 8px; flex-wrap: wrap; }
  .dg-foot .btn-ghost, .dg-foot .btn-primary { flex: 1 1 auto; }
  .dg-x { top: 7px; right: 8px; }
  .dp-row { grid-template-columns: minmax(0, 1fr) auto; }
  .dp-del, .dp-del-ph { grid-column: 2; grid-row: 1; justify-self: end; }
  .dp-val { grid-column: 1 / -1; grid-row: 2; text-align: left; }
  .dp-pre { max-height: 180px; }
}

/* ══ z490：数据画像里的「数据图」══
   图由 data-chart.js 在本地按原表画成 SVG（3~6KB），随画像一起落库。
   容器只负责留白与图注排版；SVG 自身已带 viewBox，宽度 100% 自适应缩放。 */
.dp-chart {
  margin: 2px 0 14px; padding: 10px 12px 8px;
  border: 1px solid var(--line, #E3E6EB); border-radius: 10px; background: var(--card, #fff);
}
.dp-chart-hd {
  display: flex; align-items: baseline; gap: 8px;
  font-size: calc(13px * var(--fs, 1)); font-weight: 600; color: var(--ink, #1F2329);
  margin-bottom: 6px;
}
.dp-chart-t { font-size: calc(11px * var(--fs, 1)); font-weight: 400; color: var(--ink-2); }
.dp-svg { width: 100%; overflow: hidden; }
.dp-svg svg { max-width: 100%; }
.dp-cap {
  margin-top: 6px; font-size: calc(11.5px * var(--fs, 1)); line-height: 1.62; color: var(--ink-2);
  white-space: pre-wrap; word-break: break-word;
}
/* z499：没出图时也要说话 —— 以前是静默 null，学生根本不知道这张画像没有图 */
.dp-chart-miss {
  margin: 2px 0 14px; padding: 9px 12px;
  border: 1px dashed var(--line, #E3E6EB); border-radius: 10px;
  background: var(--bg-2, #FAFBFC);
  font-size: calc(12px * var(--fs, 1)); line-height: 1.6; color: var(--ink-2);
}
.dp-chart-sub { display: block; margin-top: 3px; font-size: calc(11px * var(--fs, 1)); color: var(--ink-3, #8A9099); }
