/* 生物医药蓝：主色取自清透的天蓝，辅以青色点缀，整体偏冷、留白充足，
   中性色统一换成带蓝调的冷灰，避免与蓝色主色对冲产生脏感 */
:root {
  --primary: #0284c7;
  --primary-dark: #075985;
  --primary-soft: #e0f2fe;
  --accent: #06b6d4;
  --text: #1c2f42;
  --text-sub: #64798f;
  --border: #dce8f2;
  --bg: #f2f7fb;
  --card: #ffffff;
  --danger: #dc2626;
  --danger-soft: #fef2f2;
  --success: #0d9488;
  --warn: #d97706;
  --radius: 12px;
  --shadow: 0 1px 3px rgba(14, 60, 100, .07), 0 1px 2px rgba(14, 60, 100, .04);
}

* { box-sizing: border-box; }
html, body { height: 100%; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.6;
  overscroll-behavior-y: none;
}
/* 输入框字号需 ≥16px 才会阻止 iOS Safari 聚焦时自动放大页面；
   手机端在末尾断点里统一给到 16px，桌面端仍保持紧凑的 13.5px */
input, select, textarea { -webkit-appearance: none; appearance: none; border-radius: 8px; }
img, video, canvas, svg { max-width: 100%; }
table { max-width: 100%; }
.hidden { display: none !important; }

