/* ============================================================
   团队信息收集管家 — 样式（M5 界面改版）
   规范：主色 #4a90d9 / 动画 ≤0.5s / 移动端断点 600px
   结构：两级导航 —— 顶栏（logo + 空间切换器 + 用户）
         平台视图（空间列表 / 目录 / 我的）
         空间工作台（左栏：概览 / 资料 / 待办，底部：成员 / 账号）
         手机端（≤600px）：底部标签栏 + 底部抽屉 + 悬浮添加
   ============================================================ */

:root {
  --brand: #4a90d9;
  --brand-dark: #3579bd;
  --brand-l: #eaf2fb;
  --ink: #24303d;
  --ink-2: #5b6b7c;
  --ink-3: #8b98a6;
  --line: #e2e8ef;
  --bg: #f5f7fa;
  --card: #ffffff;
  --ok: #16a34a;
  --warn: #d97706;
  --err: #dc2626;
  --radius: 10px;
  --shadow: 0 1px 2px rgba(36, 48, 61, .06), 0 4px 14px rgba(36, 48, 61, .05);
  --dur: .25s;
  /* 手机端底部标签栏的高度：FAB 与批量条的 bottom 都要避开它 */
  --tabbar-h: 62px;

  /* ---------- 事件标签配色（M6 第六批） ----------
     色号 → CSS 变量的映射。库里存的是色号 key（如 'teal'）而不是 hex，
     好处就是这一块可以随时调 —— 深浅两套主题各自调一套，不用动数据库。
     每个颜色给三档：徽章底色 / 徽章字色（AA 对比）/ 左侧色条（更浓）。
     选色标准：色相间隔足够大（常见色觉障碍下仍可分辨）、饱和度与明度统一
     （并排时不至于一个刺眼一个发灰）。池子只 12 个 —— 再多就分不清了。 */
  --tag-indigo-b: #eef0fd; --tag-indigo-f: #3730a3; --tag-indigo-s: #4f46e5;
  --tag-teal-b:   #e2f7f3; --tag-teal-f:   #0f766e; --tag-teal-s:   #14b8a6;
  --tag-amber-b:  #fdf3e0; --tag-amber-f:  #92400e; --tag-amber-s:  #f59e0b;
  --tag-rose-b:   #fdecef; --tag-rose-f:   #9f1239; --tag-rose-s:   #f43f5e;
  --tag-violet-b: #f3ecfd; --tag-violet-f: #5b21b6; --tag-violet-s: #8b5cf6;
  --tag-azure-b:  #e8f3fd; --tag-azure-f:  #075985; --tag-azure-s:  #0ea5e9;
  --tag-lime-b:   #f0f9e4; --tag-lime-f:   #3f6212; --tag-lime-s:   #84cc16;
  --tag-orange-b: #fdeee0; --tag-orange-f: #9a3412; --tag-orange-s: #f97316;
  --tag-cyan-b:   #e2f6f9; --tag-cyan-f:   #155e75; --tag-cyan-s:   #06b6d4;
  --tag-pink-b:   #fdebf3; --tag-pink-f:   #9d174d; --tag-pink-s:   #ec4899;
  --tag-green-b:  #e7f7ec; --tag-green-f:  #14532d; --tag-green-s:  #22c55e;
  --tag-fuchsia-b:#fbeafa; --tag-fuchsia-f:#86198f; --tag-fuchsia-s:#d946ef;
  /* 账号事件固定用中性色：需求要求「标签事件与账号事件以不同色彩区分」，
     账号是树的枝干、标签是分类维度，用中性色做枝干才不会喧宾夺主。 */
  --tag-slate-b:  #eef2f7; --tag-slate-f:  #475569; --tag-slate-s:  #64748b;
}

* { box-sizing: border-box; }

/* hidden 属性必须真的能隐藏元素。
   ★ 为什么需要这么一条：本项目多处给元素设了 display（.modal{display:flex}、
   .menu{display:block} 等）—— 这些类选择器与 [hidden] 属性选择器**特异性相同**，
   而作者样式优先于浏览器默认样式，于是 el.hidden = true 完全失效：
   元素明明标了隐藏，照样显示。实测踩过一次：一进页面就弹出空的遮罩层把界面盖住。
   用 !important 一次性钉死，避免以后再加 display 时重蹈覆辙。 */
[hidden] { display: none !important; }

/* ================= 防溢出加固层（M5.8） =================
   真机反馈：内容横向超出手机屏。脚本扫描没抓到，因为 mock 数据全是
   规整中文 —— 真实数据里有「无空格长串」（长 URL、长文件名、连续
   英文数字），落在既无 ellipsis 也无断行规则的容器里就把整页撑宽。
   三层防御：
   ① 全局 overflow-wrap：只在放不下时才断，正常中文与短词零影响；
   ② 媒体对象不许超过容器宽；
   ③ html 上 overflow-x:clip 作最后防线 —— 必须用 clip 不用 hidden：
      hidden 会把 body 变成滚动容器、弄坏顶栏的 position:sticky。 */
body { overflow-wrap: break-word; }
img, video, canvas { max-width: 100%; }
html { overflow-x: clip; }
/* ④ 会渲染**任意用户内容**的文本块（M6 第十四批实测补齐）：
   第 ① 条的 `overflow-wrap: break-word` **不会**降低 min-content，而 flex/grid 项的
   自动最小尺寸取的正是 min-content。实测：一条含 120 字符长 URL 的 AI 摘要
   （`…/admission/statistics/2026/Q1/detail?token=…&utm_source=internal`）把
   .mat-summary 的 min-content 顶到 427px；手机上 .item 是 column + align-items:flex-start，
   .item-main 按 fit-content 取宽 → 整列 427px（视口 390px），右侧 56px 被
   html 的 overflow-x:clip 静默切掉，而 .item-main{min-width:0} 一点也压不住。
   `overflow-wrap: anywhere` 才真正参与 min-content 计算；它只改「实在放不下时怎么断」，
   不改 max-content，所以正常短文本一行都不变。 */
.pill, .tag,
.mat-summary, .mat-preview, .mat-textbox, .hit-line, .sync-note { overflow-wrap: anywhere; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 14px/1.6 -apple-system, BlinkMacSystemFont, "PingFang SC",
        "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", sans-serif;
}

.wrap { width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 20px; }
.grow { flex: 1; }

/* ================= 顶栏 ================= */
.topbar {
  background: var(--card);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 30;
}
.topbar-in {
  width: 100%; max-width: 1200px; margin: 0 auto; padding: 0 20px;
  display: flex; align-items: center; gap: 14px; height: 58px;
}
.brand {
  display: flex; align-items: center; gap: 10px; min-width: 0;
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: inherit; text-align: left;
}
.brand-logo {
  width: 36px; height: 36px; flex: none;
  border-radius: 10px;
  background: linear-gradient(135deg, var(--brand), #67b0f0);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 3px 8px rgba(74, 144, 217, .3);
}
.brand-logo svg { width: 20px; height: 20px; }
.brand-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.brand-text strong { font-weight: 600; font-size: 14.5px; white-space: nowrap; }
.brand-text em { font-style: normal; font-size: 11.5px; color: var(--ink-3); }

/* 空间切换器（电脑端） */
.space-wrap { position: relative; }
.space-switch {
  display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--line); background: var(--brand-l);
  color: var(--brand-dark); border-radius: 999px;
  padding: 7px 14px; font: inherit; font-size: 13px; font-weight: 500;
  cursor: pointer; max-width: 320px;
  transition: border-color var(--dur), box-shadow var(--dur);
}
.space-switch:hover { border-color: var(--brand); }
.space-switch .ss-ico { display: flex; }
.space-switch .ss-ico svg { width: 14px; height: 14px; }
.ss-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ss-caret { width: 13px; height: 13px; flex: none; }

/* 下拉菜单（空间切换器 / 添加菜单共用） */
.menu {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 50;
  min-width: 240px;
  /* 菜单是绝对定位：超宽不会撑破父容器，而是直接伸出屏幕右缘制造横向滚动 */
  max-width: min(320px, calc(100vw - 20px));
  background: var(--card); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 10px 32px rgba(36, 48, 61, .16);
  padding: 6px;
  animation: pop-in .18s ease-out;
}
.menu-it {
  display: flex; align-items: center; gap: 10px; width: 100%;
  border: 0; background: none; text-align: left; cursor: pointer;
  padding: 9px 10px; border-radius: 8px;
  font: inherit; font-size: 13px; color: var(--ink);
  transition: background var(--dur);
}
.menu-it:hover { background: var(--bg); }
.menu-it svg { width: 16px; height: 16px; flex: none; color: var(--ink-2); }
.menu-it .mi-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
/* 标题 nowrap 但没截断：长空间名会把菜单撑出屏幕（绝对定位元素的溢出直接变成页面横向滚动） */
.menu-it .mi-t {
  font-weight: 500; white-space: nowrap;
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.menu-it .mi-d, .menu-it .menu-d {
  display: block; font-size: 11.5px; color: var(--ink-3); font-weight: 400;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 「+ 添加」贴着右缘：菜单往左展开，别伸出屏幕外 */
.add-wrap .menu, .fab-wrap .menu { left: auto; right: 0; }
.menu-it .cnt { margin-left: auto; font-size: 11.5px; color: var(--ink-3); flex: none; }
.menu-it.on { background: var(--brand-l); color: var(--brand-dark); }
.menu-it:disabled { opacity: .55; cursor: not-allowed; }
.menu-sep { border-top: 1px solid var(--line); margin: 6px 4px; }
.menu-up { bottom: calc(100% + 10px); top: auto; left: 0; right: auto; }

/* 手机端工作台顶栏：返回 + 空间名 */
.back-spaces {
  display: flex; align-items: center; gap: 2px; min-width: 0;
  border: 0; background: none; padding: 4px 6px 4px 0; cursor: pointer;
  font: inherit; color: var(--ink);
}
.back-arrow { font-size: 22px; line-height: 1; color: var(--ink-2); }
.m-space-name {
  font-size: 14.5px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.user-chip {
  font: inherit; font-size: 12px; color: var(--ink-2);
  background: var(--brand-l); border: 0; border-radius: 999px;
  padding: 5px 12px; white-space: nowrap; cursor: pointer;
  max-width: 180px; overflow: hidden; text-overflow: ellipsis;
  transition: background var(--dur), color var(--dur);
}
.user-chip:hover { background: #dde9f7; color: var(--ink); }
.head-right { display: flex; align-items: center; gap: 10px; flex: none; }

/* ================= 主舞台与视图 ================= */
.stage { min-height: calc(100vh - 200px); }
.view { width: 100%; max-width: 1200px; margin: 0 auto; padding: 22px 20px 40px; }
/* 全站统一内容宽度 1200px：空间列表 / 工作台 / 空间目录 / 账号管理 / 页头页脚都对齐这一条线 */
.view-auth { padding-top: 7vh; }
.section-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin: 2px 0 14px; flex-wrap: wrap;
}
.section-head h2 { font-size: 17px; font-weight: 600; margin: 0; }
.head-acts { display: flex; gap: 8px; }

/* ================= 卡片 / 表单 / 按钮 ================= */
.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 18px 20px;
  margin-bottom: 14px;
}
.card h3 { font-size: 14px; font-weight: 500; margin: 0 0 8px; }

.auth-card { max-width: 420px; margin: 0 auto; }
.auth-switch { display: flex; gap: 6px; margin-bottom: 16px; }
.seg {
  flex: 1; appearance: none; border: 1px solid var(--line);
  background: #fbfcfe; color: var(--ink-2); font: inherit;
  padding: 9px 0; border-radius: 8px; cursor: pointer;
  transition: all var(--dur);
}
.seg.on { background: var(--brand-l); border-color: var(--brand); color: var(--brand-dark); font-weight: 500; }

.form label {
  display: block; margin-bottom: 12px;
  font-size: 13px; color: var(--ink-2);
}
.form input,
.form textarea,
.form select {
  display: block; width: 100%; margin-top: 5px;
  padding: 9px 12px; font: inherit; color: var(--ink);
  border: 1px solid var(--line); border-radius: 8px;
  background: #fff; transition: border-color var(--dur), box-shadow var(--dur);
}
.form textarea { resize: vertical; line-height: 1.7; min-height: 96px; }
.form input:focus,
.form textarea:focus,
.form select:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(74, 144, 217, .13);
}
.inline-form { display: grid; gap: 4px; }

.btn {
  appearance: none; border: 1px solid transparent; border-radius: 8px;
  font: inherit; cursor: pointer; padding: 9px 16px;
  transition: background var(--dur), color var(--dur), border-color var(--dur);
}
.btn-sm { padding: 6px 12px; font-size: 13px; }
.btn-primary { background: var(--brand); color: #fff; }
.btn-primary:hover { background: var(--brand-dark); }
.btn-ghost { background: #fff; border-color: var(--line); color: var(--ink-2); }
.btn-ghost:hover { border-color: var(--brand); color: var(--brand); }
.btn-block { width: 100%; margin-top: 4px; }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn-no { color: var(--err); border-color: #f3c7c7; }
.btn-no:hover { background: #fdf1f1; }

.row-actions { display: flex; gap: 8px; margin-top: 4px; }
.linklike {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 400; color: var(--brand);
  float: right; margin-top: 2px;
}
.linklike:hover { text-decoration: underline; }

/* ================= 空间列表 ================= */
/* auto-fit（而非 auto-fill）：空间少时空列塌缩，1 个空间也能铺满列表区，
   不会孤零零占 300px 宽；空间多时与 auto-fill 行为一致 */
.space-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; }
.space-card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 16px 18px; display: flex; flex-direction: column; gap: 8px;
  flex: 1; min-width: 0;   /* 位于 .sc-cq 壳内，撑满壳宽 */
}
/* 容器壳：@container 不能查询容器自身，所以按卡片宽度切横竖排的
   「容器」必须是独立于卡片本体的一层（卡片是它的后代才可被查询） */
.sc-cq {
  container-type: inline-size;
  display: flex; min-width: 0;
}
.space-card .sc-head { display: flex; align-items: center; gap: 12px; }
.space-card .sc-ico {
  width: 42px; height: 42px; flex: none; border-radius: 12px;
  color: #fff; font-size: 17px; font-weight: 600;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, var(--brand), #67b0f0);
  box-shadow: 0 3px 8px rgba(74, 144, 217, .28);
}
.space-card .sc-ico.g { background: linear-gradient(135deg, #16a34a, #4ade80); box-shadow: 0 3px 8px rgba(22, 163, 74, .25); }
.space-card .sc-ico.v { background: linear-gradient(135deg, #8b5cf6, #c084fc); box-shadow: 0 3px 8px rgba(139, 92, 246, .25); }
.space-card .sc-ico.o { background: linear-gradient(135deg, #d97706, #fbbf24); box-shadow: 0 3px 8px rgba(217, 119, 6, .25); }
.space-card .sc-name { min-width: 0; }
.space-card .sc-name h3 { margin: 0; font-size: 14.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.space-card .sc-name p { margin: 2px 0 0; font-size: 12.5px; color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.space-card .meta { display: flex; gap: 6px; flex-wrap: wrap; }
/* ---------- 卡片上的动作按钮（1.13.1 需求④「整齐排列」）----------
   ★ 原来是一行 flex-wrap：按钮按内容定宽，「进入空间」(78px) 与「重命名」
     (62px) 混排，换行之后每行的长短与起止位置都不一样 —— 实测一张卡
     排成三行、每行都参差，看着像没做完。
   ★ 改成**等分网格**：auto-fit + minmax(72px,1fr) 让每一格等宽，
     按钮撑满格子（width:100%），于是所有行严丝合缝、列也上下对齐。
     卡宽 250px 时自然落到 3 列 × 2 行；窄屏 320 是 3 列、更宽是 4 列，
     全由容器宽度决定，不需要再补 @media 断点。
   ★ 72px 是「删除空间」这类四字按钮的实际最小宽（4×13 + 左右 padding）。
     写死一个太小的值（比如 56px）会让四字按钮折成两行 ——
     中文可以在任意两字之间换行，而按钮一折行，整行高度就跳一下。 */
.space-card .sc-acts {
  display: grid; gap: 8px; margin-top: auto; padding-top: 6px;
  /* ★ auto-**fill** 而不是 auto-fit：auto-fit 会把空轨道收掉，
     于是「只有一颗按钮」的卡片（普通成员的退出空间 / 待审核的撤回申请）
     那颗按钮被拉成整行通栏 —— 又宽又扁，和旁边 4 列的卡片完全不像一套。
     auto-fill 保留空轨道，单颗按钮就只占 1/4 宽，右边留白，
     和上面 4 列的行首尾对得上。 */
  grid-template-columns: repeat(auto-fill, minmax(min(72px, 100%), 1fr));
}
.space-card .sc-acts .btn {
  width: 100%; min-width: 0; justify-content: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ---------- 整卡可点进入（1.13.1 需求③）----------
   「进入空间」按钮撤掉之后，卡片本体就是入口。三件事必须一起给，
   否则用户不知道这里能点：
     cursor:pointer     —— 鼠标移上去就有暗示；
     hover 边框/阴影     —— 与全站的可点卡片同一套反馈；
     focus-visible 轮廓  —— 卡片带 tabindex，键盘 Tab 到它时要有可见焦点，
                            否则「键盘能进但看不出停在哪」。
   ★ 只给 .is-enter：待审核 / 已拒绝 / 未加入的卡片点不动，就不该有手型。 */
.space-card.is-enter { cursor: pointer; }
.space-card.is-enter:hover { border-color: #cbd9e8; box-shadow: 0 4px 16px rgba(36, 48, 61, .10); }
.space-card.is-enter:focus-visible {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-l);
}

/* ---------- 空间重命名表单（M6 第八批，需求①）----------
   ★ 宽度用 width:100% 而不是 flex-basis:100%：窄栏下卡片是竖排，
     flex-basis 会落到「主轴高度」上（高度不定，行为要赌浏览器实现）；
     横排时又要靠换行才生效。width 在两种方向下都等于「占满一整行」，
     配合下面容器查询里补的 flex-wrap，横排时它会自己换到第二行。 */
.space-card .sc-rename {
  width: 100%; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding-top: 10px; border-top: 1px dashed var(--line);
}
.space-card .sc-rename input {
  flex: 1 1 160px; min-width: 0;
  padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px;
  font: inherit; font-size: 13.5px; color: var(--ink); background: #fff;
}
.space-card .sc-rename input:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-l);
}
/* 错误提示独占一行：和输入框挤在一行时，一出现就把保存按钮顶到下一行，
   手指刚离开的按钮会跑位（移动端最容易误触）。 */
.space-card .sc-rename .msg { flex: 1 1 100%; margin: 0; }

/* 宽卡横排：auto-fit 会把少量空间拉成宽卡，竖排内容在宽卡里空旷难看。
   用容器查询按「卡片自身宽度」切换 —— 1-2 列的宽卡横排（图标|名称简介|元信息|操作），
   3 列以上的窄卡保持竖排；不支持容器查询的旧浏览器自然落在竖排兜底。 */
@container (min-width: 560px) {
  .space-card { flex-direction: row; align-items: center; gap: 14px 18px; flex-wrap: wrap; }
  .space-card .sc-head { flex: 1; min-width: 0; }
  .space-card .meta { flex: none; }
  /* 横排时动作按钮跟在最后、排成一行。
     ★ 必须把基础规则里的「等分网格」收回去：宽卡横排时 .sc-acts 是
       flex:none（宽度按内容），此时 auto-fit + 1fr 没有确定宽度可等分，
       各浏览器会按 max-content 兜底 —— 看着像行了，换个浏览器又变形。
       显式写 grid-auto-flow:column + max-content 才是「就按内容宽排一行」。 */
  .space-card .sc-acts {
    margin-top: 0; padding-top: 0; flex: none;
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: max-content;
  }
}

.entry-list { display: grid; gap: 8px; }
.entry {
  display: flex; flex-direction: column; gap: 1px; width: 100%;
  border: 1px solid var(--line); background: #fbfcfe; border-radius: 9px;
  padding: 10px 14px; cursor: pointer; font: inherit; text-align: left;
  transition: border-color var(--dur), background var(--dur);
}
.entry:hover { border-color: var(--brand); background: var(--brand-l); }
.entry-t { font-size: 13.5px; font-weight: 500; color: var(--ink); display: flex; align-items: center; gap: 8px; }
.entry-d { font-size: 12px; color: var(--ink-3); }

/* ================= 空间工作台 ================= */
.work { display: flex; align-items: flex-start; width: 100%; max-width: 1200px; margin: 0 auto; }
.rail {
  width: 158px; flex: none; align-self: stretch;
  position: sticky; top: 58px;
  padding: 16px 10px 20px;
  display: flex; flex-direction: column; gap: 3px;
  min-height: calc(100vh - 58px);
}
.rail-item {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  border-radius: 9px; font: inherit; font-size: 13.5px; color: var(--ink-2); cursor: pointer;
  border: 0; background: none; text-align: left; width: 100%;
  transition: background var(--dur), color var(--dur);
}
.rail-item svg { width: 17px; height: 17px; flex: none; }
.rail-item:hover { background: #eef2f7; color: var(--ink); }
.rail-item.on { background: var(--brand-l); color: var(--brand-dark); font-weight: 600; }
.rail-badge {
  margin-left: auto; background: var(--brand); color: #fff; font-size: 11px;
  min-width: 18px; text-align: center; border-radius: 999px; padding: 0 5px; line-height: 1.7;
}
.rail-badge.wait { background: var(--warn); }
/* ★ 1.10.0（需求③）：「成员」已并入主序列（.rail-item），.rail-foot 那条
   「margin-top:auto 贴栏底」的规则随之删除。留着一个贴着栏底的容器，
   等于给后来的人留一个「还能往里塞东西」的位置，而它与手机端标签栏
   的顺序必然对不上。 */

.work-main { flex: 1; min-width: 0; padding: 22px 0 40px 14px; }
.pane-title { display: flex; align-items: baseline; gap: 12px; margin: 0 0 14px; flex-wrap: wrap; }
.pane-title h2 { font-size: 17px; font-weight: 600; margin: 0; }
.pane-sub { font-size: 12.5px; color: var(--ink-3); }

/* ---------- 导出入口（M6 第九批） ---------- */
/* 钉在标题行最右侧：导出是「对当前这一屏做的事」，贴着标题比埋进角落更说得通。
   ★ margin-left:auto 在 flex-wrap 换行之后依然把它推到行尾，不会盖住标题。
   ★ .export-menu 用绝对定位挂在 .pane-export 上（父级 position:relative），
     这样菜单不会把下面的内容顶下去（用 static 会撑开一行、页面跟着抖）。 */
/* ★ 1.13.1 需求②：这里现在是**一组**按钮（导入 + 导出），不是一个。
   改成 flex 行、gap 8px 让两颗按钮等高并排；导出仍放在最后 ——
   .export-menu 靠 right:0 定位在 .pane-export 上，导出在最后时
   菜单的右缘正好与导出按钮的右缘重合，看起来就是从它身上展开的。
   若把导入排在导出右边，菜单会整体右移到导入按钮底下，视觉上像错位。 */
.pane-export { position: relative; margin-left: auto; display: flex; align-items: center; gap: 8px; }
.pane-export .caret { margin-left: 5px; font-size: 10px; opacity: .65; }
/* ★ 1.12.0：成员卡右上角「⋯」弹出的 .mpop 与导出菜单是**同一种浮层**
   （列方向的按钮列表 + 同样的背景/边框/阴影），所以共用这一组规则。
   各写一份的后果不是「长得不一样」而是**改一处漏一处** ——
   比如把 z-index 调高时只改了一个，另一个从此被卡片压住、点不着，
   而它只在「卡片刚好挨着」时才显形。差异只有定位锚点（下面各自覆盖）。 */
.export-menu, .mpop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  display: flex; flex-direction: column; min-width: 184px; padding: 6px;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 10px 28px rgba(36, 48, 61, .16);
}
.export-menu button, .mpop button {
  padding: 9px 12px; border: 0; border-radius: 7px; background: transparent;
  font: inherit; font-size: 13.5px; color: var(--ink); text-align: left;
  cursor: pointer; transition: background var(--dur);
  /* ★ 菜单里的按钮一律不换行：它们是短词（改资料 / 移除），
     窄屏被挤成两行会让人以为点错了项。 */
  white-space: nowrap;
}
.export-menu button:hover, .export-menu button:focus-visible,
.mpop button:hover, .mpop button:focus-visible { background: var(--brand-l); }

/* ================= 概览 ================= */
.ov-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 14px; }
.ov-stat {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 13px 16px; box-shadow: var(--shadow); cursor: pointer; text-align: left;
  font: inherit; transition: border-color var(--dur);
}
.ov-stat:hover { border-color: var(--brand); }
.ov-stat .n { font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; }
.ov-stat .l { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.ov-stat.hot .n { color: var(--err); }
.ov-stat.run .n { color: var(--warn); }
.ov-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
/* 网格项的 min-width:auto = 内容最小宽：概览卡里 ellipsis 截断的长标题/长 URL
   会把这一项撑到 900px+，把整个 ov-grid 顶出视口（真机溢出案之一）。
   解除自动最小尺寸，让卡片能收缩到轨道宽，截断交给内部的 .t。 */
.ov-grid > * { min-width: 0; }
.ov-card { margin-bottom: 0; }
.ov-card h3 { display: flex; align-items: center; justify-content: space-between; }
/* 概览卡的说明行（工作/成员卡顶部的计数）——比正文小一号、不用 .m 的右对齐，
   它是一行说明而不是右侧的元信息。 */
.ov-note { margin: 0 0 4px; font-size: 12.5px; color: var(--ink-3); }
/* 概览 mini-row 里的状态胶囊占位比日期长，允许它收缩换行，别把标题挤没 */
.ov-card .mini-row .m { flex: 0 1 auto; min-width: 0; text-align: right; }
.ov-card .mini-row .m .pill + .pill { margin-left: 4px; }
.ov-admin { border-color: #f3d9a8; background: #fffdf6; }
.mini-row {
  display: flex; align-items: center; gap: 10px; padding: 8px 2px;
  border-bottom: 1px dashed var(--line); font-size: 13px;
}
.mini-row:last-child { border-bottom: 0; }
.mini-row .t {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border: 0; background: none; padding: 0; text-align: left; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--ink);
}
.mini-row .t:hover { color: var(--brand-dark); text-decoration: underline; }
/* 无可跳目标的待办（两个 id 都空）：与按钮同宽同截断，但不是按钮 ——
   不给 cursor:pointer 与 hover 下划线，否则看着能点、点了没反应。 */
.mini-row .t-plain { cursor: default; color: var(--ink-2); }
.mini-row .t-plain:hover { color: var(--ink-2); text-decoration: none; }
.mini-row .m { color: var(--ink-3); font-size: 12px; flex: none; }
/* ---------- 概览成员行右侧：状态 / 角色两列对齐（1.13.1 需求⑤）----------
   ★ 修的是「左缘参差」：两枚胶囊原来按内容定宽（「已通过」56px、「管理员」
     56px、「成员」44px），整组靠右对齐 —— 于是角色是「成员」的那几行，
     整组比「管理员」那行左移 12px，每一行看着都错开一点。
   ★ 做法：给两枚胶囊兜一个**同样的最小宽度**，两列就逐行等宽，
     左缘与右缘同时对齐。min-width 写在 .pill 上而不是给容器定死两列宽 ——
     容器宽度由内容撑开，胶囊换成更长的词（比如以后叫「空间管理员」）时，
     两列会一起变宽，不会把字挤出去。
   ★ 56px = 「已通过」（3 字 + 左右 padding）在 11.5px 字号下的实测宽。
     它只是**下限**：真实宽度更大的词仍然按自己的宽度走，也就是「至少对齐」。 */
.ov-pills {
  display: grid; grid-auto-flow: column; grid-auto-columns: max-content;
  gap: 5px; align-items: center;
}
.ov-pills .pill { min-width: 56px; text-align: center; }
.fico {
  width: 26px; height: 26px; border-radius: 7px; flex: none;
  font-size: 10.5px; font-weight: 600; color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
}
.fico.pdf { background: #ef6b5e; } .fico.doc { background: #4a83d9; } .fico.xls { background: #16a34a; }
.fico.img { background: #9a6bd9; } .fico.lnk { background: #d97706; } .fico.txt { background: #6b7d90; }

/* ================= 资料页工具 ================= */
.mat-tools { display: flex; gap: 10px; margin-bottom: 10px; }
.mat-search {
  flex: 1; display: flex; align-items: center; gap: 8px; min-width: 0;
  background: var(--card); border: 1px solid var(--line); border-radius: 999px;
  padding: 0 16px; height: 38px;
  transition: border-color var(--dur), box-shadow var(--dur);
}
.mat-search:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(74, 144, 217, .13);
}
.mat-search svg { width: 15px; height: 15px; flex: none; color: var(--ink-3); }
.mat-search input {
  flex: 1; min-width: 0; border: 0; outline: none; background: none;
  font: inherit; font-size: 13.5px; color: var(--ink); height: 100%;
}
.mat-search input::placeholder { color: var(--ink-3); }
.add-wrap { position: relative; flex: none; }
.btn-add {
  display: flex; align-items: center; gap: 7px;
  background: var(--brand); color: #fff; border: 0; border-radius: 999px;
  padding: 0 18px; height: 38px; font: inherit; font-size: 13.5px; font-weight: 500; cursor: pointer;
  box-shadow: 0 3px 8px rgba(74, 144, 217, .3);
  transition: background var(--dur);
}
.btn-add:hover { background: var(--brand-dark); }
.btn-add svg { width: 15px; height: 15px; }

.chips-row { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 12px; position: relative; flex-wrap: wrap; }
.chips { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; flex: 1; min-width: 0; }
.chip {
  border: 1px solid var(--line); background: var(--card); border-radius: 999px;
  padding: 5px 13px; font: inherit; font-size: 12.5px; color: var(--ink-2); cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  transition: all var(--dur); white-space: nowrap;
}
.chip .c { color: var(--ink-3); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.chip:hover { border-color: var(--brand); color: var(--brand-dark); }
.chip.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.chip.on .c { color: rgba(255, 255, 255, .8); }
.chip.more { border-style: dashed; color: var(--ink-3); }
.chips-tools { display: flex; gap: 8px; align-items: center; flex: none; margin-left: auto; }
.tbtn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--line); background: var(--card);
  border-radius: 999px; padding: 5px 13px; font: inherit; font-size: 12.5px; color: var(--ink-2);
  cursor: pointer; transition: all var(--dur);
}
.tbtn:hover { border-color: var(--brand); color: var(--brand-dark); }
.tbtn.on { border-color: var(--brand); color: var(--brand-dark); background: var(--brand-l); }
.tbtn svg { width: 13px; height: 13px; }
.tbtn-select { appearance: none; padding-right: 26px;
  background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235b6b7c' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center; background-size: 11px;
}
.tbtn .fn {
  background: var(--brand); color: #fff; font-size: 10.5px;
  min-width: 16px; text-align: center; border-radius: 999px; padding: 0 4px; line-height: 1.6;
}
.view-seg { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; background: var(--card); }
.view-seg button {
  border: 0; background: none; padding: 6px 11px; cursor: pointer;
  color: var(--ink-3); display: flex;
  transition: background var(--dur), color var(--dur);
}
.view-seg button svg { width: 14px; height: 14px; }
.view-seg button + button { border-left: 1px solid var(--line); }
.view-seg button.on { background: var(--brand-l); color: var(--brand-dark); }

/* 筛选面板：电脑端锚在 .chips-row 下；手机端媒体查询里改成底部抽屉 */
.filter-pop {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 40;
  width: 280px; max-height: 72vh; overflow: auto;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 10px 32px rgba(36, 48, 61, .16);
  padding: 14px 16px 12px;
  animation: pop-in .18s ease-out;
}
.filter-pop h5 { margin: 10px 0 6px; font-size: 12px; color: var(--ink-3); font-weight: 500; }
.filter-pop h5:first-child { margin-top: 0; }
.frow { display: flex; gap: 6px; flex-wrap: wrap; }
.fpill {
  border: 1px solid var(--line); background: var(--card); border-radius: 999px;
  padding: 3px 11px; font: inherit; font-size: 12px; color: var(--ink-2); cursor: pointer;
  transition: all var(--dur);
}
.fpill:hover { border-color: var(--brand); color: var(--brand-dark); }
.fpill.on { background: var(--brand-l); border-color: var(--brand); color: var(--brand-dark); font-weight: 500; }
.fp-foot { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--line); }

/* 添加面板（上传/快速记一笔/收藏链接共用容器） */
.add-panel { border-color: #bfd6ee; }
.add-panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.add-panel-head h3 { margin: 0; }

/* ================= 上传区 ================= */
.drop-zone {
  border: 1.5px dashed #c7d5e5;
  border-radius: var(--radius);
  background: #fafcff;
  padding: 26px 20px;
  text-align: center;
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color var(--dur), background var(--dur), color var(--dur);
}
.drop-zone:hover,
.drop-zone:focus-visible {
  border-color: var(--brand);
  background: var(--brand-l);
  color: var(--brand-dark);
  outline: none;
}
.drop-zone.drag {
  border-color: var(--brand);
  border-style: solid;
  background: var(--brand-l);
  color: var(--brand-dark);
}
.drop-zone svg { width: 26px; height: 26px; margin-bottom: 6px; }
.drop-zone p { margin: 0 0 4px; font-size: 13.5px; }
.drop-zone p strong { font-weight: 500; }
.drop-zone .hint { margin: 0; }

.upload-bar {
  display: flex; align-items: center; gap: 10px;
  font-size: 12.5px; color: var(--ink-2);
  padding: 7px 0;
}
.upload-bar:first-child { padding-top: 10px; }
.upload-bar .ub-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upload-bar .ub-track {
  flex: none; width: 180px; height: 5px; border-radius: 999px;
  background: #e9eef4; overflow: hidden;
}
.upload-bar .ub-fill {
  /* span 默认是 inline，height 会失效 —— 必须显式 block，否则进度条永远看不出动 */
  display: block;
  height: 100%; width: 0; background: var(--brand);
  transition: width .2s linear;
}
.upload-bar .ub-state { flex: none; width: 68px; text-align: right; }
.upload-bar.ok .ub-fill { background: var(--ok); }
.upload-bar.err .ub-fill { background: var(--err); }
.upload-bar.err { color: var(--err); }
.upload-progress { margin-top: 6px; }

/* ================= 清单 ================= */
.stat-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; min-height: 20px; }
.list-more { text-align: center; padding-top: 12px; }
.list-foot {
  display: flex; justify-content: space-between; align-items: center;
  padding: 10px 2px 0; gap: 8px;
}
.shown-note { font-size: 12px; color: var(--ink-3); }

.list { display: grid; gap: 12px; }
/* 网格项默认 min-width:auto， nowrap 长标题会把整行撑出屏幕（手机端横向溢出），
   必须显式允许收缩 */
.list > * { min-width: 0; }
.group-body > * { min-width: 0; }
.item {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 16px 18px;
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px;
}
.item-main { min-width: 0; }
.item h3 { font-size: 14px; font-weight: 500; margin: 0 0 4px; }
.item p { margin: 0; color: var(--ink-2); font-size: 13px; }
.item .meta { margin-top: 8px; display: flex; gap: 8px; flex-wrap: wrap; }
.item-actions { flex: none; display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }

.pill {
  display: inline-block; font-size: 12px; line-height: 1.5;
  padding: 2px 10px; border-radius: 999px;
  background: #eef2f6; color: var(--ink-2);
}
.pill-ok { background: #e7f6ec; color: var(--ok); }
.pill-wait { background: #fdf3e3; color: var(--warn); }
.pill-no { background: #fdecec; color: var(--err); }
.pill-brand { background: var(--brand-l); color: var(--brand-dark); }
.pill-run { background: #e7f1fd; color: var(--brand-dark); }
.pill-ai { background: #f1ecfd; color: #6d4bc4; }
.pill-lock { background: #fff6e6; color: #a5680c; }
.pill-overdue { background: #fdecec; color: var(--err); font-weight: 500; }
/* 「已人工修改」= 整条资料停了自动解析/分析（M7 第二批第 7 条）。
   刻意用灰蓝而不是暖色：它是**状态**不是**警告**，用户不需要去修什么。 */
.pill-manual { background: #e9eef5; color: #46618a; }

/* 人工接管横幅（资料详情）。与 .lock-note 区分开：那条是「某字段被锁」，
   这条是「整条不再自动跑」，后果更大，所以要更显眼一点。 */
.manual-note {
  margin: 8px 0 0; padding: 8px 12px; border-radius: 8px;
  background: #f2f5fa; border-left: 3px solid #7d97bd;
  color: var(--ink-2); font-size: 12.5px; line-height: 1.7;
  overflow-wrap: anywhere;
}
.manual-note b { color: #46618a; }

.empty { color: var(--ink-3); font-size: 13px; padding: 20px; text-align: center; }

/* ---------------- 成员行 ---------------- */
.rows { display: grid; }
.row {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0; border-top: 1px solid var(--line);
  /* ★ 1.8.0：成员行末尾现在可能有**两组**按钮（成员级 + 平台超管的账号级，
     见 adminRowActs），手机端一行放不下就整行换行。
     不写 flex-wrap 的话，第二组会被挤到视口外 → 横向滚动条，
     而「按钮看不见」与「按钮没有」在界面上长得一模一样。 */
  flex-wrap: wrap;
}
.row:first-child { border-top: 0; padding-top: 4px; }
.row-main { min-width: 0; flex: 1 1 240px; }
.row-main strong { display: block; font-weight: 500; font-size: 13.5px; }
.row-sub { font-size: 12px; color: var(--ink-3); }
.row-pills { flex: none; display: flex; gap: 6px; flex-wrap: wrap; }
.row-actions { flex: none; display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

/* 账号级动作（1.8.0 从「用户账号管理」那张卡并到成员行）。
   ★ 单独一段而不是接在 .row-actions 后面：两组权限不同
     （空间管理员 vs 平台超管），视觉上分开才解释得了
     「为什么他看得到我的按钮、我看不到他的」。 */
.row-act-acc {
  flex: 1 1 100%;
  justify-content: flex-end;
  padding-top: 7px;
  margin-top: 2px;
  border-top: 1px dashed var(--line);
}
.row-act-acc .btn { font-size: 12px; }
/* 账号停用 / 平台管理员的徽章：紧跟在名字后面，别把状态行撑开 */
.row-main .acc-flag { margin-left: 6px; vertical-align: 1px; }
.row-main .acc-flag + .row-sub { display: block; margin-top: 2px; }
.empty-row {
  color: var(--ink-3); font-size: 13px; margin: 0;
  text-align: center; padding: 26px 12px;   /* 空状态居中留白，别挤在左上角 */
}

/* ================= 成员卡片网格（1.12.0 需求二） =================
 *
 * ★★ 为什么把「一行一个人」换成卡片网格（这是「整齐」的直接解）：
 *   原版的不整齐**不是间距问题**，而是 .row-pills / .row-actions 都写着
 *   `flex: none`（＝按内容定宽）。管理员行末尾有 4~5 个按钮（改资料 / 设为管理员 /
 *   移除 / 重置密码 / 停用账号 / 强制下线 / 删除账号），普通成员行只有 0 个 ——
 *   于是每一行右侧的起点都不一样，视觉上左右乱跳。
 *   动作全部收进卡片右上角的「⋯」之后，按钮**不再参与行宽计算**，
 *   卡片因此可以做成等宽网格：不用逐行对齐，网格自己就对齐了。
 *
 * ★★ 防窄屏横向溢出的三条（本项目已栽过四次的同一族）：
 *   ① 卡片本身 `min-width: 0` —— grid / flex 项默认 min-width:auto，
 *      不肯收缩到内容以下，长邮箱/长部门名会把整行顶出视口；
 *   ② 网格轨道写 `minmax(min(212px, 100%), 1fr)` 而**不是** `minmax(212px, 1fr)`：
 *      后者在容器窄于 212px 时轨道仍要 212px ⇒ 必然横滚。
 *      加一层 `min(…, 100%)` 之后「容器多窄，轨道就多窄」；
 *   ③ 卡片里的每一个可变宽元素（.mwho / .mnum / .mtags）都标 min-width:0，
 *      并用 overflow-wrap:anywhere —— 中文可在任意两字之间换行，
 *      而 URL / 邮箱这种长串不行（它们才是撑破卡片的那类）。
 *
 * ★ 不写 @media 补丁：auto-fill 自己就会按容器宽度分档（320 一列 / 600 两列 /
 *   1200 四五列）。用 @media 写死列数的后果是「窗口再窄 20px 又溢出一次」，
 *   而每一次都要有人记得去补一条断点。
 * ============================================================ */
.mcards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(212px, 100%), 1fr));
  /* ★★ grid-auto-rows:1fr（1.13.0 需求一）：让**每一行等高**。
     没有它时行高由该行最高的那张卡决定，而卡片高度差来自脚注
     （「另有 N 项作为上级工作」/「未填写联系方式」有的卡有一行、有的两行、
     有的没有），实测第一行 252px、第二行 185px —— 于是**卡底边参差不齐、
     数字区落不到同一水平线**，肉眼就是「没对齐」。1fr 让同一行的卡片
     全部拉平到该行最高者，再配合 .mcard 的 flex 列 + .mnums 的
     margin-top:auto，数字区就固定贴卡底，整页数字区齐平。 */
  grid-auto-rows: 1fr;
  gap: 10px;
  margin-top: 4px;
  min-width: 0;
}
/* 「加载中…」「除你之外还没有成员」这类整行提示：在网格里必须横跨所有列，
   否则它只占第一格，看起来像是「第一个人没名字」。 */
.mcards > .empty-row, .mcards > .empty { grid-column: 1 / -1; }
.mcard {
  position: relative; min-width: 0;
  /* ★ 1.13.0：卡片改**竖向 flex 列**，是为了让 .mnums 能用 margin-top:auto
     顶到卡底（需求一「数字区对齐」）。配合 .mcards 的 grid-auto-rows:1fr，
     同一行所有卡片等高 ⇒ 所有数字区底边落在同一条水平线上。
     ★ 这不影响 .mcard-menu —— 它是 position:absolute，脱离流，
     在 flex/grid 容器里行为与在 block 容器里完全一致。 */
  display: flex; flex-direction: column;
  padding: 12px 13px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color var(--dur), box-shadow var(--dur);
}
.mcard:hover { border-color: #cbd9e8; box-shadow: var(--shadow); }
/* 我自己那张卡：蓝框标出来（找自己不用逐张看名字） */
.mcard.is-self { border-color: #b9d5f0; background: #fbfdff; }
/* 已不在空间（人走了、工作还在）：整卡压暗，避免被当成可操作的人 */
.mcard.is-out { background: #fafbfd; }
/* 集体卡与未开通账号卡（D6）：与成员卡区分，用虚线边框 + 淡底 */
.mcard.is-plain { border-style: dashed; background: #fbfcfe; }

/* ---- 拖动排序的手柄（1.13.2 需求②） ----
   ★ 平时 display:none，只有网格挂上 .is-custom（排序＝自定义）才出现。
     不做成「一直显示」：平时卡片上没有多余的手柄，点哪里都不会误触拖动。
   ★ touch-action:none 必须有：手机上一按住手柄，浏览器默认会开始滚页面，
     pointermove 就此断掉，表现为「拖不动」。
   ★ 手柄是卡头的第一个 flex 项（不是绝对定位）—— 绝对定位会压在头像上，
     或者要把整卡 padding 让出一块，行高与其它卡就对不齐了。 */
.mcard-grip {
  display: none; flex: none; width: 16px; height: 24px; margin-right: 2px;
  align-items: center; justify-content: center;
  color: var(--ink-3); cursor: grab; touch-action: none; user-select: none;
}
.mcards.is-custom .mcard-grip { display: flex; }
.mcard-grip:active { cursor: grabbing; }
.mcard-grip svg { display: block; fill: currentColor; }
/* 拖动中的那张卡：淡一点 + 虚线框，明确「它正在被搬走」 */
.mcard.is-dragging { opacity: .55; border-style: dashed; }

/* ---- 卡头：首字头像 + 姓名 + 登录名 ---- */
.mcard-head { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* ★ 右侧留出「⋯」按钮的位置（26px + 间隙），否则长名字会钻到按钮底下。 */
.mcard-head { padding-right: 28px; }
.mava {
  flex: none; width: 36px; height: 36px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 600; color: #fff; line-height: 1;
  background: linear-gradient(135deg, #6aa9e6, #3f83cb);
  box-shadow: 0 2px 6px rgba(74, 144, 217, .28);
  user-select: none;
}
.mava.r-admin { background: linear-gradient(135deg, #8291f0, #5b6bd8); box-shadow: 0 2px 6px rgba(91, 107, 216, .26); }
.mava.r-left  { background: linear-gradient(135deg, #bcc5d0, #98a4b2); box-shadow: none; }
.mwho { flex: 1 1 auto; min-width: 0; }
.mwho b {
  display: block; font-size: 13.5px; font-weight: 500; color: var(--ink);
  overflow-wrap: anywhere;
}
.mwho span {
  display: block; margin-top: 1px; font-size: 11.5px; color: var(--ink-3);
  overflow-wrap: anywhere;
}

/* ---- 胶囊行（角色 / 状态 / 账号停用 / 平台管理员 / 已不在空间） ---- */
.mtags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px; min-width: 0; }
.mtags .pill { font-size: 11.5px; padding: 2px 8px; }

/* ---- 数字区：年度工作 / 年度工作数 ---- */
/* ★★ margin-top:auto（1.13.0 需求一）：在 .mcard 这个 flex 列里，auto 会把
   「名字/胶囊/脚注」之后的所有剩余空间全吃掉，于是数字区**永远贴卡底**。
   卡片被 grid-auto-rows:1fr 拉平后，同一行所有卡的数字区底边 y 完全一致。
   ★ 同时保留 padding-top:10px 当作「上方没有脚注时」的最小间距 ——
   纯 margin-top:auto 在空间不足时会退化成 0，数字区就贴上胶囊了。
   ★ 不要改成 margin-top:10px：那样数字区跟着内容走，长短不一的卡
   又会错位（这正是需求一要修的现象）。 */
.mnums {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px;
  margin-top: auto; padding-top: 10px;
  /* ★ 1.13.1 需求①：两块必须**尺寸一致**。grid 的 stretch 默认就让同行
     两块等高，这里显式写出来是因为下面 .mnum 改成了 flex 列 ——
     一旦哪天有人给 .mnum 加 align-self，等高会静默失效，而肉眼只看到
     「右边那块矮了一点」。 */
  align-items: stretch;
}
/* ★ 1.13.1 需求①：文字居中（原来是左对齐）。
   为什么用 flex 列居中而不是只写 text-align:center：
   两块的标签字数不同（「工作量」3 字 / 「工作项」3 字），只靠 text-align
   时文字在格子里居中、但上下留白由行高决定，两块看起来仍不一样高；
   flex 列 + justify-content:center 是按**格子**居中，与字数无关。
   ★★ 1.13.2：`margin:0` 与 `gap:2px` 是**修 bug 的**，不是审美。
   1.13.1 给工作量那一格加了修饰类 `wl`，而全站另有一个 `.wl`
   （工作页的「工作量选择器」，见本文件后面 `.wl { display:flex;
   align-items:center; gap:10px; margin:8px 0 2px }`）—— 两个类名撞车，
   于是**只有左边那一格**吃到了 8px 上边距和 10px 内部间距：
   截图上表现为「左块矮一截、整体下沉、标签离数字更远」。
   现在修饰类改名 `mnum-wl`，并在这里把 margin/gap 写死，
   以后再撞上同类全局规则也不会再变形（margin 一旦非 0，两格就不可能等高）。 */
.mnum {
  min-width: 0; margin: 0; gap: 2px; padding: 7px 9px;
  background: #f7f9fc; border-radius: 8px;
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  text-align: center;
}
.mnum em {
  display: block; font-style: normal; font-size: 11px; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-align: center;
}
.mnum b {
  display: block; margin-top: 1px;
  font-size: 16px; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums;   /* 数字等宽：纵向扫一眼就能比大小 */
  text-align: center;
}
/* ★ 工作量那一格的数字用项目里的「工作量紫」（与 work 页同色）。
   ★★ 类名必须是 `mnum-wl`，**不能**是 `wl` —— 后者是工作页「工作量选择器」
   的类名（`.wl { display:flex; gap:10px; margin:8px 0 2px }`），
   撞上去会让这一格单独吃到外边距，两个格子立刻不等高。 */
.mnum.mnum-wl b { color: #6b3fa0; }
.mnum.zero b { color: var(--ink-3); font-weight: 500; }
.mnum.zero { background: #fafbfd; }
/* 「另有 N 项作为上级工作」（D5）：父项不计入两数，但要让人知道它们存在，
   否则「总负责人显示 0 分」看起来就是算错了。 */
.mcard-note {
  margin-top: 7px; font-size: 11.5px; color: var(--ink-3);
  overflow-wrap: anywhere;
}

/* ---- 卡片右上角「⋯」与它的菜单 ---- */
.mcard-menu { position: absolute; top: 8px; right: 8px; }
.mcard-menu > button {
  width: 26px; height: 26px; padding: 0; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  font-size: 17px; color: var(--ink-3);
  background: transparent; border: 1px solid transparent; border-radius: 7px;
  cursor: pointer; transition: background var(--dur), color var(--dur), border-color var(--dur);
}
.mcard-menu > button:hover,
.mcard-menu > button[aria-expanded="true"] {
  background: var(--brand-l); border-color: #cfe0f2; color: var(--brand-dark);
}
.mpop { top: calc(100% + 2px); min-width: 168px; }
/* 菜单里的分组小标题与分隔线（成员卡的菜单有「空间管理」「平台账号」两段） */
.mpop .mpop-l { padding: 5px 10px 3px; font-size: 11px; color: var(--ink-3); white-space: nowrap; }
.mpop .mpop-sep { height: 1px; margin: 5px 8px; background: var(--line); }
.mpop button.danger { color: var(--err); }
.mpop button.danger:hover, .mpop button.danger:focus-visible { background: #fdf1f1; }
.mpop button[disabled] { opacity: .5; cursor: not-allowed; }

/* ================= 年度统计条（1.12.0 需求二） =================
 * 统计年度下拉 + 口径说明 + 合计。合计必须**与卡片并排可见** ——
 * 它的作用是让人能自己把卡片加起来核对（「各卡片相加恒等于合计」是这套
 * 统计的口径承诺，看不到合计就没法核对）。
 * ★ 全用 flex-wrap + min-width:0，不写死宽度：320px 上它会自己折成三行。 */
.wl-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px;
  padding: 10px 12px; margin: 6px 0 12px;
  background: #f7f9fc; border: 1px solid var(--line); border-radius: var(--radius);
}
.wl-lab { flex: none; font-size: 12.5px; color: var(--ink-2); }
/* ★ 裸 <select> 的固有宽度 = **最长那个选项**的宽度（待办页责任人下拉曾因此
     在 320px 上把整页顶出 38px）。这里写 min-width:0 + max-width:100% 兜住。 */
.wl-year {
  flex: none; min-width: 0; max-width: 100%;
  padding: 6px 10px; font: inherit; font-size: 13px; color: var(--ink);
  background: #fff; border: 1px solid var(--line); border-radius: 8px; cursor: pointer;
}
.wl-year:focus { outline: none; border-color: var(--brand); }
.wl-note {
  flex: 1 1 300px; min-width: 0;
  font-size: 11.5px; line-height: 1.7; color: var(--ink-3);
  overflow-wrap: anywhere;
}
.wl-note b { font-weight: 500; color: var(--ink-2); }
.wl-sums {
  /* ★★ 绝不能写 flex:none（＝flex: 0 0 auto）—— 那会让它按 **max-content**
     定宽，空间不够时既不换行也不收缩，直接把整页顶出横向滚动条。
     实测：320px 视口下 body.scrollWidth = 360（溢出 40px），元凶就是这里。
     这是「窄屏横向溢出」的第②类元凶（按钮/胶囊组按内容定宽），
     待办页责任人下拉（第①类，裸 select）栽过，成员行按钮组（第②类）也栽过。
     ★ flex-shrink:1 + min-width:0 才是能收缩的写法；换行交给 .wl-bar 的
     flex-wrap —— 窄屏它会自己折到下一行，而不是把自己撑出视口。 */
  flex: 0 1 auto; min-width: 0;
  display: flex; flex-wrap: wrap; gap: 7px;
}
.wl-sum {
  display: flex; align-items: baseline; gap: 5px;
  padding: 4px 11px; font-size: 12px; color: var(--ink-3);
  background: #fff; border: 1px solid var(--line); border-radius: 999px;
  white-space: nowrap;
}
.wl-sum b { font-size: 13.5px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.wl-sum.p-wl b { color: #6b3fa0; }
.wl-sum.p-wl { border-color: #e0d4f2; }
.wl-bar.is-empty { justify-content: flex-start; }

/* 只读的「查看联系方式」弹层（1.12.0）：六列按同一份 PROFILE_FIELDS 渲染，
   没填的列显示「未填写」而不是留白 —— 留白会被当成还没加载出来。 */
.pv-sec { margin-top: 14px; }
.pv-sec > h4 { margin: 0 0 8px; font-size: 13px; font-weight: 500; color: var(--ink-2); }
.pv-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 14px; }
.pv-row { min-width: 0; display: flex; gap: 8px; align-items: baseline; }
.pv-row > span:first-child { flex: none; width: 56px; font-size: 12px; color: var(--ink-3); }
.pv-row > span:last-child { min-width: 0; font-size: 13px; color: var(--ink); overflow-wrap: anywhere; }
.pv-row.is-empty > span:last-child { color: var(--ink-3); }
.pv-row.wide { grid-column: 1 / -1; }
.pv-head-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-width: 0; }
.pv-head-row .mava { width: 46px; height: 46px; border-radius: 13px; font-size: 19px; }
/* 只读成员信息弹层：比资料详情窄（内容是六列个人信息 + 两个数字）。
   不加这条会沿用 .modal-box 的 720px —— 在一屏里摊开一大片空白。 */
#member-view-modal .modal-box { max-width: 560px; }

/* ---------------- 分类标签 ---------------- */
.pill-c1 { background: #e7f1fd; color: #2f6bb0; }
.pill-c2 { background: #e4f6f4; color: #1f7f78; }
.pill-c3 { background: #f1ecfd; color: #6d4bc4; }
.pill-c4 { background: #fdf1e3; color: #b06d16; }
.pill-c5 { background: #edf1f5; color: #55677a; }
.pill-c6 { background: #eef2f6; color: var(--ink-3); }

.mat-tags-line {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: 6px;
}
.tags { display: inline-flex; gap: 5px; flex-wrap: wrap; }
.tag {
  appearance: none; font: inherit; font-size: 12px; line-height: 1.5;
  padding: 2px 9px; border-radius: 999px; cursor: pointer;
  background: #f1f5fa; color: #4a6178; border: 1px solid #e3eaf3;
  transition: all var(--dur);
}
.tag:hover { background: var(--brand-l); border-color: #bfd6ee; color: var(--brand-dark); }
.tag.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.tag-more { background: none; border-color: transparent; color: var(--ink-3); cursor: default; }
.tag-more:hover { background: none; border-color: transparent; color: var(--ink-3); }

.mat-item { align-items: flex-start; }
.mat-preview {
  margin: 4px 0 0; color: var(--ink-2); font-size: 13px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.mat-error { margin: 6px 0 0; font-size: 12.5px; color: var(--err); }
.mat-title-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mat-title-line h3 { margin: 0; }
.mat-title-line .ck, .mat-row .ck { width: 15px; height: 15px; flex: none; cursor: pointer; accent-color: var(--brand); }

/* 资料就地改名（M6 第八批，需求②）：顶掉标题那一格，卡片其余内容不动。
   ★ 输入框要能长：标题是卡片上最长的东西，flex: 1 1 220px 让它在宽卡里也占满，
     靠 .mat-title-line 上已有的 flex-wrap，窄到放不下时按钮会自己换行，
     不需要为手机端另写一套断点。 */
.mat-rename {
  display: flex; align-items: center; gap: 8px;
  flex: 1 1 220px; min-width: 0; flex-wrap: wrap;
}
.mat-rename input {
  flex: 1 1 140px; min-width: 0;
  padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px;
  font: inherit; font-size: 13.5px; color: var(--ink); background: #fff;
}
.mat-rename input:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-l);
}
/* 错误提示独占一行：跟输入框挤一行时，报错一出现就把「保存」按钮顶到下一行，
   手指刚离开的按钮会跑位（移动端最容易点错） */
.mat-rename .msg { flex: 1 1 100%; margin: 0; }

/* ★ 紧凑视图里的就地改名（M6 第十四批实测）：
   .mat-row 是**单行** flex（flex-wrap: nowrap），行里已经塞了「标题 + 状态徽章 +
   四个动作按钮」而且全是 flex:none；改名输入框再挤进来，390px 手机上只分到 90px 宽
   —— 比一个按钮还窄，于是输入框 / 保存 / 取消 / 同步勾选各自占一行竖排下来，
   整行被撑到 240px 高，还把下面的行全顶下去。
   → 改名期间在行上加 .renaming：允许换行，并让改名块 order:-1 + 基准 100%
   独占第一整行。于是行变成三行：
     ① [输入框 · 保存 · 取消] ② [☑ 同时把来源事件标题改成一样] ③ [其余行内信息]
   ★ 试过三种写法，桌面 + 手机都实测过，全都有毛病：
     · 220px：手机上输入框只剩 192px、「取消」还被挤到下一行；
     · calc(100% - 40px)：想让它跟行首两个小方块同排，但复选框的 UA 默认
       margin（3px 3px 3px 4px）让"40px"算不准，差 1px 就整块掉行，太脆；
     · 100% 但不加 order:-1：行首那对小方块被留在第一行单独占一行，
       桌面端看着像"空了一行"的故障。
   → order:-1 让它们顺到最后一行的开头，和状态徽章/日期/动作按钮排在一起。
   ★ 卡片视图的父级是 .mat-title-line（本来就 flex-wrap: wrap），不受这条影响。 */
.mat-row.renaming { flex-wrap: wrap; }
.mat-row.renaming > .mat-rename { order: -1; flex: 1 1 100%; }
.mat-summary {
  margin: 6px 0 0; color: var(--ink); font-size: 13px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* 详情页内嵌的原图预览（M5.9）：等比缩到容器宽内，别撑破模态框 */
.mat-preview-img {
  display: block; max-width: 100%; max-height: 60vh;
  margin: 10px auto 0; border-radius: 10px;
  border: 1px solid var(--line); background: #fff;
}
/* 紧凑行里的预览链接：与同排按钮同观感 */
.mat-row .ra {
  color: var(--brand); text-decoration: none; font-size: 12px;
  padding: 2px 4px; border-radius: 6px;
}
.mat-row .ra:hover { background: var(--brand-l, rgba(74, 144, 217, .12)); }

/* 当前生效的筛选：必须能一眼看见并能一键取消 */
.filter-note {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 8px; font-size: 12.5px; color: var(--ink-3);
}
.filter-note .warn-inline { color: var(--warn); }

/* ================= 事件流（M6） ================= */
.ev-tools {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: center;
  margin: 0 0 14px;
}
.ev-views { display: flex; background: var(--brand-l); border-radius: 999px; padding: 3px; }
.ev-view {
  border: 0; background: none; font: inherit; font-size: 13px; color: var(--ink-2);
  padding: 5px 14px; border-radius: 999px; cursor: pointer;
  transition: background var(--dur), color var(--dur);
}
.ev-view.on { background: #fff; color: var(--brand-dark); font-weight: 600; box-shadow: 0 1px 3px rgba(31, 79, 143, .18); }
.ev-tools select {
  padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px;
  font: inherit; font-size: 13px; color: var(--ink-2); background: #fff;
}

/* 管理员通知条（M6 第二批）：自动建号 / 名单查无此人，仅空间管理员可见 */
.ev-notices { display: flex; flex-direction: column; gap: 8px; margin: 0 0 14px; }
.ev-notice {
  display: flex; align-items: flex-start; gap: 10px;
  border: 1px solid var(--line); border-left-width: 3px;
  border-radius: 8px; padding: 10px 12px; font-size: 12.5px; line-height: 1.6;
  background: #fff; color: var(--ink-2);
}
.ev-notice-ico { flex: none; font-size: 14px; line-height: 1.5; }
.ev-notice b { color: var(--ink); font-weight: 600; }
.ev-notice-acc { border-left-color: var(--brand); }
.ev-notice-acc .ev-notice-ico { color: var(--brand); }
.ev-notice-skip { border-left-color: var(--warn); }
.ev-notice-skip .ev-notice-ico { color: var(--warn); }
.ev-notice-t { flex: none; margin-left: auto; color: var(--ink-3); font-size: 12px; white-space: nowrap; }

/* 年份分节 + 中轴时间线 */
.ev-year { margin-bottom: 6px; }
/* 月份分节（M6 第八批）：时间视图由「年 → 事件」加深一层成「年 → 月 → 事件」。
   position: relative 是给下面那根横向小枝当定位基准的（小枝绝对定位到节点外面）。 */
.ev-month { margin-bottom: 4px; position: relative; }

/* ---------- 树节点头（M6 第八批）：年 / 月 / 账号分支 / 标签分支共用 ----------
   它是个 <button>，所以必须先把浏览器默认的按钮外观整份抹掉：不抹会拿到系统灰底、
   描边和居中的文字，一眼就看出「这不是我设计的样子」。
   ★ width:100% + text-align:left 是必须的 —— 按钮默认按内容收缩，不铺满就等于
     只有标题那几个字能点；而「整行都能点」是折叠控件最基本的手感。
   ★ font: inherit 要写在 font-size 前面：它是简写，会把后面的字号字重一起重置。 */
.ev-node-t {
  display: flex; align-items: center; gap: 8px;
  width: 100%; border: 0; background: none; font: inherit;
  font-size: 13.5px; font-weight: 600; color: var(--ink);
  text-align: left; cursor: pointer;
  padding: 9px 8px 9px 4px; border-radius: 8px;
  transition: background var(--dur);
}
.ev-node-t:hover { background: var(--brand-l); }
.ev-node-t:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
/* 年份是整个视图的根，比月/分支大一号，一眼能分出层级 */
.ev-year-toggle { font-size: 16px; font-weight: 700; padding-top: 13px; }
/* 折叠三角：固定 14px 宽，展开/收起时后面的标题不会左右跳动 */
.ev-caret {
  flex: none; width: 14px; font-size: 11px; line-height: 1;
  color: var(--ink-3); text-align: center;
}

/* ★ 收起一个节点 = 藏掉它的子树，仅此而已。
   不用 max-height 做过渡：子树高度不定，硬给一个数字要么裁掉尾部、
   要么留下一大段空白；而且本界面每次重渲都会换掉整段 HTML，
   过渡本来就播不完整，不如干脆不做。 */
.ev-collapsed > .ev-tree { display: none; }
/* 树状结构（M6 第六批，第八批加深到三层）：每一层节点自带一个 .ev-tree ——
   同一条枝上的子节点共用一条竖线，缩进是「一层套一层」自然叠出来的
   （年 27px → 月 54px → 事件 81px），不需要手工算 depth。
   时间视图是「年 → 月 → 事件」，账号 / 标签视图是「分支 → 事件」。
   卡片上那个 .ev-dot 只是分类色，不是树的连接点，别和这里的竖线混了。 */
.ev-tree {
  position: relative;
  margin-left: 7px; padding-left: 20px;
  display: flex; flex-direction: column; gap: 10px;
}
.ev-tree::before {
  content: ''; position: absolute; left: 0; top: 4px; bottom: 4px;
  width: 2px; background: var(--line);
}
/* 横向小枝：每个子节点朝竖干伸一条短线。没有它就只剩缩进，看不出是「树」。
   ★ 四类子节点都要列上 —— 漏一个（比如月份）就会出现「月份缩在里面、
     却和上面那根竖线毫无关系」的悬空感，这是最典型的一种「看着不对但说不清」。 */
.ev-tree > .ev-card::before,
.ev-tree > .ev-branch::before,
.ev-tree > .ev-tag-branch::before,
.ev-tree > .ev-month::before {
  content: ''; position: absolute; left: -20px; top: 18px;
  width: 14px; height: 2px; background: var(--line);
}
.ev-card {
  position: relative; background: #fff; border: 1px solid var(--line);
  border-radius: 10px; padding: 12px 14px;
  transition: box-shadow var(--dur);
}
.ev-card:hover { box-shadow: 0 2px 10px rgba(31, 79, 143, .10); }
/* 事件圆点：颜色跟着事件的第一个标签走 —— 需求要求「不同标签以不同色彩区分」，
   而圆点是卡片上最显眼的位置，比只染徽章更容易一眼分辨。 */
.ev-dot {
  position: absolute; left: -26px; top: 18px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--brand);
  border: 2px solid #fff; box-shadow: 0 0 0 1px var(--line);
}
.ev-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.ev-subject { font-weight: 600; font-size: 13.5px; color: var(--ink); }
.ev-time { margin-left: auto; font-size: 12px; color: var(--ink-2); white-space: nowrap; }
.ev-title { font-size: 13.5px; margin-top: 6px; color: var(--ink); font-weight: 500; }
.ev-desc { margin: 4px 0 0; font-size: 13px; color: var(--ink-2); }
.ev-meta { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* ---------- 标签徽章（M6 第六批） ----------
   每个色号三档：底色 -b / 字色 -f / 色条 -s。
   ★ 底色是浅色、字色是对应深色：浅底深字在 AA 下全部达标，
     而「深底白字」那个更常见的做法在这里不适用 —— 徽章有文字，
     深底会让并排的一排徽章显得很重。
   ★ 左侧色条是「色觉障碍用户」的兜底：完全不靠颜色也能看出分类差异。 */
.ev-tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 600; line-height: 1.6;
  padding: 1px 8px 1px 6px; border-radius: 999px; flex: none;
}
.ev-tag::before {
  content: ''; width: 3px; height: 11px; border-radius: 2px;
  background: var(--tag-s, var(--tag-slate-s)); flex: none;
}
.ev-tag-0 { background: var(--tag-indigo-b); color: var(--tag-indigo-f); --tag-s: var(--tag-indigo-s); }
.ev-tag-1 { background: var(--tag-teal-b);   color: var(--tag-teal-f);   --tag-s: var(--tag-teal-s); }
.ev-tag-2 { background: var(--tag-amber-b);  color: var(--tag-amber-f);  --tag-s: var(--tag-amber-s); }
.ev-tag-3 { background: var(--tag-rose-b);   color: var(--tag-rose-f);   --tag-s: var(--tag-rose-s); }
.ev-tag-4 { background: var(--tag-violet-b); color: var(--tag-violet-f); --tag-s: var(--tag-violet-s); }
.ev-tag-5 { background: var(--tag-azure-b);  color: var(--tag-azure-f);  --tag-s: var(--tag-azure-s); }
.ev-tag-6 { background: var(--tag-lime-b);   color: var(--tag-lime-f);   --tag-s: var(--tag-lime-s); }
.ev-tag-7 { background: var(--tag-orange-b); color: var(--tag-orange-f); --tag-s: var(--tag-orange-s); }
.ev-tag-8 { background: var(--tag-cyan-b);   color: var(--tag-cyan-f);   --tag-s: var(--tag-cyan-s); }
.ev-tag-9 { background: var(--tag-pink-b);   color: var(--tag-pink-f);   --tag-s: var(--tag-pink-s); }
.ev-tag-10 { background: var(--tag-green-b);  color: var(--tag-green-f);  --tag-s: var(--tag-green-s); }
.ev-tag-11 { background: var(--tag-fuchsia-b);color: var(--tag-fuchsia-f);--tag-s: var(--tag-fuchsia-s); }
/* 账号事件：中性色，与标签色明确区分（需求：「不同的标签和账号事件以不同的色彩来区分」） */
.ev-tag-acct { background: var(--tag-slate-b); color: var(--tag-slate-f); --tag-s: var(--tag-slate-s); }

/* 事件卡的左侧色条：与第一个标签同色，弱化处理（比徽章更低调的一层提示） */
.ev-card[data-tag-color="0"]  { box-shadow: inset 3px 0 0 var(--tag-indigo-s); }
.ev-card[data-tag-color="1"]  { box-shadow: inset 3px 0 0 var(--tag-teal-s); }
.ev-card[data-tag-color="2"]  { box-shadow: inset 3px 0 0 var(--tag-amber-s); }
.ev-card[data-tag-color="3"]  { box-shadow: inset 3px 0 0 var(--tag-rose-s); }
.ev-card[data-tag-color="4"]  { box-shadow: inset 3px 0 0 var(--tag-violet-s); }
.ev-card[data-tag-color="5"]  { box-shadow: inset 3px 0 0 var(--tag-azure-s); }
.ev-card[data-tag-color="6"]  { box-shadow: inset 3px 0 0 var(--tag-lime-s); }
.ev-card[data-tag-color="7"]  { box-shadow: inset 3px 0 0 var(--tag-orange-s); }
.ev-card[data-tag-color="8"]  { box-shadow: inset 3px 0 0 var(--tag-cyan-s); }
.ev-card[data-tag-color="9"]  { box-shadow: inset 3px 0 0 var(--tag-pink-s); }
.ev-card[data-tag-color="10"] { box-shadow: inset 3px 0 0 var(--tag-green-s); }
.ev-card[data-tag-color="11"] { box-shadow: inset 3px 0 0 var(--tag-fuchsia-s); }
/* hover 的阴影要盖过上面的色条，否则一点就看不见分类色了 */
.ev-card[data-tag-color]:hover {
  box-shadow: inset 3px 0 0 currentColor, 0 2px 10px rgba(31, 79, 143, .12);
}
.ev-dot[data-dot="0"]  { background: var(--tag-indigo-s); }
.ev-dot[data-dot="1"]  { background: var(--tag-teal-s); }
.ev-dot[data-dot="2"]  { background: var(--tag-amber-s); }
.ev-dot[data-dot="3"]  { background: var(--tag-rose-s); }
.ev-dot[data-dot="4"]  { background: var(--tag-violet-s); }
.ev-dot[data-dot="5"]  { background: var(--tag-azure-s); }
.ev-dot[data-dot="6"]  { background: var(--tag-lime-s); }
.ev-dot[data-dot="7"]  { background: var(--tag-orange-s); }
.ev-dot[data-dot="8"]  { background: var(--tag-cyan-s); }
.ev-dot[data-dot="9"]  { background: var(--tag-pink-s); }
.ev-dot[data-dot="10"] { background: var(--tag-green-s); }
.ev-dot[data-dot="11"] { background: var(--tag-fuchsia-s); }

.ev-flag {
  font-size: 11px; color: #b54708; background: #fef0e0;
  border-radius: 4px; padding: 1px 6px;
}
.ev-flag-edit { color: #067647; background: #e7f6ee; }
/* 1.9.0 需求①：事件来自一项已完成工作（不是从资料里抽的），用品牌蓝区分 */
.ev-flag-work { color: #1c5fa8; background: #e8f1fb; }
.ev-link { display: inline-block; margin-top: 8px; font-size: 12.5px; color: var(--brand-dark); }
.ev-link:hover { text-decoration: underline; }
.ev-link-dl { color: var(--ink-2); }

/* 账号分支视图 */
.ev-branch { margin-bottom: 10px; position: relative; }
/* ★ 容器不再自己留上下 padding，改由里面的 .ev-node-t 撑：两边都留会叠成
   20px 往上，分支头一行就吃掉半屏，树反而更看不清。 */
.ev-branch-t {
  display: flex; align-items: center; gap: 8px;
  border-bottom: 1px dashed var(--line);
}
/* 分支头右侧还有「仅管理员」锁按钮，所以头要能伸缩、且允许被压窄 */
.ev-branch-toggle { flex: 1 1 auto; min-width: 0; padding-left: 2px; }
.ev-branch-name { font-weight: 700; font-size: 14px; color: var(--ink); }
.ev-branch-n { font-size: 12px; color: var(--ink-2); flex: none; }

/* 集体账号「仅管理员」开关（M6 第三批）：分支头右侧的小锁按钮 */
.ev-lock {
  border: 1px solid var(--line); background: #fff; border-radius: 999px;
  font: inherit; font-size: 11.5px; color: var(--ink-3);
  padding: 2px 10px; cursor: pointer; flex: none;
  transition: color var(--dur), border-color var(--dur), background var(--dur);
}
.ev-lock:hover { border-color: var(--brand); color: var(--brand-dark); }
.ev-lock.on { color: #b42318; border-color: #f3c2c2; background: #fdf3f3; }
.ev-lock.on:hover { border-color: #b42318; color: #b42318; }

/* 事件卡的管理员操作（M6 第三批）：编辑 / 删除，卡片末尾右对齐 */
.ev-acts {
  display: flex; gap: 6px; justify-content: flex-end;
  margin-top: 10px; padding-top: 8px; border-top: 1px dashed var(--line);
}
.btn-danger { color: #b42318; border-color: #eecaca; background: #fff; }
.btn-danger:hover { border-color: #b42318; color: #b42318; background: #fdf3f3; }

/* ---------------- 紧凑视图 ---------------- */
.mat-row {
  background: var(--card); border: 1px solid var(--line); border-radius: 9px;
  display: flex; align-items: center; gap: 9px;
  padding: 8px 12px; margin-bottom: 6px;
  transition: background var(--dur), border-color var(--dur);
}
.mat-row:hover { background: #fafcfe; border-color: #d7e2ee; }
.mat-row .t {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border: 0; background: none; padding: 0; text-align: left; cursor: pointer;
  font: inherit; font-size: 13.5px; color: var(--ink);
}
.mat-row .t:hover { color: var(--brand-dark); text-decoration: underline; }
.mat-row .meta { font-size: 11.5px; color: var(--ink-3); flex: none; }
.row-act { display: none; gap: 4px; flex: none; }
.mat-row:hover .row-act, .mat-row:focus-within .row-act { display: flex; }
.row-act button {
  border: 1px solid var(--line); background: #fff; border-radius: 6px;
  font-size: 11px; padding: 2px 7px; cursor: pointer; color: var(--ink-2);
}
.row-act button:hover { border-color: var(--brand); color: var(--brand-dark); }
.mat-row .pill { flex: none; }
.dot { width: 7px; height: 7px; border-radius: 50%; flex: none; display: inline-block; }

/* ---------------- 分组浏览 ---------------- */
.group-wrap { margin-top: 4px; }
.group { border: 1px solid var(--line); border-radius: 10px; margin-bottom: 10px; background: #fff; }
.group > summary {
  cursor: pointer; list-style: none; padding: 10px 12px;
  display: flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--ink);
}
.group > summary::-webkit-details-marker { display: none; }
.group > summary::before {
  content: '▸'; color: var(--ink-3); font-size: 11px; transition: transform .15s;
}
.group[open] > summary::before { transform: rotate(90deg); }
.group > summary .g-name { font-weight: 500; }
.group > summary .g-count { color: var(--ink-3); font-size: 12.5px; }
.group-body { padding: 0 12px 12px; display: grid; gap: 12px; }
.group-body .item {
  border: 0; box-shadow: none; border-radius: 0;
  padding: 4px 0 12px; border-bottom: 1px solid var(--line);
}
.group-body .item:last-child { border-bottom: 0; padding-bottom: 0; }
/* 分组内的紧凑行去掉自身的卡片边，避免框里套框 */
.group-body .mat-row { border: 0; border-bottom: 1px solid var(--line); border-radius: 0; margin-bottom: 0; background: none; }
.group-body .mat-row:last-child { border-bottom: 0; }

/* ---------------- 检索命中 ---------------- */
.hit-line { margin: 6px 0 0; font-size: 12.5px; color: var(--ink-2); line-height: 1.7; }
.hit-line .hit-text { margin-right: 6px; }
.pill-hit-keyword { background: var(--brand-l); color: var(--brand-dark); }
.pill-hit-entity  { background: #e7f6ec; color: var(--ok); }
.pill-hit-tag     { background: #fdf3e3; color: var(--warn); }
.pill-hit-semantic { background: #f1ecfd; color: #6d4bc4; }
.hit-line mark { background: #fff3bf; color: inherit; padding: 0 1px; border-radius: 2px; }
.route-pills { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.route-pills .pill-num { font-variant-numeric: tabular-nums; }

/* ---------------- 批量条 ---------------- */
.batch-bar {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 12px; padding: 9px 12px;
  background: var(--brand-l); border: 1px solid var(--brand); border-radius: var(--radius);
}
.batch-bar b { color: var(--brand-dark); }
.batch-acts { margin-left: auto; display: flex; gap: 6px; flex-wrap: wrap; }

/* ---------------- 待办页 ---------------- */
.todo-tools { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.todo-tools select {
  font: inherit; font-size: 13px; color: var(--ink);
  border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 11px; background: #fff;
  /* ★ 320px 实测（真渲染、不是推断）：裸 <select> 的固有宽度由**最长那个
     选项**决定 —— 这里是「马天有、赵六、电商231班（未完成 1 / 共 1）」，
     足足 358px。而 flex 项默认 `min-width: auto` **不肯收缩到内容以下**，
     于是整个待办页横向溢出 38px（bodyScrollWidth 358 > 320）。
     ★ 只在 ≤360px 的媒体查询里修是治标：窗口再窄一点、或某天多出一个
     更长的责任人名，同一个坑会原样复现。所以这里直接给控件本身定边界 ——
     min-width:0 允许收缩，max-width:100% 给上界。 */
  min-width: 0; max-width: 100%;
}
.todo-tools select:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(74, 144, 217, .13);
}
.todo-row {
  display: flex; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--line);
  align-items: flex-start;
}
.todo-row:last-child { border-bottom: 0; }
.todo-main { min-width: 0; flex: 1; }
.todo-main .t-task { display: block; font-size: 13.5px; color: var(--ink); line-height: 1.6; }
.todo-meta { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.todo-main .t-quote { display: block; margin-top: 5px; font-size: 12px; color: var(--ink-3); }
.todo-main .t-quote::before { content: '原文：'; }
.todo-src { font-size: 12px; color: var(--ink-3); }
/* ★ 1.10.0（需求②）：.todo-check / .todo-item / .todo-list 三条规则已随
   「勾选完成」入口一起删除 —— 待办页不再有勾选框，资料详情里的待办段也删了。
   留着一套「给勾选框用的样式」只会被后来的人当成还能用。 */

/* ---------------- 详情弹层 ---------------- */
.modal {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(36, 48, 61, .42);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  animation: fade-in .2s ease-out;
}
.modal-box {
  background: var(--card); border-radius: 12px;
  box-shadow: 0 10px 40px rgba(36, 48, 61, .22);
  width: 100%; max-width: 720px; max-height: 84vh;
  min-width: 0;   /* flex 子项的 min-width:auto 会让长内容把弹层顶出视口 */
  display: flex; flex-direction: column;
  animation: pop-in .22s ease-out;
}
.modal-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--line);
}
.modal-head h3 { margin: 0; font-size: 14.5px; font-weight: 500; }
.modal-body { padding: 16px 20px; overflow: auto; }
.modal-close {
  appearance: none; border: 0; background: none; cursor: pointer;
  font-size: 20px; line-height: 1; color: var(--ink-3);
  padding: 2px 6px; border-radius: 6px;
  transition: color var(--dur), background var(--dur);
}
.modal-close:hover { color: var(--ink); background: #f0f3f7; }

/* 首登强制改密（M6 第二批）：比详情弹层更窄；没有关闭按钮 ——
   标志未清之前不允许关掉它绕过去（后端每个接口也照常放行，
   但界面先把路堵死，避免用户误以为「网站坏了」） */
.pw-modal .modal-box { max-width: 420px; }
.pw-modal .pw-lead { margin: 0 0 14px; font-size: 13px; color: var(--ink-2); line-height: 1.7; }
.pw-modal .pw-lead b { color: var(--ink); }
.pw-modal .pw-lead code {
  background: var(--brand-l); border-radius: 4px; padding: 1px 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
  color: var(--brand-dark);
}

/* 事件编辑 / 档位设置弹层（M6 第三批）：比详情弹层窄 */
#ev-edit-modal .modal-box { max-width: 520px; }
#ev-levels-modal .modal-box { max-width: 460px; }
/* 弹层页脚：删除在左、取消/保存在右（资料详情没有页脚，这是新约定） */
.modal-foot {
  display: flex; align-items: center; gap: 8px;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line);
}
/* ★ 只有「工作编辑」这一处是长表单（负责人可无限加行 + 成果清单很长），
   页脚会随内容滚到折叠线以下 —— 与 .wd-acts 同一个毛病（2026-10-15）。
   不全局 sticky：短表单弹层不滚动，sticky 只会多出一截空的内边距。
   限定作用域到 work-edit-modal，与 .wd-acts 的修法保持同一口径。 */
#work-edit-modal .modal-foot {
  position: sticky; bottom: -16px; z-index: 2;
  background: var(--card);
  margin-bottom: -16px; padding-bottom: 16px;
}
.flex-spacer { flex: 1; }
/* 档位设置行：级别数字固定不可改，只改叫法 */
.lv-row {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 10px; font-size: 13.5px; color: var(--ink);
}
.lv-row .lv-num {
  flex: none; width: 44px; text-align: center; font-size: 12px; font-weight: 600;
  color: var(--brand-dark); background: var(--brand-l); border-radius: 6px;
  padding: 8px 0;
}
.lv-row input { flex: 1; min-width: 0; }

@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes pop-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ---------------- 详情内的 AI 区块 ---------------- */
.sec-h {
  font-size: 13.5px; font-weight: 500; margin: 18px 0 8px;
  padding-top: 14px; border-top: 1px solid var(--line);
}
.ai-summary { margin: 0 0 6px; font-size: 13px; color: var(--ink); line-height: 1.75; }
.ai-points { margin: 0; padding-left: 18px; font-size: 13px; color: var(--ink-2); }
.ai-points li { margin-bottom: 4px; }
.ent-wrap { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.ent {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; padding: 3px 9px; border-radius: 999px;
  background: #f4f8fd; border: 1px solid #e1eaf5; color: #33556f;
}
.ent em { font-style: normal; color: var(--ink-3); font-size: 11px; }
.lock-note { font-size: 12px; color: var(--ink-3); }

/* ============================================================
   M6 第十四批 · 事件名 ↔ 资料名双向同步
   勾选项跟在标题输入框下面。字号跟 .lock-note 一档，但颜色更亮一档 ——
   它是**一行操作**（点了会改变另一处的数据），不是说明文字，做成灰小字
   会让人当成提示语直接略过去，然后回来问「为什么资料名没变」。
   ============================================================ */
/* ★ 选择器必须带 .form / label 这两级，不能只写 .sync-line：
     `.form label { display:block }` 与 `.form input { display:block; width:100% }`
     的特异性(0,1,1)比单个类(0,1,0)高，只写 .sync-line 会被它们整条盖掉 ——
     后果是勾选框被撑成一个满宽方块、文字跑到它下面，而且**不报任何错**。
     同理下面的 checkbox 规则也压过 .form input 才生效。 */
.form label.sync-line,
label.sync-line {
  display: flex; align-items: flex-start; gap: 7px; flex-wrap: wrap;
  font-size: 12.5px; line-height: 1.5; color: var(--ink-2);
  margin: 6px 0 0; cursor: pointer;
}
.sync-line input[type="checkbox"] {
  /* 只改这几项：宽高与内边距交给浏览器原生绘制，动 border/background 会
     把原生勾选框画坏（各家浏览器的原生样式都靠 appearance 自绘）。 */
  display: inline-block; width: auto; margin: 3px 0 0;
  flex: 0 0 auto; accent-color: var(--brand);
}
/* 对方的当前名称：跟主体文字区分开，长名字要能换行而不是撑宽容器 */
.sync-line .sync-note { color: var(--ink-3); font-style: normal; overflow-wrap: anywhere; }
/* 卡片内联重命名那条是 flex-wrap 行，勾选独占一行（与 .msg 同一处理） */
.mat-rename .sync-line { flex: 1 1 100%; margin: 0; }
.lock-line {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12px; color: var(--ink-3); margin: 0 0 6px;
}
.mat-textbox {
  margin: 8px 0 0; padding: 10px 12px;
  background: #f7f9fc; border: 1px solid var(--line); border-radius: 8px;
  font: 12.5px/1.7 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--ink-2); white-space: pre-wrap; word-break: break-word;
  max-height: 320px; overflow: auto;
}

/* ---------------- 我的信息（1.8.0 横向化） ----------------
 * ★ 为什么从纵向的 .kv 网格改成横向卡片：
 *   9 个字段纵向排列要占 9 行，整屏几乎全是自己的资料，而「修改信息」
 *   按钮被顶到很下面。现在是一排排小卡片，标签在上、值在下，自动换行。
 * ★ 轨道写 minmax(0, 1fr) 而不是 1fr：auto/max-content 轨道会被一条
 *   长简介顶到几百 px，整行横向溢出（这是本项目的经典坑）。
 *   窄屏降到 2 列；auto-fit + minmax 在手机上自然落到 1~2 列，不必写媒体查询。
 */
.acc-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 10px;
}
.acc-head h3 { margin: 0; }
.acc-info {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
  /* ★ align-items: start —— grid 默认 stretch：同一行里只要有一格内容很长
     （长邮箱、长简介），整行的格子都会被拉到一样高，短字段（"mty"、"是"）
     底下留出一大片空白，看着像坏了。
     （这是「横向化」后才出现的问题：纵向时每行各占一行，别的格子看不见。） */
  align-items: start;
}
.acc-cell {
  min-width: 0;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 7px 11px;
}
/* 简介（bio）：长文本，横跨两格，别把整行撑得高低不齐 */
.acc-cell.wide { grid-column: span 2; }
.acc-l {
  display: block; font-size: 11.5px; color: var(--ink-3);
  margin-bottom: 2px;
}
.acc-v {
  display: block; font-size: 13.5px; color: var(--ink);
  /* ★ 长邮箱 / 长简介必须 anywhere：break-word 不参与 min-content 计算，
     一条长串能把整列顶到 400px 以上（见用户级记忆里那条防溢出规则）。 */
  overflow-wrap: anywhere;
}
@media (max-width: 520px) {
  .acc-cell.wide { grid-column: span 1; }
}

/* ---------------- 备份与还原（1.13.0 需求③ · 「我的」页平台运维卡） ----------------
   ★ 报告区的数字块直接复用 .acc-cell 的视觉语言（标签在上、值在下的小方块）：
     它与「我的信息」是同一类信息（一堆并列的字段），样式没有理由长出第二套。
   ★ 下面所有 flex 行都写 min-width:0 + flex-wrap，控件也写 min-width:0 ——
     本项目栽过四次横向溢出，根因都是「flex 项默认 min-width:auto，不肯收缩到
     内容宽度以下」，而裸 <select> 的固有宽度等于**最长那个选项**的宽度。 */
.rs-pick {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 12px 0 2px;
}
.rs-pick .btn { flex: none; }
.rs-pick .hint { min-width: 0; overflow-wrap: anywhere; }

#restore-report { margin-top: 14px; min-width: 0; }

.rs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px; align-items: start; margin-bottom: 12px;
}

/* 逐段数据段计数 */
.rs-secs {
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg); padding: 10px 12px; min-width: 0;
}
.rs-secs-t { font-size: 12px; color: var(--ink-3); margin-bottom: 8px; }
.rs-secs-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 6px 10px;
}
.rs-sec { display: flex; align-items: baseline; gap: 6px; min-width: 0; font-size: 12.5px; color: var(--ink-2); }
.rs-sec-n { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; flex: none; }
.rs-sec-l { min-width: 0; overflow-wrap: anywhere; }
/* 0 的段压暗但仍显示：整段缺失与「这一段就是空的」是两件事 */
.rs-sec-zero { opacity: .42; }

/* 还原方式（复用 .work-fs 的 fieldset 外观） */
.rs-radio {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 7px 0; cursor: pointer; min-width: 0;
}
.rs-radio input { margin: 3px 0 0; flex: none; }
.rs-radio > span { min-width: 0; }
.rs-radio b { display: block; font-size: 13px; font-weight: 500; color: var(--ink); }
.rs-radio i { display: block; font-style: normal; font-size: 11.5px; color: var(--ink-3); margin-top: 1px; overflow-wrap: anywhere; }

.rs-box { margin-top: 12px; min-width: 0; }
.rs-danger {
  border: 1px solid #f0c9c9; background: #fffafa;
  border-radius: 10px; padding: 12px 13px;
}
.rs-warn { margin: 0 0 12px; font-size: 12.5px; line-height: 1.65; color: #8f2b2b; }
.rs-f { display: block; margin-bottom: 12px; min-width: 0; }
.rs-f > span { display: block; font-size: 12px; color: var(--ink-3); margin-bottom: 5px; }
.rs-f input, .rs-f select {
  display: block; width: 100%; box-sizing: border-box;
  /* ★ 裸 <select> 不写 min-width:0 时按最长选项撑开（实测 358px）——
     320px 屏幕上整页横向溢出，而界面上只是「右边被切掉一点」。 */
  min-width: 0; max-width: 100%;
  padding: 9px 12px; font: inherit; font-size: 13px; color: var(--ink);
  border: 1px solid var(--line); border-radius: 8px; background: #fff;
  transition: border-color var(--dur), box-shadow var(--dur);
}
.rs-f input:focus, .rs-f select:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(74, 144, 217, .13);
}

/* 选中目标后「会被删掉多少」 */
.rs-target-info {
  border: 1px dashed #e6b9b9; background: #fff;
  border-radius: 9px; padding: 10px 12px; margin-bottom: 12px; min-width: 0;
}
.rs-del-t { font-size: 12px; color: #8f2b2b; margin-bottom: 6px; }
.rs-del { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 12.5px; color: var(--ink-2); }
.rs-del span { min-width: 0; }
.rs-del b { color: #b42318; font-variant-numeric: tabular-nums; }

/* 拖拽落点提示：只在这张卡上加个描边，不改变布局（避免拖拽时页面跳动） */
#card-backup-restore.rs-over { border-color: var(--brand); background: var(--brand-l); }

/* ---------- Excel 导入（1.13.0 需求②） ----------
   ★ 与还原面板共用 rs-* 那套（rs-pick / rs-box / rs-f / rs-radio）：
     两者都是「选一个文件 → 出一份报告 → 确认」，外观分两套只会让人以为
     是两个不同性质的动作，而它们的风险等级完全一样（都会改数据）。
     下面只补 rs-* 里没有的三样：文件清单、逐条勾选列表、告警条。 */
.modal-box.imp-wide { max-width: 860px; }

.imp-files { display: grid; gap: 4px; margin: 10px 0 12px; min-width: 0; }
/* ★ 文件名与说明**上下两行**，不做左右并排。
   并排时 `<b>` 是 flex 项、`min-width:0` 又是必需的，于是窄屏下它被
   右侧那段固定宽度说明挤到几乎为零，中文就按「任意两字之间都能换行」
   的规则竖排成一列（实测 320px：「事件流台账.xlsx」变成 8 行单字）。
   这类坏法不报错、只是难看，但会让整块区域高得离谱。上下堆叠没有这个可能。 */
.imp-file { display: block; font-size: 12.5px; color: var(--ink-2); min-width: 0; }
.imp-file b { display: block; font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
.imp-file i { display: block; font-style: normal; color: var(--ink-3); overflow-wrap: anywhere; margin-top: 1px; }

.imp-sub { font-size: 12px; color: var(--ink-3); margin: 14px 0 6px; }

/* 新增计数：四个格子，数字大、标签小，一眼看出「这批有多少东西」 */
.imp-nums { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 8px; }
.imp-num {
  border: 1px solid var(--line); border-radius: 9px; padding: 8px 10px;
  background: var(--bg); min-width: 0;
}
.imp-num b { display: block; font-size: 17px; line-height: 1.25; font-variant-numeric: tabular-nums; color: var(--ink); }
.imp-num span { font-size: 11.5px; color: var(--ink-3); }

/* 逐条勾选覆盖：列表自己滚动，别把整个弹层撑长（240px 约 8 行，够看也够点） */
.imp-list {
  border: 1px solid var(--line); border-radius: 10px; background: var(--bg);
  max-height: 240px; overflow: auto; min-width: 0;
}
.imp-row {
  display: flex; align-items: flex-start; gap: 8px; padding: 6px 10px;
  border-bottom: 1px solid var(--line); font-size: 12.5px; min-width: 0;
  /* ★ 必须允许换行：右侧那个「已存在 / 本批重复（同批另一份文件里已有
     同一条）」是 flex:none 的定宽块，窄屏下放不下时若不许换行，它会把
     左侧的标题挤成竖排单字（中文可在任意两字之间换行）。换行后它自己
     掉到第二行，标题保住整行宽度。 */
  flex-wrap: wrap;
}
.imp-row:last-child { border-bottom: 0; }
.imp-row > input { margin: 2px 0 0; flex: none; }
/* 没有勾选框的行（成员）用它占位：宽度与 checkbox 一致，标题才不会左移一格 */
.imp-nobox { flex: none; width: 13px; height: 13px; }
.imp-row .imp-t { min-width: 0; flex: 1 1 auto; overflow-wrap: anywhere; color: var(--ink); }
.imp-row .imp-m { color: var(--ink-3); font-size: 11.5px; margin-left: 6px; }
.imp-row .imp-k { margin-left: auto; flex: none; color: var(--ink-3); font-size: 11.5px; padding-left: 8px; }
.imp-plain { padding: 8px 10px; font-size: 12.5px; color: var(--ink-2); overflow-wrap: anywhere; }

/* 告警条：中性暖色，不是「危险红」—— 这些是「你需要知道」，不是「你要出事」 */
.imp-warn {
  margin: 12px 0 0; padding: 10px 12px; border-radius: 10px;
  background: #fffaf0; border: 1px solid #f0e0b8;
  font-size: 12.5px; line-height: 1.7; color: #7a5a12; min-width: 0;
}
.imp-warn div + div { margin-top: 4px; }

.imp-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0 6px; }
.imp-bar .hint { margin: 0; min-width: 0; }

/* ---------- 回收站（1.13.0 需求③ · 第 8 步） ----------
   ★ 与 .imp-list 同一套视觉（都是「一列记录 + 右侧动作」），但**不共用类名**：
     回收站的行是三行文字 + 两个按钮，导入的行是一个勾选框 + 一行字，
     强行合并会让两边的窄屏约束互相打架（见下面 min-width:0 那几条）。 */
.trash-body { min-width: 0; }

.trash-total {
  font-size: 12px; color: var(--ink-3); margin: 10px 0 6px;
  font-variant-numeric: tabular-nums;
}

.trash-row {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 9px 11px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--bg); margin-bottom: 8px;
  /* ★ 必须允许换行：右侧是 flex:none 的两颗定宽按钮。不许换行时，
     窄屏下它们会把左侧挤到近零宽，而中文可以在任意两字之间换行 ——
     症状是空间名竖排成一列单字（「事件流台账」变成 5 行）。 */
  flex-wrap: wrap;
  min-width: 0;
}

.trash-main { flex: 1 1 220px; min-width: 0; }

.trash-t {
  font-size: 13px; font-weight: 500; color: var(--ink);
  overflow-wrap: anywhere; min-width: 0; line-height: 1.5;
}
.trash-meta { font-size: 11.5px; color: var(--ink-3); margin-top: 2px; overflow-wrap: anywhere; }
/* 真实文件名：普通用户不需要看，但排障时它是唯一能对上的东西 */
.trash-file { font-size: 11px; color: var(--ink-3); margin-top: 1px; opacity: .75; overflow-wrap: anywhere; }

.trash-badge {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 20px;
  font-size: 11px; font-weight: 400; vertical-align: 1px;
  background: var(--brand-l); color: var(--brand); white-space: nowrap;
}
/* 「不含原件」必须是看得见的一档：它改变的是「这份备份能救回什么」 */
.trash-badge-warn { background: #fff4e5; color: #a35d00; }

.trash-acts { display: flex; gap: 6px; flex: none; align-items: center; }
.trash-acts .btn { white-space: nowrap; }
/* 彻底删除不是主操作，但也不能做得像「取消」—— 用文字色区分即可 */
.trash-del { color: var(--err); }
.trash-del:hover { border-color: var(--err); }

@media (max-width: 600px) {
  /* 窄屏：按钮掉到第二行并各占一半，别让它们把标题挤成竖排 */
  .trash-acts { flex: 1 1 100%; min-width: 0; }
  .trash-acts .btn { flex: 1 1 0; min-width: 0; }
}

/* 顶栏「功能说明」按钮（1.8.0 从「我的」页快捷入口移过来，紧跟版本号右侧）。
   ★ 视觉上刻意做成「文字按钮」而不是实心按钮：它不是日常操作，
     常驻顶栏却不该抢用户对「资料 / 事件 / 工作」的注意力。 */
.features-link {
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12px; color: var(--brand);
  padding: 3px 6px; border-radius: 7px; white-space: nowrap;
  flex: none;
}
.features-link:hover { background: var(--brand-l); text-decoration: underline; }

/* ---------------- 杂项 ---------------- */
.hint { color: var(--ink-3); font-size: 12.5px; margin: 8px 0; }
.msg { min-height: 18px; font-size: 12.5px; margin: 2px 0 6px; color: var(--err); }
.msg.ok { color: var(--ok); }
.kv { margin: 0; display: grid; grid-template-columns: 96px 1fr; gap: 8px 12px; font-size: 13px; }
.kv dt { color: var(--ink-3); }
.kv dd { margin: 0; word-break: break-all; }
.code {
  background: #f7f9fc; border: 1px solid var(--line); border-radius: 8px;
  padding: 12px; font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--ink-2); overflow: auto; margin: 10px 0 0;
}

/* ---------------- 漂浮提示 ---------------- */
#tihuo-flash {
  position: fixed; left: 50%; bottom: 34px; z-index: 90;
  transform: translateX(-50%) translateY(8px);
  background: rgba(36, 48, 61, .93); color: #fff;
  font-size: 13px; padding: 9px 20px; border-radius: 999px;
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur), transform var(--dur);
}
#tihuo-flash.on { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------------- 页脚 ---------------- */
.site-footer { border-top: 1px solid var(--line); background: var(--card); margin-top: 20px; }
.site-footer .wrap { max-width: 1200px; margin: 0 auto; padding: 18px 20px 22px; }
.site-footer p { margin: 0 0 6px; font-size: 12px; color: var(--ink-3); }
.site-footer .copy { margin-top: 10px; color: #a9b4c1; }

/* ================= 手机端悬浮添加 ================= */
.fab-wrap { position: fixed; right: 16px; bottom: calc(var(--tabbar-h) + 16px); z-index: 45; }
.fab {
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--brand); color: #fff; border: 0; cursor: pointer;
  font-size: 24px; font-weight: 300; line-height: 1;
  box-shadow: 0 6px 16px rgba(74, 144, 217, .45);
  display: flex; align-items: center; justify-content: center;
  transition: background var(--dur), transform var(--dur);
}
.fab:hover { background: var(--brand-dark); }
.fab:active { transform: scale(.94); }
.fab-wrap .menu { left: auto; right: 0; min-width: 240px; }

/* ================= 手机端底部标签栏 ================= */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 44;
  display: none;
  background: var(--card); border-top: 1px solid var(--line);
  padding: 5px 0 calc(6px + env(safe-area-inset-bottom, 0px));
}
.mtab {
  flex: 1; position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 10.5px; color: var(--ink-3);
  transition: color var(--dur);
}
.mtab svg { width: 20px; height: 20px; }
.mtab.on { color: var(--brand); font-weight: 600; }
.mtab-badge {
  position: absolute; top: -3px; left: calc(50% + 7px);
  background: var(--warn); color: #fff; font-size: 9.5px; min-width: 16px;
  border-radius: 999px; text-align: center; line-height: 1.6; padding: 0 4px;
}

/* ============================================================
   移动端（≤600px）
   ============================================================ */
@media (max-width: 600px) {
  .topbar-in { height: 54px; padding: 0 14px; gap: 10px; }
  .brand-text { display: none; }          /* 手机上空间名比平台名重要，logo 只留图标 */
  .brand-logo { width: 32px; height: 32px; }
  .space-wrap { display: none; }          /* 手机上切空间走顶栏那个「‹ 空间名」（进空间列表） */
  .back-spaces.on { display: flex; }
  /* ★ 1.10.0（需求③）：账号胶囊在手机上**必须留着** —— 「我的」不再是底部
     标签栏的一项（平台入口统一收到顶栏），以前它敢隐藏是因为底下有个「我的」
     标签兜底。窄屏上收紧到 96px，名字太长就省略号。
     ★★ `min-width: 0` 不能省：flex 项默认 `min-width:auto`，就算给了 max-width
     也不会缩到内容宽度以下 —— 顶栏挤不下时它不肯让位，转而把「退出登录」挤成
     竖排三行（真截图才发现的）。空间名与账号名都带省略号，本来就该先让位。 */
  .user-chip { max-width: 96px; min-width: 48px; flex-shrink: 4; }
  /* ★ 让位的**次序**要明确：顶栏挤不下时，账号胶囊（只是一个名字）先缩，
     空间名（当前在哪个空间）尽量留住 2-3 个字，两个定长按钮一点不让。
     不写 flex-shrink 的话三方按默认权重 1 均摊，结果是「空间名只剩一个字、
     账号名还剩一大截」—— 那不是设计，是默认值。
     ★ 同理：中文可以在任意两个字之间换行，所以这两个**定长**按钮被挤窄时会
     竖着排（「退 / 出 / 登 / 录」）。它们没有省略号可截，只能不许被压缩。 */
  #btn-back-spaces { flex-shrink: 1; }
  .topbar-in .features-link,
  .topbar-in #btn-logout { flex: none; white-space: nowrap; }

  .view { padding: 16px 14px 40px; }
  /* 触控目标：手指点按的最小舒适高度（44px 是理想值，次级按钮 38-42 可接受） */
  .btn { min-height: 42px; }
  .btn-sm { min-height: 38px; }
  .back-spaces { padding: 8px 8px 8px 0; }   /* 返回箭头加大点按区 */
  .item { flex-direction: column; }
  .item-actions { width: 100%; justify-content: flex-start; }
  .kv { grid-template-columns: 80px 1fr; }
  .row { flex-wrap: wrap; }
  .row-main { flex: 1 1 100%; }
  /* ★ 320px 实测：`.row-actions` 的基础规则是 `flex: none`（＝不收缩），
     于是它按 max-content 撑开 —— 成员行上「改资料 / 启用账号 / 重置密码 /
     强制下线 / 删除账号」五颗按钮合计 351px，把整页顶出 31px 横向滚动。
     给它 1 1 100% 就是「独占一行 + 允许收缩」，行内的 `flex-wrap: wrap`
     这才真正有机会生效（按钮自己换行，而不是把页面撑宽）。
     `.row-act-acc` 本来就是这么写的，这里只是把同一套做法补到主操作行。 */
  .row-actions { flex: 1 1 100%; min-width: 0; justify-content: flex-start; }
  .space-grid { grid-template-columns: 1fr; }
  .ov-stats { grid-template-columns: repeat(2, 1fr); }
  .ov-grid { grid-template-columns: 1fr; }

  /* 工作台：左栏收起，主区铺满 */
  .work { display: block; }
  .rail { display: none; }
  .work-main { padding: 0 0 30px; }

  /* 底部标签栏（1.10.0 需求③：只在工作台里出现，见 renderChrome 的 has-tabbar）
     ★ 给 body 留出的底部空间、以及那几处「贴在标签栏上方」的悬浮元素，
       都要跟着 has-tabbar 走 —— 标签栏不在时还留 62px 空档，页脚上方会凭空
       多出一截空白（而屏幕上没有任何东西解释它为什么在那儿）。 */
  .tabbar { display: flex; }
  body.has-tabbar { padding-bottom: var(--tabbar-h); }

  /* 资料工具 */
  .mat-tools { flex-wrap: wrap; }
  .mat-search { flex: 1 1 100%; order: 1; }
  .add-wrap { display: none; }            /* 电脑端的「+ 添加」由 FAB 顶替 */
  .chips-row { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 2px; }
  .chips { flex-wrap: nowrap; flex: none; }
  .chip { flex: none; }
  .chips-tools { margin-left: 0; flex: none; position: sticky; right: 0; background: var(--bg); padding-left: 6px; }
  .tbtn-select { display: none; }         /* 排序收进筛选面板 */

  /* 筛选面板 → 底部抽屉 */
  .filter-pop {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; max-height: 78vh;
    border-radius: 18px 18px 0 0;
    padding: 16px 16px calc(14px + env(safe-area-inset-bottom, 0px));
    animation: slide-up .22s ease-out;
  }
  /* 抽屉内容超长时，操作按钮钉在底部始终可见，不许被内容卷走 */
  .filter-pop .fp-foot {
    position: sticky; bottom: 0;
    margin: 12px -16px 0;
    padding: 10px 16px;
    background: var(--card);
    border-top: 1px solid var(--line);
  }
  .fp-foot { gap: 10px; }
  .fp-foot .btn { flex: 1; padding: 10px 0; }

  /* 紧凑行：一行放不下分类与日期，只留标题与状态 */
  .mat-row .meta { display: none; }
  .row-act { display: flex; }
  /* ★★ 手机上这一行根本塞不下：状态/类型/工作成果/目录 这些胶囊都是
     `flex: none`（宽度由内容定死），再加上「预览 分析 解析 改名 删」五个按钮，
     固定宽度合计就超过 390px 了 —— 真渲染量到整页被顶出 **104px 横向滚动**
     （scrollWidth 494 / 视口 390），条目行上看得见「分析解析改名删」被切掉半截。
     把标题挤到 0 也救不回来（那是确定放不下，不是标题太宽）。
     → 允许换行，并让动作区**独占最后一行**（与就地改名 .renaming 同一处置：
      flex-wrap + flex:1 1 100%）。比缩字号或藏按钮更稳妥：功能一个都不少。
     ★ 别用 `overflow: hidden` 糊过去 —— 那只是把被切掉的按钮藏起来，用户点不到。 */
  .mat-row { flex-wrap: wrap; }
  .mat-row .row-act { order: 9; flex: 1 1 100%; justify-content: flex-end; margin-top: 4px; }
  .upload-bar { flex-wrap: wrap; }
  .upload-bar .ub-track { width: 100%; order: 3; flex: 1 1 100%; }
  .upload-bar .ub-state { width: auto; }

  /* 详情弹层 → 底部抽屉 */
  .modal { padding: 0; align-items: flex-end; }
  .modal-box { max-width: none; max-height: 88vh; border-radius: 18px 18px 0 0; animation: slide-up .22s ease-out; }

  /* 这两处「让开底部标签栏」的抬升同样只在有标签栏时生效（需求③） */
  body.has-tabbar .batch-bar { bottom: var(--tabbar-h); }
  body.has-tabbar #tihuo-flash { bottom: calc(var(--tabbar-h) + 18px); }

  .todo-meta { align-items: flex-start; }
  .group > summary { padding: 9px 10px; }
  .group-body { padding: 0 10px 10px; }
}

@keyframes slide-up { from { transform: translateY(40px); opacity: 0; } to { transform: none; opacity: 1; } }

/* 电脑端：FAB 永不出现（它是手机端的入口） */
@media (min-width: 600.02px) {
  .fab-wrap { display: none !important; }
  .back-spaces { display: none !important; }
}

/* ============================================================
   M5.5 用户账号管理（平台超管 ·「我的」页卡片）
   ============================================================ */
.au-search {
  display: flex; align-items: center; gap: 8px;
  border: 1px solid var(--line); border-radius: 10px;
  padding: 8px 12px; margin: 4px 0 12px; background: #fff;
}
.au-search:focus-within { border-color: var(--brand); }
.au-search svg { width: 15px; height: 15px; flex: none; color: var(--ink-3); }
.au-search input {
  border: none; outline: none; background: none; flex: 1; min-width: 0;
  font-size: 13.5px; color: var(--ink); font-family: inherit;
}
.au-search input::placeholder { color: var(--ink-3); }

.admin-list { display: grid; gap: 10px; }

.au-row {
  display: flex; gap: 10px; align-items: flex-start;
  border: 1px solid var(--line); border-radius: 12px;
  padding: 11px 12px; background: #fff;
}
.au-row.off { opacity: .62; background: var(--bg); }
.au-row.off .au-name { text-decoration: line-through; text-decoration-color: var(--ink-3); }

.au-main { flex: 1; min-width: 0; }
.au-line { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.au-name { font-weight: 600; font-size: 14px; color: var(--ink); }
.au-user { font-size: 12.5px; color: var(--ink-3); }
.au-meta { font-size: 12px; color: var(--ink-3); margin-top: 3px; }

.au-temp {
  margin-top: 7px; font-size: 12.5px; color: var(--warn);
  background: #fdf3e3; border: 1px solid #f3ddb2;
  border-radius: 8px; padding: 6px 9px;
}
.au-temp code {
  font-size: 13px; font-weight: 700; color: var(--ink);
  background: #fff; border: 1px solid var(--line);
  border-radius: 5px; padding: 1px 6px; user-select: all;
}

.au-act { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; max-width: 46%; }
.au-del { color: var(--err); border-color: #f3caca; }
.au-del:hover { border-color: var(--err); color: var(--err); background: #fdecec; }

@media (max-width: 600px) {
  .au-row { flex-direction: column; }
  .au-row.off { background: #f7f8fa; }
  .au-act { max-width: none; width: 100%; justify-content: flex-start; }
  .au-act .btn { flex: 1 1 40%; }
}

/* ============================================================
   M6 第五批 · 模型配置（方案列表 + 表单）
   布局：左窄栏列方案，右宽栏填参数。窄屏（≤820px）自动改为上下堆叠 ——
   表单有两列表单格，硬挤在一行会把输入框压得没法点。
   ============================================================ */
.llm-wrap {
  display: grid;
  grid-template-columns: 220px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

.llm-side {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg);
  padding: 10px;
}
.llm-side-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 0 2px 8px;
  font-size: 12.5px; font-weight: 600; color: var(--ink-2);
}
.llm-list { display: grid; gap: 6px; }

.llm-item {
  display: block; width: 100%; text-align: left;
  border: 1px solid var(--line); border-radius: 10px;
  background: #fff; padding: 9px 10px; cursor: pointer;
  font-family: inherit; transition: border-color .15s, background .15s;
}
.llm-item:hover { border-color: var(--brand); }
.llm-item:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.llm-item.on { border-color: var(--brand); background: var(--brand-soft, #eef4fd); }
/* 正在生效：左侧一道实色边，与「仅被选中编辑」区分开 */
.llm-item.live { border-left: 3px solid var(--brand); padding-left: 8px; }
.llm-item-name {
  display: flex; align-items: center; gap: 6px;
  font-size: 13.5px; font-weight: 600; color: var(--ink);
}
.llm-item-meta {
  display: block;
  font-size: 11.5px; color: var(--ink-3); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.llm-item-act { display: block; margin-top: 7px; }
.llm-item-act .btn { width: 100%; }
/* 正在生效：左侧一道实色边，与「仅被选中编辑」区分开 */
/* 正在生效的小圆点，比再加一个胶囊更省地方 */
.llm-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--brand); flex: none;
}

.llm-main { min-width: 0; }

.llm-form { display: grid; gap: 12px; }
.llm-fs {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 12px 14px 14px; margin: 0;
  display: grid; gap: 10px; background: #fff;
}
.llm-fs > legend {
  font-size: 12.5px; font-weight: 600; color: var(--ink-2);
  padding: 0 6px; margin-left: -4px;
}
.llm-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }

.llm-f { display: grid; gap: 5px; min-width: 0; }
.llm-f > span {
  font-size: 12.5px; color: var(--ink-2);
  display: flex; align-items: center; gap: 3px;
}
.llm-f > span i { color: var(--err); font-style: normal; }
.llm-f input[type="text"], .llm-f input[type="password"], .llm-f input[type="number"] {
  border: 1px solid var(--line); border-radius: 9px;
  padding: 8px 10px; font-size: 13.5px; font-family: inherit;
  color: var(--ink); background: #fff; width: 100%; min-width: 0;
}
.llm-f input:focus { outline: none; border-color: var(--brand); }
.llm-f input::placeholder { color: var(--ink-3); }
.llm-f em {
  font-style: normal; font-size: 11.5px; color: var(--ink-3); line-height: 1.5;
}
.llm-chk { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink); }
.hint-tight { margin: 0; font-size: 11.5px; }

/* 测试结果：每项一行，绿/红一眼分开，别让用户自己找哪条错了 */
.llm-test {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 10px 12px; background: #fff;
  display: grid; gap: 6px; font-size: 12.5px;
}
.llm-test b { color: var(--ink); font-weight: 600; }
.llm-test .ok { color: #16794a; }
.llm-test .no { color: var(--err); }
.llm-test-row { display: flex; gap: 8px; align-items: baseline; }
.llm-test-row > span:first-child { flex: none; width: 4.5em; color: var(--ink-2); }

#llm-form .msg { margin: 0; }
#llm-del[hidden] { display: none; }

/* ============================================================
   M6 第七批 · 模型配置界面优化
   - 模型下拉 + 「擅长什么」说明（选之前能比、选之后能确认）
   - 必填 / 可选 分段（可选区默认收起，见 index.html 的 #llm-optional）
   ============================================================ */

/* 一个模型字段 = 输入框 + 下拉 + 说明，三件套竖排 */
/* ★ 轨道必须显式写 minmax(0, 1fr)。
   grid 的 `auto` 轨道**不会**为了塞进容器而收缩，它按 max-content 定宽 ——
   于是「向服务商拉取模型」那一行把 290px 的容器撑到 419px，「我的」页在手机上
   整页横向滚动（2026-10-14 实测：文档 469px / 视口 390px，隐藏模型配置卡片即恢复 390）。
   ★ 加在**子项**上的 `min-width: 0` 治不了这个问题（.llm-f 早就写了），必须在轨道上写。 */
.llm-field { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; min-width: 0; }
.llm-field .llm-f { gap: 5px; }

.llm-pick-row { display: flex; gap: 8px; align-items: center; }
.llm-pick {
  flex: 1 1 auto; min-width: 0;
  border: 1px solid var(--line); border-radius: 9px;
  padding: 7px 9px; font-size: 13px; font-family: inherit;
  color: var(--ink); background: #fff;
}
.llm-pick:focus { outline: none; border-color: var(--brand); }
.llm-pick-row .btn { flex: none; white-space: nowrap; }

/* ★ 说明文字用 em 是语义上的「强调」，但不是斜体 —— 斜体中文很丑且难读 */
.llm-pick-desc { font-style: normal; font-size: 11.5px; color: var(--ink-3); line-height: 1.55; }
/* 清单里的模型：说明是「确认」用的，颜色要压得住，别和提示语一样灰 */
.llm-pick-desc.is-known { color: var(--ink-2); }
/* 自定义模型名：有「调用可能失败」的风险，得让人看见 */
.llm-pick-desc.is-custom { color: var(--warn); }

.llm-optbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* 「这套方案已配置可选内容」的提示（1.8.0）。
   ★ 它替代了原来的「有可选项就自动展开」：展开态一次都不落盘，
     但用户仍要能一眼看出「我配过东西」——不然会以为视觉/向量没配，
     于是把同样的地址密钥再填一遍。 */
.llm-opt-hint {
  font-size: 12px; color: var(--warn);
  background: #fdf3e3; border: 1px solid #f3ddb2;
  border-radius: 7px; padding: 3px 9px;
}
/* 展开态的按钮给一个明确的按下感 —— aria-expanded 变了但外观没变，
   用户点完只能靠「下面是不是多了一块」来确认。 */
#llm-toggle-opt.on { border-color: var(--brand); color: var(--brand-dark); background: var(--brand-l); }
.llm-optional { display: grid; gap: 12px; }

.llm-import { width: 100%; margin-top: 8px; }
/* 「密钥会沿用…」这句跟按钮是一体的，别让 .llm-side 的 padding 把它顶开 */
.llm-side .hint { margin: 6px 2px 0; font-size: 11px; line-height: 1.5; }

@media (max-width: 560px) {
  .llm-pick-row { flex-wrap: wrap; }
  .llm-pick-row .btn { width: 100%; }
}

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

/* M5.6 · OCR 识别内容标识 */
.pill-ocr {
  color: var(--warn);
  background: #fdf3e3;
  border-color: #f3ddb2;
}

/* ============================================================
   M5.7 · 内容自适应补强（平板段 / iOS 输入 / 超小屏）
   ============================================================ */

/* 平板中档 601–900px：桌面双栏布局在这段最挤 ——
   768px 实测概览「最近的待办」卡片被视口右缘裁切。
   左栏收窄 + 概览改单列 + 卡片操作按钮改小，主区立刻宽出 ~30px */
@media (min-width: 601px) and (max-width: 900px) {
  .view { padding: 18px 16px 40px; }
  .rail { width: 128px; padding: 14px 7px 20px; }
  .rail-item { padding: 8px 9px; font-size: 13px; gap: 8px; }
  .ov-grid { grid-template-columns: 1fr; }
  .ov-stats { gap: 10px; }
  .mat-item .item-actions { gap: 5px; }
  .mat-item .item-actions .btn { padding: 5px 9px; font-size: 12.5px; }
}

/* iOS Safari：输入控件字号 <16px 时，聚焦瞬间会自动放大整个页面
   （用户要再手动捏回去）。手机端统一提到 16px，桌面不受影响 */
@media (max-width: 600px) {
  .form input, .form select, .form textarea,
  .mat-search input, .au-search input,
  .todo-tools select {
    font-size: 16px;
  }
}

/* 超小屏（≤360px，如 iPhone SE 竖屏 / 老款安卓）：
   统计卡改单列，顶栏与页面边距再收紧 */
@media (max-width: 360px) {
  .ov-stats { grid-template-columns: 1fr; }
  .topbar-in { padding: 0 10px; gap: 8px; }
  .view { padding: 14px 10px 40px; }
}

/* ============================================================
   事件标签 · 管理界面（M6 第六批）
   ============================================================ */
#ev-tags-modal .modal-box { max-width: 620px; }

/* 标签列表行：色点 + 名称 + 适用情形 + 计数 + 操作 */
.tag-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; margin-bottom: 8px;
  background: var(--bg); border: 1px solid var(--line);
  border-radius: 9px;
}
.tag-row-dot {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  box-shadow: inset 0 0 0 3px #fff, 0 0 0 1px var(--line);
  background: var(--tag-s, var(--tag-slate-s));
}
/* 色号 → 颜色变量的**唯一映射**：色点 / 多选胶囊 / 筛选按钮 / 色板共用一份。
   ★ 原先 tag-row-dot 与 tag-chip 各写 12 条、共 24 条说同一件事 ——
     每次加一个带颜色的组件就要再抄一遍。合并成 12 条之后，
     任何元素只要带 data-c="N" 就自动拿到 --tag-s/-b/-f。
   ★ 变量只在这里赋值，各组件用自己的 fallback（--tag-s, var(--tag-slate-s)），
     所以顺序无关，不会互相覆盖。 */
[data-c="0"]  { --tag-s: var(--tag-indigo-s);  --tag-b: var(--tag-indigo-b);  --tag-f: var(--tag-indigo-f); }
[data-c="1"]  { --tag-s: var(--tag-teal-s);    --tag-b: var(--tag-teal-b);    --tag-f: var(--tag-teal-f); }
[data-c="2"]  { --tag-s: var(--tag-amber-s);   --tag-b: var(--tag-amber-b);   --tag-f: var(--tag-amber-f); }
[data-c="3"]  { --tag-s: var(--tag-rose-s);    --tag-b: var(--tag-rose-b);    --tag-f: var(--tag-rose-f); }
[data-c="4"]  { --tag-s: var(--tag-violet-s);  --tag-b: var(--tag-violet-b);  --tag-f: var(--tag-violet-f); }
[data-c="5"]  { --tag-s: var(--tag-azure-s);   --tag-b: var(--tag-azure-b);   --tag-f: var(--tag-azure-f); }
[data-c="6"]  { --tag-s: var(--tag-lime-s);    --tag-b: var(--tag-lime-b);    --tag-f: var(--tag-lime-f); }
[data-c="7"]  { --tag-s: var(--tag-orange-s);  --tag-b: var(--tag-orange-b);  --tag-f: var(--tag-orange-f); }
[data-c="8"]  { --tag-s: var(--tag-cyan-s);    --tag-b: var(--tag-cyan-b);    --tag-f: var(--tag-cyan-f); }
[data-c="9"]  { --tag-s: var(--tag-pink-s);    --tag-b: var(--tag-pink-b);    --tag-f: var(--tag-pink-f); }
[data-c="10"] { --tag-s: var(--tag-green-s);   --tag-b: var(--tag-green-b);   --tag-f: var(--tag-green-f); }
[data-c="11"] { --tag-s: var(--tag-fuchsia-s); --tag-b: var(--tag-fuchsia-b); --tag-f: var(--tag-fuchsia-f); }
.tag-row-main { flex: 1; min-width: 0; }
.tag-row-name { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.tag-row-applies { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.tag-row-n { font-size: 12px; color: var(--ink-2); flex: none; white-space: nowrap; }
.tag-row-act { display: flex; gap: 4px; flex: none; }

/* 色板：新建/改标签时选颜色。不预置「自动」以外的默认选中项。 */
.color-swatches { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.swatch {
  width: 26px; height: 26px; border-radius: 50%; cursor: pointer;
  border: 2px solid transparent; box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px var(--line);
  background: var(--tag-s, var(--tag-slate-s));
  transition: transform var(--dur);
}
.swatch:hover { transform: scale(1.12); }
.swatch.on { border-color: var(--ink); box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px var(--ink); }
/* 「自动」按钮：不选颜色 = 交给数据库挑第一个没被用过的 */
.swatch-auto {
  width: auto; height: 26px; padding: 0 10px; border-radius: 999px;
  font-size: 12px; color: var(--ink-2); background: var(--bg);
  display: inline-flex; align-items: center;
}

/* 事件编辑弹层里的标签多选胶囊 */
.tag-picker { display: flex; gap: 6px; flex-wrap: wrap; }
.tag-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 600; cursor: pointer;
  padding: 4px 10px; border-radius: 999px;
  background: #fff; border: 1px solid var(--line); color: var(--ink-2);
  transition: background var(--dur), border-color var(--dur);
}
.tag-chip::before {
  content: ''; width: 8px; height: 8px; border-radius: 50%;
  background: var(--tag-s, var(--tag-slate-s)); flex: none;
}
.tag-chip.on {
  background: var(--tag-b, var(--brand-l)); color: var(--tag-f, var(--brand-dark));
  border-color: var(--tag-s, var(--brand));
}
/* 色号变量来自上面那 12 条通用 [data-c="N"] 规则，这里不再各抄一遍 */
/* 未选中态也要能看到色点，否则就没法「认哪个颜色」再点它了 */
.tag-picker-empty { font-size: 12px; color: var(--ink-3); }

/* ---------- 资料标签（M7 B 组） ---------- */

/* 上面两个弹层共用 .tag-row / .tag-chip / .swatch，这里只补资料侧特有的部分 */
#mat-tags-modal .modal-box { max-width: 640px; }

/* 标签的**维度**徽标（内容 / 年份 / 文件类型）：一眼看出这一行是哪一类 */
.tag-row-kind {
  flex: none; font-size: 11px; padding: 2px 7px; border-radius: 999px;
  background: var(--brand-l); color: var(--brand-dark); white-space: nowrap;
}
.tag-row-kind[data-kind="year"] { background: var(--bg); color: var(--ink-3); }

/* 系统维护的行（年份）：没有输入框，名字用纯文本 + 一句说明 */
.tag-row-fixed {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 13.5px; font-weight: 600; color: var(--ink-2);
}
.tag-row-lock { font-size: 11px; font-weight: 400; color: var(--ink-3); }

/* 筛选面板里的标签组：比其它 frow 多一层「维度」标题，所以竖着排 */
.frow-tags { flex-direction: column; gap: 10px; align-items: stretch; }
.ftag-group { display: flex; flex-direction: column; gap: 5px; }
.ftag-h {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px; color: var(--ink-3);
}
.ftag-h i {
  font-style: normal; font-size: 10px; padding: 1px 6px; border-radius: 999px;
  background: var(--bg); color: var(--ink-3);
}
/* 带色点的标签胶囊 / 筛选项：与标签管理里的色点是**同一套变量**
   （上面 12 条 [data-c] 规则）。两处都写一遍选择器是有意的 ——
   它们共享变量，但要不要显示色点各自决定；漏掉一个的表现是
   「筛选面板里认得出颜色、卡片上的胶囊全是灰的」，而不会报任何错。 */
.tag[data-c],
.fpill[data-c] {
  display: inline-flex; align-items: center; gap: 5px;
}
.tag[data-c]::before,
.fpill[data-c]::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: var(--tag-s, var(--tag-slate-s)); flex: none;
}

/* 详情里的标签字段（标题 + 选择器 + 说明） */
.tag-field { margin: 8px 0; }
.tag-field-h {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-bottom: 6px;
  font-size: 13px; color: var(--ink-2);
}
.tag-picker-note { font-size: 11.5px; color: var(--ink-3); margin: 6px 0 0; }

/* 文字型小按钮（面板标题右侧的「管理标签…」）——
   不要做成 .btn：这里需要的是「一行小字」，抢了主按钮的视觉反而更乱 */
.link-btn {
  border: none; background: none; padding: 0; cursor: pointer;
  font-size: 12px; color: var(--brand-dark); text-decoration: underline;
  text-underline-offset: 2px;
}
.link-btn:hover { color: var(--brand); }
.h5-act {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
}

/* ---- 所属目录（M7 C 组「选择文件夹」）---------------------------------
   三处用到：筛选面板的目录行、资料卡片/紧凑行的目录胶囊、上传区里的
   「选择文件夹」按钮。目录名可能很长（一整套「2026年春季学期教学材料」
   之类），所以三处都要允许换行、都要有 min-width:0，
   否则会把整行/整列顶宽 —— 本项目在 390 视口上踩过这个（见「防溢出层」）。 */
.frow-folders { flex-wrap: wrap; }
.fpill.fp-dir,
.pill-dir {
  /* 目录名不是标签名，长得多；限宽 + 省略号，完整路径在 title 里 */
  max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pill-dir {
  /* 卡片上的目录是可点的（点一下只看这个目录），所以用 button 而不是 span */
  border: 1px solid var(--line, #e3e6ec); background: var(--bg-soft, #f6f7f9);
  color: var(--ink-2, #5a6472); cursor: pointer; font: inherit;
  font-size: 11.5px; padding: 1px 8px; border-radius: 999px;
  display: inline-flex; align-items: center; gap: 4px;
}
.pill-dir:hover { border-color: var(--brand); color: var(--brand-dark); }
/* 当前正按这个目录筛 —— 与筛选面板里的 .fpill.on 同一种视觉语言 */
.pill-dir.on { background: var(--brand-l); border-color: var(--brand); color: var(--brand-dark); font-weight: 500; }

/* 上传区里的两个入口（拖拽提示 + 「选择文件夹」按钮）。
   ★ .drop-zone 自身是 role=button 且整块可点（选文件），按钮必须
     stopPropagation 才不会连带弹出第二个文件选择器（JS 里已处理），
     这里只负责别让它把「拖到这里」那行文字挤变形。 */
.dz-acts {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  justify-content: center; margin: 8px 0 0;
}
.dz-acts .hint { margin: 0; }

/* 标签树分支头：比账号分支更紧凑，一屏能放下更多分支 */
.ev-tag-branch { margin-bottom: 8px; position: relative; }
.ev-tag-branch-t {
  display: flex; align-items: center; gap: 8px;
  border-bottom: 1px solid var(--line);
}
.ev-tag-branch-chip { font-size: 12px; font-weight: 600; padding: 2px 9px; border-radius: 999px; }

/* 顶栏版本号（M6 第六批）：常驻但不抢注意力 */
.app-version {
  font-size: 11px; color: var(--ink-3); white-space: nowrap;
  padding: 2px 8px; border-radius: 999px; background: var(--bg);
  cursor: default; user-select: none;
}
/* 手机端顶栏挤：版本号只留版本号，构建时间走 title */
@media (max-width: 760px) {
  .app-version .av-built { display: none; }
  .app-version { padding: 2px 6px; }
}

/* 账号 / 标签筛选下拉（M6 第八批）：两个都是常驻单选，外观共用一套。
   ★ 标签那个原先写成 multiple：多选框必须按住 Ctrl/⌘ 才能加选，
     不加修饰键怎么点都不动 —— 结果「能按标签筛事件」这件事几乎没人发现，
     等于白做。改成原生单选后，点一下就能切，也不需要任何键盘技巧。
   宽度给个上下限：太窄连「全部标签」都显示不全，太宽会把后面的按钮挤到第二行。 */
#ev-account, #ev-tag { min-width: 110px; max-width: 190px; }

/* ============================================================
   工作安排（M7 第一批）
   ------------------------------------------------------------
   语言沿用全站那一套：卡片 + 胶囊 + 灰蓝底；主色只在「主操作」与「在任」上用。
   时间轴是本页的核心可视化 —— 需求要的是「看得出某项工作在不同时间归谁」，
   所以那一段用左侧竖线 + 圆点，而不是一堆并列的胶囊。 */

/* ---------- 录入入口 ---------- */
.work-intake { border-left: 3px solid var(--brand); }
.work-intake .hint { margin: 0 0 12px; }
.work-intake .hint b { color: var(--ink); }

.work-tools { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin: 0 0 10px; }
.work-tools select { padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; background: #fff; font: inherit; font-size: 13px; color: var(--ink); }
.work-tools select:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-l); }
/* 工作名称搜索框（M7 第二批）：外观与同排的 select 对齐 —— 一排控件里
   混着两种高度与圆角，看起来像没做完。 */
.work-tools input[type="search"] { padding: 7px 10px; border: 1px solid var(--line); border-radius: 8px; background: #fff; font: inherit; font-size: 13px; color: var(--ink); width: 170px; max-width: 100%; }
.work-tools input[type="search"]:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-l); }
.work-mine { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink-2); cursor: pointer; }
.work-mine input { width: 16px; height: 16px; accent-color: var(--brand); }
.work-today { margin-left: auto; color: var(--ink-3); font-size: 12px; }

/* ---------- 工作卡 ---------- */
.work-card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 13px 15px; margin-bottom: 10px;
  transition: box-shadow var(--dur), border-color var(--dur);
}
.work-card:hover { border-color: #cfdcea; box-shadow: var(--shadow); }
.work-card.is-done { background: #fbfcfd; }
.work-card.is-canceled { opacity: .68; }
.work-card.is-done .work-title-btn, .work-card.is-canceled .work-title-btn { color: var(--ink-2); }

.work-card-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.work-title-btn {
  appearance: none; border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 500; color: var(--ink); text-align: left;
  min-width: 0; overflow-wrap: anywhere;
}
.work-title-btn:hover { color: var(--brand-dark); text-decoration: underline; }
.work-card.is-done .work-title-btn { text-decoration: line-through; }

/* ---------- 子工作（M7 第五批）：层级缩进 ---------- */
/* ★ 缩进分两层做，缺一层都不够：
     ① **整卡右移**（按 data-depth，下面的规则）—— 卡片的左边框排成阶梯，
        这是「从属关系」的主要线索；
     ② 卡内标题前面画**父子连线**（.work-indent 的 i 方块 + └ 弯钩）——
        说明「它挂在谁名下」，尤其是跨过折叠、子项离父项较远的时候。
   ★ 第一版只做了 ②：真渲染量到「一层的标题只比顶层右移 24px，而卡片的
     左边框**全都在同一条竖线上**」—— 整屏看下去仍然是一列平铺的卡，
     用户的原话就是「子工作要适当缩进，以体现从属关系」。
     ⇒ 只缩**内容**不缩**卡片**，等于没缩。
   ★ 缩进块必须能收缩（flex:none 的同时给固定宽），
     深层的子项在窄屏上不能把标题挤成竖排。
   ★ 每一层都要画一条竖线（祖先的「连线」）。曾有一条
     `:first-of-type` 规则想「最外层不画线」—— 它算出来的是第一个 span 兄弟
     （状态胶囊），等于整条规则没生效；而如果真按字面生效，一层深的子项
     就一根线都没有，缩进只剩空白。所以现在**无条件画线**。 */
.work-indent { display: inline-flex; gap: 0; flex: none; align-self: stretch; }
.work-indent > i {
  display: block; width: 14px; flex: none; position: relative;
  /* 左侧那条竖线 = 父子连线；画不出来的话缩进就只剩空白，看不出是层级 */
  border-left: 1px solid rgba(74,144,217,.34);
  background: linear-gradient(90deg, rgba(74,144,217,.08), rgba(74,144,217,0));
}
/* 最后一格画一个「└」弯钩：竖线 + 一截横线。有弯钩才像树；
   只有一排等距竖线时，用户会以为那只是缩进，而不是「挂在谁名下」。 */
.work-indent > i:last-child::after {
  content: ''; position: absolute; left: 0; top: 50%;
  width: 9px; height: 1px; background: rgba(74,144,217,.34);
}
/* ★ 整卡右移（每层 26px，宽度随之变窄 —— 卡列表是块级容器，margin 一加就窄）。
   深度值由 workCard 一处产出（node.depth，先序那一遍算出来的），
   CSS 只负责「第几层离多远」，不参与判层级。 */
.work-card[data-depth="1"] { margin-left: 26px; }
.work-card[data-depth="2"] { margin-left: 52px; }
.work-card[data-depth="3"] { margin-left: 78px; }
/* 第四层封顶：再深就把卡片挤成一条线了（多出来的深度靠卡内连线与「上级」胶囊） */
.work-card[data-depth="4"] { margin-left: 104px; }
/* 子项卡：左侧一条实线 + 极淡的底色，扫一眼就能分出哪些是下级。
   底色只加在**子项**上，父项保持白底 —— 树里「有背景的那几条」就是子项。 */
.work-card.is-sub {
  border-left: 3px solid rgba(74,144,217,.42);
  background: linear-gradient(90deg, rgba(74,144,217,.045), rgba(74,144,217,0) 120px);
}
/* 折叠时整张卡再压暗一点点，提示「这里还有东西没展开」 */
.work-card.is-collapsed { background: rgba(74,144,217,.03); }
/* 手机端缩进收窄：三层的活儿在 390 宽屏上每层 14px 会把标题挤没 */
@media (max-width: 600px) {
  .work-indent > i { width: 9px; }
  .work-indent > i:last-child::after { width: 6px; }
  /* 卡片的缩进也要跟着收窄 —— 桌面那 26px/层 在 390 宽上会吃掉两成宽度 */
  .work-card[data-depth="1"] { margin-left: 12px; }
  .work-card[data-depth="2"] { margin-left: 24px; }
  .work-card[data-depth="3"] { margin-left: 36px; }
  .work-card[data-depth="4"] { margin-left: 48px; }
}

/* ---------- 折叠三角 ---------- */
/* ★ 固定宽度（含叶子那个占位）：否则有子项的标题被三角形顶出去，
     同级之间标题左右错开，看着像排版坏了。 */
.work-toggle, .work-toggle-gap {
  flex: none; width: 30px; min-width: 30px;
  display: inline-flex; align-items: center; gap: 2px;
}
.work-toggle {
  appearance: none; cursor: pointer; padding: 2px 4px;
  border: 1px solid transparent; border-radius: 6px; background: none;
  font: inherit; font-size: 12px; line-height: 1.4; color: var(--ink-2);
  justify-content: center;
}
.work-toggle:hover { border-color: var(--line); background: var(--soft, #f5f7fa); color: var(--brand-dark); }
.work-toggle.is-collapsed { color: var(--brand-dark); border-color: rgba(74,144,217,.35); background: #eaf3fd; }
.work-toggle-caret { font-size: 11px; }
.work-toggle-n { font-variant-numeric: tabular-nums; }
/* 父项卡片：左侧一条竖线把它和它的子项在视觉上归成一簇 */
.work-card.is-parent > .work-card-head > .work-title-btn { font-weight: 600; }

/* 汇总徽章：父项才出现，与它旁边的普通 pill 视觉上要区分得开 */
.pill-sum { background: #eaf3fd; color: #2c6ba8; font-weight: 500; }
/* 「已折叠」提示：与汇总徽章同色系但更浅，读起来是同一件事的两半 */
.pill-fold { background: #f2f7fd; color: #5b7fa6; }
/* 派生工作量（父项 = 子项合计）：与叶子那枚区分开，免得被当成同一口径的数 */
.pill-wl.is-derived { background: #eef6ff; border: 1px dashed rgba(74,144,217,.5); }
/* 编辑弹层里父项工作量的只读展示（原先是滑块的位置）。
   ★ 必须**看得出是只读**：画成跟滑块一样的话，用户会去拖它，
     拖不动就会以为界面坏了 —— 直接换成一块说明牌。 */
.wl-derive {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  padding: 8px 10px; border: 1px dashed rgba(74,144,217,.5);
  border-radius: 8px; background: #f6faff;
}
.wl-derive-n { font-size: 20px; font-weight: 600; color: var(--brand-dark); font-variant-numeric: tabular-nums; }
.wl-derive-l { font-size: 12px; color: var(--ink-2); }

/* 编辑弹层里的子项勾选列表 */
.work-sub-label { font-size: 13px; font-weight: 500; margin: 14px 0 4px; }
.work-children {
  max-height: 220px; overflow: auto; border: 1px solid var(--line);
  border-radius: 8px; background: var(--card); padding: 4px;
}
/* ★★ 选择器必须多带一级（`.form label.work-child-row` / `.work-child-row input`）
   才压得过 `.form label{display:block}` 与 `.form input{display:block;width:100%}`
   —— 它们都是 (0,1,1)，只写单个类 (0,1,0) 会被**整条盖掉**：
   复选框被撑成满宽方块、标题被挤到下一行，每一项吃掉一大块高度，
   而且不报任何错。同一个坑在下面的 .sync-line 上已经踩过一次（见 1195 行注释），
   这里是它的第二次复发（M7 第六批，用户截图反馈「复选框和内容分别在两行」）。 */
.form label.work-child-row,
label.work-child-row {
  display: flex; align-items: center; gap: 7px;
  margin: 0; padding: 3px 8px; border-radius: 6px;
  cursor: pointer; font-size: 13px; line-height: 1.45;
}
.work-child-row:hover { background: var(--soft, #f5f7fa); }
/* ★ 勾上时整行变色：只让复选框变色的话，窄屏上那一行的选中状态很不容易看见 */
.work-child-row.is-on { background: #eaf3fd; }
/* `flex: 1 1 auto` 让长标题独占剩余宽度并在需要时换行；min-width:0 是必需的，
   否则 flex 子项的 min-content 会阻止收缩（grid/flex 的老坑）。 */
.work-child-row > span { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
/* 只清 `.form input` 带来的满宽/外边距/内边距 —— 不动 border/background，
   那两项会把浏览器原生勾选框画坏（各家都靠 appearance 自绘）。 */
.work-child-row input[type="checkbox"] {
  display: inline-block; width: auto; margin: 0; padding: 0;
  flex: 0 0 auto; accent-color: var(--brand);
}

/* 详情弹层里的层级区 */
.wd-hier { display: flex; flex-direction: column; gap: 8px; }
.wd-hier-line { font-size: 13px; color: var(--ink-2); }
.wd-kids { margin: 6px 0 0; padding-left: 18px; font-size: 13px; }
.wd-kids li { margin-bottom: 5px; line-height: 1.9; }
/* 可点开的层级链接 */
.linkish {
  appearance: none; border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--brand-dark); text-decoration: underline;
  text-underline-offset: 2px; overflow-wrap: anywhere; text-align: left;
}

.work-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.work-meta-l { font-size: 12px; color: var(--ink-3); }
.work-meta .btn { margin-left: auto; }
.work-desc { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-2); line-height: 1.7; }

/* ---------- 负责人胶囊 ---------- */
.wa-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12.5px; line-height: 1.6; padding: 1px 9px; border-radius: 999px;
  background: var(--brand-l); color: var(--brand-dark); white-space: nowrap;
}
.wa-chip i { font-style: normal; font-size: 11px; color: var(--ink-3); }
.wa-chip.wa-group { background: #eef2f7; color: #475569; }
.wa-chip.wa-noacc { box-shadow: inset 0 0 0 1px #f0d7a8; }
.wa-none { font-size: 12.5px; color: var(--ink-3); }
.wa-more { font-size: 12px; color: var(--ink-3); }

/* ---------- 详情：时间轴 ---------- */
.wd-title-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
.wd-title { margin: 0; font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }
/* .sec-h 沿用资料详情里那一条（标题 + 上边线），这里只补一个计数位 */
.sec-n { font-weight: 400; color: var(--ink-3); font-size: 12px; }
.wd-desc-text { margin: 0; font-size: 13px; color: var(--ink-2); line-height: 1.75; white-space: pre-wrap; }

.wtl { list-style: none; margin: 0; padding: 0 0 0 4px; }
.wtl-item { position: relative; display: flex; gap: 12px; padding: 0 0 16px 18px; }
.wtl-item::before {
  content: ''; position: absolute; left: 4px; top: 14px; bottom: 0;
  width: 1.5px; background: var(--line);
}
.wtl-item:last-child { padding-bottom: 2px; }
.wtl-item:last-child::before { display: none; }
.wtl-dot {
  position: absolute; left: 0; top: 5px;
  width: 9px; height: 9px; border-radius: 50%;
  background: #c3cedb; box-shadow: 0 0 0 3px #fff;
}
.wtl-item.wtl-now .wtl-dot { background: var(--ok); }
.wtl-body { min-width: 0; }
.wtl-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.wtl-name { font-size: 13.5px; font-weight: 500; color: var(--ink); }
.wtl-range { margin-top: 3px; font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* ---------- 详情：成果 ---------- */
.wout-list { display: grid; gap: 8px; }
.wout-item {
  display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap;
  border: 1px solid var(--line); border-radius: 9px; padding: 9px 12px; background: #fff;
}
.wout-main { min-width: 0; flex: 1; }
.wout-title { display: block; font-size: 13px; color: var(--ink); overflow-wrap: anywhere; }
.wout-meta { margin-top: 5px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.wout-acts { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
/* 成果的下载链接：不能直接用 .ev-link（它带 margin-top，在 flex 行里会掉下来） */
.wout-dl { font-size: 12.5px; color: var(--brand-dark); white-space: nowrap; }
.wout-dl:hover { text-decoration: underline; }
/* ★ 操作栏必须 sticky 在滚动容器的底部，不能随内容一起滚走。
   症状（2026-10-15 实测）：工作详情弹层里「上传工作成果 / 编辑 / 删除」
   整排落在折叠线以下 —— 实测按钮 top=826px 而可视区底只有 828px，
   矮一点的窗口（笔记本、开了开发者工具）就完全看不见，用户直接
   得出「没有这个功能」的结论。后端与 RLS 其实都放行上传，纯属界面问题。
   sticky 的前提是父级是滚动容器：.modal-body 有 overflow:auto，✓。
   background 不能省 —— 不用背景色的话，滚上来的正文会透在按钮下面。 */
.wd-acts {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line);
  position: sticky; bottom: -16px; z-index: 2;
  background: var(--card);
  /* bottom:-16px 抵消 .modal-body 的 16px 下内边距，否则按钮会离底 16px */
  margin-bottom: -16px; padding-bottom: 16px;
}

/* ---------- 录入 / 草稿 ---------- */
.work-wide .modal-box, .modal-box.work-wide { max-width: 860px; }
.work-label { display: block; font-size: 12.5px; color: var(--ink-2); margin: 12px 0 5px; }
.work-foot { display: flex; align-items: center; gap: 8px; margin-top: 14px; }

/* 标签右边挂一个语音按钮：同一排放下，窄屏也不换行、不挤压标签 */
.work-label-row { display: flex; align-items: center; gap: 10px; margin: 12px 0 5px; }
.work-label-row .work-label { flex: 1 1 auto; min-width: 0; margin: 0; }

/* 语音输入按钮。
   ★ svg 必须显式写 stroke（这里连 CSS 也再写一遍）：只给 stroke-width 不会继承出
     描边色 —— stroke 的初始值就是 none，图标会静默不显示，肉眼看只会以为「设计就是个色块」。 */
.mic-btn { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; flex: none; }
.mic-btn svg { width: 14px; height: 14px; fill: none; stroke: currentColor; flex: none; }
/* 探测出来当前环境用不了（http 访问 / 浏览器没这个接口）：按钮留着，但看得出「现在点不了」 */
.mic-btn.is-off { opacity: .55; }
.mic-btn.is-off:hover { border-color: var(--line); color: var(--ink-2); }
/* 正在聆听：变红 + 图标呼吸，一眼能看出「它真的在听」 */
.mic-btn.is-live,
.mic-btn.is-live:hover { border-color: var(--err); color: var(--err); background: #fff5f5; }
.mic-btn.is-live svg { animation: micPulse 1s ease-in-out infinite; }
@keyframes micPulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
/* minmax(0,1fr) 而不是 1fr：日期控件的 min-content 宽度会把 1fr 顶开，
   三列就不等宽了（同样是「看起来像坏了」的那一类小毛病） */
.work-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 4px; }
.work-grid .work-label { margin-top: 0; }

#work-parse-modal .hint { margin: 0 0 10px; }
#work-parse-modal .hint b { color: var(--ink); }
#work-input {
  width: 100%; min-height: 168px; resize: vertical;
  border: 1px solid var(--line); border-radius: 9px; padding: 11px 12px;
  font: inherit; font-size: 13.5px; line-height: 1.75; color: var(--ink);
}
#work-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-l); }

.wd-card {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 12px; background: #fbfcfe;
}
.wd-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.wd-idx { font-size: 12px; font-weight: 600; color: var(--brand-dark); background: var(--brand-l); border-radius: 6px; padding: 2px 8px; }
.wd-card input[type="text"], .wd-card textarea, .wd-card input[type="date"],
.wa-edit-row input, .wa-edit-row select {
  width: 100%; border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 9px; font: inherit; font-size: 13px; color: var(--ink); background: #fff;
}
.wd-card input:focus, .wd-card textarea:focus, .wa-edit-row input:focus, .wa-edit-row select:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-l);
}
.wd-card textarea { resize: vertical; line-height: 1.65; min-height: 52px; }

.wd-assignees { display: grid; gap: 8px; }
/* ★ 负责人行做成**固定的两行**，而不是「塞不下就换行」：
   姓名/身份/角色的长度差异很大（「马天有」vs「电子商务教研室」），
   交给自动换行会出现同一张卡里第一行撑一行、第三行溢一行，看起来像坏了。
   两行结构：第 1 行＝谁（姓名 · 个人/集体 · 角色），第 2 行＝什么时候负责 + 状态 + 操作。 */
.wd-a, .wa-edit-row {
  display: flex; flex-direction: column; gap: 7px;
  background: #fff; border: 1px solid var(--line); border-radius: 9px; padding: 9px 10px;
}
.wa-line { display: flex; align-items: center; gap: 7px; min-width: 0; }
.wa-line-b { padding-top: 1px; }
.wa-tilde { color: var(--ink-3); font-size: 12px; flex: none; }
.wd-a-name { flex: 2 1 120px; min-width: 0; }
.wd-a-kind { flex: 0 0 78px; }
.wd-a-role { flex: 1 1 100px; min-width: 0; }
.wd-a-start, .wd-a-end, .wa-start, .wa-end { flex: 0 0 140px; }
.wd-a .btn, .wd-a .pill, .wa-edit-row .btn, .wa-edit-row .pill { flex: none; }

/* ---------- 编辑弹层：负责人行 ---------- */
.work-fs { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 14px 14px; margin: 18px 0 0; min-width: 0; }
.work-fs legend { font-size: 12.5px; font-weight: 600; color: var(--ink-2); padding: 0 6px; }
.work-fs .hint { margin: 0 0 10px; }
.work-fs .hint b { color: var(--ink); }
#work-edit-assignees { display: grid; gap: 8px; margin-bottom: 10px; }
/* 行本身的样式与 .wd-a 共用（见上面的两行结构说明），这里只补列宽 */
.wa-name { flex: 2 1 120px; min-width: 0; }
.wa-kind { flex: 0 0 78px; }
.wa-role { flex: 1 1 100px; min-width: 0; }

/* ---------- D4 成员联系卡 ----------
   胶囊一行放不下就要换行：.row 默认 nowrap，六列胶囊挤在一行里
   会把整张卡顶得比视口还宽（手机上就是整页横滚）。
   word-break 用 anywhere 而不是 break-word —— 后者不参与 min-content
   计算，一个长邮箱就能把这一列顶到 400px 宽。 */
.contact-row { flex-wrap: wrap; }
.contact-chips { flex: 1 1 100%; flex-wrap: wrap; row-gap: 6px; }
.contact-chips .pill {
  max-width: 100%; white-space: normal; overflow-wrap: anywhere;
}

/* ---------- 工作量滑块（M7 第三批） ----------
   十个格子而不是 <input type=range>：滑轨没有刻度，
   「拖到哪是几分」看不出来，只能一点点试。格子能一眼数出分值。 */
.wl { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 8px 0 2px; }
.wl-label { font-size: 12.5px; color: var(--ink-2); flex: none; }
.wl-cells { display: flex; gap: 3px; flex: none; }
.wl-cell {
  width: 16px; height: 22px; padding: 0; border: 1px solid var(--line);
  border-radius: 5px; background: #fff; cursor: pointer;
  transition: background .12s ease, border-color .12s ease;
}
.wl-cell:hover { border-color: var(--brand); }
.wl-cell.on { background: var(--brand); border-color: var(--brand); }
/* 键盘焦点必须看得见：整个组件只靠方向键操作，没有焦点框就等于键盘不可用 */
.wl-cell:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.wl-val { font-size: 12.5px; color: var(--ink-3); flex: none; }
.wl-val b { color: var(--ink); font-size: 14px; }
/* 负责人行里的紧凑版：格子小一号，不抢整行的注意力 */
.wl-sm { margin: 6px 0 0; }
.wl-sm .wl-cell { width: 13px; height: 18px; border-radius: 4px; }
.wl-sm .wl-val b { font-size: 13px; }

/* 工作量胶囊：与状态胶囊同一视觉族，但换个色以便一眼区分「分值」与「状态」 */
.pill-wl { background: #f3ecff; color: #6b3fa0; }
.pill-wl.pill-ok, .pill-wl.pill-run, .pill-wl.pill-wait { background: #f3ecff; color: #6b3fa0; }
/* 负责人胶囊上的分担数字：让「谁扛了多少」不用点进编辑弹层就能看到 */
.wa-chip .wa-share {
  margin-left: 4px; font-size: 11px; font-weight: 700; color: #6b3fa0;
  background: #f3ecff; border-radius: 6px; padding: 0 5px;
}

.work-wl-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.work-wl-row .work-label { margin-top: 0; }

/* ---------- 手机端 ---------- */
@media (max-width: 600px) {
  .work-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* 滑块在窄屏上允许整体换行，否则 10 个格子 + 读数会被挤变形 */
  .wl { gap: 8px; }
  .wl-cell { flex: 1 1 auto; min-width: 14px; }
  .wl-sm .wl-cell { flex: 0 0 13px; }
  .work-today { display: none; }            /* 手机上省一行，日期在筛选提示里看不看都行 */
  .work-meta .btn { margin-left: 0; }
  /* 两行结构在窄屏上仍然成立，只是第 1 行也允许换行（姓名可能很长） */
  .wa-line { flex-wrap: wrap; }
  .wd-a-start, .wd-a-end, .wa-start, .wa-end { flex: 1 1 calc(50% - 22px); min-width: 0; }
  .wd-a-name, .wa-name { flex: 1 1 100%; }
  .wd-a-role, .wa-role { flex: 1 1 100%; }
  .wd-a-kind, .wa-kind { flex: 1 1 100%; }
  .wout-acts { width: 100%; }
  .wout-acts .btn { flex: 1; }
}

/* ============================================================
   功能说明（M7 第七批）
   一页三段：功能清单 / 版本记录 / 使用前提。内容全部来自 /api/features，
   这里只管排版。

   ★ 两处写法是**必须的**，不是风格偏好：
     1. grid 的轨道写 `minmax(0, 1fr)` —— 只写 `1fr` 时轨道按 max-content 定宽，
        一条长 URL 或长句子就能把整列顶宽，窄屏整页横滚（子项写 min-width:0 没用，
        要写在**轨道**上）。
     2. 长文本用 `overflow-wrap: anywhere` 而不是 `break-word`：
        break-word **不参与 min-content 计算**，于是它在 grid/flex 里压不住宽度，
        表现与没写完全一样。
   ============================================================ */
.feat-lead-p { margin: 0; font-size: 13px; color: var(--ink-2); line-height: 1.6; overflow-wrap: anywhere; }
.feat-lead-p b { color: var(--ink); }
.feat-jump { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }

/* 段标题：比卡片自己的 h3 低一档，用来把「清单 / 版本记录 / 使用前提」三段分开 */
.feat-sec-head {
  margin: 18px 0 8px; font-size: 12.5px; font-weight: 600;
  color: var(--ink-3); letter-spacing: .06em;
}
.feat-card + .feat-card { margin-top: 10px; }

/* ---------- 功能清单 ---------- */
.feat-list { list-style: none; margin: 0; padding: 0; }
.feat-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 10px;
  padding: 9px 2px;
  border-top: 1px solid var(--line);
}
.feat-item:first-child { border-top: 0; }
.feat-name {
  font-size: 13.5px; font-weight: 600; color: var(--ink);
  overflow-wrap: anywhere;
}
.feat-name .pill { margin-left: 6px; }
/* 「从哪个版本开始有」—— 靠右对齐的小胶囊，连续几行扫下来是一列，好找 */
.feat-since {
  align-self: start; white-space: nowrap;
  font-size: 11.5px; color: var(--ink-3);
  background: var(--bg); border-radius: 999px; padding: 1px 8px;
}
.feat-desc {
  grid-column: 1 / -1;
  font-size: 12.5px; color: var(--ink-3); line-height: 1.65;
  overflow-wrap: anywhere;
}

/* ---------- 版本记录 ---------- */
.cl-list { display: grid; gap: 12px; }
.cl-row { border-top: 1px solid var(--line); padding-top: 12px; }
.cl-row:first-child { border-top: 0; padding-top: 0; }
.cl-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cl-head b { font-size: 14px; }
.cl-head .pill { font-weight: 500; }
/* 版本小标题（如「功能说明模块」）：与版本号同一行，弱一档 */
.cl-title { font-size: 13px; color: var(--ink-2); overflow-wrap: anywhere; }
.cl-items { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 5px; }
.cl-item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 8px; align-items: start;
}
.cl-tag {
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
  border-radius: 6px; padding: 1px 7px;
  background: var(--bg); color: var(--ink-2);
}
/* 三种类型三个色：新增 / 变更 / 修复，扫一眼就知道这一版动了什么 */
.cl-new .cl-tag { background: #e7f6ec; color: var(--ok); }
.cl-change .cl-tag { background: var(--brand-l); color: var(--brand-dark); }
.cl-fix .cl-tag { background: #fdf3e3; color: var(--warn); }
.cl-text { font-size: 12.5px; color: var(--ink-2); line-height: 1.65; overflow-wrap: anywhere; }

/* ---------- 使用前提 ---------- */
.feat-note-list { list-style: none; margin: 0; padding: 0; }
.feat-note-list li { padding: 9px 0; border-top: 1px solid var(--line); }
.feat-note-list li:first-child { border-top: 0; }
.feat-note-list b { display: block; font-size: 13px; margin-bottom: 2px; }
.feat-note-list span { font-size: 12.5px; color: var(--ink-3); line-height: 1.65; overflow-wrap: anywhere; }

/* 窄屏：版本号与日期允许换行，其余布局不动（这一页本来就窄） */
@media (max-width: 600px) {
  .feat-sec-head { margin-top: 14px; }
  .feat-item { padding: 8px 0; }
  .cl-head { gap: 6px; }
}

/* ---------- 需求④：工作配对建议卡 ---------- */
.ws-card { margin: 12px 0; }
.ws-item {
  display: flex; gap: 12px; align-items: flex-start; justify-content: space-between;
  padding: 10px 0; border-top: 1px solid var(--line, #e6e8ec);
  flex-wrap: wrap;
}
.ws-item:first-of-type { border-top: none; }
.ws-main { min-width: 0; flex: 1 1 260px; }
.ws-act { font-weight: 600; margin-bottom: 2px; }
.ws-mat { color: var(--ink-2, #55606e); font-size: 13px; overflow-wrap: anywhere; }
.ws-reason { color: var(--ink-2, #55606e); font-size: 12px; margin-top: 2px; overflow-wrap: anywhere; }
.ws-meta { color: var(--ink-3, #8a94a3); font-size: 12px; margin-top: 2px; }
.ws-acts { display: flex; gap: 8px; flex: 0 0 auto; align-items: center; }