/* ---------------- 登录 ---------------- */
/* 星辰大海背景（需求 3，2026-09-27 首版 → 2026-09-28 第 2 版 → **第二十三轮需求 17 第 3 版**）
   ------------------------------------------------------------------
   第 3 版 = 现在的实现（林总 2026-10-05）：**一层 canvas 画完整片银河**，逻辑在 `public/js/starfield.js`。
   林总原文：「参照网页代码 https://zhuanlan.zhihu.com/p/3072881186 制作**浩瀚星空动画背景**，
              替换现在的登录页动画。」
   拍板：「**整体替换，去掉狮子座**」。

   ★ 前两版（纯 CSS）为什么退役：
     第 2 版是「3 层重复星点贴图 + 2 团星云 + 1 条银河带 + 3 层亮星 + 5 颗流星」堆出来的星空。
     几何再密，它也只是**一张会呼吸的壁纸** —— 星点是贴图，不会绕银河中心真的转，
     也没有「拖尾 + 加法发光」这种让星系亮起来的机制。
     需求 17 要的是参考实现那种**旋臂真的在流转**的效果，CSS 表达不了（那要每帧改几万个像素），
     所以换成 canvas。退役清单（**别再加回来**，`test/loginsky.js` 有反转后的断言守着）：
       `.sky-glow` / `.sky-nebula`(×2) / `.sky-milkyway` / `.sky-stars`(×3) / `.sky-bright`(×3) /
       `.sky-meteors` + `.meteor`(×5)，以及需求 10 的 `.sky-leo` / `.leo-stage` / `.leo-tilt` /
       `.leo-figure` / `.leo-depth`(×2)。
     ⚠️ `has .sky-leo` 这类「需求 10 的东西还在不在」的判据已经**反转**成「必须不存在」，
        不是把断言删掉 —— 见 `test/loginsky.js` 的 [1]/[3c] 两节。

   ★ 职责划分（改这段前先读）：
     · `.login-page`：深空渐变**兜底底**，只负责「canvas 还没画第一帧」那一瞬间的画面；
       canvas 一开画就把它盖住了，所以它现在的观感意义不大，主要是防白闪。
     · `.login-sky`：铺满视口的定位盒子 + `pointer-events:none` —— **绝不拦登录卡片的点击**。
     · `.sky-canvas`：画布本身。⚠️ 两种「宽高」不是一回事：
       `width:100%` 定的是 **CSS 盒子**；`canvas.width/height` 定的是**位图分辨率**，
       由 JS 按 `devicePixelRatio` 乘出来再 `setTransform` 缩回逻辑像素（否则高分屏整片星野发糊）。
     · 容器 `position:absolute` + `overflow:hidden`，绝不撑大页面
       （登录页是 `100vh` flex 居中，多一层滚动条就会把卡片顶偏）。
     · `prefers-reduced-motion: reduce` 下 **JS 根本不启动 rAF**（判断在 starfield.js 里），
       只连画几十帧让银河定格 —— CSS 这边无需额外规则。
*/
.login-page {
  position: relative;            /* 星空层的定位基准 */
  overflow: hidden;
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  /* 深空：顶部一团冷蓝穹顶 + 近黑到底部再回暖。
     ★ 底部**不能太亮** —— 实测把 #0e6d94 放到 100% 时整页泛青，
     星点对比度被吃掉了，故最亮的青蓝只到 76%，最后 24% 再压回深色。
     ★ 现在的 `DEEP` 底由 canvas 自己涂（`public/js/starfield.js` 的 `#060b16`），
       这层渐变只在首帧之前可见，色系刻意与它对齐，避免开机瞬间跳色。 */
  background:
    radial-gradient(130% 78% at 50% -6%, rgba(24, 84, 140, .55) 0%, rgba(24, 84, 140, 0) 58%),
    linear-gradient(165deg, #02060f 0%, #051229 26%, #071f3d 52%, #0a3457 76%, #061c33 100%);
  padding: 20px;
}

/* 星空容器：铺满登录页，只作装饰 */
.login-sky {
  position: absolute; inset: 0;
  overflow: hidden;
  pointer-events: none;          /* 绝不拦登录卡片的点击 */
}

/* 画布：铺满容器。
   ⚠️ `display:block` 必须写 —— canvas 默认是 inline，会带一条基线间隙，
      在 100% 高下可能撑出 1–2px 的纵向滚动条（正好把卡片顶偏）。 */
.sky-canvas {
  display: block;
  position: absolute; inset: 0;
  width: 100%; height: 100%;
}

/* 登录卡片：改为磨砂玻璃，与星空融为一体（原来是纯白不透明方块） */
.login-card {
  position: relative;                /* 压在星空层之上 */
  z-index: 1;
  width: 100%; max-width: 420px;
  background: rgba(10, 30, 61, .58);  /* 深海半透明底 */
  -webkit-backdrop-filter: blur(22px) saturate(140%);
  backdrop-filter: blur(22px) saturate(140%);
  border: 1px solid rgba(186, 230, 253, .22);
  border-radius: 16px;
  padding: 38px 34px 34px;
  box-shadow: 0 24px 60px rgba(2, 8, 23, .55), inset 0 1px 0 rgba(255, 255, 255, .08);
  color: #e8f4ff;
}
/* 磨砂玻璃下的表单排布：比全局默认更松一点，深底上挤在一起会显得压抑 */
.login-card .field { margin-bottom: 16px; }
.login-card .login-err { margin-bottom: 4px; }
/* 卡内文字在深底上必须转成浅色（否则沿用全局深字会「看不见」） */
.login-card .login-brand h1 { color: #f0f9ff; }
.login-card .login-brand p { color: rgba(203, 232, 255, .72); }
.login-card .field > label { color: rgba(203, 232, 255, .82); }
.login-card .field > input {
  background: rgba(4, 16, 35, .55);
  border: 1px solid rgba(125, 211, 252, .26);
  color: #eaf6ff;
}
.login-card .field > input::placeholder { color: rgba(186, 220, 245, .42); }
.login-card .field > input:focus {
  border-color: rgba(34, 211, 238, .72);
  box-shadow: 0 0 0 3px rgba(34, 211, 238, .16);
  background: rgba(4, 16, 35, .72);
}
.login-card .login-err { color: #fca5a5; }
/* ★ 需求 7（单点登录）：被顶下线的提示条。
   深色磨砂玻璃底 → 用琥珀色描边 + 半透明底，与红色「密码错误」（.login-err）区分开：
   红色=操作失败，琥珀=状态告知。 */
.login-card .login-notice {
  margin-bottom: 14px;
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 13px;
  line-height: 1.6;
  color: #fde68a;
  background: rgba(251, 191, 36, .12);
  border: 1px solid rgba(251, 191, 36, .38);
}
.login-card .login-notice.hidden { display: none; }
/* ★ 第一百零二轮（林总 2026-10-13）：登录验证码行 —— 输入框 + 图形 + 「更换」按钮。
   默认 `hidden`，由 `/api/auth/captcha` 的 `required` 决定露不露脸（公网显示、本机隐藏）。
   ⚠️ 输入框是 `.captcha-line > input`、不是 `.field > input`，
      所以上面那条 `.login-card .field > input` 的深色配色**罩不到它**，必须在这里重写一份
      （同 README「改了没生效，先怀疑还有第二条同属性规则 / 选择器没命中」的教训）。 */
.login-card .login-captcha.hidden { display: none; }
.login-card .captcha-line { display: flex; gap: 8px; align-items: stretch; }
.login-card .captcha-line > input {
  flex: 1 1 auto; min-width: 0;
  background: rgba(4, 16, 35, .55);
  border: 1px solid rgba(125, 211, 252, .26);
  color: #eaf6ff;
  letter-spacing: 2px;
}
.login-card .captcha-line > input::placeholder { color: rgba(186, 220, 245, .42); letter-spacing: normal; }
.login-card .captcha-line > input:focus {
  border-color: rgba(34, 211, 238, .72);
  box-shadow: 0 0 0 3px rgba(34, 211, 238, .16);
  background: rgba(4, 16, 35, .72);
}
.login-card .captcha-img {
  flex: 0 0 auto; width: 118px; height: 44px; border-radius: 8px; overflow: hidden;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
  background: rgba(4, 16, 35, .55); border: 1px solid rgba(125, 211, 252, .26);
}
.login-card .captcha-img svg { display: block; width: 100%; height: 100%; }
.login-card .captcha-refresh {
  flex: 0 0 auto; padding: 0 12px; border-radius: 8px; cursor: pointer;
  background: rgba(4, 16, 35, .55); border: 1px solid rgba(125, 211, 252, .26);
  color: #cbe8ff; font-size: 13px; transition: .15s;
}
.login-card .captcha-refresh:hover { border-color: rgba(34, 211, 238, .72); color: #f0f9ff; }
.login-brand { display: flex; gap: 14px; align-items: center; margin-bottom: 26px; }
/* ★ 需求 36（第九十轮）：系统名 + 版本号同一行，基线对齐（版本号在名字**后面**）。
   `h1` 的 `margin: 0 0 2px` 在 flex 行里会把版本号顶偏，这里收掉，改由行自身留白。 */
.login-name-row { display: flex; align-items: baseline; gap: 8px; margin-bottom: 2px; }
.login-name-row h1 { margin: 0; }
/* 「比系统名小两个号」：19px → 15px（一个号 = 2px）。窄屏系统名为 17px，此处同步 13px。 */
.login-ver { font-size: 15px; font-weight: 400; color: rgba(203, 232, 255, .6); letter-spacing: .3px; }
.login-logo {
  width: 46px; height: 46px; border-radius: 10px; color: #fff;
  background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 15px; letter-spacing: 1px;
}
.login-brand h1 { font-size: 19px; margin: 0 0 2px; }
.login-brand p { margin: 0; color: var(--text-sub); font-size: 12px; }
.login-err { color: var(--danger); font-size: 13px; min-height: 20px; }

/* ---------------- 布局 ---------------- */
.app-shell { display: flex; min-height: 100vh; }
.sidebar {
  width: 218px; flex: 0 0 218px; background: #fff; border-right: 1px solid var(--border);
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
}
.sidebar-brand {
  display: flex; gap: 10px; align-items: center; padding: 16px 16px 14px;
  border-bottom: 1px solid var(--border);
}
.brand-mark {
  width: 34px; height: 34px; border-radius: 8px; color: #fff;
  /* 蓝 → 青渐变，呼应生物医药的洁净与科技感 */
  background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
  display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.brand-text strong { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Logo 自适应：任意尺寸/比例的图片都能完整落进标识框，不拉伸变形。
   object-fit 由 JS 依配置写成 contain（完整显示，留白）或 cover（填满并居中裁切）。 */
.brand-logo-img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: center;
}
.brand-mark.has-img, .login-logo.has-img {
  background: transparent; padding: 3px; overflow: hidden;
}
.brand-mark.has-img { width: 36px; height: 36px; }
.login-logo.has-img { width: 50px; height: 50px; }

/* 品牌设置：Logo 预览 */
.logo-preview {
  width: 168px; height: 168px; border: 1px dashed var(--border);
  border-radius: var(--radius); background: var(--bg);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.logo-preview img { max-width: 100%; max-height: 100%; object-fit: contain; }
.logo-preview .ph {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 700; font-size: 34px; letter-spacing: 2px;
  background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
}
.logo-scale { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start; }
.logo-scale .cell { text-align: center; }
.logo-scale .cell span { display: block; font-size: 11px; color: var(--text-sub); margin-top: 6px; }
.logo-mini {
  width: 34px; height: 34px; border-radius: 8px; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 700; font-size: 12px;
  background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
}
.logo-mid {
  width: 46px; height: 46px; border-radius: 10px; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 700; font-size: 15px;
  background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
}
.logo-mini img, .logo-mid img { width: 100%; height: 100%; object-fit: contain; }
.logo-mini.has-img, .logo-mid.has-img { background: #fff; border: 1px solid var(--border); }

/* 备份恢复 */
.bk-meta { font-size: 12px; color: var(--text-sub); line-height: 1.9; }
.bk-meta b { color: var(--text); font-weight: 500; }
.bk-counts { font-size: 12px; color: var(--text-sub); }
.bk-counts i { font-style: normal; display: inline-block; margin-right: 10px; }
/* 自动备份计划的状态展示条 */
.bk-plan-status {
  margin-top: 4px; padding: 10px 13px; border-radius: 8px;
  background: var(--primary-soft); color: var(--primary-dark);
  font-size: 12.5px; line-height: 1.9;
}
.bk-plan-status b { font-weight: 600; }
/* 备份进度条（第二十三轮需求 2）—— 宽度 = 服务端算好的真实百分比，前端不做假动画 */
.bk-prog { padding: 2px 0; }
.bk-prog-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bk-prog-head .sub { flex: 1 1 auto; min-width: 0; font-size: 12.5px; color: var(--text-sub); }
.bk-prog-pct {
  font-weight: 600; color: var(--primary-dark); font-size: 14px;
  font-variant-numeric: tabular-nums; letter-spacing: .2px;
}
.bk-prog-bar {
  height: 8px; margin: 8px 0 6px; border-radius: 999px;
  background: var(--border); overflow: hidden;
}
.bk-prog-bar > i {
  display: block; height: 100%; width: 0; border-radius: 999px;
  background: linear-gradient(90deg, var(--primary), var(--accent));
  transition: width .3s ease;
}
.menu { flex: 1; overflow-y: auto; padding: 10px 8px; }
.menu-group { margin-bottom: 0; }
/* ★★ 第一百三十八轮（2026-10-29 林总）：「左侧系统的目录列表行距不一致，一并调整」。
   真浏览器实测（`_probe/r138-menu-spacing.js`，183 项）当时有 **7 种**「视觉行距」（step = 行高 + 净空）：
     36.4 ×115（二级项，padding 7px）· 38.4 ×19 · 39.6 ×10 · 40.4 ×28 · 44.4 ×1 · 46.4 ×3 · 48.4 ×1
   来源是三处各写各的：① 一级/分类 padding 9px vs 二级 7px（行高差 4px）；
   ② `.menu-group` / `.menu-cat` 各带 `margin-bottom: 2px`（相邻处 2px，跨容器处 4px）；
   ③ 分类标题 `margin-top: 8px`（44~48.4 那几档）。
   ⇒ 收成**唯一一套**：每行固定 36px 高 + 行间统一 2px ⇒ **所有相邻行 step 恒为 38px**，
     层级只靠缩进（二级 +26px）与展开箭头表达，不再靠改行距。
   ⚠️ 行高写死 36px 后，标签必须 `nowrap + ellipsis`（见下），否则长名字换行会溢出到下一行上。 */
.menu-item {
  display: flex; align-items: center; gap: 9px; height: 36px; padding: 0 10px; margin-bottom: 2px;
  border-radius: 8px;
  cursor: pointer; color: var(--primary-dark); font-size: 13.5px; font-weight: 500; user-select: none;
}
/* 标签占满剩余宽度并单行省略：固定行高的前提（换行会撑出 36px 的可视区，压到下一行上） */
.menu-item > span:not(.mi-icon):not(.mi-arrow) {
  flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.menu-item:hover { background: #eaf2fa; }
/* 选中项用左侧青色条标识，比单纯加深底色更清爽、层级更好辨认
   ★ 第一百三十六轮：**不再加粗**。林总要求「字体全部统一」，选中态只靠
     `--primary-soft` 底色 + 左侧 3px 青条表达（字重与兄弟项一致 500）——
     实测这已经足够辨认，且整列字重不再出现「一项特别粗」的突兀感。 */
.menu-item.active {
  background: var(--primary-soft); color: var(--primary-dark);
  box-shadow: inset 3px 0 0 var(--accent);
}
.menu-item .mi-icon { width: 18px; text-align: center; font-size: 14px; }
/* ★ 第一百三十六轮（2026-10-29 林总）：「统一成蓝色风格，字体、大小、颜色全部统一」。
   箭头是全站菜单里唯一的「非文字装饰」，跟着统一到深蓝的低透明度，别再用灰蓝 #9db4c9
   （灰蓝是中性色的尾巴，在整列深蓝里会显得脏）。 */
.menu-item .mi-arrow { margin-left: auto; font-size: 10px; color: rgba(7, 89, 133, .55); transition: transform .18s ease; }
.menu-group.open .mi-arrow { transform: rotate(90deg); }

/* 子菜单展开/收起动画（需求 4）
   ------------------------------------------------------------------
   为什么用 `grid-template-rows: 0fr → 1fr` 而不是 `max-height` 或 JS 量高度：
     · `max-height` 要写死一个上限 —— 写小了会截断（板块子项数量由林总动态增删），
       写大了过渡会先「冲一大截空白」再到位，肉眼很明显。
     · JS 量 `scrollHeight` 要额外读写布局，且**内容变化后缓存值就失效**。
     · grid 的 `0fr → 1fr` 由浏览器按实际内容算高度，**自适应且零 JS**。
   ★★ 关键约束（第一版就栽在这里，别删）：
     `grid-template-rows: 0fr` **只对「单个 grid item」生效**。
     子菜单里有 N 个 `.menu-item`，若它们直接是 grid 的子元素，就会各自占一条轨道 ——
     0fr 只压扁第一条，其余照旧撑开（实测收起态高度 188px 而非 0）。
     所以必须**包一层 `.menu-sub-inner`**：外层 grid 只有一个 item（那层包裹），
     由它统一被 0fr/1fr 控制；内层负责 overflow:hidden + min-height:0。 */
.menu-sub {
  display: grid; grid-template-rows: 0fr;
  padding-left: 26px;
  transition: grid-template-rows .22s ease, opacity .22s ease, visibility .22s;
  opacity: 0; visibility: hidden;
}
.menu-sub-inner { min-height: 0; overflow: hidden; }
.menu-group.open .menu-sub {
  grid-template-rows: 1fr; opacity: 1; visibility: visible;
}
/* 子项逐个淡入上浮，比整块推下来更「活」（错峰 18ms，太多会拖沓） */
.menu-sub .menu-item { opacity: 0; transform: translateY(-4px); transition: opacity .2s ease, transform .2s ease; }
.menu-group.open .menu-sub .menu-item { opacity: 1; transform: none; }
.menu-group.open .menu-sub .menu-item:nth-child(1) { transition-delay: .02s; }
.menu-group.open .menu-sub .menu-item:nth-child(2) { transition-delay: .04s; }
.menu-group.open .menu-sub .menu-item:nth-child(3) { transition-delay: .06s; }
.menu-group.open .menu-sub .menu-item:nth-child(4) { transition-delay: .08s; }
.menu-group.open .menu-sub .menu-item:nth-child(n+5) { transition-delay: .1s; }
@media (prefers-reduced-motion: reduce) {
  .menu-sub, .menu-sub .menu-item { transition: none; }
  .menu-sub .menu-item { opacity: 1; transform: none; }
}
/* ★ 第一百三十六轮：二级项原为 13px（比一级 13.5px 小半号）—— 这是「大小不统一」的**唯一来源**
   （实测 140 个二级项全是 13px）。现与一级 / 分类标题**同号 13.5px**，缩进只靠外层 padding 表达。
   ★ 第一百三十八轮：`padding: 7px 10px` 一并去掉 —— 它让二级行比一级矮 4px，
     是「行距不一致」的头号来源。现在上下 padding 由 `.menu-item` 的 `height: 36px` 统一给出，
     这里只保留展开动效所需的透明度/位移过渡。 */
.menu-sub .menu-item { padding: 0 10px; font-size: 13.5px; }

/* ---------------- 需求 12：左侧菜单一级归类 ---------------- */
/* 结构：.menu-cat > [.menu-item.menu-cat-head] + .menu-cat-body > .menu-cat-inner > (板块项 / 板块分组)
   展开动效复用 `.menu-sub` 那套 `grid-template-rows: 0fr → 1fr`（原因见上方长注释：
   `0fr` 只压得扁**单个** grid item，所以必须有 `.menu-cat-inner` 这层包裹）。 */
.menu-cat { margin-bottom: 0; }
/* ★ 第一百三十二轮（林总 2026-10-29）：「框选的菜单栏文字字体大小、颜色都不统一，请修正」。
   原来分类标题是**更小更浅**（12.5px / #6b8aa6），一级板块项是 13.5px / var(--text)，
   同一条竖排里一半深一半浅、一半大一半小，看着像没做完。
   ⇒ 当时先统一成「字号/颜色同款、只留字重 600 区分层级」。
   ★★ 第一百三十六轮（同日追改）：「统一成蓝色风格，字体、大小、颜色全部统一」——
   实测（`_probe/r136-menu-style.js`，真 Chrome 计算样式）仍有三套规格：
     · 字号 13.5px ×43 / 13px ×140（二级整段小半号）
     · 颜色 rgb(28,47,66) ×177 / rgb(7,89,133) ×1（只有选中项是蓝）
     · 字重 600 ×6（5 个分类标题 + 1 个选中项）/ 400 ×172
   ⇒ 本轮**收敛成唯一一套**：13.5px · 500 · var(--primary-dark)（深蓝 #075985）。
     分类标题不再加粗，层级只靠**上方 8px 间距 + 展开箭头**表达（与 §324 同思路，再降一档）。
   ⚠️ 全站唯一允许的字重差异 = **选中态 `.active` 的 600**（那是状态不是层级，且同时有
      浅蓝底 + 左侧青条，去掉加粗也认得出）。
   ⚠️ 别再给 `.menu-cat-head` / `.menu-sub .menu-item` 单写 font-size / color / font-weight
      —— 一写就又回到「多套规格」，这条已经被林总退回来两次了。
   ⚠️ `.mi-icon` 保持 14px：图标不是文字，emoji 与符号按文字号画会显小（视觉补偿，非规格分叉）。
   ★★ 第一百三十八轮（同日第三改）：「目录列表行距不一致」—— 分类标题原先多带 `margin-top: 8px`，
      使它在整列里比别的行高 8px（实测 step 44.4 / 46.4 / 48.4 三档都出自它）。
      ⇒ 现在**间距与其它行逐像素相同**（高度 36 + 行间 2），分类只靠「右侧展开箭头 + 二级缩进」区分。
      别再把 `margin-top` / 别的 padding 加回来 —— 一加这一列又会出现第二种行距。 */
.menu-item.menu-cat-head {
  padding: 0 10px;
}
/* 图标/箭头的字号由 `.menu-item .mi-icon` / `.mi-arrow` 统一给，这里**不再重复声明**
   （重复声明本身无害，但会诱使下一个人「就地改一处」，又把规格拆成两套）。 */
.menu-cat.open > .menu-cat-head .mi-arrow { transform: rotate(90deg); }
.menu-cat-body {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .24s ease, opacity .2s ease, visibility .22s;
  opacity: 0; visibility: hidden;
}
.menu-cat-inner { min-height: 0; overflow: hidden; padding-left: 9px; }
.menu-cat.open > .menu-cat-body { grid-template-rows: 1fr; opacity: 1; visibility: visible; }
/* 归类里的子菜单不再二次缩进 26px（会顶到太右边），改为 20px */
.menu-cat-inner .menu-sub { padding-left: 20px; }
@media (prefers-reduced-motion: reduce) {
  .menu-cat-body { transition: none; }
}
.sidebar-foot { padding: 10px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 6px; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  height: 56px; background: #fff; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 12px; padding: 0 18px; position: sticky; top: 0; z-index: 20;
}
.crumb { font-size: 15px; font-weight: 600; }
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.badge-level {
  background: var(--primary-soft); color: var(--primary-dark); border-radius: 20px;
  padding: 3px 11px; font-size: 12px;
}
.user-chip { font-size: 13px; color: var(--text-sub); }
/* 顶栏最右侧的「使用手册」入口（登录账号名之后）——做成链接观感但用 button 语义，
   点击触发带 token 的下载（见 app.js 的 Api.downloadUrl 绑定）。 */
.topbar-manual {
  font-size: 13px; color: var(--primary); cursor: pointer; white-space: nowrap;
  text-decoration: none; border-bottom: 1px dashed currentColor; padding-bottom: 1px;
}
.topbar-manual:hover { color: var(--primary-dark); }
.content { padding: 18px; flex: 1; overflow-x: hidden; }
.icon-btn {
  border: 1px solid var(--border); background: #fff; border-radius: 8px; width: 34px; height: 34px;
  cursor: pointer; font-size: 15px; color: var(--text-sub);
}

/* ---------------- 消息中心（需求 1） ---------------- */
/* 顶栏铃铛：未读数角标绝对定位到右上角，0 条时隐藏（[hidden] 由 JS 控制） */
.msg-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; }
.msg-badge {
  position: absolute; top: -7px; right: -7px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 9px; background: var(--danger); color: #fff; font-style: normal;
  font-size: 10.5px; font-weight: 700; line-height: 17px; text-align: center;
  /* 白描边让角标压在按钮边框上也不糊成一团 */
  box-shadow: 0 0 0 2px #fff; pointer-events: none;
}
.msg-badge[hidden] { display: none; }
.msg-badge.dot { min-width: 9px; height: 9px; padding: 0; top: -3px; right: -3px; }

/* 弹窗内的消息列表 */
.msg-tabs { display: flex; gap: 6px; padding: 0 0 10px; align-items: center; }
.msg-tabs .sp { margin-left: auto; }
.msg-list { max-height: 56vh; overflow-y: auto; border: 1px solid var(--border); border-radius: 10px; }
.msg-item {
  display: flex; gap: 10px; padding: 11px 13px; border-bottom: 1px solid var(--border);
  cursor: pointer; transition: background .15s;
}
.msg-item:last-child { border-bottom: 0; }
.msg-item:hover { background: #f5faff; }
.msg-item .m-ico { flex: none; font-size: 15px; line-height: 1.5; width: 20px; text-align: center; }
.msg-item .m-main { flex: 1; min-width: 0; }
/* 未读：黑色加粗（需求：未阅读的消息黑色显示） */
.msg-item .m-title { font-weight: 600; color: var(--text); }
.msg-item .m-meta { font-size: 12px; color: var(--text-sub); margin-top: 2px; }
.msg-item .m-content {
  font-size: 12.5px; color: var(--text-sub); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
/* 已读：整体转灰（「全部」页里仍可见，但视觉上退出待办） */
.msg-item.read .m-title { font-weight: 400; color: var(--text-sub); }
.msg-item .m-right { flex: none; text-align: right; font-size: 11.5px; color: var(--text-sub); }
.msg-item .m-unread-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  background: var(--danger); vertical-align: middle;
}

/* ---------------- 通用组件 ---------------- */
.card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
.card + .card { margin-top: 14px; }
/* 放入栅格时由 gap 统一控制间距：否则 .card + .card 会给第二列卡片额外加上边距，
   导致栅格中右侧卡片整体下移、与左侧不对齐 */
.grid > .card + .card { margin-top: 0; }
.card-head {
  padding: 13px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px;
}
.card-head h3 { margin: 0; font-size: 14.5px; font-weight: 600; flex: none; }
.card-head .sub {
  font-size: 12px; color: var(--text-sub);
  /* 说明文字可伸缩并在过长时省略，避免把右侧操作区挤到换行造成错位 */
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.card-head .right { margin-left: auto; display: flex; gap: 8px; align-items: center; flex: none; }
.card-body { padding: 16px; }
.card-body.tight { padding: 10px 16px; }

.btn {
  border: 1px solid var(--border); background: #fff; color: var(--text); border-radius: 8px;
  padding: 7px 14px; font-size: 13px; cursor: pointer; transition: .15s; white-space: nowrap;
}
.btn:hover { border-color: #bcd6e6; background: #f5fafd; }
.btn.primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn.primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); }
.btn.danger { background: #fff; border-color: #f7c9cb; color: var(--danger); }
.btn.danger:hover { background: var(--danger-soft); }
.btn.ghost { background: transparent; }
.btn.sm { padding: 4px 10px; font-size: 12.5px; }
.btn.block { width: 100%; }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn.link { border: none; background: none; color: var(--primary); padding: 0; }
/* ★ 需求 11（第九十轮）：「编辑 / 删除」两个链接按钮**贴在一起、极易点错**的根因就是
   上面那条 `padding: 0` —— 两个 `.link` 相邻时中间连 1px 都没有。
   修法两件套（缺一不可）：
     ① 标记里用 `.row` 容器撑开间距（见 views/leavebalance.js 的假期规则操作列）；
     ② 危险动作用本类改成红色，与「编辑」在**视线层面**也分开。
   ⚠️ 本类必须排在 `.btn.link` **之后** —— 两者同权重（各 2 个类），靠书写顺序取胜。 */
.btn.link.danger { color: var(--danger); }

.field { margin-bottom: 14px; }
.field > label { display: block; font-size: 13px; margin-bottom: 6px; color: #2c4a63; font-weight: 500; }
.field .hint { font-size: 12px; color: var(--text-sub); margin-top: 5px; }
input[type=text], input[type=password], input[type=number], input[type=date], input[type=datetime-local], input[type=time], select, textarea {
  width: 100%; border: 1px solid var(--border); border-radius: 8px; padding: 8px 11px; font-size: 13.5px;
  font-family: inherit; color: var(--text); background: #fff; outline: none; transition: .15s;
}
input:focus, select:focus, textarea:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(2, 132, 199, .12); }
textarea { min-height: 88px; resize: vertical; }

/* ★ 需求 50（第六十五轮）：被禁用的输入项统一「灰底 + 灰字 + 禁点」。
   首个用点 = 日程表单勾上「全天」后「时长」下拉的 `disabled`（林总原话「变成不可选」）——
   此前全站只有 `.btn:disabled` 与勾选框有禁用样式，`select:disabled` 会**看起来仍然可选**
   （浏览器默认那点灰在蓝底卡片里几乎看不出），用户会以为点不动是坏了。 */
input:disabled, select:disabled, textarea:disabled { background: var(--bg); color: var(--text-sub); cursor: not-allowed; opacity: .8; }

/* 数字输入框的「上下加减箭头」：上方 `input { appearance: none }` 在部分内核/版本下
   会把原生 spinner 一并抹掉（数字框就只剩「能打字」）。这里显式把它补回来，
   保证「点上下箭头逐年增减」在任意环境下都可用（年份、工资月份等数字字段都依赖它）。 */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
  -webkit-appearance: auto; appearance: auto; opacity: 1; height: 22px; cursor: pointer;
}

/* ---------------- 勾选框 / 单选框 ----------------
   全局 `input { appearance: none }` 会把原生勾选框的边框与勾一并抹掉，
   各页面又只写了 style="width:auto"（等于没样式）—— 结果是「勾了完全看不出状态」。
   这里统一补回自绘外观：勾/点用 SVG 背景绘制（比 ::after 伪元素在 input 上更可靠，
   Safari / Firefox 对 replaced element 的伪元素支持不一致）。

   ⚠️ 第三十一轮（2026-09-24）修复「勾了却看不出对勾」：
   方框是 16px，而勾的 `background-size` 只有 11px、线宽 2.2（在 16 的 viewBox 里）
   → 实际渲染线宽 ≈ 11/16×2.2 ≈ 1.5 物理像素，白勾又细又淡；
   在正常视距 / 低分屏上整块看起来就是一个「实心蓝方块」，用户以为没勾上。
   修法：把勾放大到 13px、线宽加到 3.4（≈2.8 物理像素），**方框尺寸与全站排版都不动**。
   （共享网盘列表、图标视图、批量条、题库选择器、智能汇总等所有勾选框一并受益。） */
input[type=checkbox], input[type=radio] {
  width: 16px !important; height: 16px !important; min-width: 16px;
  flex: 0 0 16px; margin: 0; padding: 0; cursor: pointer; vertical-align: -3px;
  border: 1.5px solid #b6c8da; border-radius: 4px;
  background: #fff center/13px 13px no-repeat;
  transition: background-color .15s, border-color .15s, box-shadow .15s;
}
input[type=radio] { border-radius: 50%; }
input[type=checkbox]:hover:not(:disabled),
input[type=radio]:hover:not(:disabled) { border-color: var(--primary); }
input[type=checkbox]:checked {
  background-color: var(--primary); border-color: var(--primary);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M12.6 4.4 6.5 11 3.4 7.8' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* 全选处于「部分选中」时给一道横杠，避免看起来像未勾选 */
input[type=checkbox]:indeterminate {
  background-color: var(--primary); border-color: var(--primary);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8h8' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E");
}
input[type=radio]:checked {
  border-color: var(--primary); background-color: #fff;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='4.2' fill='%230284c7'/%3E%3C/svg%3E");
}
input[type=checkbox]:disabled, input[type=radio]:disabled { opacity: .45; cursor: not-allowed; }

/* ---------------- 人员选择器（指派 / 协作成员 / 考核人员…） ---------------- */
.user-picker {
  display: flex; flex-direction: column; max-height: 280px;
  border: 1px solid var(--border); border-radius: 10px; background: #fff; overflow: hidden;
}
.up-tools {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-bottom: 1px solid var(--border); background: #f7fbfe;
}
.up-search {
  flex: 1 1 auto; min-width: 0; width: auto !important;
  border: 1px solid var(--border); border-radius: 8px; padding: 5px 10px;
  font-size: 13px; color: var(--text); background: #fff; outline: none; transition: .15s;
}
.up-search:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(2, 132, 199, .12); }
.up-allwrap {
  display: flex; align-items: center; gap: 6px; flex: none; cursor: pointer;
  font-size: 13px; color: var(--text-sub); white-space: nowrap;
}
.up-list { flex: 1 1 auto; overflow: auto; padding: 4px; }
.up-group-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; gap: 6px;
  padding: 6px 8px 4px; background: #fff;
  font-size: 12px; font-weight: 600; color: var(--text-sub);
}
/* 第三十轮需求 1：部门勾选框 —— 点击部门名即可整部门勾选 / 取消 */
.up-deptwrap {
  display: flex; align-items: center; gap: 6px;
  flex: 1 1 auto; cursor: pointer; user-select: none;
  border-radius: 6px; padding: 1px 4px; margin-left: -4px;
}
.up-deptwrap:hover { background: var(--primary-soft); }
.up-dept-name { font-size: 12px; font-weight: 600; color: var(--text-sub); }
/* 部门人数徽标固定靠右，不被部门名撑走 */
.up-group-head .up-group-n { margin-left: auto; flex: none; }
.up-group-n {
  padding: 0 7px; border-radius: 999px; background: var(--bg);
  font-size: 11px; font-weight: 500; color: var(--text-sub);
}
.up-item {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border: 1px solid transparent; border-radius: 8px;
  cursor: pointer; transition: background .12s, border-color .12s;
}
.up-item:hover { background: var(--primary-soft); }
.up-item.on { background: var(--primary-soft); border-color: #bae6fd; }
.up-name { font-size: 13.5px; color: var(--text); white-space: nowrap; }
.up-item.on .up-name { font-weight: 600; color: var(--primary-dark); }
.up-pos { font-size: 12px; color: var(--text-sub); white-space: nowrap; }
.up-dept { margin-left: auto; font-size: 12px; color: var(--text-sub); white-space: nowrap; }
.up-none { padding: 18px 0; text-align: center; font-size: 13px; color: var(--text-sub); }
.up-foot {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 7px 10px; border-top: 1px solid var(--border); background: #f7fbfe;
  font-size: 12.5px; color: var(--text-sub);
}
.up-count { font-weight: 600; color: var(--primary-dark); }

/* 多题库选择器（新建 / 编辑考试）：与人员选择器同一套手感 —— 整行可点、搜索、全选、实时计数 */
.bank-picker {
  display: flex; flex-direction: column; max-height: 260px;
  border: 1px solid var(--border); border-radius: 10px; background: #fff; overflow: hidden;
}
.bk-tools {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-bottom: 1px solid var(--border); background: #f7fbfe;
}
.bk-search {
  flex: 1 1 auto; min-width: 0; width: auto !important;
  border: 1px solid var(--border); border-radius: 8px; padding: 5px 10px;
  font-size: 13px; color: var(--text); background: #fff; outline: none; transition: .15s;
}
.bk-search:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(2, 132, 199, .12); }
.bk-allwrap {
  display: flex; align-items: center; gap: 6px; flex: none; cursor: pointer;
  font-size: 13px; color: var(--text-sub); white-space: nowrap;
}
.bk-list { flex: 1 1 auto; overflow: auto; padding: 4px; }
.bk-item {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 8px; border: 1px solid transparent; border-radius: 8px;
  cursor: pointer; transition: background .12s, border-color .12s;
}
.bk-item:hover { background: var(--primary-soft); }
.bk-item.on { background: var(--primary-soft); border-color: #bae6fd; }
.bk-name { font-size: 13.5px; color: var(--text); }
.bk-item.on .bk-name { font-weight: 600; color: var(--primary-dark); }
.bk-meta { margin-left: auto; font-size: 12px; color: var(--text-sub); white-space: nowrap; }
.bk-none-tip { padding: 18px 0; text-align: center; font-size: 13px; color: var(--text-sub); }
.bk-foot {
  flex: none; padding: 7px 10px 4px; border-top: 1px solid var(--border); background: #f7fbfe;
  font-size: 12.5px; color: var(--text-sub);
}
.bk-count { font-weight: 600; color: var(--primary-dark); }
.bk-stock { flex: none; padding: 0 10px 8px; background: #f7fbfe; }

.grid { display: grid; gap: 14px; }
/* ★★ 需求 44（第五十八轮续，2026-10-11 林总）：「来访 → 审批设置」的「主区 + 360px 二维码侧栏」。
   原先这段列宽写死在视图的内联 `style` 上，而**内联样式的优先级高于任何 @media 规则**
   ⇒ 断点里降不了列 ⇒ 375px 视口下右侧 360px 卡片溢出、被 `.content{overflow-x:hidden}` 裁掉。
   改类名后即可在断点里降到单列（见本文件 `@media (max-width: 900px)` 段）。 */
.vset-grid { grid-template-columns: minmax(0, 1fr) 360px; }
.g2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.g4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.row.end { justify-content: flex-end; }
.spacer { flex: 1; }

table.tbl { width: 100%; border-collapse: collapse; font-size: 13.2px; }
table.tbl th, table.tbl td { padding: 10px 12px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: middle; }
table.tbl th { background: #f4f9fd; font-weight: 600; color: #2c4a63; font-size: 12.5px; white-space: nowrap; }
table.tbl tbody tr:hover { background: #f2f8fd; }
table.tbl td.nowrap, table.tbl th.nowrap { white-space: nowrap; }

/* ⚠️ 表格容器**全宽度**生效的横向滚动（第十六轮 2026-09-22 修「排版错位」）：
   这条原先只写在移动端媒体查询里（见文件下方 `@media` 段），桌面端没有 —— 于是当表格
   列数多、比卡片还宽时，表格会**直接溢出卡片边界**、压到相邻栏上，看起来就是「错位」。
   实测场景：「访客管理 → 来访申请」右侧原本还有一列 320px 的二维码侧栏，8 列的表被挤到
   溢出（林总反馈的就是这个）。侧栏已改为弹窗；这里再把容器滚动补齐，两处一起才根治。 */
.tbl-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.tag { display: inline-block; padding: 2px 8px; border-radius: 20px; font-size: 12px; line-height: 1.7; }
.tag.blue { background: var(--primary-soft); color: var(--primary-dark); }
.tag.gray { background: #eaf2fa; color: #5a7391; }
.tag.green { background: #e9f7f5; color: #0f766e; }
/* 主题强调色（#06b6d4）的标签。原先费用管理的「水电」标签写的就是 `tag teal`，
   但**这个类从来没定义过** —— 渲染出来只剩基础 .tag 的透明底，看着像没上色。 */
.tag.teal { background: #e6f7fa; color: #0e7490; }
.tag.red { background: var(--danger-soft); color: var(--danger); }
.tag.orange { background: #fff4e8; color: #b45309; }
.text-danger { color: var(--danger); }
.text-sub { color: var(--text-sub); }
.text-sm { font-size: 12.5px; }
.overdue { color: var(--danger); font-weight: 600; }
.empty { padding: 40px 10px; text-align: center; color: var(--text-sub); }
.empty .ico { font-size: 30px; opacity: .45; }
.locked-tip { color: var(--danger); background: var(--danger-soft); padding: 12px; border-radius: 8px; }

/* 统计卡：固定列数，避免 auto-fit 在末行不足时把卡片拉伸变宽造成参差 */
.stats { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; }
.stats > .stat { display: flex; flex-direction: column; justify-content: center; min-width: 0; }
/* 窄屏列数在文件末尾的移动端断点里统一接管，避免多处覆盖相互打架 */
.stat {
  background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow);
}
.stat .label {
  font-size: 12.5px; color: var(--text-sub);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.stat .value { font-size: 26px; font-weight: 700; line-height: 1.3; }
.stat.red .value { color: var(--danger); }
.stat.blue .value { color: var(--primary-dark); }
.stat.orange .value { color: var(--warn); }
.stat.green .value { color: var(--success); }

/* 列表项 */
.list-item { padding: 12px 16px; border-bottom: 1px solid #e8f0f7; display: flex; gap: 10px; align-items: center; }
.list-item:last-child { border-bottom: none; }
.list-item .li-main { flex: 1; min-width: 0; }
.list-item .li-title { font-size: 13.6px; font-weight: 500; }
.list-item .li-sub { font-size: 12px; color: var(--text-sub); }
/* 内容板块（创赛之家 / 创赛课堂 / 行政资源 / 集采平台）文章列表：标题必须加粗突出。
   「置顶 / 等级不足」等标签不参与加粗，故只对标题文字本身加粗。 */
.list-item .li-title .li-title-name { font-weight: 700; }

/* ★ 需求 13（2026-09-28）：可点击进详情的列表项（工作台「我的任务 / 我的售后 /
   我的办公用品申请」三张卡片）。
   设计意图 —— 让用户「看得出能点」：
     · `cursor: pointer` + 悬停浅色底 + 标题转主色 三重反馈，不依赖鼠标形状一种线索；
     · `:focus-visible` 给键盘用户同样的可见反馈（用 Tab 能走到、看得见）；
     · `transition` 与全站动效口径一致（.12s），不另起节奏。
   ⚠️ 这些条目的父容器是 `.wb-card`（浅色主题底），所以悬停底色用半透明白叠加，
      深色 / 不同卡片色下都不会突兀。 */
.list-item.li-clickable { cursor: pointer; transition: background .12s; }
.list-item.li-clickable:hover { background: rgba(255, 255, 255, .72); }
.list-item.li-clickable:hover .li-title { color: var(--primary); }
.list-item.li-clickable:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.list-item.li-clickable:active { background: rgba(2, 132, 199, .08); }

/* 待办：未完成加粗突出，已完成常规字重 + 删除线 */
.list-item .li-title.todo-undone { font-weight: 700; color: var(--text); }
.list-item .li-title.todo-done { font-weight: 400; color: var(--text-sub); text-decoration: line-through; }

/* 待办单 · 多人完成（需求 6）：参与人名单 + 完成情况灰字
   已完成的人划线并转灰；未完成的保持正常字重，直到勾上。 */
.td-people { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; }
.td-person { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--text); }
.td-person .td-pname { border-bottom: 1px dashed transparent; }
.td-person.is-done { color: var(--text-sub); }
.td-person.is-done .td-pname { text-decoration: line-through; }
.td-person input[type="checkbox"] { margin: 0; }
.td-doneline { margin-top: 4px; font-size: 12px; line-height: 1.7; color: #9aa8b6; }
.wb-card .list-item .li-title.todo-undone { color: var(--text); }

/* ★ 需求 8：已完成待办折叠区。
   按钮要求「灰色字体」→ 刻意不用 .btn（蓝底白字），而是一个灰字可点行，
   靠 hover 底色 + 光标表明可点。`--text-sub` 即全站统一的次级灰。 */
.td-fold-wrap { margin-top: 10px; border-top: 1px dashed var(--border); padding-top: 8px; }
.td-fold {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 13px; color: var(--text-sub);   /* 灰字，按需求 */
  cursor: pointer; user-select: none;
  padding: 3px 8px; border-radius: 6px;
}
.td-fold:hover { background: var(--primary-soft); color: var(--primary-dark); }
.td-fold:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.td-fold-arrow { font-size: 11px; line-height: 1; }
.td-fold-body { margin-top: 6px; }
.td-fold-body[hidden] { display: none; }

/* 富文本编辑器 */
.editor-toolbar { border: 1px solid var(--border); border-bottom: none; border-radius: 8px 8px 0 0; padding: 6px; display: flex; gap: 4px; flex-wrap: wrap; background: #f4f9fd; }
.editor-toolbar button { border: 1px solid var(--border); background: #fff; border-radius: 6px; padding: 3px 9px; cursor: pointer; font-size: 12.5px; }
.editor-area { border: 1px solid var(--border); border-radius: 0 0 8px 8px; padding: 12px; min-height: 160px; background: #fff; outline: none; }
.editor-area:focus { border-color: var(--primary); }
.ed-sep { width: 1px; height: 18px; background: var(--border); margin: 0 4px; align-self: center; }

/* 需求 30（第九十轮）：富文本「字体颜色」色板。
   `.ed-color-wrap` 是面板的定位上下文（工具栏是 flex，故自身 `position:relative` 即可）；
   面板 `z-index:9` 要压过 `.editor-area`，否则会被正文盖住。 */
.ed-color-wrap { position: relative; display: inline-flex; }
.ed-colors {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 9;
  display: grid; grid-template-columns: repeat(6, 22px); gap: 4px; padding: 8px;
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(12, 40, 64, .16);
}
.ed-colors.hidden { display: none; }
.ed-c {
  width: 22px; height: 22px; padding: 0; border: 1px solid var(--border);
  border-radius: 5px; cursor: pointer; font-size: 10px; color: var(--text-sub); background: #fff;
}
.ed-c:hover { outline: 2px solid var(--primary-soft); }
.ed-c-def { grid-column: span 6; width: 100%; height: 24px; font-size: 11.5px; }

/* 需求 29（第九十轮）：评论「表情包」面板。
   面板**向上展开**（`bottom:100%` + `right:0`）—— 评论区位于详情弹窗底部，
   向下展开会被弹窗裁掉；右对齐则避免在窄屏上顶出弹窗左缘。 */
.emoji-wrap { position: relative; display: inline-flex; }
.emoji-panel {
  position: absolute; bottom: calc(100% + 6px); right: 0; z-index: 9;
  padding: 8px;
  max-height: 248px; overflow-y: auto;
  background: #fff; border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 10px 28px rgba(12, 40, 64, .18);
}
/* ★ 需求 17（第九十九轮）：面板**分两组** —— 单字符 emoji 走 28px 网格，
   颜文字（`╮(╯▽╰)╭` / `o(*￣▽￣*)o` 这类 6~12 字符）另起一行用自适应宽度按钮。
   原先两者挤在同一张 `repeat(8, 28px)` 网格里，颜文字溢出压字 ⇒ 看着像乱码。 */
.emoji-grid { display: grid; grid-template-columns: repeat(8, 28px); gap: 2px; }
.emoji-kao {
  display: flex; flex-wrap: wrap; gap: 2px;
  margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--border);
}
.emoji-c {
  width: 28px; height: 28px; padding: 0; border: none; background: none;
  border-radius: 6px; cursor: pointer; font-size: 17px; line-height: 1;
}
/* 颜文字：宽度交给内容（`width:auto`）+ 小字号 + 绝不换行，塞得下才看得清 */
.emoji-kao .emoji-c {
  width: auto; min-width: 28px; height: 24px; padding: 0 7px;
  font-size: 12px; white-space: nowrap;
}
.emoji-c:hover { background: var(--primary-soft); }
@media (max-width: 640px) {
  .emoji-grid { grid-template-columns: repeat(6, 28px); }
}

/* 文章预览页标题（创赛之家 / 创赛课堂 / 行政资源） */
.post-detail-title { font-weight: 700 !important; font-size: 18px; line-height: 1.5; color: var(--text); }

/* 正文中的图片 / 视频 / 表格 */
.post-body { line-height: 1.8; font-size: 14px; word-break: break-word; }
.post-body img { max-width: 100%; height: auto; border-radius: 8px; margin: 6px 0; }
.post-body video { max-width: 100%; border-radius: 8px; margin: 6px 0; display: block; }
.post-body table, .rich-table { border-collapse: collapse; margin: 10px 0; }
.post-body table th, .post-body table td,
.rich-table th, .rich-table td {
  border: 1px solid var(--border); padding: 8px 10px; text-align: left; font-size: 13.2px;
}
.post-body table th, .rich-table th { background: #f4f9fd; font-weight: 600; }

/* 正文与编辑器中的超链接：统一的青蓝下划线风格，邮箱与网址一视同仁 */
.post-body a, .editor-area a {
  color: var(--primary-dark);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: rgba(2, 132, 199, .45);
  word-break: break-all;
}
.post-body a:hover, .editor-area a:hover { color: var(--accent); text-decoration-color: currentColor; }
.editor-area a { cursor: pointer; }

/* 编辑器工具栏里的链接按钮组 */
.editor-toolbar button.js-link,
.editor-toolbar button.js-unlink,
.editor-toolbar button.js-autolink { white-space: nowrap; }
.editor-toolbar button.js-autolink { border-color: var(--primary-soft); background: var(--primary-soft); color: var(--primary-dark); }
.editor-area img { max-width: 100%; }
.editor-area video { max-width: 100%; }
.editor-area table { border-collapse: collapse; }
.editor-area th, .editor-area td { border: 1px solid var(--border); padding: 6px 8px; }

/* ---------- 需求 5：富文本图片对齐（左 / 中 / 右） ----------
 * 口径写在 `img[data-align]` 属性上，**编辑器与正文展示共用同一套规则** —— 两边同时列选择器，
 * 才不会出现「编辑器里居中了、发出去又靠左」的错位。`width` 用 inline style 存，不在此处覆盖。 */
.editor-area img[data-align], .post-body img[data-align] { display: block; height: auto; }
.editor-area img[data-align="left"], .post-body img[data-align="left"] { margin-left: 0; margin-right: auto; }
.editor-area img[data-align="center"], .post-body img[data-align="center"] { margin-left: auto; margin-right: auto; }
.editor-area img[data-align="right"], .post-body img[data-align="right"] { margin-left: auto; margin-right: 0; }

/* 编辑器里的图片可点（点一下才出对齐 / 缩放浮层），给个手型提示 */
.editor-area img { cursor: pointer; }
.editor-area img.ed-img-active { outline: 2px solid var(--primary); outline-offset: 2px; }

/* 图片浮层：工具条贴图片上沿、缩放手柄贴右下角。
 * 容器本身 `pointer-events:none`，只有两个子元素可点 —— 免得挡住底下的正文。
 * ⚠️ z-index 必须**高于弹窗**（`.modal-root` = 100）：富文本编辑器绝大多数时候长在弹窗里，
 *    本层最初写的 60 会被弹窗整个盖住 —— 浮层「存在、可点、但用户看不见」（真机实测踩到）。
 *    取 150：高于 modal(100)、低于 toast(200)。 */
.ed-img-ui { position: fixed; z-index: 150; pointer-events: none; }
.ed-img-ui .ed-img-bar {
  position: absolute; left: 0; top: -34px; pointer-events: auto; display: flex; align-items: center; gap: 2px;
  background: #fff; border: 1px solid var(--border); border-radius: 8px; padding: 3px 4px;
  box-shadow: 0 6px 18px rgba(12, 58, 88, .16); white-space: nowrap;
}
.ed-img-ui .ed-img-bar button {
  border: 1px solid transparent; background: transparent; border-radius: 6px; cursor: pointer;
  padding: 2px 7px; font-size: 12.5px; line-height: 20px; color: var(--text);
}
.ed-img-ui .ed-img-bar button:hover { background: var(--primary-soft); }
.ed-img-ui .ed-img-bar button.on { background: var(--primary-soft); color: var(--primary-dark); font-weight: 600; }
.ed-img-ui .ed-img-bar button.js-del:hover { background: var(--danger-soft); color: var(--danger); }
.ed-img-ui .ed-img-sep { width: 1px; height: 16px; background: var(--border); margin: 0 3px; }
.ed-img-ui .ed-img-grip {
  position: absolute; right: -7px; bottom: -7px; width: 14px; height: 14px; pointer-events: auto;
  border: 1px solid var(--primary); border-radius: 3px; background: var(--primary-soft);
  cursor: nwse-resize; box-shadow: 0 1px 4px rgba(12, 58, 88, .2);
}

/* 弹窗 */
.modal-root { position: fixed; inset: 0; pointer-events: none; z-index: 100; }
/* ---------------- 弹窗动效（需求 4，2026-09-27） ----------------
   目标：所有弹出 / 关闭都顺滑，不再是「啪」地出现与消失。
   实现要点（都是必须守住的约束）：
     · **入场**用 `animation`（一次性、自结束），**出场**用 `.closing` + 动画
       —— 出场不能也靠 animation，因为 `remove()` 会让动画来不及播。
     · 遮罩淡入 / 卡片上浮缩放，二者**时长错开**（遮罩 180ms、卡片 240ms）
       才会有「层次」，同长看起来是整块板子在平移。
     · 时长刻意压在 200–260ms：超过 300ms 在每天点几十次的后台里会「拖手」。
     · 全部挂在 `prefers-reduced-motion: reduce` 的例外里 —— 该关就关。
     · ⚠️ `--modal-anim-ms` 必须与 core.js 里 `CLOSE_MS` **同源同值**：
       JS 要在动画播完后才 remove，CSS 时长改了而 JS 没跟上 → 残影或闪回。
     · ★★ 退场这一条**写死 200ms 字面值，不要用 `var(--x)ms` 拼接**
       （第一版这么写，实测 `animation: modalFadeOut none` —— 变量替换后的
       `200ms` 不会被重新解析成合法时长，整条 animation 声明作废、退场变瞬移）。
       变量只保留给「需要被 JS 读取」的场合用。 */
:root { --modal-anim-ms: 200; }

@keyframes modalFadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalFadeOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes modalPopIn {
  from { opacity: 0; transform: translateY(-14px) scale(.965); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes modalPopOut {
  from { opacity: 1; transform: translateY(0) scale(1); }
  to   { opacity: 0; transform: translateY(-8px) scale(.98); }
}

.modal-mask {
  position: absolute; inset: 0; background: rgba(12, 58, 88, .45); display: flex;
  align-items: flex-start; justify-content: center; padding: 5vh 16px; overflow-y: auto; pointer-events: auto;
  animation: modalFadeIn 180ms ease-out both;
}
.modal-mask.closing { animation: modalFadeOut 200ms ease-in both; }

.modal {
  background: #fff; border-radius: 12px; width: 100%; max-width: 560px; box-shadow: 0 24px 60px rgba(12, 58, 88, .3);
  display: flex; flex-direction: column; margin: auto 0;
  animation: modalPopIn 240ms cubic-bezier(.22, 1, .36, 1) both;
}
.modal-mask.closing .modal { animation: modalPopOut 200ms ease-in both; }

/* 减动效偏好：一律不做位移/缩放，只留一次极短淡入（否则「无动画」会闪白） */
@media (prefers-reduced-motion: reduce) {
  .modal-mask { animation: modalFadeIn 1ms linear both; }
  .modal-mask.closing { animation: modalFadeOut 1ms linear both; }
  .modal { animation: modalFadeIn 1ms linear both; }
  .modal-mask.closing .modal { animation: modalFadeOut 1ms linear both; }
}
.modal.lg { max-width: 860px; }
.modal.xl { max-width: 1080px; }
/* ★★ 需求 4（第一百零三轮，林总 2026-10-13）：**弹窗可按住标题栏拖动**。
   `cursor: move` 只是提示；真正的拖动逻辑在 `core.js::UI.modal()`（一处实现、
   约 130 处调用点全体受益）。拖动柄是 `.modal-head`，位移用 `transform: translate()`
   —— 弹窗本来靠 `.modal-mask` 的 flex 居中，改 `left/top` 会脱离居中基准，
   translate 只需累加增量、不动布局。 */
.modal-head { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; cursor: move; }
/* 拖动进行中：禁掉标题文字被选中（起手已经 preventDefault，这里只是双保险） */
.modal.dragging { user-select: none; }
/* ★★ 被拖动过的弹窗：**永久关掉入场动画**（`.no-anim` 由 core.js 在首次 pointerdown 时挂上，此后不再摘）。
   为什么非关不可（实测踩到）：`.modal` 的入场动画 `modalPopIn` 用 transform 做位移，
   而 CSS 动画在层叠里**排在普通作者声明之上**、且带 `fill-mode: both`（结束后仍保持终态）
   ⇒ 拖动写的 inline transform 会被它整段盖掉，**松手瞬间弹窗弹回正中央**。
   ⚠️ 退场动画 `.modal-mask.closing .modal`（特异性 0,3,0）**高于**本规则（0,2,0）⇒ 关闭动效不受影响。 */
.modal.no-anim { animation: none; }
.modal-head h4 { margin: 0; font-size: 15px; }
.modal-head .close { margin-left: auto; cursor: pointer; color: var(--text-sub); font-size: 18px; line-height: 1; }
.modal-body { padding: 18px; overflow-y: auto; max-height: 70vh; }
.modal-foot { padding: 12px 18px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; }

/* ==================== 版本自检条（2026-10-15）====================
   纯 SPA 不重启就换不掉已加载的 JS ⇒ 服务端修好了、用户端还在跑旧包。
   `app.js::App.checkVersion()` 复查到指纹变了才给这条加 `.hidden` 摘除。
   层级 160：压过弹窗（`.modal-root` = 100），低于 toast（200）——
   弹窗开着时也必须看得见，否则又变成「用户不知道要刷新」。
   文案是**一行**，窄屏会换行，故用 flex + 自动高度，不写死 height。 */
.verbar {
  position: fixed; left: 0; right: 0; top: 0; z-index: 160;
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 8px 14px; background: #fff7ed; color: #8a4b00;
  border-bottom: 1px solid #f5d0a9; font-size: 13px; line-height: 1.5;
  box-shadow: 0 2px 10px rgba(14, 60, 100, .10);
}
.verbar-txt { max-width: 980px; }

/* Toast */
.toast-wrap { position: fixed; top: 18px; left: 50%; transform: translateX(-50%); z-index: 200; display: flex; flex-direction: column; gap: 8px; align-items: center; }
.toast {
  background: #0f2b45; color: #fff; padding: 9px 18px; border-radius: 8px; font-size: 13px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .2); animation: tin .18s ease;
}
.toast.ok { background: #0f766e; }
.toast.err { background: #b91c1c; }
@keyframes tin { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* 上传进度浮层（需求 21）：与 toast 同一个队列，但不自动消失 —— 由调用方收起 */
.up-panel {
  width: 320px; background: #0f2b45; color: #fff; padding: 10px 14px 11px;
  border-radius: 10px; font-size: 13px; box-shadow: 0 8px 24px rgba(0, 0, 0, .2);
  animation: tin .18s ease;
}
.up-panel .up-head { font-weight: 600; display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.up-panel .up-name { font-weight: 400; font-size: 12px; opacity: .78; max-width: 170px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.up-panel .up-track { margin-top: 8px; height: 6px; border-radius: 99px; background: rgba(255, 255, 255, .22); overflow: hidden; }
.up-panel .up-track > i { display: block; height: 100%; width: 0; border-radius: 99px;
  background: linear-gradient(90deg, var(--primary), var(--accent)); transition: width .15s linear; }
.up-panel .up-foot { margin-top: 6px; display: flex; justify-content: space-between; gap: 10px; font-size: 12px; opacity: .85; }
.up-panel .up-note { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 工作台：待办卡片内容区统一最小高度，条目数不同时左右两列依然齐整 */
.wb-list { min-height: 172px; }

/* ---------------- 工作台顶部日期时间 ---------------- */
.wb-hero {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 14px 18px; margin-bottom: 14px;
  background: linear-gradient(135deg, #eaf6fd 0%, #e8f7f6 100%);
  border: 1px solid #d3e6f4; border-radius: var(--radius); box-shadow: var(--shadow);
}
.wb-hero .wb-date { font-size: 13px; color: var(--text-sub); }
.wb-hero .wb-time {
  font-size: 26px; font-weight: 700; line-height: 1.2; color: var(--primary-dark);
  font-variant-numeric: tabular-nums; letter-spacing: 1px;
}
.wb-hero .wb-hello { margin-left: auto; text-align: right; }
.wb-hero .wb-hi { font-size: 15px; font-weight: 600; }
.wb-hero .wb-sub { font-size: 12px; color: var(--text-sub); }

/* ------- 需求 10：工作台「常用工具」快捷按钮（搜索框上方） ------- */
.wb-quick { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 12px 0 0; }
.wb-quick-hd { font-size: 12px; color: var(--text-sub); white-space: nowrap; }
.wb-quick-btn {
  display: inline-flex; align-items: center; padding: 7px 14px; border-radius: 20px;
  border: 1px solid var(--border); background: var(--card); color: var(--text);
  font-size: 13.5px; text-decoration: none; box-shadow: var(--shadow);
  transition: .15s; white-space: nowrap;
}
.wb-quick-btn:hover { border-color: var(--primary); color: var(--primary-dark); }
@media (max-width: 640px) {
  .wb-quick { gap: 8px; }
  .wb-quick-btn { flex: 1 1 auto; min-width: calc(50% - 4px); justify-content: center; padding: 10px 12px; }
}

/* ------- 需求 2：工作台全局检索（功能 / 工具 / 信息） ------- */
.wb-search { position: relative; margin: 12px 0 0; }
.wb-search-bar {
  display: flex; align-items: center; gap: 10px;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 8px 10px;
}
.wb-search-bar:focus-within { border-color: var(--primary); }
.wb-search-ico { font-size: 15px; opacity: .7; }
.wb-search-bar input[type="search"] {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
  font-size: 14px; color: var(--text); padding: 4px 0;
}
.wb-search-tabs { display: flex; gap: 4px; flex-wrap: nowrap; }
.wb-search-tabs button {
  border: 1px solid var(--border); background: var(--card); color: var(--text-sub);
  border-radius: 999px; padding: 3px 10px; font-size: 12px; cursor: pointer;
}
.wb-search-tabs button.on {
  background: var(--primary-soft); border-color: var(--primary);
  color: var(--primary-dark); font-weight: 600;
}
.wb-search-res {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 40;
  max-height: 380px; overflow: auto;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: 0 8px 24px rgba(14, 60, 100, .14);
  padding: 6px;
}
.wb-search-item { display: flex; align-items: center; gap: 8px; padding: 7px 9px; border-radius: 8px; cursor: pointer; }
.wb-search-item:hover { background: var(--primary-soft); }
.wb-si-kind {
  flex: 0 0 auto; font-size: 11px; padding: 1px 6px; border-radius: 999px;
  background: var(--bg); color: var(--text-sub); border: 1px solid var(--border);
}
.wb-si-kind.k-tool { background: #ecfeff; color: #0e7490; border-color: #a5f3fc; }
.wb-si-kind.k-info { background: #f0fdf4; color: #15803d; border-color: #bbf7d0; }
.wb-si-kind.k-menu { background: var(--primary-soft); color: var(--primary-dark); border-color: #bae6fd; }
.wb-si-ico { flex: 0 0 auto; }
.wb-si-main { min-width: 0; display: flex; flex-direction: column; }
.wb-si-main b { font-size: 13px; font-weight: 600; }
.wb-si-main em {
  font-size: 12px; color: var(--text-sub); font-style: normal;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wb-search-foot {
  padding: 6px 9px 2px; font-size: 12px; color: var(--text-sub);
  border-top: 1px solid var(--border); margin-top: 4px;
}

/* ---------------- 工作台卡片配色 ----------------
   统一在「蓝 → 青」冷色系内取极低饱和的浅色调，并配一条左侧色带作区分，
   保证一眼可辨识又不花哨。 */
.wb-card { border-left: 3px solid var(--primary); }
.wb-card > .card-head { background: rgba(255, 255, 255, .62); border-radius: 0 10px 0 0; }
.wb-card.c-task   { background: #eaf3fd; border-color: #cfe2f6; border-left-color: #0284c7; }
.wb-card.c-as     { background: #e8f6f4; border-color: #cbe8e4; border-left-color: #0d9488; }
.wb-card.c-todo   { background: #edf4fd; border-color: #d3e0f8; border-left-color: #2563eb; }
.wb-card.c-collab { background: #e9f7fb; border-color: #cbe8f2; border-left-color: #06b6d4; }
.wb-card.c-report { background: #f0f5fc; border-color: #d8e2f5; border-left-color: #0369a1; }
.wb-card.c-sched  { background: #eff6fa; border-color: #d5e5f1; border-left-color: #0e7490; }
.wb-card.c-file   { background: #f4f9fd; border-color: #dce9f5; border-left-color: #64798f; }
.wb-card.c-cal    { background: #f7fbfe; border-color: #dce9f5; border-left-color: #155e75; }
.wb-card.c-supplies { background: #eef6fb; border-color: #d3e6f3; border-left-color: #0891b2; }
.wb-card.c-library  { background: #f1f6fc; border-color: #dae5f4; border-left-color: #4f46e5; }
/* 需求 10（第九十轮）：两张询价卡片 —— 取紫罗兰（与既有 10 种配色都错得开），
   并**必须**同时在下方 `[data-board="workbench"]` 主题里给一份，否则工作台主题下会落回默认底色。 */
.wb-card.c-inquiry  { background: #f6f1fe; border-color: #e4d8fb; border-left-color: #7c3aed; }
/* 卡片内的列表项在浅色底上用白色分隔，层次更清楚 */
.wb-card .list-item { border-bottom-color: rgba(255, 255, 255, .85); }
.wb-card .card-body.tight { background: rgba(255, 255, 255, .55); }

/* ---------------- 工作台卡片拖拽排序（需求 5 起，需求 6 改为插入式） ----------------
   卡片尺寸不同，因此只允许「整行拖拽」；半宽行固定 2 张半宽卡片，
   整宽行（日程日历）独占一行，不会出现尺寸混排。 */
.wb-row + .wb-row { margin-top: 14px; }
/* 需求 6：**同行自动等高** —— 显式写死，不再依赖 `.grid` 的默认 `align-items: stretch`。
   默认值一旦被别处改动，同行卡片就会各自高矮不齐，而且不报任何错、只是「看着怪」。 */
.wb-row { align-items: stretch; }
/* ★★ 需求 9（2026-10-04）修复：这里原先是 `display:flex`（nowrap）+ 子项 `flex:1 0 100%` ——
   **nowrap 容器里的 `flex-basis:100%` 不会换行**，只会把同一行的兄弟项压到极限。实测后果（1440×900）：
   「拖动卡片左上角 ⠿ 调整顺序」被压成 **13px 宽 × 240px 高**（一个字一行，竖排成一条），
   「新增卡片 / 重置布局」两个按钮被挤出卡片右边界（x=1428 / 1506，而卡片右边只有 1422），
   整个 `.wb-hero` 被撑到 **270px 高**、按钮看不见。
   ⇒ 改法：**纵向两行、右对齐** —— 第一行是「版本更新内容」链接（占满一行，天然满足「在按钮上面」），
   第二行是「提示 + 两个按钮」。子项一律**不再写 `flex-basis:100%`**（纵向容器里 basis 是高度，
   写 100% 会把链接行撑满整个高度，是同一类坑的另一面）。
   ★ 静态守卫见 `test/changelog.js [A8]`（已从「断言旧写法」**反转**为「不得回潮」）。 */
.wb-layout { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
/* 需求 2：工作台「版本更新内容」文字超链接 —— 独占一行，排在「新增卡片 / 重置布局」之上 */
.wb-changelog-line { display: flex; justify-content: flex-end; }
.wb-layout-btns { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.wb-changelog-link { color: var(--primary); font-size: 13px; text-decoration: underline; cursor: pointer; }
.wb-changelog-link:hover { opacity: .75; }
/* 需求 2：版本更新内容弹窗（正文滚动复用 .modal-body 的 max-height:70vh + overflow-y:auto） */
.cl-note { color: #7b8a99; font-size: 12px; margin-bottom: 12px; }
.cl-entry { border-left: 3px solid var(--primary); padding: 0 0 2px 10px; margin: 0 0 16px; }
.cl-entry:last-child { margin-bottom: 0; }
.cl-head { display: flex; align-items: baseline; gap: 8px; }
.cl-ver { color: var(--primary); font-weight: 600; font-size: 14px; }
.cl-date { color: #7b8a99; font-size: 12px; }
.cl-sub { color: #7b8a99; font-size: 12px; margin: 6px 0 2px; }
.cl-list { margin: 0; padding-left: 18px; }
.cl-list li { font-size: 13px; line-height: 1.7; }
.cl-fix li { color: #b3541e; }
.wb-grip {
  flex: none; width: 22px; height: 22px; padding: 0; margin-right: 2px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: 6px; background: transparent;
  color: #9db2c6; font-size: 13px; line-height: 1; cursor: grab; transition: .15s;
}
.wb-grip:hover { color: var(--primary); border-color: var(--border); background: #fff; }
.wb-grip:active { cursor: grabbing; }
.wb-card.wb-dragging { opacity: .45; }
/* 需求 6：插入式落点指示 —— 在落点卡片的左 / 右边缘画一条竖线（插在它前面 / 后面）。
   ⚠️ 用 `box-shadow` 的 inset，**不**改 border：卡片左边框已承载 `.wb-card` 的主题色
   （c-supplies / c-library 等都覆盖了 `border-left-color`），改边框会把主题色一起吃掉。
   ⚠️ 必须把卡片自身的 `var(--shadow)` 一起写进同一声明 —— 否则这一条会把 `.card`
   的投影整条盖掉（同特异性下后写者胜）。 */
.wb-card.wb-ins-before { box-shadow: inset 3px 0 0 0 #06b6d4, var(--shadow); }
.wb-card.wb-ins-after  { box-shadow: inset -3px 0 0 0 #06b6d4, var(--shadow); }
body.wb-grabbing, body.wb-grabbing * { cursor: grabbing !important; }

/* ---------------- 需求 11：卡片右上角的「移除」叉号 + 「新增卡片」 ----------------
   · 叉号绝对定位在卡片右上角 ⇒ `.wb-card` 必须是定位上下文；`.card-head` 右侧留出 30px，
     免得它压住「查看全部 / + 申请」这类头部按钮（那些按钮是 flex 靠右的）。
   · `#wbHiddenCards` 是被移除卡片的**停放容器**（与 `#wbRows` 同级）：
     节点不离开文档，卡片的按 id 取节点的加载器才不会把它写空。
     双保险：`hidden` 属性 + 内联 `display:none`（站点样式里若有更具体的 display 规则会盖掉前者）。 */
.wb-card { position: relative; }
.wb-card > .card-head { padding-right: 30px; }
.wb-x {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 20px; height: 20px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: 6px; background: transparent;
  color: #9db2c6; font-size: 15px; line-height: 1; cursor: pointer; transition: .15s;
}
.wb-x:hover { color: #dc2626; border-color: #f2cfcf; background: #fff; }
#wbHiddenCards { display: none; }
.wb-addlist { display: flex; flex-wrap: wrap; gap: 8px; }
.wb-empty-hint {
  padding: 18px 16px; border: 1px dashed var(--border); border-radius: 10px;
  color: var(--text-sub); font-size: 13px; text-align: center;
}

/* 工作台「我的汇报」：四个周期的汇报义务卡片 */
.rp-duty-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.rp-duty { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: #fff; }
.rp-duty .n { font-weight: 600; font-size: 13.5px; }
.rp-duty .l { color: var(--text-sub); font-size: 12px; margin: 2px 0 6px; }
.rp-duty.todo { border-color: #f7c9cb; background: #fdf0f0; }
.rp-duty.ok { border-color: #a5e3d8; background: #eafaf7; }
.rp-duty .btn { margin-top: 8px; }
/* 窄屏列数交给文件末尾的移动端断点统一处理 */

/* 日历 */
.cal-head { display: flex; align-items: center; gap: 8px; padding: 10px 16px; border-bottom: 1px solid var(--border); }
/* ★★ 需求 20（第九十轮）：七等分列里的 `1fr` **不是**「等分」——
   它的隐含最小值是 `minmax(auto, 1fr)`，而 `auto` 对 grid item 取 **min-content**。
   日历格里的事件 `.ev` 带 `white-space: nowrap`（标题不换行），于是**有长标题的那一列**
   的 min-content 被撑到整整一行文字的宽度，把 7 列挤成「119px / 22px / 22px / 119px …」，
   合计 398px > 容器 351px ⇒ **右边的日期被挤出屏幕**（手机端表现为「部分日期丢失」）。
   实测（林旭程 · 375×667 · 2026-10）：`beyondViewport = 10` 个格子在视口之外、`maxRight = 629`。
   修法：① 列宽改 `minmax(0, 1fr)`（允许压到 0，真正的等分）；
        ② `.cal-cell` 加 `min-width: 0`（同样的原因，防 grid item 自身撑破行）。
   ⚠️ 这两条**同时**作用于「工作台日历」与「日程板块日历」（共用 `.cal-cell`），
      免得只修一处、另一处照旧溢出（同一能力多处实现必须同源）。 */
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border-bottom: 1px solid var(--border); }
.cal-grid .dow { text-align: center; padding: 8px 0; font-size: 12px; color: var(--text-sub); background: #f4f9fd; min-width: 0; }
.cal-cells { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-cell {
  min-height: 84px; min-width: 0; border-right: 1px solid #e8f0f7; border-bottom: 1px solid #e8f0f7; padding: 5px 6px; cursor: pointer;
}
.cal-cell:hover { background: #f5fafd; }
.cal-cell.other { background: #f7fafd; color: #bcd6e6; }
.cal-cell.today { background: #dcf0fd; }
.cal-cell .dnum { font-size: 12px; font-weight: 600; }
.cal-cell .ev { font-size: 11px; background: var(--primary-soft); color: var(--primary-dark); border-radius: 4px; padding: 1px 5px; margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-cell .ev.mine { background: #e9f7f5; color: #0f766e; }
/* 需求 11：日历格改为「小圆点」表达「有日程」；点圆点进只读详情（见 schedule.js） */
.cal-dots { display: flex; align-items: center; gap: 3px; margin-top: 5px; min-height: 8px; cursor: pointer; flex-wrap: wrap; }
.cal-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--primary); flex: 0 0 auto; }
.cal-dot.mine { background: #0f766e; }
.cal-dot-more { font-size: 10px; color: var(--text-sub); line-height: 1; }
.cal-cell.has-ev { cursor: pointer; }
.cal-dots:hover .cal-dot { box-shadow: 0 0 0 2px rgba(2,132,199,.25); }
/* ★ 需求 8（第八十七轮）：日历格的内容形态按**屏宽**分流 ——
   电脑端格子够宽，直接在日历上显示**日程简介**（标题，超出省略号）；
   只有手机等窄屏（可用宽度有限）才退回「小圆点」。
   默认按窄屏渲染（简介隐藏、圆点显示），在桌面断点（≥768px）反转。 */
.cal-evs { display: none; margin-top: 4px; }
.cal-ev {
  font-size: 11px; line-height: 1.4; color: var(--primary-dark);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;
}
.cal-ev.mine { color: #0f766e; font-weight: 600; }
.cal-ev-more { font-size: 10px; color: var(--text-sub); line-height: 1.2; }
@media (min-width: 768px) {
  .cal-dots { display: none; }
  .cal-evs { display: block; }
}
/* 日程只读详情弹窗的行式键值对（与全站表单观感一致） */
.sed-kv { display: grid; grid-template-columns: 76px 1fr; gap: 8px 12px; font-size: 13.5px; }
.sed-kv .k { color: var(--text-sub); }
.sed-kv .v { color: var(--text); word-break: break-word; }
.sed-ct { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 10px; }
.sed-ct .hd { font-weight: 600; margin-bottom: 6px; font-size: 13.5px; }
.sed-ct .bd { font-size: 13.5px; color: var(--text); line-height: 1.7; }

/* ★ 需求 20（第九十轮）：周视图这两条是**同一缺陷的另一处实现**（`1fr` 的 min-content 撑宽），
   与 `.cal-grid` / `.cal-cells` 一起改成 `minmax(0, 1fr)` —— 同一能力多处实现必须同源，
   否则改了月视图、周视图照旧被长标题撑破。 */
.week-wrap { display: grid; grid-template-columns: 60px repeat(7, minmax(0, 1fr)); border-bottom: 1px solid var(--border); background: #f4f9fd; }
.week-wrap > div { padding: 8px 6px; text-align: center; font-size: 12px; }
.week-row { display: grid; grid-template-columns: 60px repeat(7, minmax(0, 1fr)); border-bottom: 1px solid #e8f0f7; }
.week-row > div { min-height: 46px; border-right: 1px solid #e8f0f7; padding: 4px; font-size: 11px; }

/* 文件选择 */
.file-input { display: flex; gap: 8px; align-items: center; }
.file-input .fname { flex: 1; border: 1px dashed var(--border); border-radius: 8px; padding: 7px 11px; font-size: 13px; color: var(--text-sub); }

/* 网盘 */
.breadcrumb { display: flex; gap: 6px; align-items: center; font-size: 13px; padding: 10px 16px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.breadcrumb a { color: var(--primary); cursor: pointer; }
.drive-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; padding: 16px; }
.drive-node {
  border: 1px solid var(--border); border-radius: 10px; padding: 12px; cursor: pointer; background: #fff; transition: .15s; position: relative;
}
.drive-node:hover { border-color: var(--primary); box-shadow: var(--shadow); }
.drive-node .dn-ico { font-size: 26px; }
.drive-node .dn-name { font-size: 13px; margin-top: 6px; word-break: break-all; }
.drive-node .dn-meta { font-size: 11.5px; color: var(--text-sub); margin-top: 3px; }

/* 考试 */
.q-item { border: 1px solid var(--border); border-radius: 10px; padding: 14px; margin-bottom: 12px; background: #fff; }
.q-title { font-weight: 600; margin-bottom: 8px; }
.q-opts label { display: block; padding: 6px 10px; border: 1px solid var(--border); border-radius: 8px; margin-bottom: 6px; cursor: pointer; }
.q-opts label:hover { border-color: var(--primary); background: #f5fafd; }
.q-opts input { width: auto; margin-right: 8px; }

/* 附件 */
.att-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.att-chip { border: 1px solid var(--border); border-radius: 6px; padding: 3px 9px; font-size: 12px; background: #f4f9fd; }
/* ★ 需求 9（第九十九轮）：附件 chip 里的**文件类型图标**。
   来源 freeicon「文件类型 · 扁平」图标集（个人及商业用途免费，无需署名），
   资源在 `public/js/icons/*.png`（256px），这里压到 16px 显示。
   `vertical-align:-3px` 让图片与 12px 的文字基线对齐（不写会整体抬高一行）；
   `flex:0 0 auto` 防止它被 `.att-list{flex-wrap}` 或长文件名挤扁。 */
.att-ico { width: 1em; height: 1em; vertical-align: -0.2em; margin-right: 3px; flex: 0 0 auto; border-radius: 2px; }
/* 可下载附件（文章详情）：chip 变成链接，hover 时给出明确的「可点」反馈 */
a.att-chip.att-dl { color: var(--primary); text-decoration: none; cursor: pointer; }
a.att-chip.att-dl:hover { border-color: var(--primary); background: var(--primary-soft); }
a.att-chip.att-dl .att-dl-tip { font-weight: 600; margin-left: 2px; }

/* ★ 第九十一轮需求 9：报销单附件的「预览 + 下载」成对 chip -------------------------------
   左边是本体的「点击预览」，右边紧跟一个小圆钮「⬇」专职下载。
   ⚠️ 刻意**不复用** `.att-dl` / `.att-preview` 两个类名 —— 它们是 `UI.bindAtts()` 的通用钩子
      （认 `data-path`），本处的 chip 走板块内接口、用 `data-id`+`data-idx`，
     同名会被通用绑定抢走 onclick 且拿到 `undefined`，是**静默失效**。详见 views/expense.js。 */
.att-pair { display: inline-flex; align-items: stretch; gap: 2px; vertical-align: middle; }
.att-pair > a.att-chip:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
a.att-chip.att-prev { color: var(--primary); text-decoration: none; cursor: pointer; }
a.att-chip.att-prev:hover { border-color: var(--primary); background: var(--primary-soft); }
a.att-chip.att-dlbtn { margin-left: -1px; padding: 3px 6px; cursor: pointer; color: var(--primary);
  border-top-left-radius: 0; border-bottom-left-radius: 0; text-decoration: none; }
a.att-chip.att-dlbtn:hover { border-color: var(--primary); background: var(--primary-soft); }
/* ★ 第五十五轮需求 36：报销单详情里发票 chip 的「✕」删除（与 ⬇ 下载同排、同圆角接缝）。
   用 `--danger` 而不是 `--primary`：这是**移除**动作，颜色要与「下载」区分开，
   免得用户把「删掉这张票」点成「下载这张票」。 */
a.att-chip.att-delbtn { margin-left: -1px; padding: 3px 7px; cursor: pointer; color: var(--danger);
  border-top-left-radius: 0; border-bottom-left-radius: 0; text-decoration: none; }
a.att-chip.att-delbtn:hover { border-color: var(--danger); background: #fdecec; }

/* 「账号 → 档案」同步产生的待人工审核副本：醒目但不刺眼 */
.sync-warn { margin: 14px 0; padding: 10px 12px; border-left: 3px solid #e0a800; background: #fff9e6;
  border-radius: 4px; }
.sync-warn > b { color: #b07800; }

.post-body { font-size: 14px; line-height: 1.85; }
.post-body img { max-width: 100%; }
.comment-item { padding: 10px 0; border-bottom: 1px dashed #dce8f2; }
.comment-item:last-child { border-bottom: none; }

/* ================================================================
   投票（一份投票 · 多个项目 · 文本 / 图片 / 视频作答 · 视频预览）
   ================================================================ */

.vote-card { border-bottom: 1px solid var(--border); }
.vote-card:last-child { border-bottom: none; }

.vote-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.vote-title { font-size: 14.5px; }
.vote-time { white-space: nowrap; }
.vote-actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* 分享链接直达时高亮目标投票 */
.vote-card.vote-shared {
  background: var(--accent-soft, #eaf3fd);
  box-shadow: inset 3px 0 0 var(--primary, #2f7fd1);
  animation: voteSharedPulse 1.6s ease-out 1;
}
@keyframes voteSharedPulse {
  0% { background: var(--primary-soft, #d9eaff); }
  100% { background: var(--accent-soft, #eaf3fd); }
}

/* ---- 选项 ---- */
.vote-options { margin-top: 11px; }
.vote-opt {
  display: grid; grid-template-columns: minmax(0, 1fr) 140px 96px;
  align-items: center; gap: 10px; margin-bottom: 8px;
}
.vote-opt > label {
  display: flex; align-items: center; gap: 8px; cursor: pointer; min-width: 0;
}
.vote-opt > label input { flex: none; }
.vo-text { overflow: hidden; text-overflow: ellipsis; }
.vo-bar {
  display: block; height: 10px; border-radius: 6px; overflow: hidden;
  background: #eaf2fa;
}
.vo-bar > span { display: block; height: 100%; background: var(--primary); transition: width .25s; }
.vo-num { text-align: right; white-space: nowrap; }

/* ---- 自定义项目 ---- */
.vote-fields { margin-top: 12px; display: flex; flex-direction: column; gap: 12px; }
.vote-field {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 11px 13px; background: #fbfdff;
}
.vf-head { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-bottom: 8px; }
.vf-label { font-weight: 600; font-size: 13.5px; }
.vf-text { width: 100%; }
textarea.vf-text { min-height: 70px; }

/* ---- 媒体缩略图 ---- */
.vf-media, .vf-all-list { display: flex; flex-wrap: wrap; gap: 9px; align-items: flex-start; }
.vf-chip {
  position: relative; border: 1px solid var(--border); border-radius: 8px;
  padding: 5px; background: #fff; max-width: 148px;
}
.vf-thumb {
  display: block; width: 128px; height: 82px; object-fit: cover;
  border-radius: 6px; background: #0f2b45; border: none; cursor: pointer; padding: 0;
}
button.vf-thumb { position: relative; }
.vf-play {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 22px; text-shadow: 0 2px 8px rgba(0, 0, 0, .5);
}
.vf-chip .vf-cname {
  display: block; font-size: 11.5px; color: var(--text-sub); margin-top: 4px;
  max-width: 128px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.vf-rm {
  position: absolute; top: 2px; right: 2px; width: 20px; height: 20px;
  border: none; border-radius: 50%; background: rgba(15, 43, 69, .72); color: #fff;
  font-size: 11px; line-height: 1; cursor: pointer; padding: 0;
}
.vf-rm:hover { background: var(--danger); }
.vf-thumb.inline {
  width: 76px; height: 50px; display: inline-block; vertical-align: middle; margin: 0 4px;
}

/* ---- 折叠面板：全部素材 / 最近作答 ---- */
.vf-all { margin-top: 9px; }
.vf-all > summary {
  cursor: pointer; font-size: 12.5px; color: var(--primary-dark);
  list-style: none; user-select: none;
}
.vf-all > summary::-webkit-details-marker { display: none; }
.vf-all > summary::before { content: '▸ '; }
.vf-all[open] > summary::before { content: '▾ '; }
.vf-all-list, .vf-recent { margin-top: 8px; }
.vf-recent-item {
  padding: 8px 0; border-bottom: 1px dashed #dce8f2; font-size: 13px;
}
.vf-recent-item:last-child { border-bottom: none; }

/* ---- 新建投票弹窗里的两个区块 ---- */
.vf-block {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 12px 13px; margin-bottom: 14px; background: #fbfdff;
}
.vf-block-head { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; margin-bottom: 10px; }
.vf-block-head b { font-size: 13.5px; }
.vf-field-row { margin-bottom: 8px; }
.vfr-main { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.vfr-main .vff-label { flex: 1 1 180px; min-width: 140px; }
.vfr-main .vff-type { flex: 0 0 116px; width: auto; }

/* ---- 题目块（问卷星式：一份投票多道题） ---- */
.vote-questions { display: flex; flex-direction: column; gap: 4px; margin-top: 12px; }
.vote-question { padding-top: 11px; border-top: 1px dashed var(--border); }
.vote-question:first-child { border-top: none; padding-top: 0; }
.vq-qhead { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 7px; }
.vq-qno { color: var(--primary); font-size: 13px; letter-spacing: .3px; }
.vq-qlabel { font-weight: 600; }
.vote-question .vote-options { margin-top: 0; }

/* ---- 发起投票弹窗：题目卡片 ---- */
.vq-card {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 11px 12px; margin-bottom: 10px; background: #fff;
}
.vq-chead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.vq-chead .vq-no { font-size: 13px; color: var(--primary); white-space: nowrap; }
.vq-chead .vq-type { width: auto; flex: 0 0 118px; }
.vq-card .vq-label { width: 100%; margin-bottom: 8px; }
.vq-optbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ---- 结果：按题统计 ---- */
.vr-stats { display: flex; flex-direction: column; gap: 12px; margin-bottom: 12px; }
.vr-stat { border: 1px solid var(--border); border-radius: 9px; padding: 10px 12px; background: #fbfdff; }

/* ---- 结果明细 ---- */
.vr-list { display: flex; flex-direction: column; gap: 10px; }
.vr-row { border: 1px solid var(--border); border-radius: 9px; padding: 10px 12px; }
.vr-head { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; margin-bottom: 5px; }
.vr-line { font-size: 13px; line-height: 1.9; display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }

/* ---- 媒体预览弹窗 ---- */
.media-viewer { text-align: center; }
.mv-video {
  width: 100%; max-height: 62vh; border-radius: 10px; background: #0f2b45;
  display: block; margin: 0 auto;
}
.mv-image { max-width: 100%; max-height: 62vh; border-radius: 10px; }
.mv-meta {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  flex-wrap: wrap; margin-top: 10px; font-size: 12.5px;
}
.mv-nav {
  display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 12px;
}

/* ================================================================
   移动端自适应
   断点体系：1100px（平板横屏）/ 900px（抽屉菜单 + 表格卡片化）
            640px（手机单列）/ 420px（小屏微调）
   ================================================================ */
.sidebar-close { display: none; margin-left: auto; }
.sidebar-mask { display: none; }

@media (max-width: 1100px) {
  .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .g4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rp-duty-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  /* ---- 侧边栏改为抽屉 ---- */
  .sidebar {
    position: fixed; z-index: 60; left: 0; top: 0;
    transform: translateX(-100%); transition: transform .22s ease;
    box-shadow: 6px 0 24px rgba(12, 58, 88, .18);
    width: 260px; flex-basis: 260px;
  }
  .sidebar.show { transform: none; }
  .sidebar-close { display: inline-flex; align-items: center; justify-content: center; }
  .sidebar-mask {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: rgba(12, 58, 88, .42); opacity: 0; pointer-events: none;
    transition: opacity .2s;
  }
  .sidebar-mask.show { opacity: 1; pointer-events: auto; }
  body.menu-open { overflow: hidden; }

  /* ---- 栅格降列 ---- */
  .g2, .g3, .g4 { grid-template-columns: minmax(0, 1fr); }
  .g2.keep-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 需求 44：主区 + 360px 定宽侧栏 ⇒ 窄屏一律降为单列（360px 在 375 视口里放不下） */
  .vset-grid { grid-template-columns: minmax(0, 1fr); }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }

  /* ---- 顶栏压缩 ---- */
  .topbar { height: 52px; padding: 0 12px; gap: 8px; }
  .crumb { font-size: 14px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .badge-level { display: none; }
  .user-chip { font-size: 12px; max-width: 116px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .content { padding: 12px; }

  /* ---- 卡片头允许换行，操作区下移 ---- */
  .card-head { flex-wrap: wrap; row-gap: 8px; padding: 12px 13px; }
  .card-head .sub { flex: 1 0 100%; white-space: normal; }
  .card-head .right { margin-left: 0; flex: 1 0 100%; flex-wrap: wrap; }
  /* ★ 需求 7（第五十五轮）：统一移动端「搜索框 / 条件筛选框」尺寸。
     症结：各视图用内联 `style="width:170px"` 写死宽度，input 被这条规则统一了、
     但 select（条件下拉）没有 → 同一行里 `<select>` 与 `<input>` 宽窄不一、看着乱。
     口径：移动端所有筛选行内的 input / select **等宽等高等字号**，一行两个（calc(50% - 4px)），
     拉满剩余宽度；`width:auto !important` 是为了压过视图里的内联宽度。 */
  .card-head .right > input,
  .card-head .right > select { flex: 1 1 calc(50% - 4px); width: auto !important; min-width: 0; }
  .card-head .right > input,
  .card-head .right > select,
  .filter-bar > input,
  .filter-bar > select { height: 34px; font-size: 16px; }

  /* ★★ 需求 44（第五十八轮续，2026-10-11 林总）：iOS 聚焦输入框时，若字号 <16px 会
     **自动放大整页**（用户得手动缩回去）—— 这是「手机端能不能用」的硬伤，不是观感问题。
     本条防护原先只写在 `@media (max-width: 640px)` 里，选择器是
       `input[type=text], input[type=password], input[type=number], input[type=date], …`
     两个漏洞让它在真机上大面积失效（真浏览器探针 M3 在 375 视口实测仍有 32 处）：
       ① **属性选择器匹配不到「没有 type 属性」的 input** —— `<input id="wbSearchInput">`
          浏览器按 text 处理，但 `input[type=text]` 选择器匹配不上（如工作台搜索框 14px）；
       ② `.sm-prompt-ta`（12.5px）这类 **class 规则优先级高于元素选择器**，又定义在本文件更后面
          ⇒ 照样压过 16px（如三处「智能汇总」的提示词输入域）。
     ⇒ 改为「全量排除法 + `!important`」，一次把每个**可输入**的控件兜住；
        排除 checkbox / radio / range / file / color / image / submit / reset / button 这类
        没有「字号」语义的控件。放在 ≤900 这一档（与「≤900px 即移动布局」的既定口径一致，
        顺带覆盖 iPhone 横屏 852px）。 */
  input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]):not([type=color]):not([type=image]):not([type=submit]):not([type=reset]):not([type=button]),
  select, textarea { font-size: 16px !important; }
  /* 按钮不参与伸缩，避免被压成小方块 */
  .card-head .right > .btn { flex: 0 0 auto; }
  .card-body { padding: 13px; }

  /* ---- 表格：整表横向可滑，避免窄屏被挤断 ---- */
  .tbl-wrap, .card-body > table.tbl, #ctList > table.tbl, .modal-body table.tbl {
    display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch;
  }
  table.tbl { font-size: 12.8px; }
  table.tbl th, table.tbl td { padding: 9px 10px; white-space: nowrap; }
  table.tbl th.wrap, table.tbl td.wrap { white-space: normal; }

  /* ---- 触摸热区：手指点得准 ----
     ★★ 需求 44（第五十八轮续，2026-10-11 林总）：真浏览器在 375/393/412/480 四个手机上量得
     **52 处**可点元素高度不足（`.btn.sm` 26px、工作台检索分段按钮 24px、`.btn.link`
     「放弃修改」17px）—— 鼠标点得中，手指点不中。
     口径：移动端一律保底 **34px 可点高度**（Apple HIG 建议 44、Material 48；本项目是
     桌面为主的管理系统，34 是「点得准」与「不把密集列表撑肥」的折中），
     配 `inline-flex` 居中，使 `.btn.link`（`padding:0`）这类纯文字按钮也居中不顶头。
     ⚠️ 只写在这一档里 ⇒ **桌面端观感一个字不变**。 */
  .btn { min-height: 34px; display: inline-flex; align-items: center; justify-content: center; }
  .btn.sm { min-height: 34px; }
  .wb-search-tabs button { min-height: 32px; padding: 5px 12px; }

  /* 需求 44：备份页的「备份目录 / 数据文件」是 **Windows 长路径**（整串没有空格），
     默认折行规则找不到断点 ⇒ `<b>` 撑到 401px 把卡片顶破、右侧被 .content 裁掉。
     ⚠️ 只写 `word-break` 是不够的 —— 本文件另有一条 `.bk-meta{white-space:nowrap}`
        （给「自动备份计划状态条」用的，见上文），`nowrap` 会让任何断词规则失效。
        两条一起覆盖才真正生效（首版只改 word-break，探针复验仍报 4 处，就是这个原因）。
     只在窄屏生效，桌面端照旧。 */
  .bk-meta, .bk-meta b { white-space: normal; word-break: break-all; overflow-wrap: anywhere; }

  /* 需求 44：工作台卡片的拖拽手柄（22×22）与右上角「移除卡片」按钮（20×20）
     在手机上都点不准 ⇒ 抬到 32px 触达。`.wb-x` 是绝对定位，同步把内缩改小，
     保证 32×32 完整落在卡片内、不越出圆角边界。 */
  .wb-grip { width: 32px; height: 32px; margin-right: 0; }
  .wb-x { width: 32px; height: 32px; top: 4px; right: 4px; }

  /* ---- 弹窗：接近全屏，底部按钮更好点 ---- */
  .modal-mask { padding: 3vh 10px; align-items: flex-start; }
  .modal, .modal.lg, .modal.xl { max-width: 100%; width: 100%; }
  .modal-body { max-height: 72vh; padding: 14px; }
  .modal-head { padding: 12px 14px; }
  .modal-foot { padding: 11px 14px; flex-wrap: wrap; }
  .modal-foot .btn { flex: 1 1 auto; }

  /* ---- 表单栅格单列 ---- */
  .form .row { flex-wrap: wrap; }
  .form .row > .field { flex: 1 1 100% !important; }

  /* ---- 编辑器工具栏可横向滚动 ---- */
  .editor-toolbar { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; padding: 6px 8px; }
  .editor-toolbar::-webkit-scrollbar { height: 0; }
  .editor-toolbar button { flex: none; }
  .editor-area { min-height: 140px; }

  /* ---- 日历 / 周视图 ---- */
  .cal-cell { min-height: 62px; padding: 4px; }
  .cal-cell .ev { font-size: 10px; }
  .week-wrap, .week-row { grid-template-columns: 44px repeat(7, minmax(0, 1fr)); }
  .week-wrap > div, .week-row > div { padding: 6px 2px; font-size: 11px; }

  /* ---- 网盘 ---- */
  .drive-grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 10px; padding: 12px; }

  /* ---- 工作台 ---- */
  .wb-hero { padding: 12px 14px; gap: 12px; }
  .wb-hero .wb-time { font-size: 22px; }
  .wb-hero .wb-hello { margin-left: 0; text-align: left; flex: 1 0 100%; }
  .wb-list { min-height: 0; }

  /* ---- 列表项 ---- */
  .list-item { padding: 11px 13px; flex-wrap: wrap; }
  .breadcrumb { padding: 9px 13px; }
  .logo-preview { width: 132px; height: 132px; }

  /* ---- 投票：选项进度条挪到第二行，避免窄屏三列挤爆 ---- */
  .vote-opt { grid-template-columns: minmax(0, 1fr) auto; gap: 6px 10px; }
  .vote-opt .vo-bar { grid-column: 1 / -1; }
  .vote-opt .vo-num { grid-column: 2; grid-row: 1; }
  .vote-head .vote-time { flex: 1 0 100%; white-space: normal; }
  .vote-head .vote-actions { flex: 0 0 auto; }
  .vf-thumb { width: 104px; height: 68px; }
  .vf-chip { max-width: 124px; }
  .vf-chip .vf-cname { max-width: 104px; }
  .vfr-main .vff-type { flex: 1 1 100%; }
  .mv-video { max-height: 46vh; }
  .mv-image { max-height: 46vh; }
}

@media (max-width: 640px) {
  body { font-size: 13.5px; }
  .content { padding: 10px; }
  /* 「iOS 聚焦输入框时 <16px 会放大整页」的防护**已上移到 ≤900 那一档**
     （需求 44，覆盖面更全；此处不再重复一份，避免两处实现口径分叉）。 */
  .card { border-radius: 10px; }
  .card + .card { margin-top: 10px; }
  .stat { padding: 11px 13px; }
  .stat .value { font-size: 22px; }
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .rp-duty-row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .wb-hero { flex-direction: column; align-items: flex-start; }
  /* 需求 9：窄屏下 hero 已是纵向，右侧对齐会显得「飘」⇒ 与上方时间/问候语统一左对齐 */
  .wb-layout { align-items: flex-start; }
  .wb-changelog-line { justify-content: flex-start; }
  .wb-layout-btns { justify-content: flex-start; }
  .login-card { padding: 24px 20px 20px; border-radius: 12px; }
  /* 第一百零二轮：窄屏把验证码图缩窄，保证「输入框 + 图 + 更换」仍在同一行不换行 */
  .login-card .captcha-img { width: 96px; height: 40px; }
  .login-card .captcha-refresh { padding: 0 9px; font-size: 12px; }
  .login-brand h1 { font-size: 17px; }
  .login-ver { font-size: 13px; }      /* 需求 36：窄屏同步「小两个号」（17 → 13） */
  .login-logo { width: 40px; height: 40px; }
  .login-logo.has-img { width: 44px; height: 44px; }
  /* 手机端按钮统一放大触摸区 */
  .btn { padding: 8px 14px; }
  .btn.sm { padding: 5px 11px; }
  .icon-btn { width: 36px; height: 36px; }
  .modal-foot { gap: 6px; }
  .tag { font-size: 11.5px; }
}

@media (max-width: 420px) {
  .stats { grid-template-columns: minmax(0, 1fr); }
  .user-chip { display: none; }
  .topbar-manual { font-size: 12px; }
  .drive-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cal-cell { min-height: 52px; }
  .cal-grid .dow { font-size: 11px; }
}

/* 触摸设备：去掉 hover 造成的粘滞高亮 */
@media (hover: none) {
  .menu-item:hover, .btn:hover, .drive-node:hover, .q-opts label:hover { background: inherit; }
  .btn:hover { border-color: var(--border); }
  .btn.primary:hover { background: var(--primary); border-color: var(--primary); }
  table.tbl tbody tr:hover { background: transparent; }
}

/* ============================ 数据对比（超级工具） ============================ */
.cm-slots { align-items: start; }
.cm-slot {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px;
  background: var(--bg);
  min-width: 0;
}
.cm-slot-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.cm-badge {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 500;
  background: var(--primary-soft); color: var(--primary-dark);
}
.cm-badge.alt { background: #faeeda; color: #854f0b; }
.cm-meta { margin-top: 8px; min-width: 0; }
.cm-meta .cm-sheetsel { margin-left: 4px; }
.cm-cols { margin-top: 10px; min-width: 0; }
.cm-pick { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.cm-pick .cm-colsel { flex: 1; min-width: 0; }
.cm-sug { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.cm-sugbtn {
  border: 1px solid var(--border); background: var(--color-background-primary, #fff);
  border-radius: 999px; padding: 2px 10px; font-size: 12px; cursor: pointer;
  color: var(--primary-dark);
}
.cm-sugbtn:hover { border-color: var(--primary); background: var(--primary-soft); }
.cm-tblwrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 10px; background: #fff; }
table.tbl-sm { font-size: 12.5px; }
table.tbl-sm th, table.tbl-sm td { padding: 5px 8px; white-space: nowrap; }
.cm-sum { display: flex; gap: 10px; flex-wrap: wrap; }
.cm-sumitem {
  flex: 1 1 120px; min-width: 110px;
  border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px; background: var(--bg);
  display: flex; flex-direction: column; gap: 2px;
}
.cm-sumv { font-size: 20px; font-weight: 500; line-height: 1.2; }
.cm-sumk { font-size: 12px; color: var(--text-sub); }
.cm-sumitem.danger { border-color: #f0c8c8; background: var(--danger-soft); }
.cm-sumitem.danger .cm-sumv { color: var(--danger); }
.cm-sumitem.ok { border-color: #bfe5df; background: #e9f7f5; }
.cm-sumitem.ok .cm-sumv { color: #0f766e; }
.cm-cmpinfo { margin-top: 10px; word-break: break-all; }
.cm-arrow { margin: 0 6px; color: var(--primary); }
.cm-warn {
  margin-top: 10px; padding: 8px 12px; border-radius: 10px;
  background: #faeeda; color: #854f0b; font-size: 12.5px;
}
/* ★ 需求 8（第一百零五轮，2026-10-13 林总）：多条件对比 ——
   ① 两侧比对列改**多选**（按住 Ctrl/⌘ 或 Shift 多选）；
   ② 条件配对提示条（第 1 个对第 1 个…）；
   ③ 历史对比卡片的「按时间批量删除」工具条（字段成组，与 .dl-filters 同款口径）。 */
.cm-colsel[multiple] { min-height: 96px; padding: 4px; }
.cm-colsel[multiple] option { padding: 3px 6px; }
.cm-pairs {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: 12px; padding: 8px 12px;
  background: #f2f7fb; border: 1px solid var(--border); border-radius: var(--radius);
}
.cm-pairchip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 999px; font-size: 12.5px;
  background: var(--primary-soft); color: var(--primary);
}
.cm-pairchip .cm-pairno { font-weight: 600; }
.cm-pairmode { font-size: 12.5px; color: var(--text-sub); }
.cm-purge {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 10px 12px; margin: 0 0 12px;
  background: #fdf3f3; border: 1px solid #f0d5d5; border-radius: var(--radius);
}
.cm-purge input[type="date"] {
  height: 30px; border: 1px solid var(--border); border-radius: 6px;
  padding: 0 8px; font: inherit; font-size: 13px; color: inherit; background: #fff;
}

@media (max-width: 900px) {
  .cm-sumitem { flex: 1 1 calc(50% - 6px); }
  .cm-pick { flex-direction: column; align-items: stretch; }
}
@media (max-width: 640px) {
  .cm-sumitem { flex: 1 1 100%; }
}

/* ============================ 考勤管理 ============================ */
.at-sum { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 14px; }
.at-sumitem { flex: 1 1 130px; background: var(--panel-2, #f6fafd); border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; }
.at-sumk { font-size: 12px; color: var(--text-sub, #7b8a99); margin-bottom: 4px; }
.at-sumv { font-size: 20px; font-weight: 700; color: var(--accent, #2b7fd4); }
.at-chip { display: inline-flex; align-items: center; gap: 3px; font-size: 11.5px; line-height: 1; padding: 3px 7px; margin: 2px 4px 2px 0; background: #eaf4fd; color: #2b7fd4; border: 1px solid #cfe6f8; border-radius: 999px; white-space: nowrap; }
.at-chip b { font-weight: 700; }
.at-detail { display: flex; flex-direction: column; gap: 10px; }
.at-row { display: flex; gap: 12px; align-items: flex-start; }
.at-k { flex: 0 0 84px; color: var(--text-sub, #7b8a99); font-size: 13px; padding-top: 2px; }
.at-v { flex: 1; font-size: 13.4px; word-break: break-word; }
.at-days { display: flex; flex-wrap: wrap; gap: 5px; }
.at-day { font-size: 11.5px; padding: 2px 6px; background: #f2f7fb; border: 1px solid var(--border); border-radius: 6px; color: var(--text); }
/* 考勤符号说明：列表与明细下方都复用这一块，做「符号 → 含义」的文字解释 */
.at-legend { margin-top: 12px; padding: 10px 12px; background: var(--panel-2, #f6fafd); border: 1px solid var(--border); border-radius: 10px; }
.at-legend-title { display: block; font-size: 12px; color: var(--text-sub, #7b8a99); margin-bottom: 8px; }
.at-legend-items { display: flex; flex-wrap: wrap; gap: 6px 16px; }
.at-lg { display: inline-flex; align-items: baseline; gap: 5px; font-size: 12.5px; color: var(--text); }
.at-lg b { display: inline-block; min-width: 22px; text-align: center; padding: 2px 6px; background: #fff; border: 1px solid var(--border); border-radius: 6px; color: var(--accent, #2b7fd4); font-weight: 700; font-size: 12.5px; }
.at-detail .at-legend { margin-top: 4px; }

/* 考勤 · 人工录入表单（需求 10）：各类考勤情况按次数填写，一行多列 */
.at-form-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px 12px; margin-top: 4px; }
.at-form-grid .at-fitem { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; color: var(--text-sub); }
.at-form-grid .at-fitem input { width: 100%; }
/* 考勤 · 导入整单失败时的逐条原因 */
.at-imperr { margin-top: 10px; border: 1px solid var(--danger, #d9534f); border-radius: 10px; padding: 10px 12px; background: var(--danger-soft, #fdf1f1); max-height: 320px; overflow: auto; }
.at-imperr-h { font-size: 13px; font-weight: 600; color: var(--danger, #d9534f); margin-bottom: 8px; line-height: 1.6; }
.at-imperr table.tbl { background: #fff; }
.at-imperr td { font-size: 12.5px; vertical-align: top; }
@media (max-width: 640px) { .at-k { flex: 0 0 64px; } .at-sumitem { flex: 1 1 45%; } }

/* ============================ 通讯录（创赛之家） ============================ */
.dc-group { border-bottom: 1px solid var(--border); }
.dc-group:last-child { border-bottom: 0; }
.dc-group-head { display: flex; align-items: center; gap: 8px; padding: 10px 14px; cursor: pointer; user-select: none; }
.dc-group-head:hover { background: var(--panel-2, #f6fafd); }
.dc-caret { color: var(--text-sub, #7b8a99); font-size: 12px; width: 12px; }
.dc-members { padding: 4px 14px 14px; }
.dc-group.collapsed .dc-members { display: none; }
.dc-card { border: 1px solid var(--border); border-radius: 10px; padding: 12px; background: var(--panel, #fff); }
.dc-card.off { opacity: .62; }
.dc-head { display: flex; align-items: center; gap: 10px; }
.dc-avatar { flex: 0 0 38px; width: 38px; height: 38px; border-radius: 50%; background: var(--accent-soft, #eaf4fd); color: var(--accent, #2b7fd4); font-size: 17px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.dc-who { min-width: 0; }
.dc-name { font-size: 15px; font-weight: 700; color: var(--text); display: flex; align-items: center; gap: 6px; }
.dc-pos { font-size: 12.5px; color: var(--text-sub, #7b8a99); margin-top: 2px; word-break: break-all; }
.dc-tags { margin: 8px 0 6px; }
.dc-lines { display: flex; flex-direction: column; gap: 3px; }
.dc-line { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.dc-ic { flex: 0 0 16px; text-align: center; font-size: 12px; }
.dc-label { flex: 0 0 30px; color: var(--text-sub, #7b8a99); font-size: 12px; }
.dc-val { flex: 1; color: var(--text); word-break: break-all; }
.dc-copyall { flex: 0 0 auto; margin-left: auto; opacity: .62; white-space: nowrap; }
.dc-card { cursor: pointer; }
.dc-card:hover .dc-copyall { opacity: 1; }
@media (max-width: 900px) { .dc-members { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) { .dc-members { grid-template-columns: minmax(0, 1fr); } }

/* 工作台卡片列表被截断时的提示行（如「我的待办」「我的协作」默认只显示 5 条） */
.wb-more { padding: 8px 14px 4px; border-top: 1px dashed var(--border); margin-top: 2px; }

/* ==================== 文档盖章：预览内拖拽定位 ==================== */
/* 布局：左侧 PDF 页面（印章绝对定位叠在上面），右侧切分示意 / 定位说明 */
.stg-stage { margin-top: 14px; }
.stg-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 10px; margin-bottom: 10px;
  background: #f7fbfe; border: 1px solid var(--border); border-radius: 10px;
}
.stg-bar .stg-grp { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.stg-bar .stg-sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; }
.stg-pager { font-size: 12.5px; color: var(--text-sub); min-width: 84px; text-align: center; }
.stg-num { width: 62px; padding: 3px 6px; font-size: 12.5px; border: 1px solid var(--border); background: #fff; color: var(--text); }
.stg-range { width: 150px; padding: 3px 6px; font-size: 12.5px; border: 1px solid var(--border); background: #fff; color: var(--text); }

.stg-body { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.stg-pagewrap {
  position: relative; display: inline-block; line-height: 0;
  background: #fff; border: 1px solid var(--border); border-radius: 6px;
  box-shadow: var(--shadow); padding: 0;
}
.stg-pagecv { display: block; border-radius: 6px; }
/* 印章层：普通章为整章；骑缝章只截取本页那一片（外层 overflow:hidden 做裁剪） */
.stg-seal {
  position: absolute; left: 0; top: 0; cursor: move;
  outline: 1px dashed rgba(2, 132, 199, .55); outline-offset: 1px;
  touch-action: none; user-select: none;
}
.stg-seal::after {
  content: ''; position: absolute; left: 0; top: 0; right: 0; bottom: 0;
  border: 1px solid rgba(2, 132, 199, .35); border-radius: 2px; pointer-events: none;
}
.stg-seal.is-drag { outline-color: var(--primary); box-shadow: 0 0 0 3px rgba(2, 132, 199, .16); }
.stg-slice { position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: hidden; }
/* max-width:none 必须显式声明：全局 `img { max-width:100% }` 会把骑缝章的印章图
   压缩到「切片宽度」，导致偏移裁剪失效、每页都显示整章 */
.stg-img { position: absolute; left: 0; top: 0; display: block; opacity: .9; pointer-events: none; max-width: none; }
.stg-hs {
  position: absolute; right: -5px; bottom: -5px; width: 11px; height: 11px;
  background: var(--primary); border: 2px solid #fff; border-radius: 3px;
  cursor: nwse-resize; box-shadow: 0 1px 2px rgba(0, 0, 0, .2); touch-action: none;
}
.stg-side { flex: 1 1 240px; min-width: 220px; max-width: 320px; }
.stg-sidetitle { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 6px; }
.stg-sliceview {
  display: block; background: #fff; border: 1px solid var(--border);
  border-radius: 6px; box-shadow: var(--shadow); margin-bottom: 8px;
}
.stg-sidenote { line-height: 1.7; }
.stg-done { background: #f0fdf4; border-color: #bbf7d0; }
.text-ok { color: var(--success); }

@media (max-width: 900px) {
  .stg-side { flex: 1 1 100%; max-width: none; }
  .stg-pagewrap { max-width: 100%; }
}

/* ==================== 历史文档（超级工具） ==================== */
.dh-stats { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.dh-stat {
  flex: 1 1 110px; min-width: 100px; padding: 10px 12px; background: #f7fbfe;
  border: 1px solid var(--border); border-radius: 10px; text-align: center;
}
.dh-stat b { display: block; font-size: 18px; color: var(--primary-dark); line-height: 1.3; }
.dh-stat span { font-size: 12px; color: var(--text-sub); }
.dh-filter { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dh-date { width: 132px; padding: 3px 6px; font-size: 12.5px; border: 1px solid var(--border); background: #fff; color: var(--text); }

/* ★ 需求 19（第九十轮）：「历史文档」页面的**起止时间筛选框排版有问题** —— 修正。
   改前的四宗罪（都在同一排里，所以是「看着乱」而不是「坏了」）：
     ① `.dh-date` 没有 `box-sizing:border-box` ⇒ 132px 之外又加了 1px 边框 + 6px 内边距，
        实际占位比同排的 `<select>` / `<input type="text">` 宽出一圈，右边缘参差；
     ② 没有 `height` ⇒ 日期框比同排按钮/下拉**矮 2~4px**，基线对不齐；
     ③ `width` 写死在 `.dh-date` 上 ⇒ 窄屏时它不肯收缩，把同排其它控件挤到下一行，
        出现「起 …（换行）… 止」这种把一句提示拆两行的排版；
     ④ `<label>起 <input/></label>` 里的「起」是**文本节点**，宽度随字体变，
        与下面「止」那一行对不齐。
   统一口径（与项目里 `.rq-filters` 同款做法）：
     · 同排控件**等高**（32px）+ `box-sizing:border-box`，宽度由内容与 flex 决定；
     · 「起 / 止」做成 `inline-flex` 且 `white-space:nowrap` 的标签，字与框永远同一行；
     · 手机端（≤640px）整排**等宽铺满**、一项一行，不再出现半截换行。 */
.dh-filter label { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; line-height: 1; margin: 0; }
.dh-filter select, .dh-filter input[type=text], .dh-filter input[type=date], .dh-filter .btn { height: 32px; box-sizing: border-box; }
.dh-date { width: 132px; padding: 0 6px; border-radius: 6px; }
@media (max-width: 640px) {
  /* `#dhTool` / `#dhKeyword` 带行内 `style="width:…"`，要 `!important` 才压得住 */
  .dh-filter > select, .dh-filter > input[type=text], .dh-filter > label { flex: 1 1 100%; width: 100% !important; }
  .dh-date { flex: 1 1 auto; width: auto !important; min-width: 0; }
}

.dh-tbl { width: 100%; }
.dh-tbl td { vertical-align: top; }
.dh-file { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 2px 0; }
.dh-file + .dh-file { border-top: 1px dashed var(--border); }
.dh-name { word-break: break-all; }

/* ==================== 文档合并（超级工具 · 第二十轮需求 12） ==================== */
/* 待合并列表：一行一个文件，「序号 + 缩略图 + 说明 + 上移/下移/删除」。 */
.mj-list { display: flex; flex-direction: column; gap: 8px; }
.mj-item {
  display: flex; align-items: center; gap: 12px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: 10px; background: #fff;
}
.mj-no {
  flex: 0 0 26px; width: 26px; height: 26px; border-radius: 50%;
  background: rgba(2, 132, 199, .12); color: var(--primary-dark);
  display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 600;
}
.mj-thumbbox {
  flex: 0 0 auto; width: 96px; min-height: 60px; display: flex;
  align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 8px; background: #fafcfe; overflow: hidden;
}
.mj-thumb { display: block; max-width: 96px; max-height: 128px; }
.mj-cv { width: auto; height: auto; }
.mj-meta { flex: 1 1 auto; min-width: 0; }
.mj-name { font-weight: 600; word-break: break-all; margin-bottom: 2px; }
.mj-ops { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* 窄屏换行收纳（桌面端不生效）—— 见「新增板块必做的自适应」惯例 */
@media (max-width: 640px) {
  .mj-item { flex-wrap: wrap; align-items: flex-start; }
  .mj-meta { flex: 1 1 100%; }
  .mj-ops { flex: 1 1 100%; }
}

/* ==================== 版本号（右上角 / 登录页） ==================== */
.app-version {
  font-size: 12px; color: var(--text-sub); white-space: nowrap;
  padding: 3px 9px; border-radius: 20px; background: #eef6fc; border: 1px solid var(--border);
}

/* ==================== 礼品管理 ==================== */
.gift-stats { display: flex; gap: 10px; flex-wrap: wrap; padding: 4px 16px 14px; }
.gift-stat {
  flex: 1 1 104px; min-width: 96px; padding: 9px 12px; background: #f7fbfe;
  border: 1px solid var(--border); border-radius: 10px; text-align: center;
}
.gift-stat-num { font-size: 19px; font-weight: 700; line-height: 1.25; color: var(--text); }
.gift-tip {
  margin: 0 16px 12px; padding: 9px 12px; font-size: 12.5px; line-height: 1.7;
  color: var(--text-sub); background: #f2f9fd; border: 1px solid var(--border);
  border-left: 3px solid var(--primary); border-radius: 6px;
}
.gift-tip b { color: var(--primary-dark); }

/* 列表缩略图：inline 尺寸固定，需屏蔽全局 img{max-width:100%} 的干扰 */
.gift-thumb {
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 6px; background: #f4f9fd;
  font-size: 18px; object-fit: cover; max-width: none; vertical-align: middle;
}
img.gift-thumb { overflow: hidden; }
.gift-thumb.lg { width: 96px; height: 96px; font-size: 34px; }
/* 多图（第一百二十轮）：封面缩略图容器 + 右上角张数角标 */
.gift-thumb-wrap { position: relative; display: inline-block; line-height: 0; }
.gift-thumb-n {
  position: absolute; right: -5px; bottom: -5px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 8px; background: var(--primary); color: #fff;
  font-size: 10.5px; line-height: 16px; text-align: center; font-weight: 600;
  box-shadow: 0 0 0 2px #fff; box-sizing: border-box;
}

.gift-detail { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.gift-detail-photo { flex: 0 0 132px; }
.gift-detail-photo img {
  width: 132px; height: 132px; object-fit: cover; border-radius: 10px;
  border: 1px solid var(--border); background: #f4f9fd;
  cursor: zoom-in;
}
/* 详情里「首图 + 其余小图」的竖排缩略图条：3 列网格，最多显示一行多一点，
   不用滚动条（照片再多也能一眼看到数量，需要细看就点开预览左右翻） */
.gift-photo-strip {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; margin-top: 4px;
}
.gift-photo-strip img {
  width: 100%; height: 40px; object-fit: cover; border-radius: 6px;
  border: 1px solid var(--border); background: #f4f9fd; cursor: zoom-in;
}
.gift-detail .grid { gap: 6px 16px; }

/* 多图上传字段（UI.imageListField）：缩略图网格 + 添加按钮 */
.imglist-field { display: block; }
.imglist-grid { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.imglist-item {
  position: relative; width: 78px; height: 78px; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border); background: #f4f9fd;
}
.imglist-item img { width: 100%; height: 100%; object-fit: cover; display: block; cursor: zoom-in; }
.imglist-del {
  position: absolute; top: 2px; right: 2px; width: 19px; height: 19px; padding: 0;
  border: 0; border-radius: 50%; background: rgba(0, 0, 0, .55); color: #fff;
  font-size: 13px; line-height: 19px; text-align: center; cursor: pointer;
}
.imglist-del:hover { background: rgba(200, 30, 30, .9); }
.imglist-cover {
  position: absolute; left: 0; bottom: 0; padding: 1px 5px;
  background: rgba(2, 132, 199, .88); color: #fff; font-size: 10.5px;
  border-radius: 0 6px 0 0; line-height: 1.5;
}
.imglist-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.imglist-empty {
  width: 100%; padding: 14px 0; text-align: center;
  border: 1px dashed var(--border); border-radius: 8px; background: #fafcfe;
}

/* ---------------- 图书管理：概览条 / 导入结果 / 位置管理 ---------------- */
.lib-stats {
  display: flex; flex-wrap: wrap; gap: 8px 22px;
  margin: 0 16px 12px; padding: 9px 12px;
  font-size: 12.5px; color: var(--text-sub);
  background: #f2f9fd; border: 1px solid var(--border);
  border-left: 3px solid var(--primary); border-radius: 6px;
}
.lib-stats b { color: var(--primary-dark); font-size: 13.5px; }
.text-ok { color: var(--success); }
.text-warn { color: var(--warn); }

.lib-imp-res {
  padding: 10px 12px; font-size: 13px; line-height: 1.9;
  border: 1px dashed var(--border); border-radius: 8px; background: #f8fbfe;
}
.lib-imp-res .text-sm { font-size: 12px; }

/* ---------------- 详情弹窗的「实时刷新」高亮（UI.liveModal） ----------------
 * 审批 / 流转成功后详情页会原地换新，这里给内容区一次短暂的高亮，
 * 让用户明确看到「状态已经变了」，而不是以为点了没生效。 */
@keyframes liveFlash {
  0%   { background-color: rgba(6, 182, 212, .16); }
  100% { background-color: transparent; }
}
.modal-body.live-flash { animation: liveFlash .85s ease-out 1; border-radius: 6px; }

/* ================================================================
   协作文档 · 内置表格（Excel 风格，见 public/js/sheet.js）
   ================================================================ */

/* ---- 工具栏 ---- */
.sh-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
  padding: 7px 8px; background: #f7fbfe;
  border: 1px solid var(--border); border-bottom: none; border-radius: 10px 10px 0 0;
}
.sh-b {
  min-width: 28px; height: 26px; padding: 0 7px; line-height: 1; cursor: pointer;
  font-size: 12.5px; color: var(--text); background: #fff;
  border: 1px solid var(--border); border-radius: 6px;
}
.sh-b:hover { border-color: var(--primary); color: var(--primary-dark); }
/* 按下态：与单元格当前样式联动（没有它，用户点完不知道到底生效没有） */
.sh-b.on { background: var(--primary); border-color: var(--primary); color: #fff; }
.sh-bb { font-weight: 700; }
.sh-bi { font-style: italic; }
.sh-bu { text-decoration: underline; }
.sh-bs { text-decoration: line-through; }
.sh-sep { width: 1px; height: 18px; margin: 0 3px; background: var(--border); }
.sh-sel {
  height: 26px; padding: 0 4px; font-size: 12.5px; background: #fff;
  border: 1px solid var(--border); border-radius: 6px;
}
.sh-cgroup {
  display: inline-flex; align-items: center; gap: 2px; height: 26px; padding: 0 5px;
  background: #fff; border: 1px solid var(--border); border-radius: 6px;
}
.sh-clabel { font-size: 12px; font-weight: 700; color: var(--text-sub); }
.sh-clabel-bg { font-size: 11px; }
.sh-cin { width: 16px; height: 16px; padding: 0; border: none; background: none; cursor: pointer; }
.sh-swatches { display: inline-flex; align-items: center; gap: 3px; }
.sh-sw {
  display: inline-block; width: 14px; height: 14px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 3px;
}

/* ---- fx 编辑栏：格里显示计算结果，这一栏显示公式原文 ---- */
.sh-fx {
  position: relative;   /* 需求 4b 的「常用公式」菜单要以它为定位基准 */
  display: flex; align-items: center; gap: 6px; padding: 5px 8px;
  background: #fff; border: 1px solid var(--border);
}
.sh-addr {
  min-width: 76px; padding: 2px 6px; text-align: center;
  font-family: Consolas, Menlo, monospace; font-size: 12.5px;
  color: var(--primary-dark); background: var(--primary-soft); border-radius: 5px;
}
.sh-fx-icon { font-size: 12.5px; font-weight: 700; font-style: italic; color: var(--text-sub); }
.sh-fx-in { flex: 1; height: 26px; padding: 0 8px; font-size: 13px; border: 1px solid var(--border); border-radius: 6px; }
.sh-fx-in.is-formula { color: var(--primary-dark); font-weight: 600; }

/* ---- 需求 4b：fx 常用公式菜单 ---- */
.sh-fx-btn {
  font: inherit; font-size: 12.5px; font-weight: 700; font-style: italic;
  color: var(--text-sub); background: #fff;
  border: 1px solid var(--border); border-radius: 5px; padding: 1px 6px; cursor: pointer;
}
.sh-fx-btn:hover { color: var(--primary-dark); border-color: var(--primary); background: var(--primary-soft); }
.sh-fxmenu {
  position: absolute; left: 0; top: calc(100% + 4px); z-index: 30;
  display: flex; flex-direction: column; gap: 1px;
  min-width: 230px; max-height: 264px; overflow: auto; padding: 4px;
  background: #fff; border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, .14);
}
.sh-fxmenu[hidden] { display: none; }
.sh-fx-item {
  display: flex; flex-direction: column; gap: 1px; align-items: flex-start;
  padding: 6px 9px; border: 0; border-radius: 6px;
  background: transparent; text-align: left; cursor: pointer; font: inherit; color: var(--text);
}
.sh-fx-item:hover { background: var(--primary-soft); }
.sh-fx-item b { font-size: 13px; }
.sh-fx-item b i { font-style: normal; font-weight: 500; font-size: 12px; color: var(--primary-dark); }
.sh-fx-item span { font-size: 12px; color: var(--text-sub); }
.sh-fx-tip { color: var(--primary-dark); font-weight: 600; }
.sh-bfn { font-weight: 600; }

/* ---- 需求 4a：协作文档标题「点击即改」 ---- */
.cd-title-edit {
  cursor: pointer; border-radius: 4px; padding: 1px 4px; margin: 0 -4px;
  border-bottom: 1px dashed transparent;
}
.cd-title-edit:hover { color: var(--primary-dark); border-bottom-color: var(--primary); background: var(--primary-soft); }
.cd-title-in {
  font: inherit; font-size: inherit; font-weight: inherit; color: var(--text);
  min-width: 180px; max-width: 46vw; padding: 1px 6px;
  border: 1px solid var(--primary); border-radius: 5px; background: #fff;
}

/* ---- 网格 ---- */
.sh-wrap {
  position: relative; overflow: auto; max-height: 62vh; outline: none;
  background: #fff; border: 1px solid var(--border); border-radius: 0 0 10px 10px;
}
.sh-tbl { border-collapse: separate; border-spacing: 0; table-layout: fixed; font-size: 13px; }
.sh-tbl th, .sh-tbl td { border-right: 1px solid #e8eef5; border-bottom: 1px solid #e8eef5; }
.sh-corner, .sh-ch, .sh-rh {
  position: relative; background: #f4f8fc; color: var(--text-sub);
  font-size: 12px; font-weight: 600; text-align: center;
  user-select: none; -webkit-user-select: none;
}
.sh-ch { height: 24px; cursor: pointer; }
.sh-rh { width: 46px; cursor: pointer; }
.sh-ch.on, .sh-rh.on { background: var(--primary-soft); color: var(--primary-dark); }
.sh-td {
  height: 100%; padding: 3px 6px; font-size: 13px; vertical-align: middle; cursor: cell;
  background: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* 选区：底色 + 内描边。只用底色的话，带填充色的单元格（inline background 优先级更高）
   完全看不出被选中；加一圈内描边两种情况下都能看清。 */
.sh-td.sel { background: rgba(2, 132, 199, .07); box-shadow: inset 0 0 0 1px rgba(2, 132, 199, .22); }
.sh-td.act { outline: 2px solid var(--primary); outline-offset: -2px; }
.sh-td.err { color: var(--danger); }
.sh-td.ro { cursor: default; }

/* 编辑态输入框：绝对定位浮在单元格上，**不能**放进 table 里 ——
   重绘 tbody 时会把它一起销毁，输入到一半就丢字 */
.sh-edit {
  position: absolute; z-index: 5; padding: 2px 6px; font-family: inherit; font-size: 13px;
  background: #fff; border: 2px solid var(--primary); border-radius: 0; outline: none;
}

/* 列宽 / 行高拖拽把手：压在表头右缘、行头下缘 */.sh-grip { position: absolute; z-index: 3; }
/* ★★ 第九十九轮需求 20：列把手**必须高到盖住整个表格**。
   林总原文：「鼠标移动到**列与列之间的竖线**拖动列宽，依然无法拖动」。
   先取证（真 Chromium，`scripts/_probe_grip20.js`）：
     · 表头（y∈[200,223]）竖线正中 → 命中 `span.sh-grip-c`，cursor=col-resize，拖 100→160px **可用**；
     · **数据区（y∈[224,382]）竖线正中 → 命中 `td.sh-td`，cursor=cell，拖不动**；
     · 把手实测高 **23px** —— 旧写法 `height:100%` 只等于表头那一行。
   而「列与列之间的竖线」在数据区同样是可见的一条线，用户自然会在那里拖 ⇒ 表现为「依然无法拖动」。
   改法：高度由 JS 在 `bindGrips()` 里按**整表高度**写成 px（`th` 是 `position:relative` 且
   `overflow:visible`，子元素能溢出到 tbody 之上；`z-index:3` 高于单元格）。
   `right:-5px; width:8px` 让把手**正中心**正好落在 1px 的列右边框上（旧值 -3px/7px 会偏左 1.5px）。 */
.sh-grip-c { top: 0; right: -5px; width: 8px; cursor: col-resize; }
.sh-grip-r { left: 0; bottom: -3px; width: 100%; height: 7px; cursor: row-resize; }
/* 悬停反馈：列把手自身保持透明，只画一条 2px 竖线 ——
   全高实心蓝带（旧写法）在数据区会盖住一列 8px 宽的内容，观感像「表格被划了一道」。 */
.sh-grip-c:hover {
  background: linear-gradient(to right,
    transparent calc(50% - 1px), rgba(2, 132, 199, .9) calc(50% - 1px),
    rgba(2, 132, 199, .9) calc(50% + 1px), transparent calc(50% + 1px));
}
.sh-grip-r:hover { background: rgba(2, 132, 199, .35); }

/* 需求 37（第九十轮）：拖动列头**移动整列**的三个状态样式。
   · sh-moving   —— 正在被拖的那一列（半透明，让用户知道「搬的是它」）
   · sh-drop-before / sh-drop-after —— 松手会插到哪：3px 竖线画在目标列的左缘 / 最后列的右缘
   ⚠️ 用 `box-shadow: inset` 而不是 `border-left` —— 加 border 会挤动 `table-layout:fixed`
      的列宽，拖动过程中整排列头会跟着抖。 */
.sh-ch.sh-moving { opacity: .45; }
.sh-ch.sh-drop-before { box-shadow: inset 3px 0 0 var(--primary); }
.sh-ch.sh-drop-after { box-shadow: inset -3px 0 0 var(--primary); }

/* ---- 状态栏 ---- */
.sh-status {
  display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 6px; padding: 6px 10px;
  font-size: 12.5px; color: var(--text-sub);
  background: #f7fbfe; border: 1px solid var(--border); border-radius: 8px;
}
.sh-status b { color: var(--primary-dark); }
.sh-ro-tip { margin-left: auto; color: var(--warn); }

/* ---- 工作表标签条（第二十二轮需求 4.2：多 sheet 切换与查看） ---- */
.sh-tabs {
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
  margin-top: 6px; padding: 5px 8px;
  background: #f7fbfe; border: 1px solid var(--border); border-radius: 8px;
}
.sh-tabs[hidden] { display: none; }
.sh-tab {
  height: 26px; padding: 0 12px; font-size: 12.5px; color: var(--text-sub);
  background: #fff; border: 1px solid var(--border); border-radius: 6px 6px 0 0;
  border-bottom: 2px solid transparent; cursor: pointer;
  max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sh-tab:hover { color: var(--primary-dark); border-color: var(--primary); }
.sh-tab.on {
  color: var(--primary-dark); font-weight: 600; background: var(--primary-soft);
  border-color: var(--primary); border-bottom-color: var(--primary-dark);
}
.sh-tab.editing { padding: 0 4px; display: inline-flex; align-items: center; }
.sh-tab-in {
  width: 130px; height: 22px; padding: 0 6px; font-size: 12.5px;
  border: 1px solid var(--primary); border-radius: 4px; outline: none;
}
.sh-tab-add, .sh-tab-del {
  height: 26px; padding: 0 10px; font-size: 12.5px; cursor: pointer;
  background: #fff; border: 1px dashed var(--border); border-radius: 6px; color: var(--text-sub);
}
.sh-tab-add:hover, .sh-tab-del:hover { color: var(--primary-dark); border-color: var(--primary); border-style: solid; }
.sh-tab-del:hover { color: var(--danger, #c0392b); border-color: var(--danger, #c0392b); }
.sh-tab-info { margin-left: auto; font-size: 12px; color: var(--text-sub); }

/* 窄屏：工具栏横向滚动，别把表格挤没 */
@media (max-width: 720px) {
  .sh-bar { flex-wrap: nowrap; overflow-x: auto; }
  .sh-swatches { display: none; }          /* 调色板让位给按钮，取色仍可用取色器 */
  .sh-wrap { max-height: 50vh; }
  .sh-status { gap: 4px 12px; }
  .sh-tab { max-width: 108px; padding: 0 8px; }
  .sh-tab-info { display: none; }
}

/* ============================================================================
   快捷向导（第十二轮需求 2，2026-09-20）
   扁平化：只用 1px 描边 + 极淡底色，不用阴影与渐变；大板块可拖动排序（管理员）。
   变量全部取自 :root（--primary / --border / --text-sub / --card …），不写死品牌色。
   ============================================================================ */
#gdBody { padding: 0 16px 16px; }

.gd-cat {
  margin: 12px 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  overflow: hidden;
}
.gd-cat-h {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--primary-soft);
  font-weight: 600;
}
.gd-cat-ico { font-size: 16px; line-height: 1; }
.gd-cat-name { font-weight: 600; }
.gd-cat-n { color: var(--text-sub); font-size: 12px; font-weight: 400; }

/* 拖动柄 + 拖动反馈（仅管理员可见，因为非管理员渲染时压根不加 draggable） */
.gd-grip {
  cursor: grab;
  color: var(--text-sub);
  font-size: 15px;
  line-height: 1;
  user-select: none;
  padding: 0 2px;
}
.gd-grip:active { cursor: grabbing; }
.gd-cat.gd-dragging { opacity: .55; }
.gd-cat.gd-over { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }

.gd-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px;
  padding: 12px 14px;
}

.gd-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  cursor: pointer;
  background: var(--card);
  transition: border-color .15s ease, background .15s ease;
}
.gd-card:hover { border-color: var(--primary); background: var(--primary-soft); }
.gd-card-h { display: flex; align-items: center; gap: 8px; }
.gd-ico { font-size: 16px; line-height: 1; }
.gd-name { font-weight: 600; font-size: 14px; color: var(--text); }

/* 二级快捷链接（子板块）—— 小圆角 chips，扁平无立体感 */
.gd-kids { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.gd-chip {
  display: inline-block;
  font-size: 12px;
  line-height: 1.8;
  color: var(--text-sub);
  text-decoration: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 9px;
  background: transparent;
  transition: color .15s ease, border-color .15s ease;
}
.gd-chip:hover { color: var(--primary); border-color: var(--primary); text-decoration: none; }

/* 「其他（未归类）」—— 弱化处理，提示它是待办而不是正式分组 */
.gd-other { border-style: dashed; }
.gd-other .gd-cat-h { background: transparent; }

@media (max-width: 720px) {
  .gd-grid { grid-template-columns: 1fr 1fr; }
}

/* ==========================================================================
   出差打卡（第十六轮需求 2，2026-09-22）——  `fin.clock`
   需求「手机端自适应页面」：本块样式**以手机为基准**写，再用 min-width 往宽屏加码。
   记录列表用卡片而不是表格 —— 表格在 360px 宽的手机上必然横向溢出
   （本项目多处踩过「列被裁掉 / 右边卡片被挤压」），卡片天然自适应：
   窄屏竖排、宽屏照片与信息并排，一套 DOM 走完。
   ========================================================================== */

.ck-punch {
  margin: 0 16px 14px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
}
.ck-punch-h { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.ck-punch-h b { font-size: 14px; }

.ck-field { margin-bottom: 12px; }
.ck-field > label { display: block; font-size: 12.5px; color: var(--text-sub); margin-bottom: 6px; }
.ck-field > select, .ck-field > input { width: 100%; }
.ck-field .hint { font-size: 12px; color: var(--text-sub); margin-top: 5px; line-height: 1.5; }

/* 取景框 / 已拍照片共用一块方框，切换时不跳动（has-shot 决定显示哪一个） */
.ck-cam {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  max-height: 60vh;
  border-radius: var(--radius);
  overflow: hidden;
  background: #0e1a24;
  display: flex; align-items: center; justify-content: center;
}
.ck-cam video, .ck-cam .ck-shot { width: 100%; height: 100%; object-fit: cover; display: block; }
.ck-cam .ck-shot { display: none; }
.ck-cam.has-shot video { display: none; }
.ck-cam.has-shot .ck-shot { display: block; }
.ck-cam-mask {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: #8fa8bd; font-size: 13px; pointer-events: none;
}
.ck-cam.has-shot .ck-cam-mask { display: none; }
.ck-cam-hint { margin: 8px 0 10px; line-height: 1.5; }

.ck-row { display: flex; gap: 8px; margin-bottom: 12px; }
.ck-row .btn { flex: 1 1 0; }
.btn.block { display: block; width: 100%; }

/* 电脑端：说明卡（打卡区只在手机端渲染，这里给人话解释 + 正确做法） */
.ck-desktop {
  display: flex; gap: 14px; align-items: flex-start;
  margin: 0 16px 14px; padding: 16px;
  border: 1px dashed var(--primary); border-radius: var(--radius);
  background: var(--primary-soft);
}
.ck-desktop-ico { font-size: 30px; line-height: 1; }
.ck-desktop-t { font-weight: 600; margin-bottom: 4px; }

/* 记录卡片列表 */
.ck-list { display: flex; flex-direction: column; gap: 10px; padding: 0 16px 4px; }
.ck-item {
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: var(--radius);
  background: var(--card);
  padding: 11px 12px;
}
/* 「非出差期」的打卡不隐藏、也不判错，只把左侧色条换成警示色，让人一眼看到 */
.ck-item.off { border-left-color: var(--warn); }
.ck-item-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 9px; }
.ck-item-h .ck-who { font-weight: 600; }
.ck-item-h .ck-time { margin-left: auto; white-space: nowrap; }
.ck-item-b { display: flex; gap: 12px; align-items: flex-start; }
.ck-thumb {
  flex: 0 0 132px; width: 132px; height: 99px;
  object-fit: cover; border-radius: 8px; background: var(--bg);
  cursor: zoom-in; display: block;
}
.ck-thumb-none {
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; color: var(--text-sub); cursor: default;
}
.ck-item-meta { flex: 1 1 auto; min-width: 0; }
.ck-line { display: flex; gap: 8px; font-size: 13px; line-height: 1.7; }
.ck-line .ck-k { flex: 0 0 48px; color: var(--text-sub); }
.ck-line > span:last-child { min-width: 0; word-break: break-word; }

@media (min-width: 720px) {
  /* 宽屏下信息两列并排，一屏能多看几条 */
  .ck-item-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; align-content: start; }
}
@media (max-width: 640px) {
  .ck-punch { margin: 0 10px 12px; padding: 12px; }
  .ck-desktop { margin: 0 10px 12px; }
  .ck-list { padding: 0 10px 4px; }
  /* 窄屏：照片占满一行、信息竖排在下面 —— 比「缩成小图 + 挤成一列」好读得多 */
  .ck-item-b { flex-direction: column; }
  .ck-thumb { flex: none; width: 100%; height: auto; aspect-ratio: 4 / 3; }
  .ck-item-h .ck-time { margin-left: 0; flex: 1 1 100%; }
  .ck-line .ck-k { flex: 0 0 44px; }
}

/* ==================================================================================
 * 访客管理 · 来访申请（第十六轮需求 3，2026-09-22）
 * 前缀 `vp-`（visitor page），与「出差打卡」的 `ck-` 分开，互不干扰。
 * ================================================================================== */

/* 筛选条：范围 / 时间段 / 关键字 / 搜索重置，一行排开，窄屏自动换行 */
.vp-filter { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.vp-filter > select { width: auto; min-width: 132px; }
.vp-filter > input[type="date"] { width: 150px; }
.vp-filter > input[type="text"].vp-kw { width: 220px; }
.vp-ish { font-size: 12.5px; color: var(--text-sub); }

/* 操作列：多按钮并排且**不换行** —— 换行会让同一条记录撑成两行高，整张表看起来参差不齐 */
.vp-ops { display: flex; gap: 6px; align-items: center; }
.vp-ops .btn { padding: 5px 10px; font-size: 12.5px; }

/* 扫码申请入口弹窗里的二维码：白底卡片居中，尺寸固定好打印 */
.vp-qrbox {
  margin-top: 12px; padding: 12px; text-align: center;
  background: #fff; border: 1px solid var(--border); border-radius: 10px;
}
.vp-qrbox svg { display: block; margin: 0 auto; width: 260px; height: 260px; max-width: 100%; }

/* 导出弹窗的条数回执 */
.vp-exinfo {
  margin-top: 12px; padding: 9px 12px; border-radius: 8px; font-size: 13px;
  background: var(--primary-soft); color: var(--primary-dark);
}
.vp-exinfo.is-empty { background: #fff6e8; color: #a2620a; }
.vp-exbtn { justify-content: flex-end; margin-top: 12px; }

/* ==================================================================================
 * 第十九轮需求 1：内容板块顶部 Banner（企业文化）—— 2026-09-22
 *   配置在「系统设置 → 品牌设置」，权限同系统设置（仅管理员）；本段只管展示。
 * ================================================================================== */
.ct-banner {
  margin-bottom: 14px;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
  background: var(--card);
  line-height: 0;              /* 干掉 img 基线下方的空隙 */
}
.ct-banner .ct-bn-link { display: block; text-decoration: none; }
.ct-banner img {
  display: block;
  width: 100%;
  height: var(--bn-h, 180px);  /* 高度由管理端的「展示高度」决定 */
  object-fit: cover;           /* 窄屏等比裁切，不变形 */
  object-position: center;
}

/* 管理端预览框（品牌设置页）：按实际展示效果裁切，所见即所得 */
.bn-preview {
  height: 160px;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.bn-preview.has-img { border-style: solid; background: #fff; }
.bn-preview img { display: block; width: 100%; height: 100%; object-fit: cover; }
.bn-preview input[type="range"] { width: 100%; }

/* 各子板块 Banner 列表（第二十三轮需求 6）：一行一个板块，缩略图 + 设置项同屏可改 */
.bn-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 12px; }
.bn-slot { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; background: var(--bg); }
.bn-slot-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-bottom: 8px; }
.bn-slot-body { display: flex; gap: 12px; align-items: flex-start; }
.bn-thumb {
  flex: 0 0 150px; height: 54px;
  border: 1px dashed var(--border); border-radius: 8px; background: #fff;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.bn-thumb.has-img { border-style: solid; }
.bn-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.bn-slot-fields { flex: 1; min-width: 0; }
.bn-slot-fields .bn-link { width: 100%; }
@media (max-width: 720px) {
  .bn-slots { grid-template-columns: 1fr; }
  .bn-slot-body { flex-direction: column; }
  .bn-thumb { flex: none; width: 100%; height: 84px; }
}

/* ==================================================================================
 * 第十九轮需求 2：工作台 / 快捷向导 —— 小清新主题（2026-09-22）
 * ----------------------------------------------------------------------------------
 * 口径：**只改颜色**（色值 / 描边 / 渐变 / 阴影），不动任何布局属性
 *       （尺寸 / 间距 / 网格 / 字体 / 排列），排版与改动前完全一致。
 * 作用域 = `#content[data-board="…"]`，标记由 `public/js/app.js` 的 route() 打上；
 * 其他板块继续沿用 :root 的生物医药蓝，互不影响。
 * 色系：薄荷青绿主色 + 天蓝 / 樱花粉 / 薰衣草 / 蜜桃 / 青柠的糖果色卡片。
 * ================================================================================== */
#content[data-board="workbench"],
#content[data-board="guide"] {
  --primary: #14b8a6;          /* 薄荷青绿（原为品牌天蓝） */
  --primary-dark: #0f766e;
  --primary-soft: #e4fbf6;
  --accent: #fb7185;           /* 樱花粉点缀（原为青色点缀） */
  --text: #1f3a3d;             /* 深青灰，比原冷灰更透亮 */
  --text-sub: #6d8d92;
  --border: #d9efe9;
  --bg: #f6fdfb;
  --shadow: 0 1px 3px rgba(15, 118, 110, .08), 0 1px 2px rgba(15, 118, 110, .05);
}

/* ---------------- 工作台 ---------------- */

/* 顶部问候区：薄荷 → 天蓝 → 樱花粉 的柔和渐变 */
#content[data-board="workbench"] .wb-hero {
  background: linear-gradient(135deg, #e8fbf5 0%, #e6f4ff 48%, #fdeef6 100%);
  border-color: #d5efe9;
}
#content[data-board="workbench"] .wb-hero .wb-time { color: #0d9488; }

/* 卡片底色：糖果色系，仍是低饱和浅色，一眼可辨识又不花哨 */
#content[data-board="workbench"] .wb-card > .card-head { background: rgba(255, 255, 255, .66); }
#content[data-board="workbench"] .wb-card .list-item { border-bottom-color: rgba(255, 255, 255, .9); }
#content[data-board="workbench"] .wb-card .card-body.tight { background: rgba(255, 255, 255, .58); }

#content[data-board="workbench"] .wb-card.c-task     { background: #e6faf4; border-color: #c2f0e2; border-left-color: #34d399; }
#content[data-board="workbench"] .wb-card.c-as       { background: #e8f3ff; border-color: #c9e1fc; border-left-color: #60a5fa; }
#content[data-board="workbench"] .wb-card.c-todo     { background: #f1efff; border-color: #ddd7fb; border-left-color: #a78bfa; }
#content[data-board="workbench"] .wb-card.c-collab   { background: #e5f9fc; border-color: #c3eff6; border-left-color: #22d3ee; }
#content[data-board="workbench"] .wb-card.c-report   { background: #fdeef5; border-color: #f8d4e5; border-left-color: #f472b6; }
#content[data-board="workbench"] .wb-card.c-sched    { background: #fff2e7; border-color: #fbdcc3; border-left-color: #fb923c; }
#content[data-board="workbench"] .wb-card.c-file     { background: #fdf8e4; border-color: #f6ecc0; border-left-color: #fbbf24; }
#content[data-board="workbench"] .wb-card.c-cal      { background: #e8f6ff; border-color: #cbe8fa; border-left-color: #38bdf8; }
#content[data-board="workbench"] .wb-card.c-supplies { background: #f0fbe6; border-color: #d8f2c3; border-left-color: #84cc16; }
#content[data-board="workbench"] .wb-card.c-library  { background: #f2f0ff; border-color: #dcd8fb; border-left-color: #818cf8; }
#content[data-board="workbench"] .wb-card.c-inquiry  { background: #f5f0ff; border-color: #ded4fe; border-left-color: #c084fc; }

/* 全局检索：结果分类标签也换成糖果色 */
#content[data-board="workbench"] .wb-si-kind.k-tool { background: #ecfdfa; color: #0f766e; border-color: #99f6e4; }
#content[data-board="workbench"] .wb-si-kind.k-info { background: #f0fbe6; color: #4d7c0f; border-color: #d8f2c3; }
#content[data-board="workbench"] .wb-si-kind.k-menu { background: var(--primary-soft); color: var(--primary-dark); border-color: #99f6e4; }
#content[data-board="workbench"] .wb-grip { color: #9ecfc6; }
#content[data-board="workbench"] .wb-card.wb-ins-before { box-shadow: inset 3px 0 0 0 #2dd4bf, var(--shadow); }
#content[data-board="workbench"] .wb-card.wb-ins-after  { box-shadow: inset -3px 0 0 0 #2dd4bf, var(--shadow); }

/* ---------------- 快捷向导 ---------------- */

#content[data-board="guide"] .gd-cat { border-color: #d9efe9; }
#content[data-board="guide"] .gd-card:hover { border-color: #5eead4; background: #f0fdfa; }
#content[data-board="guide"] .gd-chip { border-color: #d9efe9; color: #6d8d92; }
#content[data-board="guide"] .gd-chip:hover { color: #0f766e; border-color: #5eead4; background: #f0fdfa; }
#content[data-board="guide"] .gd-other { border-color: #e3e8ef; }

/* 大板块标题：四色循环渐变，青春靓丽（顺序由「板块管理」的归类决定，这里只上色） */
#content[data-board="guide"] .gd-cat:nth-of-type(4n+1) > .gd-cat-h { background: linear-gradient(90deg, #e8fbf5 0%, #e6f4ff 100%); }
#content[data-board="guide"] .gd-cat:nth-of-type(4n+2) > .gd-cat-h { background: linear-gradient(90deg, #fdeef6 0%, #fff2e7 100%); }
#content[data-board="guide"] .gd-cat:nth-of-type(4n+3) > .gd-cat-h { background: linear-gradient(90deg, #f1efff 0%, #e8f3ff 100%); }
#content[data-board="guide"] .gd-cat:nth-of-type(4n)   > .gd-cat-h { background: linear-gradient(90deg, #f0fbe6 0%, #fdf8e4 100%); }
/* 「其他（未归类）」保持无底色，但别被上面那条 4n 规则染上 */
#content[data-board="guide"] .gd-other > .gd-cat-h { background: transparent; }

/* ============================ 应急通讯录（第二十轮需求 2）============================
   两处共用这套样式：
     · 通讯录页搜索框下方的应急区块（.em-head / .em-grid / .em-card …）
     · 系统设置「应急通讯设置」页与表单里的提示条（.em-note，需求点名的那句话）
   配色用**语义警示色**（红/琥珀）而不是主题变量 —— 「紧急情况下启用」这件事本身要靠颜色
   与普通通讯录区分开，主题换色时这一层语义不该跟着变。结构色（边框/次要文字）仍走变量。 */
.em-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.em-head h4 { margin: 0; font-size: 15px; color: #b91c1c; }
.em-note {
  display: inline-block; font-size: 12.5px; color: #b45309;
  background: #fffbeb; border: 1px solid #fde68a; border-radius: 6px; padding: 3px 10px;
}
.em-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
.em-card {
  border: 1px solid #f6d5d5; border-left: 3px solid #ef4444;
  border-radius: 8px; padding: 10px 12px; background: #fffafa;
}
.em-top { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.em-phone { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.em-phone a { font-weight: 600; color: #b91c1c; text-decoration: none; letter-spacing: 0.4px; }
.em-phone a:hover { text-decoration: underline; }
.em-remark { margin-top: 6px; font-size: 12.5px; color: var(--text-sub); }

/* ============================================================================
 * 共享网盘：图标 / 列表双视图 + 顶部批量操作（第二十轮需求 4，2026-09-22）
 *
 * ⚠️ `.seg`（分段按钮）与 `.drive-batch` 是**网盘页专有**的：批量条要贴着面包屑下方常驻，
 *   与 `.card-head` 的按钮区区分开，避免用户以为「全选」是全局开关。
 * ========================================================================= */
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.seg .btn { border: 0; border-radius: 0; }
.seg .btn + .btn { border-left: 1px solid var(--border); }
.seg .btn.active { background: var(--primary); color: #fff; }

.drive-batch {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 16px; border-bottom: 1px solid var(--border);
}
.drive-batch .dr-all { display: flex; align-items: center; gap: 6px; font-size: 13px; cursor: pointer; }
.drive-batch .dr-all input { width: auto; }
.drive-batch.has-sel { background: rgba(2, 132, 199, 0.06); }
.drive-batch .btn[disabled] { opacity: 0.5; cursor: not-allowed; }

/* 卡片右上角：勾选框在左、『⋯』菜单在右，两个都浮在内容之上 */
.drive-node .dr-tools { position: absolute; left: 8px; right: 8px; top: 6px; display: flex; align-items: center; justify-content: space-between; }
.drive-node .dr-pick { display: flex; align-items: center; cursor: pointer; }
.drive-node .dr-grid-pick, .drive-list .dr-list-pick { width: auto; }
/* 选中态用「描边 + 底色」表达，不只靠方框 —— 一屏几十个卡片时方框太小看不见 */
.drive-node.picked, .drive-list tr.picked { border-color: var(--primary); background: rgba(2, 132, 199, 0.08); }

.drive-list { padding: 0; }
.drive-list table.tbl { min-width: 640px; }
.drive-list .dr-c { text-align: center; width: 44px; }
.drive-list .dr-ico { font-size: 18px; }
.drive-list .dr-name { font-weight: 600; }
.drive-list tbody tr { cursor: pointer; }

/* ---------------- 共享网盘 · 回收站（第二十二轮需求 8，2026-09-23） ----------------
 * `.dr-badge` 挂在「回收站」入口按钮上，文字是**最紧急那条的剩余天数**（不是条数）；
 * 剩 ≤3 天转 `--danger` 提醒。`.dr-days` 是表格里每行自己的剩余天数。 */
.dr-badge {
  display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 999px;
  font-size: 11px; line-height: 16px; vertical-align: 1px;
  background: var(--primary-soft); color: var(--primary-dark);
}
.dr-badge.warn { background: var(--danger-soft); color: var(--danger); }
.dr-days { font-size: 12.5px; color: var(--text-sub); white-space: nowrap; }
.dr-days.warn { color: var(--danger); font-weight: 600; }

/* ============================================================================
   智能汇总（report.summary）+ 语言模型设置（sys.ai）—— 第二十一轮需求 10
   变量全部取自 :root，不写死颜色；两页共用同一套提示条 / 条件区样式。
   ============================================================================ */
.sm-note {
  border-radius: var(--radius); padding: 10px 14px; font-size: 13px; line-height: 1.8;
  border: 1px solid var(--border); background: var(--card); margin: 0 0 14px;
}
.sm-note.ok { border-color: rgba(13, 148, 136, .35); background: rgba(13, 148, 136, .08); color: var(--text); }
.sm-note.warn { border-color: rgba(217, 119, 6, .40); background: rgba(217, 119, 6, .10); color: var(--text); }

/* ============================================================================
   模板设置（sys.printtpl）—— 第十八轮需求
   `.pt-ph` = 可复制的占位符（点一下就进剪贴板）。等宽字体 + `user-select:all`
   —— 手动复制时**一次选中整个 `{{xxx}}`**，不会只选到一半花括号。
   ============================================================================ */
.pt-ph {
  display: inline-block; padding: 1px 7px; border-radius: 5px; cursor: copy;
  background: #eef4fa; border: 1px solid #d8e6f3; color: #0c4a6e;
  font-family: Consolas, Menlo, "Courier New", monospace; font-size: 12.5px;
  user-select: all; -webkit-user-select: all;
}
.pt-ph:hover { background: #dfecf8; border-color: #b9d4ea; }

.sm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px; }
.sm-cell { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; background: var(--card); }
.sm-label { display: block; font-weight: 600; margin-bottom: 8px; color: var(--text); }
.sm-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.sm-chip {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 999px; padding: 4px 12px;
  font-size: 13px; background: var(--bg);
}
.sm-chip:hover { border-color: var(--primary); }
.sm-chip input { width: auto; margin: 0; }
/* 提交人一多就滚动，避免把「生成」按钮顶到屏幕外 */
.sm-people { max-height: 190px; overflow-y: auto; display: flex; flex-wrap: wrap; gap: 8px; padding-right: 4px; }

/* 第二十三轮需求 5：智能汇总的「语言指令」编辑框。
   指令是多行纯文本（发给大模型的 system prompt），因此用等宽字体便于对齐「1) 2) 3)」这类条数。 */
.sm-prompt-ta {
  width: 100%; margin-top: 10px; min-height: 200px; resize: vertical;
  font-family: Consolas, Menlo, monospace; font-size: 12.5px; line-height: 1.75;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg); color: var(--text); padding: 12px 14px;
}
.sm-prompt-ta:focus { outline: none; border-color: var(--primary); }

.sm-out {
  white-space: pre-wrap; word-break: break-word; line-height: 1.9;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--bg); padding: 16px 18px; max-height: 620px; overflow-y: auto;
  font-size: 14px;
}

/* ============================================================
 * 询价管理（2026-09-23 第二十四轮需求 A）
 *
 * 需求原文：「当询价单状态为待处理，整行文字红色显示，待处理加粗红色」。
 *   · 整行红 → 类挂在 <tr> 上。`table.tbl td` **不设 color**（只设 padding / 边框），
 *     所以 tr 上的颜色会正常继承到每个单元格；若哪天有人给 td 加了显式 color，
 *     这里会静默失效 —— 所以断言不看 CSS，而是断到**渲染出的 tr 上有这个类**。
 *   · 「待处理」再单独加粗（`.tag.inq-pending`）—— 一行红 + 一个粗红标签，
 *     扫一眼就知道哪几张单没人接。
 * ============================================================ */
table.tbl tr.inq-pending > td { color: var(--danger); }
.tag.inq-pending { font-weight: 700; }

/* ★ 第一百二十四轮需求（2026-10-27 林总）：参与人员标签可点 = 按此人筛选。
   只加「可点」的视觉提示（指针 + 悬停微亮），不改配色 —— 标签颜色本身在表达「已提交 / 待提交」。 */
.tag.inq-fh { cursor: pointer; }
.tag.inq-fh:hover { filter: brightness(0.94); }

/* 处理记录时间线：建单那条用浅底与「一次处理提交」区分开；
   富文本正文按 `.post-body` 的口径换行，长词/长链接强制断行避免撑破弹窗。 */
.inq-rec-create { background: var(--bg); }
.inq-rec-content { word-break: break-word; line-height: 1.85; }
.inq-rec-content img { max-width: 100%; }

/* ==================================================================================
 * 部门层级架构图（第三十五轮，2026-09-24 林总需求）
 * ----------------------------------------------------------------------------------
 * 需求原文：「部门管理中，部门架构需要按照层级架构图的方式展示出来，
 *           一级就在上面，后面每一级都要逐级下降。」
 *
 * 与第三十二轮的区别：那一轮是**缩进列表**（每级 padding-left 18px，仍是竖向流水），
 * 本轮改成**真正的组织架构图** —— 每级一层、层级间用连线相连，视觉上逐级下降。
 *
 * 实现方式：**纯 CSS + DOM 套嵌**（不用 SVG、不用 canvas）——
 *   ① 每一级 = 一个 `.dept-org-level`（flex 横向排布该级全部节点）；
 *   ② 父子关系靠**竖线 + 横线**拼出来（`.dept-org-kids` 的 ::before/::after）；
 *   ③ 节点本身是可点的卡片，沿用既有 `.tag` / 按钮体系，交互零改动。
 * 为什么不用 SVG：节点数是动态的（管理员可随时增删部门）、文字要能选中复制、
 * 按钮要能聚焦 —— DOM 天然满足；SVG 要自己量文字宽度重算坐标，脆且难维护。
 * ================================================================================== */

/* 画布：横向可滚动（部门极多时兜底），但默认靠**换行**容纳，避免把带子部门的节点挤到屏幕外 */
.dept-org { overflow-x: auto; padding: 6px 2px 2px; }

/* 一个「子树」容器：内部先放父节点，再放子树行 */
.dept-org-sub { display: flex; flex-direction: column; align-items: center; }

/* 某一级的一行：该级所有兄弟节点横向排布、顶部对齐（保证同级的纵深一致）。
   ★ 允许换行，但**换行单位是整个子树**（`.dept-org-sub` 是这一行的直接子元素）：
     所以哪怕折到第二行，也是「一棵完整的子树」整体挪下去，
     父在上、子在下的相对关系**永远不会被打散** —— 这正是组织架构图该有的行为。
     不允许子元素内部横向溢出屏幕（顶层部门常有十几个）。 */
.dept-org-level {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  justify-content: center; gap: 18px 0;
}

/* 单个节点卡片 */
.dept-org-node {
  display: flex; flex-direction: column; align-items: center;
  min-width: 132px; max-width: 190px; margin: 0 7px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--card); box-shadow: var(--shadow);
  padding: 9px 12px; text-align: center; position: relative;
}
/* 顶层部门：主色描边 + 浅色底，一眼区分「一级」 */
.dept-org-node.lv1 { border-color: var(--primary); background: var(--primary-soft); font-weight: 600; }
.dept-org-node.lv1 .dept-org-name { color: var(--primary-dark); }
/* 节点内两行：名称 + 人数 */
.dept-org-name {
  font-size: 13px; line-height: 1.45; color: var(--text);
  word-break: break-word; overflow-wrap: anywhere;
}
.dept-org-meta { font-size: 11px; color: var(--text-sub); margin-top: 3px; white-space: nowrap; }
/* 层级徽标（左上角小角标，标出「第 N 级」） */
.dept-org-lv {
  position: absolute; top: -8px; left: -6px;
  font-size: 10px; line-height: 1; padding: 3px 6px; border-radius: 8px;
  background: var(--primary); color: #fff; font-weight: 700;
}
.dept-org-node:not(.lv1) .dept-org-lv { background: var(--text-sub); }

/* ---- 连线：父 → 子群 ----
   结构：.dept-org-kids（父节点下方那一块）内 ::before 画**向下的竖线**，
   子级那一行用 ::after 画**横跨的横线**，每个子节点上方再来一小段竖线接头。
   这套「竖-横-竖」三笔是组织架构图的标准画法。 */
.dept-org-kids {
  display: flex; flex-direction: column; align-items: center;
  position: relative; padding-top: 20px;
}
/* 父节点 → 横线的竖线 */
.dept-org-kids::before {
  content: ''; position: absolute; top: 0; left: 50%;
  width: 2px; height: 20px; background: var(--border);
  transform: translateX(-1px);
}
/* 横线：连接所有子节点。宽度由 JS 按「首末子节点中心距」算出后内联，
   这里只定样式 —— 宽度写死会在兄弟数量变化时错位。
   注意：横线**不带**自己的两端竖线 —— 每根竖线由子节点自己的 ::before 出，
   否则同一位置会叠出双线（2px 叠成看起来更粗的 2px，边缘还会发虚）。 */
.dept-org-bridge { height: 2px; background: var(--border); position: relative; }

/* 子级那一行：每个子节点上方各补一小截竖线接头（接到横线）。
   统一由子节点自己出竖线，好处是**换行 / 单子节点 / 多子节点三种情形同一套规则**，
   不会出现「换行后连线凭空消失」。仅限 `.dept-org-kids` 内 —— 顶层那行上方没有横线，
   加 padding 只会平白多出一段空白。 */
.dept-org-kids .dept-org-level > .dept-org-sub { position: relative; padding-top: 18px; }
.dept-org-kids .dept-org-level > .dept-org-sub::before {
  content: ''; position: absolute; top: 0; left: 50%; width: 2px; height: 18px;
  background: var(--border); transform: translateX(-1px);
}

/* 只有一个子部门时不需要横线（竖线直连更自然），用 class 收掉 */
.dept-org-kids.single .dept-org-bridge { display: none; }

/* 空部门提示（无任何部门时） */
.dept-org-empty { padding: 18px 0; text-align: center; color: var(--text-sub); font-size: 13px; }

/* 窄屏：节点收窄 + 允许换行（小屏实在放不下，宁可折行也别横向拖）
   —— 这是**降级**行为，仅在窄屏生效；桌面上靠弹窗开 xl 保证 nowrap 的一整行。 */
@media (max-width: 720px) {
  .dept-org-level { flex-wrap: wrap; gap: 14px 0; }
  .dept-org-node { min-width: 108px; }
}

/* ==================================================================================
   公司架构图（第四十八轮需求 5，2026-09-27）
   ==================================================================================
   与上面的「部门架构图」是**两套独立 class**（`.corg-*` vs `.dept-org-*`），刻意不共用：
     · 部门图是**树**：层级不定、递归渲染；
     · 公司图是**两层**：公司 → 股东（股东不再往下展开，因为林总拍板「不做多层穿透」）。
   形状不同，硬套递归会让两边的 CSS 互相牵制 —— 改一个宽度要回归两处。

   连线画法沿用同一套「竖-横-竖」三笔（这是组织架构图的标准画法）：
     .corg-kids::before  —— 公司卡片 → 横线的竖线
     .corg-bridge        —— 横线（宽度由 JS 按「首末股东卡中心距」量出来，见 companyorg.js）
     .corg-sub::before   —— 每个股东卡上方的一小截竖线接头
   ================================================================================== */

.corg { overflow-x: auto; padding: 10px 4px 4px; }

/* 一个「子树」容器：先放公司卡片，再放股东那一行 */
.corg-sub { display: flex; flex-direction: column; align-items: center; }

/* 某一级的一行：允许换行，但**换行单位是整个子树**（`.corg-sub` 是直接子元素），
   所以折到第二行也是「一家公司 + 它的股东」整体挪下去，父上子下的关系不会被打散。 */
.corg-level {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  justify-content: center; gap: 22px 0;
}

/* 公司卡片 */
.corg-node {
  display: flex; flex-direction: column; align-items: center;
  min-width: 208px; max-width: 268px; margin: 0 10px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--card); box-shadow: var(--shadow);
  padding: 11px 14px; text-align: center; position: relative;
}
/* 公司节点用主色描边（顶层语义，与部门图的 `.lv1` 一致） */
.corg-node.lv1 { border-color: var(--primary); background: var(--primary-soft); }
.corg-name {
  font-size: 13px; font-weight: 600; line-height: 1.45; color: var(--primary-dark);
  word-break: break-word; overflow-wrap: anywhere;
}
/* 卡片内的信息行（状态 / 注册资本 / 法人 / 股东数） */
.corg-meta {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 5px; margin-top: 5px;
}
.corg-kv { font-size: 11px; color: var(--text-sub); white-space: nowrap; }
.corg-acts { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; justify-content: center; }

/* 股东小卡：自然人 / 公司两种类型用左侧色条区分（色条比底色更不抢眼，也更能容纳文字） */
.corg-holder {
  display: flex; flex-direction: column; align-items: center;
  min-width: 128px; max-width: 180px; margin: 0 8px;
  border: 1px solid var(--border); border-left: 3px solid var(--text-sub);
  border-radius: 9px; background: var(--card); box-shadow: var(--shadow);
  padding: 9px 12px; text-align: center;
}
.corg-holder.person { border-left-color: var(--primary); }
.corg-holder.company { border-left-color: var(--warn, #f59e0b); }
.corg-htype {
  font-size: 10px; line-height: 1; padding: 3px 6px; border-radius: 8px;
  background: var(--border); color: var(--text-sub); font-weight: 700;
}
.corg-hname {
  font-size: 13px; line-height: 1.45; color: var(--text); margin-top: 5px;
  word-break: break-word; overflow-wrap: anywhere;
}
.corg-hratio { font-size: 15px; font-weight: 700; color: var(--primary-dark); margin-top: 3px; }

/* ---- 连线 ---- */
.corg-kids {
  display: flex; flex-direction: column; align-items: center;
  position: relative; padding-top: 20px;
}
/* 公司卡片 → 横线的竖线 */
.corg-kids::before {
  content: ''; position: absolute; top: 0; left: 50%;
  width: 2px; height: 20px; background: var(--border);
  transform: translateX(-1px);
}
/* 横线：宽度由 JS 内联（写死会在股东数变化时错位）。
   不带自己的两端竖线 —— 竖线由股东卡自己的 ::before 出，否则同位置叠出双线。 */
.corg-bridge { height: 2px; background: var(--border); position: relative; }

/* 股东卡上方的竖线接头（仅 `.corg-kids` 内 —— 顶层那行上方没有横线，多出 padding 只是空白） */
.corg-kids .corg-level > .corg-holder { position: relative; margin-top: 18px; }
.corg-kids .corg-level > .corg-holder::before {
  content: ''; position: absolute; top: -18px; left: 50%; width: 2px; height: 18px;
  background: var(--border); transform: translateX(-1px);
}

/* 只有一个股东时不需要横线（竖线直连更自然） */
.corg-kids.single .corg-bridge { display: none; }

/* 空态提示 */
.corg-empty { padding: 20px 0; text-align: center; color: var(--text-sub); font-size: 13px; }

/* ==================== 需求 2（第五十五轮）：整合架构树（自上而下） ====================
   与上面的「公司 → 股东」两层图不同：这里画的是**公司间的持股链路**（上级公司 → 下级公司），
   递归、层级不定，故用「垂直缩进 + 左侧竖线」表达层级（比横线桥更好排版、窄屏也稳）。
   DOM（见 companyorg.js 的 treeNodeHtml）：
     .corg-trees                 —— 所有树的容器
       .corg-tree                —— 一棵树（有独立标题）
         .corg-tree-hd           —— 树标题（架构 N）
         .corg-tnode[data-lv=N]  —— 节点（公司卡 + 子节点容器）
           .corg-tbox            —— 公司卡本体
           .corg-tchildren       —— 子节点容器（左侧竖线 + 缩进）
   层级缩进用嵌套的 padding-left 累积，无需 JS 量宽。 */
.corg-trees { padding: 6px 0; }
.corg-tree { border:1px solid var(--border); border-radius:10px; margin-bottom:14px; overflow:hidden; }
.corg-tree-hd { padding:7px 12px; background:rgba(2,132,199,.06); font-size:12px; font-weight:600; color:var(--text-sub); }
.corg-tnode { padding: 0 12px; }
.corg-tbox { display:inline-block; min-width:190px; max-width:320px; border:1px solid var(--border); border-radius:8px;
             padding:8px 12px; margin:10px 0 0; background:var(--card,#fff); text-align:left; vertical-align:top; }
.corg-tnode[data-lv="0"] > .corg-tbox { border-color: var(--primary); background: var(--primary-soft); }
.corg-held { font-size:11px; color:var(--text-sub); margin-left:6px; }
.corg-chips { display:flex; flex-wrap:wrap; gap:4px; margin-top:6px; }
.corg-chip { font-size:11px; padding:1px 6px; border-radius:10px; border:1px solid var(--border); background:rgba(2,132,199,.05); white-space:nowrap;
             display:inline-flex; align-items:center; gap:4px; }
.corg-chip.person { border-color:var(--primary); color:var(--primary-dark); }
.corg-chip.company { border-color:var(--warn,#f59e0b); color:var(--warn,#b45309); }
.corg-chip b { font-weight:700; }
/* ★ 需求 18：股东标签内的「改 / 删」小按钮（树视图下直接编辑股权） */
.corg-chip-btn { font:inherit; font-size:10px; line-height:1; padding:1px 4px; border-radius:6px; cursor:pointer;
                 border:1px solid currentColor; background:transparent; color:inherit; opacity:.7; }
.corg-chip-btn:hover { opacity:1; background:rgba(2,132,199,.12); }
.corg-chip-btn.del { color:#dc2626; }
.corg-chip-btn.del:hover { background:rgba(220,38,38,.1); }
/* ★ 需求 18：「支持每层折叠」—— 有下级公司的节点标题前一个折叠开关 */
.corg-fold { font:inherit; font-size:12px; line-height:1; width:18px; height:18px; padding:0; margin-right:4px; cursor:pointer;
             border:1px solid var(--border); border-radius:4px; background:var(--card,#fff); color:var(--text-sub);
             display:inline-flex; align-items:center; justify-content:center; vertical-align:middle; }
.corg-fold:hover { border-color:var(--primary); color:var(--primary); background:var(--primary-soft); }
.corg-fold-ph { display:inline-block; width:18px; margin-right:4px; }
.corg-name { display:flex; align-items:center; }
.corg-tchild-count { font-size:11px; color:var(--text-sub); padding:2px 0 0 2px; }
.corg-tnode.collapsed > .corg-tbox { opacity:.86; }
.corg-tchildren { margin-left:22px; padding-left:14px; border-left:2px solid var(--border); }
.corg-tchildren > .corg-tnode { position: relative; }
.corg-tchildren > .corg-tnode::before { content:''; position:absolute; left:-14px; top:26px; width:12px; height:2px; background:var(--border); }
@media (max-width: 720px) {
  .corg-tbox { min-width:150px; max-width:240px; padding:7px 10px; }
  .corg-tchildren { margin-left:12px; padding-left:10px; }
  .corg-tchildren > .corg-tnode::before { left:-10px; width:8px; }
  .corg-chip-btn { font-size:9px; padding:1px 3px; }
}

/* 窄屏降级：卡片收窄 + 允许折行 */
@media (max-width: 720px) {
  .corg-level { flex-wrap: wrap; gap: 16px 0; }
  .corg-node { min-width: 168px; }
  .corg-holder { min-width: 108px; }
}

/* ==== ★ 需求 33（第三十七轮 2026-10-07）：类天眼查的「关系图谱」 ====
   与上面的「层级卡片树」（`.corg-t*`）及更早的卡片图（`.corg-node` / `.corg-holder`）
   **并列共存、各画各的**（页内用 `.js-cview` 切换显隐），故这里只新增 `.corg-g*` 一组新 class，
   不改动上面任何既有规则 —— 改老样式会连带影响「层级卡片」视图。

   DOM：
     .corg-graph        —— 图谱根（relative；连线层压在里面）
       svg.corg-gsvg    —— SVG 连线层（绝对定位铺满；`pointer-events:none` 保证不挡方框点击）
         g#corgGedges   —— 连线实际写在这里
       .corg-grows      —— 关系组容器（★ 需求 35：一组 = 一个连通分量，自上而下堆叠）
         .corg-gcomp         —— 一个关系组（组头 + 本组的层行）
           .corg-gcomp-hd    —— 组头（组号 / 源头 / 计数）
           .corg-gcomp-rows  —— 本组的层行容器（组内的行距在这里）
             .corg-glay      —— 一层（左侧很淡的层号标尺 + 该层的方框们）
               .corg-gnode   —— 一个主体（公司 / 自然人两种配色）

   ⚠️ 连线的坐标**一律由 JS 量算**（见 companyorg.js::drawGraphEdges）而不是 CSS 画：
      「谁连谁」是数据决定的，CSS 画不出来。这里只负责摆位与外观。

   ★ 需求 35（第三十八轮 2026-10-07）：不相干的关系网要**分开摆**（参考文章用 relation-graph 的
      `layoutName:'tree'` —— 每个根各排一棵树、彼此留间距 `min_per_width:150` / `min_per_height:180`）。
      落地为：组内沿用小行距（34px，层与层本来就该靠近），**组与组之间**给足留白（46px）
      + 一条虚线分隔，让「这是另一个不相干的关系网」一眼可辨。 */
.corg-chartbar { border-bottom: 1px solid var(--border); }
.corg-graph { position: relative; padding: 4px 0 12px; overflow-x: auto; }
.corg-gsvg { position: absolute; left: 0; top: 0; pointer-events: none; overflow: visible; }
.corg-grows { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 46px; }
.corg-gcomp { display: flex; flex-direction: column; }
/* 组间分隔：只在第二组及以后加（第一组不必顶着一条线），用虚线表示「弱关系 / 无关系」 */
.corg-gcomp + .corg-gcomp { padding-top: 26px; border-top: 1px dashed var(--border); }
.corg-gcomp-hd { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.corg-gcomp-no { font-size: 12px; font-weight: 600; color: var(--text); }
.corg-gcomp-src { font-size: 11px; color: var(--text); opacity: .8; }
.corg-gcomp-meta { font-size: 11px; color: var(--text-sub); }
.corg-gcomp-rows { display: flex; flex-direction: column; gap: 34px; }
.corg-glay { display: flex; align-items: flex-start; gap: 10px; }
/* 层号标尺：很淡、不抢视线 —— 只用来体现「层次关系」（需求原文「下方通过架构展示出来层次关系」） */
.corg-glay-n { flex: none; width: 46px; padding-top: 11px; font-size: 11px; color: var(--text-sub); text-align: right; opacity: .65; }
.corg-glay-items { display: flex; flex-wrap: wrap; gap: 16px 22px; }
.corg-gnode {
  min-width: 170px; max-width: 250px; padding: 8px 12px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--card);
  cursor: pointer; transition: box-shadow .15s, border-color .15s;
}
.corg-gnode:hover { border-color: var(--primary); box-shadow: 0 2px 10px rgba(2, 132, 199, .16); }
.corg-gnode:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.corg-gnode.company { border-left: 3px solid var(--warn, #f59e0b); }
/* 自然人 = 源头持股人：与公司明显区分（同 `.corg-holder.person` / `.corg-chip.person` 的配色口径） */
.corg-gnode.person { border-left: 3px solid var(--primary); background: var(--primary-soft); }
.corg-gname { font-size: 13px; font-weight: 600; color: var(--text); word-break: break-all; }
.corg-gsub { margin-top: 4px; font-size: 11px; color: var(--text-sub); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.corg-gedge { fill: none; stroke: var(--border); stroke-width: 1.5; }
.corg-garrow { fill: var(--border); }
/* 比例标签：白色描边（paint-order）压在线上，避免线与数字互相糊住 */
.corg-glabel { font-size: 10px; fill: var(--text-sub); paint-order: stroke; stroke: var(--card); stroke-width: 3px; stroke-linejoin: round; }
@media (max-width: 720px) {
  .corg-glay-n { width: 30px; font-size: 10px; }
  .corg-glay-items { gap: 12px 14px; }
  .corg-gnode { min-width: 138px; max-width: 190px; padding: 6px 9px; }
  .corg-gname { font-size: 12px; }
  .corg-grows { gap: 34px; }
  .corg-gcomp + .corg-gcomp { padding-top: 18px; }
  .corg-gcomp-rows { gap: 26px; }
}

/* ==== 我的待审批（第九十五轮需求 1）：分域筛选标签 + 分页条 ====
   全部走主题变量（`--border` / `--primary` / `--text-sub` …），不写死品牌色，
   这样「工作台 / 快捷向导」的小清新配色作用域与本页互不影响。 */
.ap-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 8px; }
.ap-tab {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 12px; border: 1px solid var(--border); border-radius: 20px;
  font-size: 13px; color: var(--text-sub); background: transparent; text-decoration: none;
  cursor: pointer; transition: all .15s;
}
.ap-tab:hover { border-color: var(--primary); color: var(--primary); }
.ap-tab.on { border-color: var(--primary); color: #fff; background: var(--primary); }
.ap-tab b { font-weight: 600; }
/* 分域标签上的条数：选中时用半透明白，未选中时弱化，避免与标签文字抢焦点 */
.ap-tab:not(.on) b { color: var(--text-sub); opacity: .75; }
.ap-pager { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.ap-pager:empty { display: none; }
@media (max-width: 720px) {
  .ap-tabs { gap: 6px; }
  .ap-tab { padding: 3px 10px; font-size: 12px; }
}

/* ★ 第十八轮（2026-10-03）送货管理页排版重做（林总截图标注 1 / 2）：
     ① 筛选行（标注 1）从「标签行正下方」下移到「统计卡与列表之间」，并**成组**排版 ——
        「送货日期 从…至…」是一个字段（原先被拆成两个裸 label「送货日期从」「到」），
        动作按钮（查询 / 导出结算确认单）**靠右**，不与筛选字段挤在同一列；
     ② 统计卡（标注 2）**上移**到标签行正下方，并改**固定 4 列 grid** ——
        原先是内联 flex（display:flex + flex-wrap），四张卡按内容宽度排布、宽窄参差。
   口径与 §172 需求 32 的 .cm-filters 同款：**字段成组 + 动作靠右**；
   统计卡沿用 .stat 的既有皮肤（白底 + 圆角 + 阴影），只改**列宽分配**。 */
.dl-tabs { display: flex; gap: 8px; padding: 0 0 10px; }
.dl-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; padding: 0 0 12px; }
.dl-filters {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px;
  padding: 10px 12px; margin: 0 0 12px;
  background: #f2f7fb; border: 1px solid var(--border); border-radius: var(--radius);
}
.dl-filters .fld { display: flex; align-items: center; gap: 6px; }
.dl-filters .fld.grow { flex: 1 1 170px; min-width: 0; }
.dl-filters .lab { font-size: 12.5px; color: var(--text-sub); white-space: nowrap; }
.dl-filters .sep { font-size: 12.5px; color: var(--text-sub); }
.dl-filters .acts { margin-left: auto; display: flex; gap: 8px; flex: none; }
.dl-filters input, .dl-filters select {
  height: 30px; border: 1px solid var(--border); border-radius: 6px;
  padding: 0 8px; font: inherit; font-size: 13px; color: inherit; background: #fff;
}
/* ★ 宽度基准是量出来的：1040px 内容宽下要**单行**排下「日期组 + 师傅 + 搜索 + 两个按钮」，
   实测 118/120/170 + 3 个 14px 间距 ≈ 900px 仍有余量；再放大就会把按钮挤到第二行。 */
.dl-filters input[type="date"] { min-width: 118px; }
.dl-filters select { min-width: 120px; }
.dl-filters .fld.grow input { flex: 1 1 auto; min-width: 0; }
@media (max-width: 720px) {
  .dl-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dl-filters .acts { margin-left: 0; width: 100%; }
  /* ★★ 需求 44（第五十八轮续，2026-10-11 林总）：窄屏下这组筛选字段会溢出被裁。
     根因是两条规则打架：防 iOS 聚焦缩放的「输入控件一律 16px」把 `<input type=date>`
     撑宽了（date 控件的宽度随字号走），而 `.fld` 作为 flex item 的 `min-width:auto`
     又不肯收缩到内容最小宽以下 ⇒ 「送货日期 从…至…」这一组实测 379px > 容器 327px。
     ⇒ 窄屏下每组占满一行、组内控件平分剩余宽度，并把 date/select 的最小宽度放开。 */
  .dl-filters .fld, .dl-filters .fld.grow { flex: 1 1 100%; min-width: 0; flex-wrap: wrap; }
  .dl-filters .fld > input, .dl-filters .fld > select { flex: 1 1 auto; min-width: 0; }
  .dl-filters input[type="date"], .dl-filters select { min-width: 0; }
  .dl-filters .lab { flex: 0 0 auto; }
}

/* ★ 第十八轮第二处（2026-10-03）列表长文本「截断 + 悬停浮窗」
   —— 需求（林总截图）：送货单列表的「送货信息」是**几百字的产品清单**，
      直接铺开会把整行撑到几百像素高（截图里那行占了整屏）⇒ 列表里只显示前面几个字 + 省略号，
      鼠标移上去用浮窗显示**完整信息**。
   ★★ 为什么浮窗必须用 position:fixed + JS 定位（表格里做 tooltip 的第一大坑）：
      表格外层 `.tbl-wrap` 是 `overflow-x: auto` ⇒ 纯 CSS 的 ::after 绝对定位浮窗会被**裁掉**。
   ★ 为什么不用原生 title：几百字的清单不换行、会撑成超宽横条（且出现有延迟）⇒ 读不全。 */
.dl-clip {
  display: block; max-width: 320px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; cursor: help;
}
.dl-clip:hover { color: var(--primary-dark); text-decoration: underline dotted; }

/* ★ 第十八轮第三处（2026-10-03）送货信息「多行输入 + 换行维持格式」：
   每一行是一个 textarea（不再是 input）—— 长清单能分行写、看得全。
   · min-height 给到 3 行，resize: vertical 允许用户自己再拉高；
   · align-items: flex-start ⇒ 删号按钮与**首行**对齐（原来居中，多行后看着歪）。 */
.dl-info-row { display: flex; gap: 6px; margin-bottom: 8px; align-items: flex-start; }
.dl-info-row textarea {
  flex: 1; min-width: 0; min-height: 62px; resize: vertical;
  font: inherit; font-size: 13px; line-height: 1.6; color: inherit;
  border: 1px solid var(--border); border-radius: 8px; padding: 7px 10px; background: #fff;
}
.dl-info-row textarea:focus { outline: none; border-color: var(--primary); }
.dl-info-row .btn { flex: none; }
.dl-tip {
  position: fixed; z-index: 1200; max-width: 560px; max-height: 60vh; overflow: auto;
  padding: 9px 12px; border-radius: 8px; background: #10233a; color: #eaf3fb;
  font-size: 12.5px; line-height: 1.7; white-space: pre-wrap; word-break: break-word;
  box-shadow: 0 8px 24px rgba(6, 24, 45, .28); pointer-events: none; display: none;
}
.dl-tip.on { display: block; }

/* ★★ 第十八轮第十六处（2026-10-03，需求 39 附带的 UI 问题）：
   「可点击的胶囊页签」`.cl-chips / .cl-chip` —— **提到全局**。

   根因（林总反馈「备用金管理页上面三个超链接」+「费用预审页面也存在相同的问题」）：
   这套样式原先**只写在「报销申请」视图自己的 `render()` 里**（一段内联 `<style>`）。
   SPA 切页时**当前视图的 innerHTML 连同那段 `<style>` 一起被丢弃** ⇒
   而「备用金管理」（`#pyTabs`）和「费用预审」（`#epChips`）**也在用同一套 `cl-chip` 类名**，
   却拿不到样式 ⇒ 页签退化成了**裸超链接**（林总看到的现象）。

   ⇒ 解法：**把这段 CSS 提到全局**（此处），三个页面共用。
   ★ **零逻辑改动**：类名、事件绑定、选中态（`.active`）全部不变，只是不再依赖某一个视图的 style 块。
   ★ 计数胶囊 `<b>` 与红点（`.alert`）一并搬过来 —— 待办为 0 时不加红点，与其它模块同款做法。 */
.cl-chips { display:flex; gap:8px; flex-wrap:wrap; margin:0 0 10px; align-items:center; }
.cl-chip { display:inline-flex; align-items:center; gap:6px; padding:6px 14px; border:1px solid var(--border);
           border-radius:20px; background:#fff; color:var(--text); cursor:pointer; font-size:13.5px;
           text-decoration:none; line-height:1.5; }
.cl-chip:hover { border-color:var(--primary); color:var(--primary-dark); }
.cl-chip.active { background:var(--primary); border-color:var(--primary); color:#fff; font-weight:600; }
.cl-chip b { display:inline-block; min-width:18px; text-align:center; padding:0 6px; border-radius:10px;
             background:rgba(2,132,199,.12); color:var(--primary-dark); font-size:12px; }
.cl-chip.active b { background:rgba(255,255,255,.25); color:#fff; }
.cl-chip.alert b { background:#fee2e2; color:#b91c1c; }
.cl-chip.alert.active b { background:rgba(255,255,255,.25); color:#fff; }

/* ★★★ 第十八轮第三十三处（2026-10-04，需求 3）：报销单列表「上传」按钮上的**附件数量徽标**。
 * 需求原话「对于已上传附件的，在该按钮上显示上传附件的数量」。
 * 唯一实现：待核销 / 已核销 / 详情弹窗三处都由 `claimUploadBtnHtml()` 产出本类名，
 * 视图里不许自己拼一段 innerHTML（否则三个入口的样式迟早长得不一样）。 */
.cm-cnt {
  display: inline-block; min-width: 16px; height: 16px; line-height: 16px;
  margin-left: 5px; padding: 0 4px; border-radius: 8px;
  background: rgba(2,132,199,.13); color: var(--primary-dark);
  font-size: 11px; font-weight: 600; text-align: center; vertical-align: 1px;
}

/* ★★★ 第五十三轮需求 27：启动加载失败覆盖层。
 * 需求原话「每次刷新网页……都会闪回登陆界面……登录成功之后 4 小时之内无需重新做登录验证」。
 * 根因是「一次加载失败就被当成退出登录」⇒ 本层是**保留登录态**后的失败兜底：
 *   不回登录页、不清 token，给「重试」与「退出登录」两个明确出口。
 * 与登录页（#loginPage）互斥显示，都由 app.js 控制。 */
.boot-err { position: fixed; inset: 0; z-index: 9000; display: flex; align-items: center; justify-content: center;
            padding: 20px; background: linear-gradient(135deg, #eaf4fb 0%, #f6fbff 60%, #eef7fd 100%); }
.boot-err.hidden { display: none; }
.boot-err-card { max-width: 420px; width: 100%; background: #fff; border: 1px solid var(--border);
                 border-radius: 14px; box-shadow: 0 8px 28px rgba(20, 60, 100, .12); padding: 22px 22px 18px; }
.boot-err-t { font-size: 17px; font-weight: 700; color: #b45309; margin-bottom: 8px; }
.boot-err-m { font-size: 13.5px; color: var(--text); line-height: 1.6; word-break: break-all; }
.boot-err-n { margin-top: 8px; font-size: 12.5px; color: var(--text-sub); line-height: 1.6; }
.boot-err-h { margin-top: 16px; display: flex; gap: 8px; }

/* ==================================================================
   记事本（第一百三十一轮，2026-10-28 林总 · 需求文档 OA-REQ-NOTE-001）
   ------------------------------------------------------------------
   结构：.nb-shell = 三栏（左 220 / 中 300 / 右 自适应），三栏各自滚动，
   整页**不出现外层滚动条** —— 这是「应用型页面」与「文档型页面」的关键差别：
   让三栏一起被整页滚动的话，用户往下翻笔记正文时左栏也跟着跑掉。

   ★ 高度：`100vh - 顶栏(56) - .content 上下内边距(18×2)`。
     写死 92px 而不是用 flex 撑满，是因为 `.content` 的父级 `.main` 没有定高
     （它是 `min-height:100vh` 的 flex 列），给子元素 `height:100%` 会塌成 0 —— 本项目踩过。
   ★ 主题色一律走 `var(--primary)` 等既有变量，不新造色号。
   ================================================================== */

.nb-shell {
  display: flex; gap: 12px;
  height: calc(100vh - 92px);
  min-height: 520px;
  align-items: stretch;
}
.nb-side { flex: 0 0 216px; width: 216px; display: flex; flex-direction: column; overflow: hidden; }
.nb-listcol { flex: 0 0 300px; width: 300px; display: flex; flex-direction: column; overflow: hidden; }
.nb-main { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }

/* ---------------- 左栏 ---------------- */
.nb-side-head {
  display: flex; align-items: center; gap: 8px; padding: 11px 12px;
  border-bottom: 1px solid var(--border); flex: none;
}
.nb-side-title { font-size: 14px; font-weight: 600; flex: 1; }
.nb-side-body { flex: 1; overflow-y: auto; padding: 8px 8px 4px; }
.nb-side-foot {
  flex: none; border-top: 1px solid var(--border); padding: 8px 12px;
  display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-sub);
}
.nb-foot-link { color: var(--primary); text-decoration: none; cursor: pointer; }
.nb-foot-link:hover { color: var(--primary-dark); }
.nb-foot-hint { margin-left: auto; }

.nb-nav {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 8px;
  cursor: pointer; font-size: 13.5px; color: var(--text);
}
.nb-nav:hover { background: #eaf2fa; }
.nb-nav.active { background: var(--primary-soft); color: var(--primary-dark); font-weight: 600; }
.nb-nav-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nb-nav-count { font-size: 11.5px; color: var(--text-sub); }
.nb-nav.active .nb-nav-count { color: var(--primary-dark); }

.nb-sec { padding: 2px 0 8px; border-bottom: 1px solid #eef5fa; margin-bottom: 6px; }
.nb-sec-head {
  display: flex; align-items: center; gap: 6px; padding: 4px 10px 6px;
  font-size: 12px; color: var(--text-sub); letter-spacing: .3px;
}
.nb-sec-head span { flex: 1; }
.nb-mini {
  border: 1px solid var(--border); background: #fff; color: var(--text-sub);
  width: 20px; height: 20px; line-height: 1; border-radius: 6px; cursor: pointer; font-size: 13px;
}
.nb-mini:hover { border-color: var(--primary); color: var(--primary); }
.nb-tree-empty { padding: 6px 10px; font-size: 12.5px; color: var(--text-sub); }

.nb-tree-row {
  display: flex; align-items: center; gap: 6px; padding: 6px 8px 6px 10px;
  border-radius: 8px; cursor: pointer; font-size: 13px;
}
.nb-tree-row:hover { background: #eaf2fa; }
.nb-tree-row.active { background: var(--primary-soft); color: var(--primary-dark); font-weight: 600; }
.nb-tree-row.nb-depth-1 { padding-left: 24px; font-size: 12.6px; }
.nb-tree-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nb-tree-count { font-size: 11.5px; color: var(--text-sub); }
.nb-tree-more {
  border: none; background: transparent; color: #a9c0d4; cursor: pointer;
  padding: 0 2px; font-size: 15px; line-height: 1; opacity: 0;
}
.nb-tree-row:hover .nb-tree-more { opacity: 1; }
.nb-tree-more:hover { color: var(--primary); }

/* ---------------- 中栏（列表） ---------------- */
.nb-list-head { padding: 10px 12px 0; flex: none; }
.nb-list-title { font-size: 14px; font-weight: 600; display: flex; align-items: baseline; gap: 6px; }
.nb-list-sub { font-size: 11.5px; color: var(--text-sub); font-weight: 400; }
.nb-list-tools { display: flex; gap: 6px; align-items: center; margin-top: 8px; }
.nb-search { flex: 1; min-width: 0; font-size: 12.8px; padding: 5px 9px; }
.nb-sort { flex: none; width: 96px; font-size: 12.5px; padding: 5px 6px; }
.nb-list-meta {
  padding: 7px 12px; font-size: 11.8px; color: var(--text-sub);
  border-bottom: 1px solid #eef5fa; flex: none;
}
.nb-list-body { flex: 1; overflow-y: auto; }
.nb-list-foot { flex: none; padding: 8px 12px; border-top: 1px solid var(--border); }

.nb-item { padding: 10px 12px; border-bottom: 1px solid #eef5fa; cursor: pointer; }
.nb-item:hover { background: #f7fbfe; }
.nb-item.active { background: var(--primary-soft); box-shadow: inset 3px 0 0 var(--accent); }
.nb-item-top { display: flex; align-items: baseline; gap: 8px; }
.nb-item-title {
  flex: 1; min-width: 0; font-size: 13.4px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nb-item-time { flex: none; font-size: 11px; color: var(--text-sub); }
.nb-item-preview {
  font-size: 12.2px; color: var(--text-sub); margin-top: 3px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  line-height: 1.5;
}
.nb-item-foot { display: flex; align-items: center; gap: 6px; margin-top: 6px; font-size: 11.5px; color: var(--text-sub); flex-wrap: wrap; }
.nb-item-foot .tag { font-size: 11px; padding: 1px 6px; }
.nb-item-acts { display: flex; gap: 6px; margin-top: 8px; }
.nb-dim { color: #93a9bd; }

/* ---------------- 右栏（编辑器） ---------------- */
.nb-edit-head {
  display: flex; align-items: center; gap: 8px; padding: 9px 12px;
  border-bottom: 1px solid var(--border); flex: none;
}
.nb-back {
  border: 1px solid var(--border); background: #fff; border-radius: 8px;
  width: 28px; height: 28px; cursor: pointer; color: var(--text-sub); flex: none;
  display: none;                       /* 桌面端不需要（三栏都看得见）；手机端才显示 */
}
.nb-title-input {
  flex: 1; min-width: 0; border: none; background: transparent; font-size: 15.5px;
  font-weight: 600; padding: 4px 2px; color: var(--text);
}
.nb-title-input:focus { outline: none; box-shadow: inset 0 -2px 0 var(--primary-soft); }
.nb-edit-tools { display: flex; align-items: center; gap: 4px; flex: none; }
.nb-save { font-size: 11.8px; color: var(--text-sub); margin-right: 6px; white-space: nowrap; }
.nb-save.busy { color: var(--warn); }
.nb-save.ok { color: #0f766e; }
.nb-save.err { color: var(--danger); cursor: pointer; text-decoration: underline dotted; }
.nb-tool {
  border: none; background: transparent; cursor: pointer; font-size: 15px;
  width: 30px; height: 30px; border-radius: 8px; color: var(--text-sub); line-height: 1;
}
.nb-tool:hover { background: #eaf2fa; color: var(--primary); }

.nb-toolbar {
  display: flex; align-items: center; gap: 4px; padding: 6px 10px;
  /* 编辑器栏在 1280 宽下只有 ~486px，工具栏约 700px：
     原来横向滚动 ⇒ 工具栏下面挂一条常显的滚动条，看着像「没排好」。
     改为**换行**：宁可占两行，也不让一排按钮被切掉一半。 */
  border-bottom: 1px solid var(--border); flex: none; flex-wrap: wrap; row-gap: 2px;
  background: #fbfdff;
}
.nb-tgroup { display: flex; align-items: center; gap: 2px; padding-right: 6px; margin-right: 6px; border-right: 1px solid #e8f0f7; }
.nb-tgroup:last-child { border-right: none; }
.nb-tbtn {
  border: none; background: transparent; cursor: pointer; min-width: 28px; height: 28px;
  border-radius: 6px; font-size: 13px; color: #2c4a63; line-height: 1; padding: 0 5px;
}
.nb-tbtn:hover { background: #e6f2fb; }
.nb-tbtn.active { background: var(--primary-soft); color: var(--primary-dark); }
.nb-select { height: 28px; font-size: 12.5px; padding: 0 4px; border-radius: 6px; }
/* 字体颜色色板（需求 30）：与通用编辑器同一份 20 色，浮在工具栏下方 */
.nb-tgroup { position: relative; }
.nb-colors {
  position: absolute; top: 32px; left: 0; z-index: 40; width: 214px; padding: 8px;
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px;
  background: #fff; border: 1px solid #dbe8f2; border-radius: 8px;
  box-shadow: 0 8px 24px rgba(15, 50, 80, .14);
}
.nb-colors[hidden] { display: none; }
.nb-c {
  width: 22px; height: 22px; border-radius: 5px; border: 1px solid rgba(0, 0, 0, .12);
  cursor: pointer; padding: 0;
}
.nb-c:hover { transform: scale(1.08); }
.nb-c-def { width: 100%; grid-column: span 7; height: 24px; background: #fff; font-size: 12px; color: #33506b; }

.nb-paper-wrap { flex: 1; overflow-y: auto; background: #eef4f9; padding: 16px; }
.nb-paper {
  --nb-paper-bg: #ffffff;
  position: relative;
  max-width: 820px; margin: 0 auto; min-height: 100%;
  background-color: var(--nb-paper-bg);
  border-radius: 10px; border: 1px solid #e2edf5;
  box-shadow: 0 2px 10px rgba(14, 60, 100, .06);
  padding: 30px 40px 60px;
}
.nb-paper.theme-eye_yellow { --nb-paper-bg: #fdf8e4; border-color: #f0e6bf; }
.nb-paper.theme-eye_green { --nb-paper-bg: #eef8ee; border-color: #d6ebd6; }
.nb-paper.theme-custom { background-color: var(--nb-paper-bg); }
.nb-paper.theme-grid {
  --nb-paper-bg: #fff;
  background-image:
    linear-gradient(#eaf2f8 1px, transparent 1px),
    linear-gradient(90deg, #eaf2f8 1px, transparent 1px);
  background-size: 22px 22px;
}
.nb-paper.theme-lined {
  --nb-paper-bg: #fff;
  background-image: linear-gradient(#e8f1f8 1px, transparent 1px);
  background-size: 100% 30px;
}
.nb-paper.theme-dot {
  --nb-paper-bg: #fff;
  background-image: radial-gradient(#d8e6f0 1.1px, transparent 1.1px);
  background-size: 20px 20px;
}

.nb-editor {
  outline: none; font-size: var(--nb-font, 15px); line-height: 1.95; color: var(--text);
  min-height: 240px; word-break: break-word;
}
.nb-editor:empty::before { content: '开始写…'; color: #b6c8d8; }
.nb-editor h1 { font-size: 1.6em; margin: .6em 0 .4em; }
.nb-editor h2 { font-size: 1.35em; margin: .6em 0 .35em; }
.nb-editor h3 { font-size: 1.15em; margin: .5em 0 .3em; }
.nb-editor p { margin: .4em 0; }
.nb-editor ul, .nb-editor ol { padding-left: 1.5em; margin: .4em 0; }
.nb-editor blockquote {
  margin: .6em 0; padding: 4px 14px; border-left: 3px solid #cfe4f5;
  color: #5a738a; background: #f7fbfe; border-radius: 0 6px 6px 0;
}
.nb-editor pre {
  background: #f4f8fc; border: 1px solid #e6eef6; border-radius: 8px;
  padding: 10px 12px; overflow-x: auto; font-size: .92em; line-height: 1.7;
}
.nb-editor img, .nb-img { max-width: 100%; border-radius: 6px; margin: 6px 0; display: block; }
.nb-editor a { color: var(--primary); }
.nb-editor hr { border: none; border-top: 1px solid #e2edf5; margin: 14px 0; }
/* 待办项：`contenteditable=false` 的方框，点一下勾上（逻辑在 views/notes.js） */
.nb-task { display: flex; align-items: flex-start; gap: 7px; margin: .25em 0; }
.nb-task .nb-box {
  flex: none; cursor: pointer; user-select: none; color: #7c96ab;
  font-size: 1.05em; line-height: 1.6;
}
.nb-task.is-done .nb-box { color: var(--success); }
.nb-task.is-done > *:not(.nb-box) { color: #9aabbb; text-decoration: line-through; }

.nb-status {
  flex: none; border-top: 1px solid var(--border); padding: 6px 12px;
  display: flex; align-items: center; gap: 6px; font-size: 11.8px; color: var(--text-sub);
}
.nb-status-right { margin-left: auto; display: flex; gap: 10px; }
.nb-link { border: none; background: none; color: var(--primary); cursor: pointer; font-size: 12px; padding: 0; }
.nb-link:hover { color: var(--primary-dark); }

/* 空态 / 锁屏 / 阅读 / 评论 / 手写 / 设置 */
.nb-blank { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 30px; }
.nb-blank-ico { font-size: 40px; opacity: .5; }
.nb-blank-title { font-size: 14.5px; font-weight: 600; }
.nb-blank-tip { font-size: 12.5px; color: var(--text-sub); max-width: 420px; text-align: center; line-height: 1.8; }

.nb-lock { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; padding: 30px; }
.nb-lock-ico { font-size: 34px; }
.nb-lock-title { font-size: 15px; font-weight: 600; }
.nb-lock-tip { font-size: 12.5px; color: var(--text-sub); }
.nb-lock-row { display: flex; gap: 8px; margin-top: 6px; }
.nb-lock-input { width: 200px; }
.nb-lock-err { color: var(--danger); font-size: 12.5px; min-height: 18px; }

.nb-read-head {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  border-bottom: 1px solid var(--border); flex: none;
}
.nb-read-title { flex: 1; font-size: 14.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nb-read-tools { display: flex; align-items: center; gap: 8px; flex: none; }
.nb-read-page { font-size: 12px; color: var(--text-sub); }
.nb-read-wrap { flex: 1; overflow-y: auto; background: #eef4f9; padding: 16px; }
.nb-paper-read { padding: 34px 44px; font-size: var(--nb-font, 16px); line-height: 2.05; }
.nb-paper-read img { max-width: 100%; border-radius: 6px; }
.nb-paper-read h1, .nb-paper-read h2, .nb-paper-read h3 { line-height: 1.4; }

.nb-comments { flex: none; border-top: 1px solid var(--border); padding: 10px 12px; max-height: 220px; overflow-y: auto; }
.nb-comments-head { font-size: 12.5px; font-weight: 600; margin-bottom: 6px; }
.nb-comments-list { max-height: 120px; overflow-y: auto; }
.nb-comment { padding: 5px 0; border-bottom: 1px dashed #eef5fa; font-size: 12.8px; }
.nb-comment-who { font-weight: 600; margin-right: 6px; }
.nb-comment-body { color: #33506b; }
.nb-comment-form { display: flex; gap: 6px; margin-top: 8px; }
.nb-comment-form input { flex: 1; }

.nb-ink-holder { margin-top: 14px; }
.nb-ink-bar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 6px 0; border-top: 1px dashed #dbe8f2; }
.nb-ink-label { font-size: 12.5px; color: var(--text-sub); }
/* 手写面板的小控件：不做约束时 select 会被全局 `width:100%` 撑满一整行，
   面板看着像「一排控件塌了」（第一百三十一轮界面取证时实测到）。 */
.nb-ink-bar select { width: auto; min-width: 62px; }
.nb-ink-bar input[type="color"] { width: 34px; height: 26px; padding: 0 2px; border: 1px solid #dbe8f2; border-radius: 6px; background: #fff; }
/* 画布：默认 canvas 是 inline 元素，不加这条会按位图宽度撑出去（可视区外） */
.nb-canvas { display: block; width: 100%; margin-top: 8px; border: 1px dashed #cfe0ee; border-radius: 8px; background: #fff; touch-action: none; }
.nb-canvas { width: 100%; background: #fff; border: 1px dashed #c7dbe9; border-radius: 8px; touch-action: none; display: block; cursor: crosshair; }
.nb-ink-view { border: 1px dashed #dbe8f2; border-radius: 8px; overflow: hidden; background: #fff; }
.nb-ink-view canvas { width: 100%; display: block; }
.nb-ink-hint { font-size: 11.5px; color: var(--text-sub); padding: 2px 8px 6px; }

.nb-themes { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px; }
.nb-theme-card {
  height: 68px; border: 2px solid var(--border); border-radius: 10px; cursor: pointer;
  display: flex; align-items: flex-end; justify-content: center; padding-bottom: 6px;
  background-color: #fff; font-size: 12px; color: #45637d;
}
.nb-theme-card.theme-eye_yellow { background-color: #fdf8e4; }
.nb-theme-card.theme-eye_green { background-color: #eef8ee; }
.nb-theme-card.theme-grid { background-image: linear-gradient(#e3eef7 1px, transparent 1px), linear-gradient(90deg, #e3eef7 1px, transparent 1px); background-size: 12px 12px; }
.nb-theme-card.theme-lined { background-image: linear-gradient(#e3eef7 1px, transparent 1px); background-size: 100% 14px; }
.nb-theme-card.theme-dot { background-image: radial-gradient(#cfdde9 1px, transparent 1px); background-size: 10px 10px; }
.nb-theme-card.theme-custom { background-image: linear-gradient(135deg, #fff 0%, #ffe9c7 100%); }
.nb-theme-card.active { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }

.nb-pick { max-height: 210px; overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; padding: 4px 6px; margin-top: 6px; }
.nb-pick-row { display: flex; align-items: center; gap: 7px; padding: 5px 4px; font-size: 13px; cursor: pointer; border-radius: 6px; }
.nb-pick-row:hover { background: #f4fafe; }
.nb-pick-row .nb-dim { font-size: 11.5px; margin-left: auto; }
.nb-radio { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; cursor: pointer; }

.nb-menu { display: flex; flex-direction: column; }
.nb-menu-item {
  border: none; background: transparent; text-align: left; padding: 9px 8px; border-radius: 8px;
  cursor: pointer; font-size: 13.4px; color: var(--text);
}
.nb-menu-item:hover { background: #eef7fd; }
.nb-menu-item.danger { color: var(--danger); }
.nb-menu-item:disabled { opacity: .45; cursor: not-allowed; }

.nb-ver-list { max-height: 340px; overflow-y: auto; }
.nb-ver-row { display: flex; align-items: center; gap: 10px; padding: 8px 2px; border-bottom: 1px solid #eef5fa; }
.nb-ver-row > div:first-child { flex: 1; min-width: 0; }

.nb-tabs { display: flex; gap: 4px; }
.nb-tab {
  border: 1px solid var(--border); background: #fff; border-radius: 20px;
  padding: 3px 12px; font-size: 12.4px; cursor: pointer; color: var(--text-sub);
}
.nb-tab.active { background: var(--primary-soft); border-color: #b9dcf3; color: var(--primary-dark); font-weight: 600; }

.nb-settings .card { margin-bottom: 14px; }
.nb-set-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 12px 18px; margin-bottom: 10px; }
.nb-stats { display: flex; flex-wrap: wrap; gap: 10px; }
.nb-stat {
  flex: 1 1 100px; min-width: 96px; padding: 10px 12px; background: #f7fbfe;
  border: 1px solid #e6f1f9; border-radius: 10px; text-align: center;
}
.nb-stat b { display: block; font-size: 19px; color: var(--primary-dark); }
.nb-stat span { font-size: 12px; color: var(--text-sub); }

/* ---------------- 手机 / 平板 ---------------- */
@media (max-width: 1000px) {
  .nb-shell { height: auto; min-height: 0; flex-wrap: wrap; }
  .nb-side { flex: 1 1 100%; width: auto; max-height: 240px; }
  .nb-listcol { flex: 1 1 100%; width: auto; max-height: 320px; }
  .nb-main { flex: 1 1 100%; min-height: 420px; }
  /* 打开了笔记就把左右两栏收起来 —— 手机屏幕放不下三栏 */
  .nb-shell.nb-has-note .nb-side,
  .nb-shell.nb-has-note .nb-listcol { display: none; }
  .nb-shell.nb-has-note .nb-main { min-height: 60vh; }
  .nb-back { display: inline-flex; align-items: center; justify-content: center; }
  .nb-paper { padding: 20px 16px 40px; }
  .nb-paper-read { padding: 22px 18px; }
}

/* ============================================================================
 * 渠道授权 · 数字大屏（第一百四十轮，2026-10-30 林总）
 * ----------------------------------------------------------------------------
 * 全部类名以 `cb-` 开头、且样式都挂在 `#cbRoot` 下 —— 大屏是深色底，绝不能泄漏到
 * 全站（本项目历史上吃过「写全局选择器改掉上百张表」的亏，见 README §62）。
 * 尺寸用 grid + minmax(0,1fr)：`minmax(0,…)` 不能去掉，否则长单位名会把列撑破。
 * ========================================================================== */
#cbRoot.cb-root {
  --cb-bg: #050b14;
  --cb-panel: rgba(9, 22, 38, .74);
  --cb-line: rgba(56, 189, 248, .15);
  --cb-accent: #22d3ee;
  --cb-text: #e6f6ff;
  --cb-sub: #7fa6c0;
  position: relative; margin: -18px; padding: 16px 18px 12px;
  min-height: 620px;
  background:
    radial-gradient(1200px 520px at 12% -10%, rgba(34, 211, 238, .10), transparent 60%),
    radial-gradient(900px 460px at 88% 8%, rgba(167, 139, 250, .10), transparent 62%),
    radial-gradient(700px 420px at 50% 120%, rgba(56, 189, 248, .08), transparent 60%),
    var(--cb-bg);
  color: var(--cb-text);
  overflow: hidden;
  font-variant-numeric: tabular-nums;
}
/* 细扫描线纹理（不挡点击：pointer-events:none） */
#cbRoot .cb-scan {
  position: absolute; inset: 0; pointer-events: none; opacity: .55;
  background: repeating-linear-gradient(0deg, rgba(56, 189, 248, .028) 0 1px, transparent 1px 3px);
}
#cbRoot .cb-head {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 14px;
  padding-bottom: 10px; border-bottom: 1px solid var(--cb-line); flex-wrap: wrap;
}
#cbRoot .cb-title {
  margin: 0; font-size: 22px; font-weight: 700; letter-spacing: 1.2px;
  background: linear-gradient(90deg, #9beaff 0%, #38bdf8 55%, #a78bfa 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 0 14px rgba(56, 189, 248, .35));
}
#cbRoot .cb-sub { margin-top: 3px; font-size: 12px; color: var(--cb-sub); letter-spacing: .6px; }
#cbRoot .cb-head-right { display: flex; align-items: center; gap: 14px; }
#cbRoot .cb-live { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #9fe8c8; }
#cbRoot .cb-dot {
  width: 7px; height: 7px; border-radius: 50%; background: #34d399;
  box-shadow: 0 0 10px rgba(52, 211, 153, .9); animation: cbPulse 1.9s ease-in-out infinite;
}
#cbRoot .cb-clock {
  font-family: ui-monospace, Consolas, monospace; font-size: 18px; letter-spacing: 1.5px;
  color: #a8e9ff; text-shadow: 0 0 12px rgba(56, 189, 248, .45);
}
#cbRoot .cb-btn {
  border: 1px solid rgba(56, 189, 248, .38); background: rgba(56, 189, 248, .08);
  color: #b6ecff; border-radius: 7px; padding: 5px 12px; font-size: 12px; cursor: pointer;
  transition: all .18s ease;
}
#cbRoot .cb-btn:hover { background: rgba(56, 189, 248, .2); box-shadow: 0 0 12px rgba(56, 189, 248, .35); }
#cbRoot .cb-btn-sm { padding: 2px 8px; font-size: 11px; }

#cbRoot .cb-body {
  display: grid; grid-template-columns: 208px minmax(0, 1.06fr) minmax(318px, .7fr);
  gap: 12px; margin-top: 12px; align-items: start;
}
#cbRoot .cb-col { min-width: 0; }
#cbRoot .cb-col-kpi { display: flex; flex-direction: column; gap: 9px; }

#cbRoot .cb-kpi {
  position: relative; padding: 11px 13px; border-radius: 10px;
  background: linear-gradient(158deg, rgba(16, 38, 60, .96), rgba(8, 20, 34, .96));
  border: 1px solid var(--cb-line); overflow: hidden;
  animation: cbFadeUp .5s ease both;
}
#cbRoot .cb-kpi::before {
  content: ''; position: absolute; left: 0; top: 10px; bottom: 10px; width: 2px;
  background: linear-gradient(180deg, #22d3ee, rgba(34, 211, 238, 0));
}
#cbRoot .cb-kpi:nth-child(1) { animation-delay: .02s; }
#cbRoot .cb-kpi:nth-child(2) { animation-delay: .06s; }
#cbRoot .cb-kpi:nth-child(3) { animation-delay: .10s; }
#cbRoot .cb-kpi:nth-child(4) { animation-delay: .14s; }
#cbRoot .cb-kpi:nth-child(5) { animation-delay: .18s; }
#cbRoot .cb-kpi:nth-child(6) { animation-delay: .22s; }
#cbRoot .cb-clickable { cursor: pointer; transition: transform .18s ease, border-color .18s ease; }
#cbRoot .cb-clickable:hover { transform: translateY(-2px); border-color: rgba(56, 189, 248, .55); }
#cbRoot .cb-kpi-label { font-size: 11.5px; color: var(--cb-sub); letter-spacing: .5px; }
#cbRoot .cb-kpi-value {
  font-size: 29px; font-weight: 700; line-height: 1.15; color: #eafaff;
  text-shadow: 0 0 18px rgba(56, 189, 248, .42); font-variant-numeric: tabular-nums;
}
#cbRoot .cb-kpi-main .cb-kpi-value { font-size: 34px; }
#cbRoot .cb-kpi-ok .cb-kpi-value { color: #6ee7b7; text-shadow: 0 0 18px rgba(52, 211, 153, .45); }
#cbRoot .cb-kpi-warn .cb-kpi-value { color: #fcd34d; text-shadow: 0 0 18px rgba(251, 191, 36, .45); }
#cbRoot .cb-kpi-danger .cb-kpi-value { color: #fda4af; text-shadow: 0 0 18px rgba(251, 113, 133, .45); }
#cbRoot .cb-kpi-note { margin-top: 2px; font-size: 10.5px; color: rgba(127, 166, 192, .9); }
#cbRoot .cb-kpi-hint {
  position: absolute; right: 9px; bottom: 7px; font-style: normal; font-size: 9.5px;
  color: rgba(34, 211, 238, .75);
}

#cbRoot .cb-panel {
  position: relative; padding: 11px 12px; border-radius: 12px;
  background: var(--cb-panel); border: 1px solid var(--cb-line);
  backdrop-filter: blur(2px); min-width: 0;
}
#cbRoot .cb-panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 7px;
}
#cbRoot .cb-panel-head h3 {
  margin: 0; font-size: 13.5px; font-weight: 600; color: #cfefff; letter-spacing: .5px;
  display: flex; align-items: center; gap: 7px;
}
#cbRoot .cb-panel-head h3::before {
  content: ''; width: 3px; height: 12px; border-radius: 2px;
  background: linear-gradient(180deg, #22d3ee, #38bdf8); box-shadow: 0 0 8px rgba(34, 211, 238, .7);
}
#cbRoot .cb-mini { font-size: 10.5px; color: var(--cb-sub); }
#cbRoot .cb-badge {
  font-size: 11px; padding: 1px 8px; border-radius: 10px;
  background: rgba(34, 211, 238, .14); color: #a5eeff; border: 1px solid rgba(34, 211, 238, .3);
}
#cbRoot .cb-chip {
  font-size: 10.5px; padding: 1px 7px; border-radius: 5px;
  background: rgba(56, 189, 248, .1); color: #9fd8ee;
}
#cbRoot .cb-legend-bar {
  width: 62px; height: 7px; border-radius: 4px;
  background: linear-gradient(90deg, #12324a, #22d3ee);
}

#cbRoot .cb-col-map { display: grid; grid-template-rows: minmax(0, 1fr) auto; gap: 12px; }
#cbRoot .cb-panel-map { display: flex; flex-direction: column; }
#cbRoot .cb-map-tools { display: flex; align-items: center; gap: 6px; }
#cbRoot .cb-map-wrap { position: relative; flex: 1; min-height: 424px; }
#cbRoot #cbMap { display: block; width: 100%; height: 100%; }
#cbRoot .cb-tip {
  position: absolute; display: none; pointer-events: none; z-index: 3;
  background: rgba(3, 12, 22, .94); border: 1px solid rgba(56, 189, 248, .45);
  border-radius: 6px; padding: 3px 8px; font-size: 11.5px; color: #d9f6ff; white-space: nowrap;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .5);
}
#cbRoot .cb-map-empty {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 12px; color: var(--cb-sub); pointer-events: none;
}
#cbRoot .cb-map-foot {
  display: flex; justify-content: space-between; gap: 8px; margin-top: 5px;
  font-size: 10.5px; color: rgba(127, 166, 192, .85);
}
#cbRoot .cb-panel-drill { max-height: 262px; display: flex; flex-direction: column; }
#cbRoot .cb-drill { overflow: auto; flex: 1; padding-right: 2px; }
#cbRoot .cb-item {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: 'l r' 'm m'; gap: 1px 8px;
  padding: 6px 0; border-bottom: 1px dashed rgba(56, 189, 248, .1);
}
#cbRoot .cb-item-l { grid-area: l; display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 12.5px; }
#cbRoot .cb-item-l b { font-weight: 500; color: #e2f6ff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#cbRoot .cb-item-brand {
  flex: none; font-size: 10px; padding: 0 5px; border-radius: 3px;
  border: 1px solid rgba(56, 189, 248, .35); color: #8fd6f0;
}
#cbRoot .cb-item-r { grid-area: r; text-align: right; display: flex; flex-direction: column; align-items: flex-end; }
#cbRoot .cb-item-meta { grid-area: m; font-size: 10.5px; color: rgba(127, 166, 192, .85); }
#cbRoot .cb-st { width: 6px; height: 6px; border-radius: 50%; flex: none; box-shadow: 0 0 6px currentColor; }

#cbRoot .cb-col-rank { display: flex; flex-direction: column; gap: 12px; }
#cbRoot .cb-tabs { display: flex; gap: 4px; border-bottom: 1px solid rgba(56, 189, 248, .12); margin-bottom: 6px; }
#cbRoot .cb-tab {
  background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer;
  padding: 5px 8px; font-size: 12px; color: var(--cb-sub); transition: color .16s ease;
}
#cbRoot .cb-tab:hover { color: #a5eeff; }
#cbRoot .cb-tab.cb-on {
  color: #a5eeff; border-bottom-color: #22d3ee; text-shadow: 0 0 10px rgba(34, 211, 238, .55);
}
#cbRoot .cb-rank { overflow: auto; max-height: 286px; }
#cbRoot .cb-rank-item {
  display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; gap: 8px; align-items: center;
  padding: 6px 0; border-bottom: 1px dashed rgba(56, 189, 248, .09);
}
#cbRoot .cb-rank-bad { background: linear-gradient(90deg, rgba(251, 113, 133, .07), transparent 70%); }
#cbRoot .cb-rank-no { font-size: 11px; color: var(--cb-sub); text-align: center; }
#cbRoot .cb-rank-hot { color: #22d3ee; font-weight: 700; text-shadow: 0 0 8px rgba(34, 211, 238, .6); }
#cbRoot .cb-rank-main { min-width: 0; }
#cbRoot .cb-rank-1 b {
  font-size: 12.5px; font-weight: 500; color: #e8f8ff;
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#cbRoot .cb-rank-2 { font-size: 10.5px; color: rgba(127, 166, 192, .9); margin-top: 1px; }
#cbRoot .cb-rank-type {
  margin-left: 4px; padding: 0 4px; border-radius: 3px;
  border: 1px solid rgba(56, 189, 248, .28); color: #8fd6f0;
}
#cbRoot .cb-rank-right { text-align: right; display: flex; flex-direction: column; align-items: flex-end; }
#cbRoot .cb-rank-date { font-size: 10.5px; color: rgba(127, 166, 192, .8); }
#cbRoot .cb-num { font-size: 15px; color: #a5eeff; }
#cbRoot .cb-d-ok { color: #7fe3bd; font-weight: 600; font-size: 12px; }
#cbRoot .cb-d-mid { color: #fcd34d; font-weight: 600; font-size: 12px; }
#cbRoot .cb-d-warn { color: #fbbf24; font-weight: 700; font-size: 12px; }
#cbRoot .cb-d-done { color: #fda4af; font-weight: 700; font-size: 12px; }
#cbRoot .cb-d-na { color: #94a3b8; font-weight: 600; font-size: 12px; }
#cbRoot .cb-buckets { display: flex; flex-direction: column; gap: 6px; }
#cbRoot .cb-bucket {
  display: grid; grid-template-columns: 60px minmax(0, 1fr) 34px; gap: 6px; align-items: center;
  font-size: 11px; color: var(--cb-sub);
}
#cbRoot .cb-bk-bar { height: 7px; border-radius: 4px; background: rgba(56, 189, 248, .08); overflow: hidden; }
#cbRoot .cb-bk-bar i {
  display: block; height: 100%; border-radius: 4px;
  background: linear-gradient(90deg, #22d3ee, #a78bfa);
  box-shadow: 0 0 8px rgba(34, 211, 238, .5);
  transition: width .6s cubic-bezier(.2, .8, .2, 1);
}
#cbRoot .cb-bk-num { text-align: right; color: #cfefff; }

#cbRoot .cb-row2 {
  grid-column: 1 / -1; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px;
}
#cbRoot .cb-donut-wrap { display: flex; align-items: center; gap: 10px; }
#cbRoot .cb-donut { flex: none; display: block; }
#cbRoot .cb-legend { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; font-size: 11px; }
#cbRoot .cb-lg-row { display: grid; grid-template-columns: 8px minmax(0, 1fr) auto; gap: 5px; align-items: center; }
#cbRoot .cb-lg-dot { width: 8px; height: 8px; border-radius: 2px; }
#cbRoot .cb-lg-name { color: #dff4ff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#cbRoot .cb-lg-num { color: #a5eeff; font-weight: 700; }
#cbRoot .cb-lg-sub { grid-column: 2 / -1; font-size: 10px; color: rgba(127, 166, 192, .85); }
#cbRoot .cb-chart-wrap { height: 148px; position: relative; }
#cbRoot .cb-chart { display: block; }
#cbRoot .cb-regions { display: flex; flex-direction: column; gap: 5px; max-height: 160px; overflow: auto; }
#cbRoot .cb-region {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) 26px; gap: 7px; align-items: center;
  font-size: 12px; cursor: pointer; padding: 2px 3px; border-radius: 5px; transition: background .16s ease;
}
#cbRoot .cb-region:hover { background: rgba(56, 189, 248, .1); }
#cbRoot .cb-rg-name { color: #dff4ff; }
#cbRoot .cb-rg-bar { height: 6px; border-radius: 4px; background: rgba(56, 189, 248, .08); overflow: hidden; }
#cbRoot .cb-rg-bar i {
  display: block; height: 100%; border-radius: 4px;
  background: linear-gradient(90deg, #38bdf8, #22d3ee); box-shadow: 0 0 8px rgba(34, 211, 238, .45);
}
#cbRoot .cb-rg-num { text-align: right; color: #a5eeff; }
#cbRoot .cb-rg-foot {
  margin-top: 4px; padding-top: 4px; border-top: 1px dashed rgba(56, 189, 248, .12);
  font-size: 10.5px; color: rgba(127, 166, 192, .9);
}
#cbRoot .cb-rg-warn { color: #fbbf24; }

#cbRoot .cb-foot {
  display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  margin-top: 10px; padding-top: 6px; border-top: 1px solid rgba(56, 189, 248, .1);
  font-size: 10.5px; color: rgba(127, 166, 192, .75);
}
#cbRoot .cb-src { color: rgba(127, 166, 192, .55); }
#cbRoot .cb-loading, #cbRoot .cb-empty {
  padding: 26px 10px; text-align: center; font-size: 12px; color: var(--cb-sub);
}
#cbRoot .cb-error {
  margin: 28px auto; max-width: 460px; padding: 16px 18px; border-radius: 10px;
  border: 1px solid rgba(251, 113, 133, .4); background: rgba(251, 113, 133, .08);
  color: #ffd7dd; font-size: 13px; text-align: center; line-height: 1.7;
}
#cbRoot .cb-error span { font-size: 11.5px; color: rgba(255, 215, 221, .7); }

@keyframes cbPulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.55); opacity: .45; }
}
@keyframes cbFadeUp {
  from { opacity: 0; transform: translateY(7px); }
  to { opacity: 1; transform: none; }
}

/* 窄屏：先并成两列（KPI 横排、地图吃满宽度），再退成一列 */
@media (max-width: 1380px) {
  #cbRoot .cb-body { grid-template-columns: 190px minmax(0, 1fr); }
  #cbRoot .cb-col-rank { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr; }
  #cbRoot .cb-row2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 960px) {
  #cbRoot.cb-root { margin: -12px; padding: 12px; }
  #cbRoot .cb-body { grid-template-columns: minmax(0, 1fr); }
  #cbRoot .cb-col-kpi { flex-direction: row; flex-wrap: wrap; }
  #cbRoot .cb-kpi { flex: 1 1 132px; }
  #cbRoot .cb-col-rank { grid-template-columns: 1fr; }
  #cbRoot .cb-row2 { grid-template-columns: minmax(0, 1fr); }
  #cbRoot .cb-map-wrap { min-height: 280px; }
}

/* ============================================================================
   区域多选器（第一百四十二轮，2026-10-30 林总）
   新增证书的「授权区域」从自由填写改为省级行政区勾选，可多选、可检索。
   ⚠️ 一律用主题变量（--primary / --border / --text-sub）而不是写死色值 ——
      本文件下半段还有一套换肤变量（2593 行起），写死就换肤时露馅。
   ========================================================================== */
.rp { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--card); }
.rp-head { display: flex; align-items: center; gap: 8px; padding: 8px; border-bottom: 1px solid var(--border); background: var(--bg); }
.rp-q { flex: 1 1 auto; min-width: 0; border: 1px solid var(--border); border-radius: 8px; padding: 6px 10px; font-size: 13px; background: var(--card); color: var(--text); }
.rp-q:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.rp-count { flex: 0 0 auto; font-size: 12px; color: var(--text-sub); white-space: nowrap; }
.rp-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px; border-bottom: 1px dashed var(--border); min-height: 34px; }
.rp-chip { display: inline-flex; align-items: center; gap: 2px; background: var(--primary-soft); color: var(--primary-dark); border: 1px solid var(--border); border-radius: 999px; padding: 2px 4px 2px 10px; font-size: 12px; }
.rp-x { border: 0; background: transparent; color: inherit; cursor: pointer; font-size: 14px; line-height: 1; padding: 0 3px; border-radius: 50%; }
.rp-x:hover { background: rgba(0, 0, 0, .06); }
.rp-null { font-size: 12px; color: var(--text-sub); }
.rp-list { max-height: 210px; overflow: auto; padding: 6px; display: grid; grid-template-columns: repeat(auto-fill, minmax(152px, 1fr)); gap: 2px 6px; }
.rp-item { display: flex; align-items: center; gap: 6px; padding: 5px 8px; border-radius: 6px; font-size: 13px; cursor: pointer; color: var(--text); }
.rp-item:hover { background: var(--bg); }
.rp-item.on { background: var(--primary-soft); font-weight: 600; }
.rp-item input { width: auto; margin: 0; flex: 0 0 auto; }
.rp-tip { font-style: normal; font-size: 11px; color: var(--text-sub); }
.rp-empty { padding: 12px; font-size: 12px; color: var(--text-sub); grid-column: 1 / -1; }
