
:root{
  --bg:#E9EEF5; --card:#F8FBFF; --ink:#1f2733; --sub:#5b6b80;
  --line:#D2E3F7; --blue:#2f6fed; --green:#23a06b; --orange:#f0972b;
  --red:#e0594f; --purple:#7b5cd6; --cyan:#1aa6b7;
  /* 护眼配色（2026-09-18 调整）：页面底 --bg 走中性冷灰（彩度 37%，不与图表色彩抢注意力），
     卡片仍保留淡蓝体系（底 --card / 头带 --card-hd / 斑马纹 --row-alt / 悬停 --row-hover），
     靠「色相 + 明度」双重差把卡片从背景里托起来。四色锚点与「卡片非纯白」约定由 smoke-eyecare-theme 守护。 */
  --card-hd:#EAF3FE; --row-alt:#F2F8FF; --row-hover:#DBEAFB;
  /* 明细行编辑高亮：修改中整行变淡蓝（比悬停更深一档），保存完成后自动恢复 */
  --row-edit:#C6DFFA; --row-edit-bar:#2f6fed;
}

/* ═══════════════════════════════════════════════════════════════════════════════
   设计令牌层 · v1.1（D301 第一批）
   真源 .design-tokens.json ｜ 人读版 .design-tokens.md ｜ 校验 _audit-tokens.js --def
   ───────────────────────────────────────────────────────────────────────────────
   本层**只补名字，不改任何现存令牌的值**。这是第一批的纪律：先把「表」和「码」对齐成
   一张完整的地图，再让第二批拿着地图去改 853 处硬编码。表和值同时动，出问题分不清
   是表错了还是替换错了。

   四层结构（色板按主题分四份，三张标尺只有一份）：
     L 浅色      ← 本块 + 上方 :root                 （打印 / PDF / 报表底，非屏显）
     D 屏显深色  ← @media screen{ body:not(.view-report) }（G1，用户真正看到的那套）
     H 高对比    ← body:not(.view-report).hc          （只提亮前景，不动底色）
     R 周报一页纸 ← body.view-report                   （D 的等值拷贝，视线不在页面上）
     S 标尺      ← 只在本块定义（与主题无关，不许在主题层重复定义）

   ⚠ 本批新增的令牌**一律零引用**（check-tokens-301 断言这一点）。零引用 ⇒ 零视觉变化，
     这是「风险≈0」能被证明的前提，靠肉眼看截图证明不了。
   ⚠ 每新增一个色角色，必须问「哪个主题的哪个层」，四层缺一层就是打印或周报页会掉色。
   ═══════════════════════════════════════════════════════════════════════════════ */
:root{
  /* ── 色板 · 浅色层补齐（14 个）。此前的缺口全是「深色层定义了、浅色层没有」，
        打印时 var() 求值为空 ⇒ 那条属性静默失效。实测：这些令牌的 51+58+79 处引用
        **全部落在 @media screen 内**，所以在打印态补值对现有输出零影响（实测确认）。 ── */
  --stroke:#B9D2FF;                 /* 强边框 / 输入框边（此前只在深色层定义过） */
  --ink-head:#1F3866;               /* 标题深蓝：浅色专属，深色层等同 --ink（37 处手写值待批二收敛） */
  --disabled:#A6B4C6;               /* 禁用：WCAG 对禁用态豁免，是全站唯一允许低于 AA 的文字角色 */
  --pure:#FFFFFF;                   /* 纯白：浅色=弹窗面；深色=最高对比文字 */
  --blue-solid:#2F6FED;             /* 实心蓝面（只用于承载白字的实心块）。--blue 是图形色，
                                       深色层取 #4C8DFF（对白字 3.20）——同一个蓝不能兼任两职：
                                       要白字 4.5 需 L≤.183，要亮底醒目需 L≥.26，物理上不可兼得。 */
  --muted:#5B6B80;                  /* 说明文字（三级）：58 处引用此前只在深色层有值 */
  --blue-fg:#1D4ED8;                /* 以下 5 个 -fg 是「文字专用色」：非 -fg 的语义色只准填充/描边 */
  --green-fg:#1A7F4B;               /* 浅色 4.84:1（对 #F8FBFF）—— 刚好过 AA，批二不许再调暗 */
  --orange-fg:#A04606;
  --red-fg:#C0392B;
  --purple-fg:#6D28D9;

  /* ── tint 三元组：私有色值全部由语义色派生，不再新增 hex。
        pattern = rgba(var(--x-rgb), α)，α ∈ .08/.12/.16/.18/.26（面）或 .28/.30/.32/.34（边）。
        ⚠ 只写 "R,G,B"，不带 rgba() 外壳 —— 外层要能自己拼透明度。 ── */
  --blue-rgb:47,111,237; --green-rgb:35,160,107; --orange-rgb:240,151,43;
  --red-rgb:224,89,79;   --purple-rgb:123,92,214; --cyan-rgb:26,166,183;

  /* ── 间距标尺 · 11 档（2px 栅格，8 以上走 4px 节奏）+ 1 例外 ──
        实测：拿全站 1771 个间距边值拟合，本标尺直接命中 63.1%，未命中的 73.3% 只需移动 ≤1px
        ⇒ 合计 90.1% 视觉不变。超过 32px 的视为布局常量，不进标尺。 ── */
  --s-0:0; --s-0-5:1px; --s-1:2px; --s-1-5:3px; --s-2:4px; --s-3:6px;
  --s-4:8px; --s-5:10px; --s-6:12px; --s-8:16px; --s-10:20px; --s-12:24px; --s-16:32px;
  /* D304 补 --s-0-5(1px) / --s-1-5(3px)：1px 用了 63 次、3px 用了 73 次，
     全部落在徽章与 chip 的内边距上 —— 压到 0 会让文字贴边，这是真实需求不是漂移。
     命名规则：--s-N 中 N = 像素 ÷ 2，半档加 -5 后缀，所以不需要重命名任何一个老名字。 */
  --sp-card:14px;                   /* 唯一例外：只给 .card/.kpi/.modalbox/.addpanel-box 的内距 */

  /* ── 圆角 · 4 档 + 1 个形状。相邻档差 1px 的「假层级」（6/7/8/9/10）在批二收敛 ── */
  --r-1:4px;   /* 行内标记、小色块、进度条端点 */
  --r-1-5:6px; /* D304 新增：小号控件（窄按钮 / 小输入框 / tab 项）—— 实测用了 47 次且全在控件上 */
  --r-2:8px;   /* 按钮、输入框、下拉、芯片 */
  --r-2-5:10px;/* D304 新增：中等容器（分组卡 / 内嵌面板）—— 实测 32 次 */
  --r-3:12px;  /* 卡片、KPI、表格容器 */
  --r-4:16px;  /* 弹窗、抽屉、大面板 */
  --r-pill:999px;                   /* 形状而非尺寸：只给「高度 ≤32px 且看起来该可点」的元素 */

  /* ── 动效 · 3 档时长 + 1 种缓动（--ease 已在上方定义，此处只补扁平缓动）──
        ⛔ 页面级过渡只准用 opacity：.wrap 里有 position:fixed 的系统状态胶囊，
           祖先一 transform 它就跑位（D296 实测）。这条比时长重要得多。 ── */
  --t-1:120ms;  /* 按压、颜色切换（不涉及位移） */
  --t-1-5:150ms;/* D304 新增：实测最常用的 hover / 边框过渡（26 处，占动效违规 72%） */
  --t-2:180ms;  /* 展开/收起、淡入、行状态变化 */
  --t-3:260ms;  /* 弹窗、抽屉、视图级过渡 */
  --ease-flat:ease;                 /* 颜色、透明度、阴影 */
}

*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,"PingFang SC","Microsoft YaHei",Segoe UI,Roboto,sans-serif;
  background:var(--bg);color:var(--ink);font-size:14px;line-height:1.5;padding-left:172px}
header{background:linear-gradient(120deg,#2f6fed,#137F8D);color:var(--pure);padding:18px 28px;box-shadow:0 2px 12px rgba(47,111,237,.25);display:flex;align-items:center;gap:16px;flex-wrap:wrap}
header h1{margin:0;font-size:22px;font-weight:700;letter-spacing:.5px}
header .meta{margin-top:6px;font-size:12px;opacity:.92;width:100%}
.viewall-btn{background:var(--pure);color:var(--blue);border:none;border-radius:10px;padding:8px 18px;font-size:13px;font-weight:700;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.15);white-space:nowrap;transition:all .2s}
.viewall-btn:hover{transform:translateY(-2px);box-shadow:0 4px 14px rgba(0,0,0,.2)}
.brand-pill{display:flex;align-items:center;overflow:hidden;border-radius:10px;background:var(--pure);box-shadow:0 2px 10px rgba(0,0,0,.18)}
.brand-logo{background:var(--pure);padding:var(--s-2) 10px;display:flex;align-items:center;height:44px;box-sizing:border-box}
.brand-logo img{height:34px;width:auto;display:block}
.brand-text{background:#3b82f6;color:var(--pure);padding:0 18px;font-size:22px;font-weight:700;letter-spacing:2px;white-space:nowrap;display:flex;align-items:center;height:44px;box-sizing:border-box}
.statusbar{margin-top:10px;display:flex;flex-wrap:wrap;gap:12px;align-items:center}
/* 左侧深色分组导航（部门管理 / 业务模块 / 数据管理 / 系统权限 / 个人中心） */
.sidebar{position:fixed;left:0;top:0;bottom:0;width:123px;background:#222b3d;display:flex;flex-direction:column;z-index:60;overflow:hidden;box-sizing:border-box;padding:0;box-shadow:2px 0 14px rgba(10,18,35,.35)}
.sb-app{display:flex;align-items:center;gap:8px;color:var(--pure);font-size:15px;font-weight:800;letter-spacing:.5px;padding:16px 14px 12px;border-bottom:1px solid rgba(255,255,255,.08);flex:none}
.sb-app-ico{font-size:16px}
.sb-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 8px 10px;display:flex;flex-direction:column;gap:2px;min-height:0}
.sb-scroll::-webkit-scrollbar{width:6px}
.sb-scroll::-webkit-scrollbar-thumb{background:rgba(255,255,255,.16);border-radius:var(--r-1)}
.sb-sec{font-size:11px;font-weight:700;color:#7f8ba1;letter-spacing:2px;padding:12px 8px 4px;flex:none}
.sb-item{display:flex;align-items:center;gap:8px;width:100%;background:none;border:none;color:#c6cfdd;padding:var(--s-4) 10px;font-size:13px;font-weight:600;border-radius:8px;cursor:pointer;text-align:left;box-sizing:border-box;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:inherit;flex:none}
.sb-item:hover{background:rgba(255,255,255,.07);color:var(--pure)}
.sb-item.active{background:#2f6fed;color:var(--pure)}
/* 登录 / 角色门控：未登录仅显示总览与登录按钮；管理员项仅管理员可见 */
.sb-item.auth-item{display:none}
body.authed .sb-item.auth-item{display:flex}
.sb-sec.auth-item{display:none}
body.authed .sb-sec.auth-item{display:block}
.sb-item.admin-item{display:none!important}
body.authed.is-admin .sb-item.admin-item{display:flex!important}
.sb-sec.admin-sec{display:none!important}
body.authed.is-admin .sb-sec.admin-sec{display:block!important}
.sb-item.injury-item{display:none!important}
body.authed.can-injury .sb-item.injury-item{display:flex!important}
.sb-sec.injury-item{display:none!important}
body.authed.can-injury .sb-sec.injury-item{display:block!important}
.sb-logout{margin-top:auto;flex:none;color:#8fe3b0;border-top:1px solid rgba(255,255,255,.08);border-radius:0;padding:14px 12px}
/* 侧边栏子入口（「售后调试部门」下方的部门按钮）：登录后且处于部门视图才显示 */
.sb-subnav{display:none;flex-direction:column;gap:2px;margin:2px 0 4px;padding-left:10px;margin-left:12px;border-left:1px solid rgba(255,255,255,.14);flex:none}
/* 二级导航按「父项 ID」分别放行：部门子按钮只在部门视图展开，仓库子按钮只在仓库视图展开，互不串台（2026-09-14）
   ⚠ 2026-10-11 第一百七十六轮：下面补齐第 **五** 条（prob）—— 此前只有四条，prob 那条被漏写了。 */
body.authed.view-dept.subnav-open #deptSubNav{display:flex}
body.authed.view-ware.subnav-open #wareSubNav{display:flex}
body.authed.view-emp.subnav-open #empSubNav{display:flex}
/* 员工信息二级栏（2026-10-10 第一百六十一轮）：在职 / 离职 */
body.authed.view-staff.subnav-open #staffSubNav{display:flex}
/* 现场问题收集二级栏（2026-10-11 第一百七十六轮补）：本模块按**部门**分栏，与其他四项同款胶囊子按钮。
   ⚠⚠ 此前**漏写了这一条规则** —— `.sb-subnav` 只有上面 259 行的 display:none，四条门控里没有 prob：
   JS 侧 switchView('prob') 明明把 subnavOpen 置成了 true、applyProbNavVisibility 也把内联 display
   清成了 ''，它仍然被 display:none 摁住 ⇒ 侧栏「现场问题收集」下方的部门子按钮**从不显示**，
   该模块的**换部门入口等于消失**（renderProbSubNav() 一直老老实实在渲染「部门名 + 待办 N / N 条」
   的按钮，只是没人看得见）。用户 2026-10-11 拍板「修」，本行即为其修复。
   ⚠ 与 staff 那条同源教训：**容器的「开」只能靠 CSS 门控**；写内联 display 的门控
   （applyProbNavVisibility 的 show ? '' : 'none'）只能「藏」不能「开」—— '' 是「交回 CSS」，
   而 CSS 若无门控就只剩 display:none。⛔ 别改成内联 display:flex 绕过：那样收起时就再也收不回来。 */
body.authed.view-prob.subnav-open #probSubNav{display:flex}
.sb-subbtn{display:flex;align-items:center;width:100%;background:none;border:none;color:#aab6c8;padding:6px 10px;font-size:12px;font-weight:600;border-radius:var(--r-2);cursor:pointer;text-align:left;box-sizing:border-box;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:inherit}
.sb-subbtn .sb-n{margin-left:auto;padding-left:6px;font-size:11px;font-weight:700;opacity:.75;flex:none}
.sb-subbtn.active .sb-n{opacity:.95}
.sb-subbtn:hover{background:rgba(255,255,255,.07);color:var(--pure)}
.sb-subbtn.active{background:#2f6fed;color:var(--pure)}
.sb-subbtn::before{content:'•';margin-right:6px;color:#5b6b82}
.wh-perm-addrow{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}
.sb-subbtn.active::before{color:var(--pure)}
/* ===== 部门条目（左侧「售后调试部门」下方）：两行「部门名 / 总负责人」 =====
   2026-09-16 把顶部「各部门总负责人」标签行的内容与功能并到这里。
   ⚠ 侧边栏只有 123px 宽（--sbw），而「海外调试部」5 个字已占满一行 ——
   负责人不可能同行显示，只能另起一行用 10px 小字。
   仓库子按钮（#wareSubNav）仍走「单行 + 右侧 N 项」，所以以下规则全部限定在 #deptSubNav 内。 */
#deptSubNav .sb-subbtn{padding:var(--s-2) 10px}
#deptSubNav .sb-sub-txt{display:flex;flex-direction:column;min-width:0;flex:1 1 auto;line-height:1.25}
#deptSubNav .sb-sub-n{margin-left:0;padding-left:0;font-size:12px;font-weight:600;opacity:1;overflow:hidden;text-overflow:ellipsis}
#deptSubNav .sb-sub-o{margin-top:1px;font-size:10px;font-weight:600;opacity:.62;overflow:hidden;text-overflow:ellipsis}
#deptSubNav .sb-subbtn.active .sb-sub-o{opacity:.92}
/* 权限范围外的部门：置灰不可点。判据与旧顶部标签的 .mgrchip-locked 完全一致，
   只是显示方式从「横排置灰」变成「竖排置灰」（旧的 .mgrchip-locked 规则已随标签行一起删除）。 */
#deptSubNav .sb-subbtn.locked{opacity:.4;cursor:not-allowed}
#deptSubNav .sb-subbtn.locked:hover{background:transparent;color:#aab6c8}
/* 综合部与调试部门之间的分隔线 */
#deptSubNav .sb-sub-sep{height:1px;margin:6px 10px 6px 0;background:rgba(255,255,255,.13);flex:none}
/* 部门分区子项（2026-09-16）：选中部门后才出现，缩进 + 虚线竖线表示从属于上面的部门条目 */
#deptSubNav .sb-subsec{display:flex;flex-direction:column;gap:1px;margin:1px 0 4px;padding-left:8px;margin-left:22px;border-left:1px dashed rgba(255,255,255,.2);flex:none}
#deptSubNav .sb-subsec-btn{display:flex;align-items:center;gap:6px;width:100%;background:none;border:none;color:#9fb0c8;padding:4px 8px;font-size:12px;font-weight:600;border-radius:6px;cursor:pointer;text-align:left;box-sizing:border-box;font-family:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#deptSubNav .sb-subsec-btn:hover{background:rgba(255,255,255,.07);color:var(--pure)}
#deptSubNav .sb-subsec-btn.active{background:rgba(47,111,237,.32);color:var(--pure)}
#deptSubNav .sb-subsec-btn .sb-dot{width:4px;height:4px;border-radius:50%;background:currentColor;opacity:.55;flex:none}
#deptSubNav .sb-subsec-btn.active .sb-dot{opacity:1}
.sb-logout:hover{background:rgba(255,255,255,.06);color:var(--pure)}
body.authed .sb-logout{color:#ff8177}
body.authed .sb-logout:hover{background:rgba(255,90,80,.14);color:#ffa49d}
.btn{background:var(--pure);color:var(--blue);border:none;border-radius:8px;padding:8px 16px;font-size:13px;font-weight:700;cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.12)}
.btn:hover{background:#eef3ff}
.impstat{font-size:12px;opacity:.95}
.import-status.error{color:var(--red-fg);font-weight:600}
.import-status.success{color:#1e7f4f;font-weight:600}
#connStat{padding:2px 10px;border-radius:20px;background:#eafaf1;color:#16713F;font-weight:600}
#connStat.off{background:#fdecea;color:#AE3227}
.wrap{max-width:1340px;margin:0 auto;padding:20px 22px 60px}
/* 8 列 = 主卡(.pri) span 2 + 6 张次卡各 1（第二批 2026-09-18：主次分级，行数与改造前一致） */
/* D294：卡片 7 → 5（规模指标并入 #ctxBar）。主卡跨 2 列 ⇒ 需要 6 格，故 8 → 6 列；
   ⛔ 别只删卡不改列数：留 2 个空列会让整排右半边空出来，看着像渲染失败。 */
.kpis{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;margin-bottom:16px}
.kpi{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:10px 12px 12px;box-shadow:0 1px 3px rgba(20,30,60,.04);min-width:0}
/* 未登录时仅保留 KPI 概览，其余主内容隐藏 */
body.guest-mode .wrap > #generalPanel,
body.guest-mode .wrap > .grid2,
body.guest-mode .wrap > .card.full,
body.guest-mode .wrap > .tablewrap,
body.guest-mode .wrap > .note{display:none}
body.guest-mode .wrap{padding-top:18px}
.kpi .v{font-size:21px;font-weight:800;letter-spacing:.2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:3px}
.kpi .l{font-size:12px;color:var(--sub);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.kpi .s{font-size:11px;color:var(--sub);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* 副标题两行化（2026-09-19 第四轮，用户：「这里的小字体显示不全」）：
   8 列 grid 下每张次卡内容宽仅 121px，而「正式 774 + 负责人 32 + 内援 77，占比 69%」需 220px
   → 单行 ellipsis 会把句子从中间切断（「正式 774 + 负责人 32…」），读者拿不到后半句。
   ⇒ 窄列里改成「最多两行」，宁可占高也不再断句。
   ⚠ **不能加 `min-width` 断点**：375px 手机上卡片内容宽只有 152px，比桌面 121px 更挤
     （实测「正式工」「内援人力」「覆盖部门」「线体 / 设备」四张卡仍被切 48/18/12/10px）——
     所以这条规则必须在所有宽度生效。
   ⚠ 唯一的例外是**首卡**（` .pri`，跨 2 列）：它内容宽 277px 本来就放得下，
     且作为主指标不该占两行 → 用 `:not(.pri)` 排除。
   ⚠ 两行会让卡内容高 11px，但**卡总高不变**（114px）——#kpis 是 grid 且行高由
     首卡的大字号 .v（48px）决定，次卡内容变高不会撑高行。实测两行文字底边 223.5px
     仍在卡底 242.5px 之内，无溢出。因此 #kpis 骨架无需改动（H3 仍 71/71 通过）。 */
.kpi:not(.pri) .s{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
/* 骨架专用：首屏占位的 .s 只有一个 `&nbsp;`，撑不满两行 → 直接按「两行」给它固定高。
   这样骨架高度与真实渲染一致（真实内容可能 1 行也可能 2 行，但**卡高由最高的那张决定**，
   而两行正是上限），H3 的「骨架 ≈ 真实」在任何宽度都成立。
   ⚠ 用 em 而非 px：字号在 640px 断点会从 11px 降到 10px，行高跟着变，px 会在窄屏失准。 */
.kpi:not(.pri) .s.skel{display:block;min-height:3em}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px 8px;box-shadow:0 1px 3px rgba(20,30,60,.04)}
.card h3{margin:0 0 6px;font-size:15px;font-weight:700}
.card .hint{font-size:12px;color:var(--sub);margin:0 0 8px}
.chart{width:100%;height:330px}
/* ══ D296：同一张卡两套画法 —— **桌面环形图 / ≤640px 横向堆叠条** ══════════════════
   ⚠ 为什么分端（这是对 D295 的返工）：D295 我把「为手机首屏省纵向空间」的改动**无条件套到了两端**，
     桌面上那张本来读得好好的环形图被换成了条；可桌面这一行的高度由右侧「各部门人力构成」柱图
     决定（435px），左卡变矮只会在网格里留一块空白 —— 空间没省下，却白丢了一个更直观的形态。
   ⇒ 现在按断点分流：数据**同源**（renderPie 里的 segs），只是「怎么画」不同。
     两套尺寸也必须跟着断点切，否则桌面会拿到 52px 的画布（环形被压扁）。
   ⚠ 高度一律用 **id 选择器**：#cPie 的两档高度必须压过 .chart 的 330 / 260 / 240
     （后两处在媒体查询里、全是 class ⇒ id 恒胜，不需要 !important）。 */
#pieLegend{display:none}          /* 桌面：环上已有外置标签，明细行是冗余 */
/* 明细行四列真对齐：色块 / 名称 / 人数 / 占比，数字等宽右对齐 —— 对账时要能竖着扫。
   ⚠ 规则写在基础层、只在窄屏 display 出来：这样断点来回切不用重写一份样式。 */
#pieLegend .pl-row{display:grid;grid-template-columns:10px 1fr auto 62px;align-items:center;gap:var(--s-4);
  padding:6px 2px;font-size:12.5px;line-height:1.3;border-bottom:1px solid var(--line)}
#pieLegend .pl-row:last-child{border-bottom:0}
#pieLegend .pl-dot{width:10px;height:10px;border-radius:var(--r-1);flex:none}
#pieLegend .pl-nm{color:var(--ink-2);font-weight:600;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#pieLegend .pl-vl{color:var(--ink);font-variant-numeric:tabular-nums;text-align:right}
#pieLegend .pl-pc{color:var(--sub);font-variant-numeric:tabular-nums;text-align:right}
/* 合计行：三块相加 = 在册总人力。这条恒等式用户会自己加一遍，直接写出来省得他按计算器。 */
#pieLegend .pl-total{border-top:1px dashed var(--line);border-bottom:0}
#pieLegend .pl-total .pl-nm{color:var(--ink);font-weight:700}
#pieLegend .pl-total .pl-vl{font-weight:700}
/* 口径说明里的「怎么读」跟着画法变：桌面说「图上标签」，窄屏说「下方明细」。
   两段都写在 HTML 里、用 CSS 二选一 —— 比在 JS 里改 textContent 安全（不会覆盖掉整句）。 */
.pie-hint-d{display:inline}
.pie-hint-m{display:none}
@media(max-width:640px){
  .pie-hint-d{display:none}
  .pie-hint-m{display:inline}
  /* ⚠ 导出按钮（bindChartExport 注入的 .expbtn）默认浮在图表右上角：52px 高的条上，
     22px 的按钮正好压住占比 7.5% 的「外协」段 —— 那一段正是这张图最需要被看见的地方。
     ⇒ 让 #cPie 不做定位上下文（JS 会内联写 relative，必须 !important），
       按钮改相对整张卡定位，落到卡片右上角标题那一行的空处。
     （桌面画布 330px，按钮浮在右上角只占一角，维持原样不动。） */
  #cardPie{position:relative}
  #cPie{position:static!important;height:52px}
  #pieLegend{display:flex;flex-direction:column;margin:6px 0 2px;border-top:1px solid var(--line)}
  #pieLegend .pl-row{font-size:12px;padding:var(--s-2) 2px;grid-template-columns:9px 1fr auto 54px;gap:var(--s-3)}
}
.mgrtable{width:100%;border-collapse:collapse;font-size:12px}
.mgrtable th{background:var(--card-hd);color:#334155;font-weight:600;padding:8px 10px;border-bottom:2px solid #cfe0f4;text-align:left;white-space:nowrap;position:sticky;top:0;z-index:1}
.mgrtable td{padding:6px 10px;border-bottom:1px solid #eef1f5;vertical-align:middle}
.mgrtable tr:hover{background:var(--row-hover)}
.mgrtable tbody tr:nth-child(even){background:var(--row-alt)}
/* 「负责人构成」卡内的分块小标题（2026-09-16）：该卡里同时有「负责人明细宽表」与
   「负责人 × 阶段」两块，用它分区。配色一律走变量，避免护眼主题检查抓到硬编码灰蓝值。 */
.mgrsec-t{margin:14px 0 8px;padding-left:8px;border-left:3px solid var(--blue);font-size:13px;font-weight:700;color:var(--ink)}
.mgrsec-t:first-child{margin-top:0}
.toolbar{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:6px 0 12px}
.toolbar label{font-size:12px;color:var(--sub)}
/* 工具栏：左侧筛选、右侧操作，分组更清晰 */
.tb-filters{display:flex;flex-wrap:wrap;gap:10px;align-items:center;flex:1 1 auto;min-width:0}
.tb-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-left:auto;padding-left:14px;border-left:1px solid #e7f0fb}
/* ===== 视图切换：总览 / 部门管理 / 工伤管理 ===== */
.viewbar{display:none;align-items:center;gap:12px;flex-wrap:wrap;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:10px 14px;margin-bottom:14px;box-shadow:0 1px 3px rgba(20,30,60,.04)}
.viewbar .vb-title{font-size:14px;font-weight:800;color:var(--ink-head)}
.viewbar .vb-hint{font-size:12px;color:var(--sub)}
#deptKpis{display:none}
body.view-dept #deptKpis{display:grid}
body.view-dept #kpis,body.view-dept #cardPie,body.view-dept #cardTop15,
/* ⚠⚠ 必须带 !important：renderInjuryCard() 会给 #injuryCard 写**内联** display:block，
   而内联样式优先级高于任何选择器 —— 不加这条，部门视图下只要「当前选中的部门有工伤记录」，
   工伤台账就会漏进**每一个分区**（2026-09-21 用户报障：「内援调动确认里面为什么会显示工伤的数据」。
   探针实测：无分区 / 内援调动确认 / 明细编辑 三个分区全漏，而总览与分级视图因为有 !important 才没事）。
   ⇒ 这里漏 !important 是**布局谎言**：面板类分区（deptsec-transfer）看起来「只剩调动面板」，
     实际下面还挂着一张工伤台账，用户会以为是调动模块把工伤数据带出来的。
   ⚠ 若哪天要放开「部门视图显示工伤台账」，请改这里而不是删这条 —— 删掉就会重新漏回去。 */
body.view-dept #injuryByDeptCard,body.view-dept #injuryCard{display:none!important}
/* 华南售后总览不显示工伤卡片（工伤内容只在「工伤汇报管理」视图展示） */
body.view-overview #injuryByDeptCard,body.view-overview #injuryCard{display:none!important}
/* 「负责人 × 阶段分布」也不在总览显示（2026-09-16 用户要求）——
   #cardDept 这张卡是「总览」与「售后调试部门」共用的（renderDept 只看 #fDept.value，
   不看视图）：总览里手动筛一个部门，它同样会从柱状图切成负责人明细。总览那边只保留
   上半部分的宽表，阶段卡片整块（小标题 + 卡片，见 renderDept 里的 #mgrStageSec）隐藏。
   ⚠ 这是 CSS 兜底，JS 侧同源判定在 renderDept() 的 showStages=(curView!=='overview')；
     两处条件必须同步改，否则会出现「DOM 都不生成却还在找元素」或反之的错位。 */
body.view-overview #mgrStageSec{display:none!important}
/* 视图级按钮显隐：总览隐藏「编辑/保存本部门」；部门视图隐藏「保存本部门」（行级保存已足够） */
body.view-overview #tableEditBtn,body.view-overview #deptSaveBtn{display:none!important}
body.view-dept #deptSaveBtn{display:none!important}
body.view-dept #gridConstitute,body.view-dept #gridPhoneTop{grid-template-columns:1fr}
body.view-injury #injuryActionBar{display:flex}
body.view-injury #kpis,body.view-injury #cardDept,body.view-injury #cardPie,
body.view-injury #cardPhone,body.view-injury #cardTop15,
body.view-injury #tableWrapSec{display:none}
/* ⚠⚠ 上面那条 display:none **压不住展开态的电话簿**（2026-09-21 用户报障：工伤汇报页冒出整张
   「负责人电话号码查询」）。成因：下方 @media(min-width:981px) 里的
     body:not(.view-dept) #gridPhoneTop > #cardPhone:not(.folded){display:flex}
   权重更高（类+2 id + 属性选择器 vs 类 + 1 id），而 view-injury 同样满足 :not(.view-dept)，
   于是「卡片展开 + 视口≥981px」时它把 display 翻回 flex。
   ⇒ 折叠态看着是好的，只有展开过的账号才会漏出来，所以这个 bug 能潜伏很久。
   修法：连 grid 容器一起整块隐藏并加 !important（只藏卡片会在原位留一个空 grid 占位）。
   ⚠ 若哪天要放开「工伤页显示电话」，请改这里而不是删这一条 —— 删掉就会重新漏回去。 */
body.view-injury #gridPhoneTop{display:none!important}
/* 工伤汇报管理视图：OSS 台账完全不加载（不渲染、也不发 /api/oss-reminder 请求，见 renderOss 顶部） */
body.view-injury #ossSec{display:none!important}
/* 「手动新增项目」按钮：2026-09-16 从顶部「各部门总负责人」标签行迁到明细表工具栏右侧。
   必须带视图闸门 —— 它落在 .tb-actions 里，而明细表在「华南售后总览」与「售后调试部门」
   两个视图都会显示，不加这条总览视图也会冒出这个按钮。
   ⚠ 与 applyLoginUI() 的权限门控可叠加：那个函数写的是**内联** style.display（有权限时写 ''），
   内联值优先于这里的选择器，所以「视图隐藏」与「权限隐藏」互不干扰。 */
#deptAddBtn{display:none}
body.view-dept.deptsec-detail #deptAddBtn{display:inline-block}
/* 「⇄ 批量调往」（D226）：与 #deptAddBtn 同款双闸 —— 视图闸门在这里，
   权限闸门在 applyLoginUI()（负责人 / 总负责人 / 管理员 / 有编辑权的二级管理员）。 */
#batchMoveBtn{display:none}
body.view-dept.deptsec-detail #batchMoveBtn{display:inline-block}
/* ===== 常规部门视图只读（2026-09-16）=====
   「常规部门视图」（点部门名后的默认界面 = 未选任何分区）只做展示：明细表、负责人电话簿、
   OSS 台账全部只读，**视图内的编辑入口一个都不出现**。编辑能力按分区归属、各回各家：
     · 「明细编辑」分区（deptsec-detail）= 明细表编辑 / 手动新增项目 / OSS 台账增删改
     · 「负责人构成」分区（deptsec-mgr）  = 负责人电话簿的增删改（见 renderPhone 的 phoneEditable）
   ⚠ 用 :not(.deptsec-detail) **反向排除**，而不是「先全部 display:none、再单独 display:inline-block 恢复」——
   恢复时必须写死一个 display 值，而已有 .btn 的原始 display 未必是 inline-block，
   写错就把按钮排布改了（这一片按钮挤在 .tb-actions 里，改动会立刻看出来）；
   不写恢复规则、让 .btn 自己生效，才和改动前的渲染完全一致。
   ⚠ 只拦「视图内」的入口。工具栏「功能」菜单里的「导入导出 & 备份恢复」是全局运维面板
   （其中也含「手动添加记录」），不随视图变化 —— 那是用户主动打开的独立面板，不属于某个视图的展示内容。 */
body.view-dept:not(.deptsec-detail) #tableEditBtn,
body.view-dept:not(.deptsec-detail) #deptAddBtn,
body.view-dept:not(.deptsec-detail) #batchMoveBtn,
body.view-dept:not(.deptsec-detail) #deptSaveBtn{display:none!important}
/* ⚠ 2026-09-28（第七十五轮）：OSS 台账的编辑入口**不再**跟「明细编辑」分区走 ——
   它已从明细编辑里拆出来，成为紧随其后的独立分区（deptsec-oss）。
   两组的 :not(...) 必须**各跟各的分区**，否则会出现
   「在明细编辑里能改 OSS、在 OSS 分区里反而不能改」的双向错位。
   ⚠ 外派支援用专属类 xsup-op / xsup-op-h 作闸门，**不能**拿 td.wh-op 顶替 ——
     wh-op 是 wh-table 家族的共用类，别的表也可能带它，一拦就误伤。 */
body.view-dept:not(.deptsec-oss) #ossAddBtn,
body.view-dept:not(.deptsec-oss) th.oss-op-h,
body.view-dept:not(.deptsec-oss) td.oss-op,
body.view-dept:not(.deptsec-xsup) #xsupAddBtn,
body.view-dept:not(.deptsec-xsup) th.xsup-op-h,
body.view-dept:not(.deptsec-xsup) td.xsup-op,
/* 顶级「外派支援」= 只读汇总页：操作列整列不出现（与 body.view-oss 隐藏 OSS 操作列同一手法）。
   ⚠ 整列隐藏后剩下 10 列的百分比之和 < 100%，fixed 布局下浏览器按比例放大补齐 ——
     实测列宽正常；汇总页要的就是「一个增删改入口都没有」。 */
body.view-xsup th.xsup-op-h,
body.view-xsup td.xsup-op{display:none!important}
/* 「部门空白页」（pagemode + #deptPage + .dep-page-*）已于 2026-09-16 整体下线：
   点左侧部门名不再弹出那个专属页，只把部门筛选切过去，右侧保持常规部门视图
   （KPI 卡片 + 明细表 + OSS 台账全显示）。相关 HTML / CSS / JS 一并删除。 */
/* 部门分区（2026-09-16）：侧边栏部门条目下挂「负责人构成」「明细编辑」两个子项，点哪个只显示哪一组。
   分区块按「整块容器」隐藏而不是单张卡 —— #gridConstitute 里还有 #cardPie、
   #gridPhoneTop 里还有 #cardTop15，它们在部门视图本来就 display:none，
   只藏卡片会留下空 grid 占位。 */
body.view-dept.deptsec-mgr #tableWrapSec,
body.view-dept.deptsec-mgr #ossSec,
body.view-dept.deptsec-mgr #xsupSec{display:none!important}
body.view-dept.deptsec-detail #deptKpis,
body.view-dept.deptsec-detail #gridConstitute,
body.view-dept.deptsec-detail #gridPhoneTop,
body.view-dept.deptsec-detail #ossSec,
body.view-dept.deptsec-detail #xsupSec{display:none!important}
/* 「OSS 汇报」分区（deptsec-oss，2026-09-28 第七十五轮）：用户要求把 OSS 从「明细编辑」里
   拆出来、做成紧随其后的**独立子项**（原话：「将明细编辑里面的 OSS 汇报做到明细编辑后面加一个导航」）。
   点它 → 右侧只留 OSS 台账（可增删改）；KPI / 两图 / 电话簿 / 明细表 / 外派表全部收起。 */
body.view-dept.deptsec-oss #deptKpis,
body.view-dept.deptsec-oss #gridConstitute,
body.view-dept.deptsec-oss #gridPhoneTop,
body.view-dept.deptsec-oss #tableWrapSec,
body.view-dept.deptsec-oss #xsupSec{display:none!important}
/* 「内援调动确认」分区（deptsec-transfer，2026-09-21 第十六轮）：与上面两个分区同构，
   点侧栏部门下的第三个子项 → 右侧只留调动面板（KPI / 两图 / 电话簿 / 明细表 / OSS 全收起）。
   ⚠ 必须用**整块容器**隐藏（#gridConstitute / #gridPhoneTop），只藏卡片会在原位留空 grid 占位。
   ⚠ #deptKpis 的默认态是 `body.view-dept #deptKpis{display:grid}`，权重不低，必须带 !important。 */
body.view-dept.deptsec-transfer #deptKpis,
body.view-dept.deptsec-transfer #gridConstitute,
body.view-dept.deptsec-transfer #gridPhoneTop,
body.view-dept.deptsec-transfer #tableWrapSec,
body.view-dept.deptsec-transfer #ossSec,
body.view-dept.deptsec-transfer #xsupSec{display:none!important}
#transferSec{display:none}
body.view-dept.deptsec-transfer #transferSec{display:block}
/* 「外派支援」分区（deptsec-xsup，2026-09-28 第七十五轮）：用户要求把**编辑**放到
   「内援调动确认」后面，而顶级「外派支援」只做只读汇总（原话：「左侧任务栏的外派支援
   只做汇总显示，编辑导航做到内援调动确认后面」）。
   ⇒ 点它 → 右侧只留**本部门**（跟随左侧选中的部门）的外派台账，可增删改。
   ⚠ #xsupSec 默认 display:none，这里的 display:block 是它唯一的「部门视图出口」；
     另一个出口是 body.view-xsup（顶级只读汇总页），见文件上方那段。 */
body.view-dept.deptsec-xsup #deptKpis,
body.view-dept.deptsec-xsup #gridConstitute,
body.view-dept.deptsec-xsup #gridPhoneTop,
body.view-dept.deptsec-xsup #tableWrapSec,
body.view-dept.deptsec-xsup #ossSec{display:none!important}
body.view-dept.deptsec-xsup #xsupSec{display:block}
/* 调动面板（.xfer-*）与全局提醒条（#xferAlert） */
.xfer-card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px;box-shadow:0 1px 3px rgba(20,30,60,.04)}
.xfer-t{font-size:15px;font-weight:800;margin:0 0 var(--s-2)}
.xfer-sub{font-size:12px;color:var(--sub);line-height:1.7;margin:0 0 12px}
.xfer-box{border:1px solid var(--line);border-radius:10px;padding:12px 14px;margin-bottom:12px}
/* ⚠⚠⚠ 容器级横向滚动兜底（第117 轮）：表格定宽后若在**极窄屏**仍放不下，
 *   宁可让这一块自己横向滚，也不要让它把**整页**撑出横向滚动条
 *   （整页一横滚，左侧栏也跟着动，所有页面都被这一张表拖下水）。
 *   ⚠⛔ 绝不用 overflow-x:hidden —— 那会把「操作」列按钮**裁掉**，
 *     用户就点不到「发起返还」/「一键分配」了，比溢出更糟。
 *   ⚠ 只给 .xfer-box，不给 body：body 一旦自己滚，全站布局的
 *     `position:sticky` 顶栏与侧栏会失效（第 108 轮已踩过 sticky 与横滚互斥）。 */
.xfer-box{overflow-x:auto}
.xfer-box>h4{margin:0 0 var(--s-4);font-size:13px;font-weight:700;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.xfer-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px}
.xfer-row>label{font-size:12px;color:var(--sub);font-weight:600}
.xfer-row select,.xfer-row input[type=text]{border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-2) var(--s-4);font-size:12px;background:var(--pure);color:var(--ink);font-family:inherit}
.xfer-empty{font-size:12px;color:var(--sub);padding:4px 0}
.xfer-chips{display:flex;flex-wrap:wrap;gap:6px;max-height:180px;overflow:auto;padding:2px 0 4px}
.xfer-chip{border:1px solid var(--line);background:var(--pure);border-radius:999px;padding:3px var(--s-5);font-size:12px;cursor:pointer;color:var(--ink-2);font-family:inherit}
.xfer-chip:hover{border-color:#2f6fed}
.xfer-chip.on{background:#2f6fed;border-color:#2f6fed;color:var(--pure);font-weight:600}
.xfer-sel{display:flex;flex-wrap:wrap;gap:6px;margin:2px 0 8px;min-height:24px;align-items:center}
.xfer-tag{background:#eaf2ff;color:var(--blue-fg);border-radius:999px;padding:2px 10px;font-size:12px;font-weight:600}
.xfer-list{width:100%;border-collapse:collapse;font-size:12px;margin-top:4px}
.xfer-list th{text-align:left;background:var(--card-hd);color:var(--sub);font-weight:600;padding:6px var(--s-4);border-bottom:1px solid var(--line);position:static;top:auto}
/* ⚠⚠⚠ `white-space:normal` 是**必须**的，不是风格选择（2026-10-27 第二百三十轮）：
   本页第 815 行有一条**全局** `th,td{white-space:nowrap}`，而本模块三张表都没覆盖它
   ⇒ 「人数 / 名单」列（历史表定宽 18% ≈ 233px）装 14 个「姓名 + 工号」时实测 scrollWidth 1240px，
     超出 1007px **横向压在「处理结果」列上**——两段文字叠在一起，用户看到的就是一团乱码
     （用户原话：「这种信息显示多的，你的显示就是有问题，一点都不好看」）。
   ⚠ 第117 轮写的 `.xfer-list-hist td{overflow-wrap:anywhere}` 在这之前是**死代码**：
     overflow-wrap 只在允许换行时才有意义，nowrap 一压，它一行都断不了。
   ⚠ 状态胶囊自带 nowrap（`.xfer-st`），不受影响；「处理人 / 时间」列会变成两行，
     但那是**正确**的：nowrap 时「撤销：管理员 · 2026/10/7 18:17:39」同样会溢到隔壁列上去。 */
.xfer-list td{padding:6px var(--s-4);border-bottom:1px solid var(--line);vertical-align:top;white-space:normal}
/* ⚠⚠⚠ 横向溢出根治（2026-10-06 第一百一十七轮，用户报障「显示都溢出区了」）。
 *
 * 实测（沙箱 _probe117-oflow.js，1440×950、管理员、调试一部、12 条历史）：
 *   容器 .xfer-box  client=1220scroll=1419   ⇒ 内容比容器宽 199px
 *     .wrap         client=1316  scroll=1467
 *     整页documentElement client=1440 scroll=1591 ⇒ ⚑ **整页**横向溢出 151px
 *   每列 scrollW == w（实测 133/278/72/175/578/169，逐列都不超自己那份）
 *   ⇒ **不是某格文字不换行，是六列宽度之和 1405 > 容器 1220。**
 *
 * ⚠⚠⚠ 为什么必须同时改**两处**，只改一处都会留下另一种溢出：
 *   ① 只给列定宽（table-layout:fixed）⇒「处理结果」压到 ~40% 后，
 *      里面的长文本改为**换行**，行高变高 ⇒ 表格纵向拉长、难扫。
 *      实测「处理结果」里最长的不可断行片段有 40 字
 *      （"分配：已完成（管理员 · 2026/9/26 16:39:16：已分配 2 人）"）⇒ 必须允许断行。
 *   ② 只给容器加滚动 ⇒ 整页不再横向溢出，但 6 列挤在 1220px 里、
 *      「处理结果」只剩~250px、每行 4~5 行高 —— 能用但很难看。
 *   ⇒ 组合：① 列宽定死 + 允许断行（让宽度可控）② 容器横向滚动（兜住窄屏仍放不下）。
 *
 * ⚠⚠⚠ **只对历史表用定宽布局**（`.xfer-list-hist`），不全局改：
 *   本模块另有两张 .xfer-list —— 待确认表 7 列、我发起表 5 列，
 *   列数与内容长度都不同，全局 fixed 会把它们一起改掉
 *   （那正是记忆里「多出口只修一个＝没修 / 一改改全部」的坑）。
 *   那两张表内容都是短文本（部门名 / 姓名 / 时间），实测不溢出，不动它们。
 *
 * ⚠⚠⚠ `table-layout:fixed` 的前提是 `width:100%` 已定（本表已有），
 *   且定宽后**任何单元格的最长不可断行片段都不能超过列宽** ——
 *   fixed 只是「不再按内容分配」，**不是**「强制不溢出」。
 *   ⇒ 所以下面必须补 overflow-wrap:anywhere：
 *      中文默认可断，但混排的 `SJ08386`、`2026/9/26 16:39:16`、`D264（1L）`
 *      这类**半角片段不会断**，在窄列里会顶出去。
 *
 * ⚠ 刻意**不给** .xfer-box 加 overflow-x:hidden：那会把「操作」列按钮**裁掉**，
 *   用户就点不到「发起返还」了，比溢出更糟。要滚动，不要裁剪。 */
.xfer-list-hist{table-layout:fixed}
/* 列宽：方向/状态/操作 给固定值（内容长度有界：部门名 5~6 字、胶囊 3 字、按钮 6~8 字），
 * 其余三列给百分比（长度随人数变化）。
 * ⚠ 百分比 + 固定值必须正好铺满 100%，差 1% 都会让 fixed 算出一列溢出。 */
.xfer-list-hist col.c-dir{width:152px}
.xfer-list-hist col.c-st{width:66px}
.xfer-list-hist col.c-ops{width:148px}
.xfer-list-hist col.c-cnt{width:18%}
.xfer-list-hist col.c-by{width:15%}
/* 余下全给「处理结果」（≈37%）
 * ⚠⚠⚠ 定宽后的实测仍有两列内容超宽（117 轮沙箱实测）：
 *   方向给了 84 实测内容要 124、状态给了 60 内容要 63。
 *   两处都不是「文字太长」，是**不允许断的元素**：
 *     · 状态列的 `.xfer-st` 胶囊有 `white-space:nowrap`（第 445 行），
 *       「已返还」三个字 + padding 在 12px 字号下就是 63px —— 这是该列的**硬下限**。
 *   · 方向列实测内容要 124px：「调试二部 → 调试一部」这行是**纯文本节点**
 *     （不在任何元素里，所以元素测量列不出来），中文虽可断行，
 *     但「调试一部 ← 调试二部」这种 6+6 字连排不断就很宽 ⇒ 按实测值给 124px。
 *   ⇒ 定宽值必须**按实测内容宽度给**，不能拍脑袋。
 * ⚠⚠⚠ 第230 轮（2026-10-27）方向列 124 → **152px**：
 *   上一轮之所以敢给 124，是因为全局 `td{nowrap}` 兜着 —— 内容是「不许断」的纯文本节点，
 *   124px 是**它当时被截断的宽度**，不是它需要的宽度（实际要 ~134px，尾巴一直压在状态列上）。
 *   本轮把 td 的 nowrap 放开后，124px 立刻表现为「调试四部 ← 调试一 / 部」两行。
 *   ⇒ 补够宽度 + 给部门名包 `.xf-nm`（见 xferDirText），断行只允许落在箭头两侧。
 *   代价从「处理结果」那列扣（它是唯一没写死宽度的列，余量自动归它）。 */
.xfer-list-hist td{overflow-wrap:anywhere;word-break:normal}
/* 状态胶囊是 nowrap ⇒ 它的宽度就是该列的**下限**，列宽不得小于它 */
.xfer-list-hist td:nth-child(3) .xfer-st{display:inline-block}
/* 窄屏（手机）：6 列必然挤。与其让用户左右拖到晕，
 * 收窄固定列、把余量留给「处理结果」（真正要读的那列）。 */
/* 窄屏（手机）：⚠⚠⚠ **不要压列宽**。
 *   第117 轮实测：压到 100px 时方向列内容要 124px ⇒ 溢出被**放回去**，
 *   而手机上让用户左右拖 6 列表格才是更糟的体验。
 *   ⇒ 窄屏只做一件事：给 .xfer-box 一个**最小宽度**，
 *     让它自己横滚（.xfer-box 已 overflow-x:auto），整页仍然不横滚。
 *   ⚠ 最小宽度 = 各列实测下限之和（152+66+148+两份百分比按 15% 折算≈330）
 *     ⇒ 取 620px：够放得下「操作」列按钮（用户最需要点的那列）。
 *     ⚠ 230 轮把方向列 124→152 后，四项固定宽度合计 620px，与这个 min-width 正好相等；
 *       再往大加任何一列都要同步抬这里，否则手机上「操作」列会被挤出可视区。 */
@media(max-width:760px){
  .xfer-box>.xfer-list-hist{min-width:620px}
}
.xfer-st{display:inline-block;border-radius:999px;padding:1px var(--s-4);font-size:12px;font-weight:700;white-space:nowrap}
.xfer-st.pending{background:#fff4e5;color:var(--orange-fg)}
.xfer-st.accepted{background:#eafaf1;color:#177245}
.xfer-st.rejected{background:#fdeceb;color:var(--red-fg)}
.xfer-arrow{color:var(--blue-fg);font-weight:700}
.xfer-ops{display:flex;gap:6px;flex-wrap:wrap}
.xfer-note{font-size:12px;color:var(--sub);line-height:1.6;margin:6px 0 0}
/* ---- 「处理结果」里返还记录的**归组块**（2026-10-27 第二百三十轮）----
   用户报障原文：「这种信息显示多的，你的显示就有问题了，一点都不好看」。
   实测：同一条 14 人的返还记录，原来逐人一行 = 14 行几乎**逐字重复**的
   「· 张三 SJ12345 ← 原挂靠 6DC（2L） → 已加回「调试四部」谷部/毛依都体（1X） ⚠ 原行已不存在，兜底落位」，
   行高 342px（同表其它行 90px），右半边全是重复文字。
   ⇒ 改成：一行标题「原挂靠行 → 落位行 · N 人」，下面一行列名字，兜底/未加回每组只说一次。
   ⚠ 信息一条都没少：组内原挂靠行不一致时，逐人补注「（原 X）」。
   ⚠ `.xf-nm` 让「姓名 + 工号」不被拆到两行（单元格已允许换行，不包的话会在
     「李柏林 SJ12926」中间断开，比挤一点更难看）；断行只发生在「、」处。 */
.xf-g{margin:6px 0 0;padding:var(--s-2) 0 0;border-top:1px dashed var(--line)}
.xf-g:first-child{margin-top:3px;padding-top:0;border-top:0}
.xf-g-h{font-size:12px;font-weight:700;color:var(--ink);line-height:1.65}
.xf-g-h .xfer-arrow{margin:0 2px}
.xf-g-c{font-weight:600;color:var(--sub);margin-left:var(--s-3)}
.xf-g-l{font-size:12px;color:var(--ink);line-height:1.8;margin-top:1px}
.xf-nm{white-space:nowrap}
/* 多来源组里的「来源行」标签（6DC（2L）：张三…）—— 只是把「从哪来」说清楚，权重比名字低 */
.xf-sub{color:var(--sub);font-weight:700}
.xf-more{color:var(--sub);cursor:help;border-bottom:1px dashed currentColor;white-space:nowrap}
/*「③ 历史记录」上方的「你可以操作 N 条」提示块（2026-10-07 第119 轮）。
   ⚠⚠⚠ 为什么需要它：用户报障「发起返还我没有看到负责人可以进行的操作」。
     实测（沙箱 _probe119b-why.js，mgr=韦盛旺/调试一部、线上真实 13 条历史）：
       · 权限判据 `xferCanCreateReturnFor` 返回 **true**、按钮也确实进了 innerHTML
         ⇒ **不是权限问题**（第113 轮那套判据是对的），是**按钮藏得太深**：
           桌面首屏只放得下 5 行、按钮在第 6/8 行（需下滚 80/238px）；
           手机端表格 620px / 容器 336px，按钮在右侧 284px 处、不横滚完全看不到。
     ⇒ 光「有按钮」不够，得让人**知道有**。这条把可操作行数与按钮名直接报出来。
   ⚠ 用 CSS 变量 + prefers-color-scheme，浅/深两套都对（护眼/深色主题检查会抓硬编码色）。 */
.xfer-canact{
  border:1px solid var(--brand-soft,#c9d8f6); background:var(--brand-soft,#eef3fd); color:#23459e;
  border-radius:10px; padding:10px 14px; margin:10px 0 12px; font-size:12.5px; line-height:1.7;
}
.xfer-canact b{font-weight:700}
@media(max-width:760px){
  .xfer-canact{padding:var(--s-4) 12px; font-size:12px}
}
/* 「处理结果」逐人明细里的工号（2026-10-06 第一百一十五轮）：
   同名双键管控之后，**光看姓名已经不能定位到人**（第 14t 轮立的规矩），
   所以历史记录里每一条调动都必须带上工号，否则「摘的是谁」这个问题在同名时无解。
   ⚠ 用等宽 + 弱化色：不抢姓名的视觉主位，但一眼能扫到、也能与姓名区分开。
   ⚠ 不加背景/边框 —— 这一格是表格 td 内的一段行内文本，任何盒模型都会把列宽挤动
     （.xfer-ret 那里已经踩过一次 border-left 改盒模型的坑）。 */
.xfer-no{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--sub);opacity:.85}
/* 返还行（2026-09-21 第十八轮）：历史里要能一眼把「还回来」的记录与「借出去」的分开。
   用 inset 阴影而不是 border-left —— 加边框会改单元格盒模型、把整张表的列宽挤动，
   而 box-shadow 不参与布局，零副作用。底色走主题变量（护眼主题检查会抓硬编码色）。 */
.xfer-ret>td{background:var(--card-hd)}
.xfer-ret>td:first-child{box-shadow:inset 3px 0 0 #2f6fed}
/* 返还人员多选浮层（2026-09-21 第十九轮）：
   ① 必须 fixed —— 在表格行里展开会改行高、把 sticky 表头与后续行整体顶动（项目铁律）；
   ② 颜色一律走主题变量，硬编码色会被护眼主题检查抓走；
   ③ z-index 夹在侧栏(60)/表头(65) 之上、模态(90)之下：它是个轻量浮层，不该盖住弹窗。 */
.xfer-pop{position:fixed;z-index:86;background:var(--card);border:1px solid var(--line);border-radius:12px;
  padding:12px 14px;box-shadow:0 14px 36px rgba(15,35,70,.22),0 2px 6px rgba(15,35,70,.10)}
/* ⚠ 分配浮层内容更高（人员 chips + 车间下拉 + 候选行 + 预览）：必须封顶并允许内部滚动，
   否则在小屏/横屏上底部按钮会越出视口且**无法**通过滚动找回（fixed 定位）。
   2026-09-24 第三十八轮实测：1440×900 下浮层高 457px，按钮 y=967 > 视口 900 ⇒ 点不到。 */
/* ⚠⚠ 手机上的 100vh / innerHeight 是**大视口**（地址栏收起后的高度），比当前可视高度
   大 100px 上下 ⇒ 用它封顶，浮层底部（含按钮）会被系统地址栏 / 导航栏挡住。
   故：先给 100vh 兜底，再给 100dvh（动态视口）；JS 另用 visualViewport.height 显式封顶。
   2026-09-24 用户反馈：真机浮层遮住下方、且无法移动 ⇒ 见 xferPopFit / xferPopDrag。 */
#xferDspPop{max-height:calc(100vh - 16px);max-height:calc(100dvh - 16px);overflow:auto;overscroll-behavior:contain}
#xferRetPop{max-height:calc(100vh - 16px);max-height:calc(100dvh - 16px);overflow:auto;overscroll-behavior:contain}
.xfer-pop .xfp-hd{font-size:13px;font-weight:800;margin:0 0 var(--s-2);cursor:move;
  user-select:none;-webkit-user-select:none;touch-action:none}
.xfer-pop .xfp-tip{font-size:12px;color:var(--sub);line-height:1.6;margin:0 0 var(--s-4)}
/* ⚠⚠ 按钮区**常驻**：浮层内容高于可视高度时靠内部滚动，若按钮跟着内容滚，
   用户（尤其手机）会以为「功能没了」。sticky 让它在浮层可视底部一直可见 ——
   这是不依赖「高度算得准」的第二道保障。 */
.xfer-pop .xfp-btns{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px;
  position:sticky;bottom:0;z-index:2;background:var(--card);
  padding:8px 0 2px;border-top:1px solid var(--line)}
/* 支援人员分配浮层（2026-09-24 第三十八轮）：与返还浮层同构，多两级选择（车间 → 线体行）。
   ① 候选行用「按钮 + .on 类」模拟单选，而不是 <input type=radio> —— 手机端原生 radio 热区太小，
      且各机型渲染差异大；按钮方案还能顺手把「负责人 / 专案 / 线体」三行文字排进去；
   ② 颜色一律走主题变量（硬编码色会被护眼主题检查抓走）；
   ③ 车间下拉 flex:1 1 150px + min-width:0：窄屏（360px）下不会把浮层撑破。 */
.xfer-pop .xfp-fld{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap;margin:0 0 var(--s-4);font-size:12px}
.xfer-pop .xfp-fld>label{color:var(--sub);font-weight:600;flex:none}
.xfer-pop .xfp-fld select{flex:1 1 150px;min-width:0;max-width:100%;border:1px solid var(--line);border-radius:var(--r-2);
  padding:var(--s-2) var(--s-4);font-size:12px;background:var(--pure);color:var(--ink);font-family:inherit}
.xfer-rows{max-height:200px;overflow:auto;border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-2);background:var(--pure)}
.xfer-rows .xfr-item{display:block;width:100%;text-align:left;border:1px solid transparent;background:transparent;
  color:var(--ink);border-radius:var(--r-2);padding:6px var(--s-4);font-size:12px;line-height:1.55;cursor:pointer;font-family:inherit;margin:0 0 3px}
.xfer-rows .xfr-item:hover{background:var(--row-alt)}
.xfer-rows .xfr-item.on{background:var(--card-hd);border-color:var(--blue);font-weight:700}
.xfer-rows .xfr-empty{padding:var(--s-4);font-size:12px;color:var(--sub)}
.xfer-pop .xfp-prev{margin:var(--s-4) 0 0;padding:8px 10px;border:1px dashed var(--line);border-radius:var(--r-2);
  font-size:12px;line-height:1.65;background:var(--row-alt)}
#xferAlert{display:none;margin:0 0 14px;border-radius:12px;padding:var(--s-5) 14px;font-size:12px;line-height:1.65;
  background:linear-gradient(135deg,#fff8ec,#fffdf8);border:1px solid #f5d9ac;color:#8a5300;
  box-shadow:0 1px 3px rgba(20,30,60,.05)}
#xferAlert .xa-hd{font-weight:800;font-size:13px;margin-bottom:var(--s-2);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
#xferAlert .xa-item{margin:2px 0}
#xferAlert .xa-item b{color:#7a3d00}
#xferAlert .xa-btns{margin-top:var(--s-4);display:flex;gap:8px;flex-wrap:wrap}
#deptSubNav .sb-subsec-btn .sb-sec-t{overflow:hidden;text-overflow:ellipsis;min-width:0;flex:1 1 auto}
#deptSubNav .sb-subsec-btn .sb-badge{margin-left:auto;background:#e8590c;color:var(--pure);border-radius:999px;
  font-size:11px;font-weight:800;padding:1px 6px;flex:none;line-height:1.5}
#deptSubNav .sb-subsec-btn.active .sb-badge{background:var(--pure);color:var(--blue-fg)}
/* 工伤台账：由 renderInjuryCard 按当前选中部门是否有工伤记录决定显隐 */
/* 分级部门视图：按「部门 → 负责人 → 明细行」三级展示（只读） */
#levelSec{display:none}
body.view-level #levelSec{display:block}
body.view-level #kpis,body.view-level #deptKpis,body.view-level #cardDept,body.view-level #cardPie,
body.view-level #cardPhone,body.view-level #cardTop15,body.view-level #tableWrapSec,
body.view-level #injuryActionBar,
/* ⚠ 必须带 !important：renderInjuryCard() 会给 #injuryCard 写**内联** display:block，
   而内联样式优先级高于本选择器 —— 不加这条，工伤台账会漏进分级视图
   （2026-09-16 手机端截图发现；同文件 line 176 / 202 同场景用的也是 !important）。 */
body.view-level #injuryByDeptCard,body.view-level #injuryCard{display:none!important}
.lvl-dept{border:1px solid var(--line);border-radius:10px;margin:10px 0;background:var(--card);overflow:hidden}
.lvl-dept>summary{cursor:pointer;padding:10px 14px;font-size:14px;display:flex;gap:10px;align-items:center;flex-wrap:wrap;list-style:none}
.lvl-dept>summary::-webkit-details-marker{display:none}
.lvl-dept>summary::before{content:'▸';color:var(--muted);transition:transform .15s}
.lvl-dept[open]>summary::before{transform:rotate(90deg)}
.lvl-dept>summary:hover{background:var(--row-alt)}
.lvl-meta{font-size:12px;color:var(--muted)}
.lvl-warn{color:var(--red-fg)}
.lvl-sumwrap{display:flex;flex-direction:column;gap:2px;min-width:0}
.lvl-deptline{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap}
.lvl-headmgr{font-size:12px;color:#2f54b0;font-weight:600}
/* 负责人卡片：蓝色圆角卡片，内部按「阶段」分块 */
.lvl-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:12px;margin:0 12px 12px;align-items:stretch}
.lvl-card{border:1px solid #3358b8;border-radius:14px;overflow:hidden;background:#f2f6ff;box-shadow:0 1px 3px rgba(31,56,102,.08)}
.lvl-card-hd{background:#3358b8;color:var(--pure);padding:8px 14px;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.lvl-card-hd .lvl-mgrname{color:var(--pure)}
.lvl-cardmeta{font-size:12px;color:#dbe5ff}
.lvl-stages{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px;padding:10px}
.lvl-stage{background:var(--card);border:1px solid #d5def2;border-radius:var(--r-2);overflow:hidden}
.lvl-stage>summary{cursor:pointer;padding:6px 10px;font-size:12px;display:flex;gap:8px;align-items:center;justify-content:space-between;list-style:none}
.lvl-stage>summary::-webkit-details-marker{display:none}
.lvl-stage>summary:hover{background:var(--row-alt)}
.lvl-stage-badge{font-weight:700;color:#2f54b0}
.lvl-stage-cnt{font-size:12px;color:var(--muted);white-space:nowrap}
.lvl-stage-none{border-color:#e8b34b}
.lvl-stage-none .lvl-stage-badge{color:var(--orange-fg)}
.lvl-stage-list{border-top:1px dashed #d5def2;padding:6px 10px;max-height:230px;overflow:auto;display:flex;flex-direction:column;gap:var(--s-2)}
.lvl-stage-row{display:flex;flex-direction:column;gap:1px;font-size:12px;border-bottom:1px dotted #e7f0fb;padding-bottom:4px}
.lvl-stage-row:last-child{border-bottom:none;padding-bottom:0}
.lvl-stage-row.logged{cursor:pointer;border-radius:6px;padding:4px 6px;margin:-4px -6px;transition:background .15s}
.lvl-stage-row.logged:hover{background:var(--row-hover,#dbeafb)}
.lvl-row-loc{font-weight:600;color:var(--ink-head)}
.lvl-row-proj{color:var(--ink-2)}
.lvl-row-eq{color:var(--muted)}
.lvl-row-nums{color:var(--blue-fg);font-size:12px}
/* 负责人（mgr）不开放：数据管理 / 操作日志 入口 */
body.is-mgr .mgr-hide{display:none!important}
/* 登录门禁：未登录整页锁定，仅显示登录窗口 */
body.gate-locked header,body.gate-locked #sideBar,body.gate-locked .wrap{display:none!important}
body.gate-locked{background:#3d5a99}
body.gate-locked #loginModal{display:flex!important;align-items:center;justify-content:center;background:linear-gradient(160deg,#4a6ab5 0%,#2a4a8f 55%,#1f3866 100%)}
body.gate-locked #loginClose{display:none}
body.gate-locked .modalbox{box-shadow:0 24px 80px rgba(0,0,0,.45)}
/* D248 · 首屏骨架（boot-gate）：body 一出现就带这个类，登录框立刻可见，
   不等后面 2MB 脚本执行。脚本一就绪就撤掉，正式门控交回 gate-locked / guest-mode。
   ⚠ 这几条必须与上面 gate-locked 的视觉一致，否则「骨架态」和「就绪态」之间会跳一下。 */
body.boot-gate header,body.boot-gate #sideBar,body.boot-gate .wrap{display:none!important}
body.boot-gate{background:#3d5a99}
body.boot-gate #loginModal{display:flex!important;align-items:center;justify-content:center;background:linear-gradient(160deg,#4a6ab5 0%,#2a4a8f 55%,#1f3866 100%)}
body.boot-gate #loginClose{display:none}
/* 骨架态：按钮看起来「还不能点」，并说明原因。⛔ 不要用 pointer-events:none ——
   点了完全没反应比「看得见按钮 + 一行说明」更让人困惑。
   ⚠ 用绝对定位贴在按钮右侧：手机屏矮，这行字如果占布局高度，会把登录按钮顶出卡片外
     （实测 1094px 高的卡片，多一行就把按钮裁掉一截）。 */
.boot-tip{display:none;position:absolute;right:16px;bottom:22px;font-size:12px;color:#7b8aa0;line-height:1.6}
body.boot-gate .modalbox{position:relative}
body.boot-gate .boot-tip{display:block}
body.boot-gate #lgSubmit{opacity:.45}
/* 登录窗品牌标题区（图标 + 系统名 + 副标题） */
.lg-brand{text-align:center;padding:2px 0 14px;border-bottom:1px solid #e7f0fb;margin-bottom:14px}
.lg-brand-ico{width:46px;height:46px;margin:0 auto;display:block}
.lg-brand-t{font-size:20px;font-weight:800;color:#1f2733;letter-spacing:1px;margin-top:8px}
.lg-brand-s{font-size:12px;color:var(--muted);margin-top:4px;letter-spacing:.5px}
/* 个人中心（简洁版式：邮箱 + 修改密码） */
.pf-box{position:relative;width:460px;max-width:94vw}
.pf-x{position:absolute;top:10px;right:12px;background:none;border:none;font-size:15px;color:#9aa3b2;cursor:pointer;padding:4px 8px;border-radius:6px}
.pf-x:hover{background:#f0f2f6;color:#4a5568}
.pf-t{font-size:14.5px;font-weight:600;color:var(--ink);margin:0 0 10px}
.pf-sub{font-size:12px;color:var(--muted);margin:-6px 0 12px}
.pf-row{display:flex;gap:10px;align-items:stretch}
.pf-row input{flex:1;min-width:0;box-sizing:border-box;padding:var(--s-4) 12px;border:1px solid #cfe0f4;border-radius:8px;font-size:13.5px}
.pf-row input:focus{outline:none;border-color:var(--blue,#2f6fed);box-shadow:0 0 0 2px rgba(47,111,237,.15)}
.pf-divider{border-top:1px solid #e7f0fb;margin:18px 0 16px}
.pf-lab{font-size:13px;color:#3a4552;margin:0 0 6px}
.pf-in{width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid #cfe0f4;border-radius:8px;font-size:13.5px;margin-bottom:12px}
.pf-in:focus{outline:none;border-color:var(--blue,#2f6fed);box-shadow:0 0 0 2px rgba(47,111,237,.15)}
.pf-red{background:#e5484d;color:var(--pure);border:none;border-radius:8px;padding:10px 24px;font-size:13.5px;font-weight:600;cursor:pointer;margin-top:4px}
.pf-red:hover{background:#d63b40}
.pf-red:disabled{opacity:.5;cursor:wait}
/* ===== 功能 hub 弹窗（数据管理 / 权限管理 / 个人中心 / 提醒通知） ===== */
.dh-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:6px}
.dh-btn{display:flex;flex-direction:column;gap:3px;align-items:flex-start;text-align:left;background:var(--card);border:1px solid #cfe0f4;border-radius:10px;padding:12px 14px;cursor:pointer;font-family:inherit}
.dh-btn span{font-size:18px}
.dh-btn b{font-size:13px;color:#1f3866}
.dh-btn i{font-size:12px;color:var(--muted);font-style:normal}
.dh-btn:hover{border-color:#2f6fed;background:var(--row-hover)}
.dh-btn.off{opacity:.45;cursor:not-allowed}
.dh-btn.off:hover{border-color:#cfe0f4;background:var(--card)}
/* D252：权限管理 hub 的分组（只作用于 .dh-group 内，⛔ 不影响数据管理 / 个人中心 / 提醒通知那三个 hub）。
   ⚠ 卡片说明压成一行用 line-clamp，**不用 nowrap**：本项目踩过「nowrap + 定宽 = 越框叠字」的坑，
     换成 line-clamp 后浏览器不支持时退化成完整显示（变高但不叠字），是更安全的降级方向。 */
.dh-group{margin-top:14px}
.dh-group:first-of-type{margin-top:8px}
.dh-group-h{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:700;color:var(--sub);letter-spacing:.4px;margin-bottom:6px}
.dh-group-h::after{content:'';flex:1;height:1px;background:var(--line)}
.dh-group-note{font-size:12px;line-height:1.55;color:var(--sub);margin:0 0 var(--s-3)}
.dh-group .dh-btn{min-height:62px}
.dh-group .dh-btn i{display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;overflow:hidden}
.pf-sec-t{font-size:13px;font-weight:800;color:#1f3866;margin-bottom:8px}
.nt-radio{display:inline-flex;align-items:center;gap:6px;font-size:13px;margin-right:18px;cursor:pointer;color:#33415b}
/* ===== 「🔔 发送提醒通知」的提醒事项分区（2026-09-28 第七十三轮） =====
   ⚠ 配色一律走 :root 变量（护眼主题检查 smoke-eyecare-theme 会抓硬编码灰蓝值，
     而这个弹窗原本就有一批硬编码色 —— 新增部分不再加码）。 */
.nt-sec{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-bottom:10px;background:var(--card)}
.nt-sec-hd{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:700;color:var(--sub);margin-bottom:8px}
.nt-sec-hd .nt-opt{font-weight:400;font-size:12px;opacity:.85}
.nt-sec-hd .nt-mini:first-of-type{margin-left:auto}
.nt-mini{padding:2px var(--s-4);font-size:12px}
.nt-kinds{display:flex;flex-wrap:wrap;gap:var(--s-3) 18px}
.nt-kind-row{display:inline-flex;align-items:center;gap:6px}
.nt-kind{display:inline-flex;align-items:center;gap:6px;font-size:13px;cursor:pointer;color:var(--ink)}
.nt-kind input{cursor:pointer}
.nt-go{border:1px solid var(--line);background:transparent;color:var(--blue);border-radius:6px;
  font-size:11px;padding:1px var(--s-3);cursor:pointer;line-height:1.5;white-space:nowrap}
.nt-go:hover{background:var(--row-hover)}
.nt-hint{font-size:12px;color:var(--sub);margin-top:8px;line-height:1.6}
.nt-pv{border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin-top:12px;background:var(--card-hd)}
.nt-pv-hd{font-size:12px;font-weight:700;color:var(--ink);margin-bottom:6px}
.nt-pv-sum{font-size:12px;color:var(--ink);line-height:1.9}
.nt-pv-skip{color:var(--orange)}
.nt-pv-warn{color:var(--red)}
.nt-pv-mail{border:1px solid var(--line);border-radius:8px;background:var(--card);margin-top:8px;padding:8px 10px;
  font-size:12px;line-height:1.6;color:var(--ink);white-space:pre-wrap;word-break:break-word;
  max-height:280px;overflow:auto;font-family:ui-monospace,Consolas,Menlo,monospace}
.nt-pv-details{margin-top:8px}
.nt-pv-details summary{font-size:12px;color:var(--blue);cursor:pointer}
.nt-pv-rows{max-height:210px;overflow:auto;margin-top:6px;font-size:12px;color:var(--ink)}
.nt-pv-rows div{padding:3px 0;border-bottom:1px dashed var(--line)}
.nt-pv-rows b{font-weight:700}
.nt-pv-rows .nt-dim{color:var(--sub)}
@media(max-width:640px){ .dh-grid{grid-template-columns:1fr} }

/* ── D337：权限管理改成「整页式面板」 ─────────────────────────────────────
   用户原话：「把这个弹窗先做大，做成页面的那种」。
   原来是个 560px 的小弹窗，里面塞着 15 个入口 + 按人查结果，一屏只看得见两三行，
   PC 上要滚半天 —— 这不是"权限太复杂"，是**窗口太小**。
   ⚠ 只作用于 #permHubModal：⛔ 不动 .modal-box 本体（其它十几个弹窗还是小尺寸才对）。
   ⚠ 标题与底部按钮**吸顶/吸底**，只有中间滚动 —— 滚到一半还能看见"关"和"这是哪一页"。 */
#permHubModal .modal-box{
  width:min(1180px,96vw);
  max-width:96vw;
  max-height:94vh;
  padding:0;
  display:flex;
  flex-direction:column;
  overflow:hidden;
}
#permHubModal .pc-head{flex:none;padding:18px 24px 12px;border-bottom:1px solid rgba(128,140,160,.18);background:var(--pure)}
#permHubModal .pc-body{flex:1 1 auto;min-height:0;overflow:auto;padding:14px 24px 8px}
#permHubModal .pc-foot{flex:none;padding:10px 24px 14px;border-top:1px solid rgba(128,140,160,.18);background:var(--pure)}
#permHubModal .pc-body .dh-grid{grid-template-columns:1fr 1fr 1fr}
@media(max-width:1100px){ #permHubModal .pc-body .dh-grid{grid-template-columns:1fr 1fr} }
/* ── D341：专案代码速查（整页式面板，复用 D337 的三段结构与尺寸规则）── */
#projCodeModal .modal-box{
  width:min(1080px,96vw);
  max-width:96vw;
  max-height:90vh;
  padding:0;
  display:flex;
  flex-direction:column;
  overflow:hidden;
}
#projCodeModal .pc-head{flex:none;padding:16px 22px 10px;border-bottom:1px solid rgba(128,140,160,.18);background:var(--pure)}
#projCodeModal .pc-body{flex:1 1 auto;min-height:0;overflow:auto;padding:12px 22px 8px}
#projCodeModal .pc-foot{flex:none;padding:9px 22px 13px;border-top:1px solid rgba(128,140,160,.18);background:var(--pure)}
#pcTable table{width:100%;border-collapse:collapse;font-size:13px}
#pcTable th{position:sticky;top:0;z-index:1;background:var(--card);text-align:left;padding:7px 8px;border-bottom:1px solid rgba(128,140,160,.35);color:var(--sub);font-weight:600;white-space:nowrap}
#pcTable td{padding:6px 8px;border-bottom:1px solid rgba(128,140,160,.14);vertical-align:top}
#pcTable tr.pc-new td{background:rgba(112,173,71,.10)}
#pcTable .pc-code{font-family:ui-monospace,Consolas,monospace}
#pcTable .pc-proj{color:var(--sub);font-size:12px}
@media(max-width:640px){
  #projCodeModal .modal-box{width:100vw;max-width:100vw;height:100vh;max-height:100vh;border-radius:0}
  #projCodeModal .pc-head{padding:12px 12px 8px}
  #projCodeModal .pc-body{padding:8px 12px 6px}
  #projCodeModal .pc-foot{padding:8px 12px 10px}
  #pcTable th,#pcTable td{padding:5px 6px}
}
@media(max-width:640px){ #permHubModal .modal-box{width:100vw;max-width:100vw;height:100vh;max-height:100vh;border-radius:0}
  #permHubModal .pc-head{padding:14px 14px 10px}
  #permHubModal .pc-body{padding:10px 14px 6px}
  #permHubModal .pc-foot{padding:8px 14px 12px}
  #permHubModal .pc-body .dh-grid{grid-template-columns:1fr}
}
select,input[type=text]{font-size:13px;padding:var(--s-3) 10px;border:1px solid var(--line);border-radius:8px;background:var(--pure);color:var(--ink);outline:none}
select:focus,input:focus{border-color:var(--blue)}
.tablewrap{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:14px 16px;box-shadow:0 1px 3px rgba(20,30,60,.04)}
.table-scroll{max-height:500px;overflow:auto}
table{border-collapse:collapse;width:100%;font-size:13px}
th,td{padding:8px 10px;text-align:left;border-bottom:1px solid var(--line);white-space:nowrap}
th{background:var(--card-hd);color:var(--sub);font-weight:600;position:sticky;top:0;z-index:2}
tbody tr:hover{background:var(--row-hover)}
.tag{display:inline-block;padding:1px 8px;border-radius:20px;font-size:12px;font-weight:600}
.tag.f{background:#e8f1ff;color:var(--blue)}
.tag.t{background:#fff1e3;color:var(--orange)}
.tag.s{background:#eafaf1;color:var(.green)}
.ecell{cursor:text;outline:none;min-width:28px;display:inline-block;text-align:center;transition:box-shadow .15s;border-radius:var(--r-1);padding:1px 4px}
.ecell:focus{box-shadow:0 0 0 2px var(--blue);background:var(--pure)}
.ecell.ro{cursor:not-allowed;opacity:.6;background:transparent!important}
.ecell.ro:focus{box-shadow:none}
/* 明细行编辑高亮：正在修改的那一行整行变蓝（左端加蓝色竖条），保存完成后自动恢复 */
tbody tr.row-editing{background:#C6DFFA;background:var(--row-edit,#C6DFFA)!important;transition:background .35s ease}
tbody tr.row-editing:hover{background:#C6DFFA;background:var(--row-edit,#C6DFFA)!important}
/* 整行 + 每个单元格一起上色：只给 tr 上色时，若列内容超宽（横向滚动），
   浏览器只把行背景画到表格的布局宽度，滚动到右侧会看到"没变蓝"的空白，所以单元格也要上色。 */
tbody tr.row-editing>td{background:#C6DFFA;background:var(--row-edit,#C6DFFA)!important}
/* 手机卡片视图：整张卡片由 tr 统一上色，单元格保持透明（否则会盖住卡片圆角） */
@media(max-width:640px){
  body:not(.tv-table) tbody tr.row-editing>td{background:transparent!important}
}
tbody tr.row-editing>td:first-child{box-shadow:inset 4px 0 0 0 var(--row-edit-bar)}
body:not(.tv-table) .tablewrap tr.row-editing{background:#C6DFFA;background:var(--row-edit,#C6DFFA)!important}
/* ===== 数量点击展开姓名录入 ===== */
.cntcell{vertical-align:top}
.cntbadge{display:inline-block;min-width:30px;padding:2px 10px;border-radius:20px;font-size:13px;font-weight:700;cursor:pointer;user-select:none;transition:.15s;border:1px solid transparent}
.cntbadge.f{background:#e8f1ff;color:var(--blue-fg)}
.cntbadge.s{background:#eafaf1;color:var(--green-fg)}
.cntbadge.t{background:#fff1e3;color:var(--orange-fg)}
.cntbadge.e{background:#f3efff;color:#7c4dbe}
.cntbadge.edit:hover{box-shadow:0 0 0 2px currentColor;filter:brightness(.97)}
.cntbadge:not(.edit){cursor:pointer;opacity:.85}
.namelist{position:fixed;z-index:78;margin:0;display:none;width:max-content;min-width:0;max-width:calc(100vw - 20px);background:var(--pure);border:1px solid #cfe1f7;border-radius:10px;padding:var(--s-4) 10px;box-shadow:0 12px 32px rgba(15,35,70,.20),0 2px 6px rgba(15,35,70,.08)}
.nlhead{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:var(--s-3);padding-bottom:6px;border-bottom:1px solid #d9e8fa}
.nlhead-title{font-size:12px;font-weight:600;color:var(--ink)}
.nlhead-qty{display:flex;align-items:center;gap:var(--s-2);font-size:11px;color:var(--sub);white-space:nowrap}
.nlhead-qty input[type=number]{width:54px;padding:1px 4px;border:1px solid #cdd6e4;border-radius:var(--r-1);font-size:12px;-moz-appearance:textfield}
.nlhead-qty input[type=number]::-webkit-inner-spin-button,
.nlhead-qty input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0;display:none}
.nlboxes{display:flex;flex-direction:column;gap:4px;max-height:200px;overflow-y:auto;overflow-x:hidden}
.nlname{flex:0 0 auto;width:88px;min-width:0;box-sizing:border-box;padding:3px var(--s-3);border:1px solid #d6deea;border-radius:6px;font-size:12px;outline:none}
.nlname:focus{border-color:var(--blue);box-shadow:0 0 0 2px rgba(56,114,234,.15)}
.nlname[readonly]{background:#ecf3fd;color:#566;cursor:default}
.nlname.tonly{width:88px;flex:0 0 auto}
/* 只读视图：姓名框收窄为 88px，右侧紧贴管理职小标签，浮层整体按内容自适应宽度 */
.nlname.rohalf{flex:0 0 auto;width:88px;min-width:0}
.nlroletag{flex-shrink:0;font-size:11px;line-height:1.5;color:#2f5bd0;background:#eef4ff;border:1px solid #d6e2f7;border-radius:4px;padding:0 var(--s-2);white-space:nowrap}
.nlempty{font-size:12px;color:var(--muted);padding:2px 0}

.note{background:#fff8e9;border:1px solid #f3e0b5;border-radius:10px;padding:12px 16px;font-size:12px;color:#7a5b14;margin-top:16px;line-height:1.7}
.note b{color:#5b4310}
.full{grid-column:1 / -1}
.btn.primary{background:#2f6fed;color:var(--pure);border-color:#2f6fed}
.btn.primary:hover{background:#2257c4}
.btn.ghost{background:var(--pure);color:#6b7280;border-color:#cfe0f4}
.btn.ghost:hover{background:#ecf3fd}
.btn.active{background:#1e7f4f;color:var(--pure);border-color:#1e7f4f}
.btn.active:hover{background:#176a40}
.btn:disabled{opacity:.6;cursor:default}
.addpanel{background:var(--card-hd);border:1px solid #d8e7f9;border-radius:12px;padding:14px 16px;margin:-4px 0 16px}
.addgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px 16px}
.addgrid label{display:flex;flex-direction:column;font-size:12px;color:var(--sub);gap:4px}
.addgrid input{font-size:13px;padding:var(--s-3) var(--s-4);border:1px solid #cfe0f4;border-radius:8px;background:var(--pure);color:#1f2733}
.addgrid input:focus{outline:none;border-color:#2f6fed;box-shadow:0 0 0 2px #dbe7ff}
.addgrid select.combosel{width:100%;font-size:13px;padding:var(--s-3) var(--s-4);border:1px solid #cfe0f4;border-radius:8px;background:var(--pure);color:#1f2733}
.addgrid select.combosel:focus{outline:none;border-color:#2f6fed;box-shadow:0 0 0 2px #dbe7ff}
.comboback{align-self:flex-start;font-size:11px;padding:3px var(--s-4);border:1px solid #cfe0f4;border-radius:6px;background:#eef5fe;color:var(--sub);cursor:pointer}
.comboback:hover{border-color:#2f6fed;color:var(--blue-fg)}
.addbtns{display:flex;align-items:center;gap:12px;margin-top:12px;flex-wrap:wrap}
.adderr{color:var(--red-fg);font-size:12px}
.addhint{color:#8a93a3;font-size:12px}
.manualtag{cursor:pointer;color:var(--red-fg);font-weight:600;border:1px solid #f3c6c1;background:#fdeceb;border-radius:6px;padding:1px 6px;white-space:nowrap}
.manualtag:hover{background:#f9d7d3}
.rowdel{cursor:pointer;color:var(--red-fg);font-weight:600;border:1px solid #f3c6c1;background:#fdeceb;border-radius:6px;padding:1px 6px;white-space:nowrap;display:inline-block}
.rowdel:hover{background:#f9d7d3}
.rowsave{cursor:pointer;color:var(--blue-fg);font-weight:600;border:1px solid #d6e2ff;background:#eef3ff;border-radius:6px;padding:1px 6px;white-space:nowrap;display:inline-block;font-size:12px;margin-left:4px}
.rowsave:hover{background:#dbe7ff}
.rowsave:disabled{opacity:.5;cursor:wait}
.cntbadge.warn{background:#fff4e5!important;color:var(--red-fg)!important;border-color:#f0b66b}
.nlwarn{font-size:12px;color:var(--red-fg);margin-left:auto;white-space:nowrap}
@media(max-width:980px){.addgrid{grid-template-columns:1fr 1fr}}
@media(max-width:980px){.kpis{grid-template-columns:repeat(3,1fr)}.grid2{grid-template-columns:1fr}}
/* 「各部门总负责人」标签行（.mgrbar / .mgrbarlabel / .mgrchip / .general-chip）2026-09-16 整体下线，
   内容与功能已并入左侧边栏「售后调试部门」下的部门子按钮（见 .sb-subbtn 的 .sb-sub-o / .locked） */
/* 电话簿：每人独立保存按钮 */
.phsave{padding:3px 10px;border:1px solid #d6e2ff;background:#eef3ff;color:var(--blue-fg);border-radius:6px;font-size:12px;cursor:pointer;margin-left:6px;white-space:nowrap}
.phsave:hover{background:#dbe7ff}
.phsave:disabled{opacity:.5;cursor:wait}
.loginbanner{background:#1f3a8a;color:var(--pure);font-size:13px;padding:var(--s-4) 14px;border-radius:10px;margin:0 0 16px}
.loginbanner b{color:#ffe08a}
.modal{position:fixed;inset:0;background:rgba(15,23,42,.45);display:none;align-items:center;justify-content:center;z-index:70}
/* ===== 使用手册浮层（第七十五轮）=====
   层级选 9990：需高于普通弹窗(70~960)，低于 9997~10000 那几个全局兜底层
   （它们是登录闸门/强制绑邮箱这类"必须压住一切"的层）。 */
.manual-ov{position:fixed;inset:0;background:rgba(15,23,42,.55);z-index:9990;
  display:flex;align-items:center;justify-content:center;padding:18px;box-sizing:border-box}
.manual-panel{background:var(--pure);border-radius:14px;width:min(1080px,100%);height:min(92vh,100%);
  display:flex;flex-direction:column;overflow:hidden;box-shadow:0 18px 60px rgba(0,0,0,.35)}
.manual-bar{flex:none;display:flex;align-items:center;gap:12px;padding:10px 14px;
  border-bottom:1px solid #e3e7ee;background:#f7f9fc}
.manual-title{font-size:15px;font-weight:800;color:#1c2430}
.manual-hint{font-size:12px;color:var(--muted);flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.manual-acts{flex:none;display:flex;gap:8px}
.manual-btn{font-size:12.5px;padding:6px var(--s-6);border-radius:8px;cursor:pointer;
  border:1px solid #d6dce6;background:var(--pure);color:#3c4757;text-decoration:none;
  font-family:inherit;line-height:1.5}
.manual-btn:hover{background:#eef3fb;border-color:#b9c8e0}
.manual-btn.primary{background:#2f5fd0;border-color:#2f5fd0;color:var(--pure);font-weight:700}
.manual-btn.primary:hover{background:#2851b8}
.manual-frame-wrap{flex:1;min-height:0;background:#eef1f6}
.manual-frame{width:100%;height:100%;border:0;display:block;background:var(--pure)}
/* 手机端：手册本来就是窄版阅读，浮层改为整屏、标题栏压缩为两行 */
@media(max-width:640px){
  .manual-ov{padding:0}
  .manual-panel{width:100%;height:100%;border-radius:0}
  .manual-bar{flex-wrap:wrap;padding:8px 10px;gap:6px}
  .manual-hint{display:none}
  .manual-acts{margin-left:auto}
}
.modalbox{background:var(--pure);border-radius:14px;padding:22px 24px;width:380px;max-width:92vw;box-shadow:0 12px 40px rgba(0,0,0,.25);max-height:90vh;overflow-y:auto}
.modaltitle{font-size:16px;font-weight:700;margin-bottom:14px;display:flex;justify-content:space-between;align-items:center}
.modal .x{cursor:pointer;color:var(--muted);font-size:15px;padding:2px 6px;border-radius:6px}
.modal .x:hover{background:#e9f1fb}
.modalfield{display:flex;flex-direction:column;gap:var(--s-2);margin-bottom:12px;font-size:12px;color:var(--sub)}
.modalfield select,.modalfield input{font-size:13px;padding:8px 10px;border:1px solid #cfe0f4;border-radius:8px;background:var(--pure);color:#1f2733}
.modalfield select:focus,.modalfield input:focus{outline:none;border-color:#2f6fed;box-shadow:0 0 0 2px #dbe7ff}
.lghint{font-weight:600;color:#1f3a8a}
.modalbtns{display:flex;gap:10px;margin-top:6px}
.loginerr{color:var(--red-fg);font-size:12px;min-height:16px;margin-top:8px}
.mgredit{margin-top:14px;border-top:1px solid #eef0f4;padding-top:10px}
.mgredit summary{cursor:pointer;font-size:12px;color:var(--muted)}
.mgreditbox{display:flex;flex-direction:column;gap:8px;margin:10px 0}
.mgrow{display:flex;align-items:center;gap:10px;font-size:12px}
.mgrow span{width:110px;color:var(--sub)}
.mgrow input{flex:1;font-size:13px;padding:6px var(--s-4);border:1px solid #cfe0f4;border-radius:8px}
/* ── 负责人角色矩阵（D317）─────────────────────────────────────────────
   设计取舍：这是「一行一个部门、三个角色并列」的表，天然是宽表。
   ⛔ 窄屏不缩放字号（D315 的教训：816px 的表压到 340px 后字号只剩 5–6px，读不了），
      改为**保原宽度 + 容器横向滚动**：.hm-body 设 overflow:auto，.hm-tbl 设 min-width。
   ⛔ 单元格不加 nowrap + 定宽（§174 的教训：那会越框叠字）；只有部门列用 nowrap。
   ⚠⚠ 颜色一律走令牌，**不写死 hex**：本页有屏显深色主题（L/D 两层），
      第一版这里写死 #22304a / #eef4fc / #fff，在深色下变成「深字配深底」，
      截图实测几乎读不出（这就是为什么必须真浏览器验收，jsdom 量不出对比度）。
      语义色（橙/红）只准用 -fg 变体做文字、用 rgb 三元组做面与边。 */
.hm-box{width:min(1000px,96vw)}
.hm-body{margin:10px 0;max-height:44vh;overflow:auto;border:1px solid var(--line);border-radius:var(--r-2-5);-webkit-overflow-scrolling:touch;background:var(--card)}
.hm-tbl{width:100%;min-width:640px;border-collapse:collapse;font-size:12.5px}
.hm-tbl thead th{position:sticky;top:0;z-index:2;background:var(--card-hd);color:var(--ink-head);font-weight:700;font-size:12px;padding:var(--s-4) var(--s-5);text-align:left;border-bottom:1px solid var(--line);white-space:nowrap}
.hm-tbl td{padding:var(--s-4) var(--s-5);border-bottom:1px solid var(--line);vertical-align:top;color:var(--ink)}
.hm-tbl tbody tr:hover td{background:var(--row-hover)}
.hm-dept{font-weight:600;color:var(--ink-head);white-space:nowrap}
.hm-cell{display:flex;flex-direction:column;gap:var(--s-1);min-width:160px}
.hm-nm{font-weight:600;color:var(--ink);word-break:break-all}
.hm-meta{font-size:11px;color:var(--sub);line-height:1.5}
.hm-tag{display:inline-block;font-size:11px;line-height:1.6;padding:0 var(--s-3);border-radius:var(--r-pill);margin-left:var(--s-1);font-weight:600}
.hm-tag.act{background:rgba(var(--orange-rgb),.12);color:var(--orange-fg);border:1px solid rgba(var(--orange-rgb),.28)}
.hm-tag.vac{background:rgba(var(--blue-rgb),.06);color:var(--sub);border:1px solid var(--line)}
.hm-tag.drift{background:rgba(var(--red-rgb),.12);color:var(--red-fg);border:1px solid rgba(var(--red-rgb),.28)}
.hm-ops{display:flex;gap:var(--s-3);flex-wrap:wrap;margin-top:var(--s-0-5)}
.hm-mini{font:inherit;font-size:11px;padding:var(--s-0-5) var(--s-4);border-radius:var(--r-1-5);border:1px solid rgba(var(--blue-rgb),.28);background:rgba(var(--blue-rgb),.10);color:var(--blue-fg);cursor:pointer}
.hm-mini:hover{background:rgba(var(--blue-rgb),.18);border-color:var(--blue)}
.hm-mini.end{color:var(--red-fg);border-color:rgba(var(--red-rgb),.32);background:rgba(var(--red-rgb),.08)}
.hm-mini.end:hover{background:rgba(var(--red-rgb),.16)}
/* ⚠ 这里**不能**用 var(--pure) 做按钮面：它在深色层仍是 #FFFFFF（"最高对比文字"那个语义），
   白底配 #FF8FA9 的文字实测只有 2.15:1 —— 截图上看就是一块刺眼的白斑。
   面一律用「语义色的低透明度 tint」，字用对应的 -fg 变体，深浅两层都能过 4.5。 */
.hm-form{display:flex;flex-direction:column;gap:var(--s-1-5);margin-top:var(--s-1);padding:var(--s-4);border:1px dashed var(--stroke);border-radius:var(--r-2);background:var(--row-alt)}
.hm-form input{font-size:12px;padding:var(--s-1) var(--s-4);border:1px solid var(--stroke);border-radius:var(--r-1-5);width:100%;box-sizing:border-box;background:var(--pure);color:var(--ink)}
.hm-form .hm-err{font-size:11px;color:var(--red-fg);min-height:0}
.hm-hint{font-size:12px;color:var(--sub);margin-top:var(--s-3);line-height:1.6}
.hm-empty{padding:var(--s-8);text-align:center;color:var(--sub);font-size:12.5px}
.hm-hist{margin-top:var(--s-6);border-top:1px solid var(--line);padding-top:var(--s-5)}
.hm-hist-h{font-size:12px;font-weight:700;color:var(--ink-head);margin-bottom:var(--s-3)}
.hm-hist-list{max-height:104px;overflow:auto;font-size:12px;color:var(--sub);line-height:1.75}
.hm-hist-list b{color:var(--ink)}
/* ── 统一授权工作台（D318）──────────────────────────────────────────────
   沿用既有令牌与组件，⛔ 不引入新视觉风格。
   ⚠ 面用 var(--card) / rgba(var(--x-rgb),.08~.18)，字用 var(--ink) / var(--sub) / --*-fg；
     ⛔ 不用「纯白面令牌」（--pure）做按钮面（深色屏显层它是 #FFFFFF，白底配浅字只有 2.15:1）。
   ⚠ 字号只用 11 / 12 / 13 / 14px 整档；单元格不 nowrap + 定宽；
     宽列表用 min-width + 容器 overflow:auto，⛔ 不缩字号。
   ⚠ 间距只用 var(--s-*)、圆角 var(--r-*)。 */
.gw-box{width:min(1240px,96vw)}
.gw-3{display:grid;grid-template-columns:1fr 1.15fr 1.25fr;gap:var(--s-4);align-items:start}
.gw-col{background:var(--card);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-4);min-width:0;box-sizing:border-box}
.gw-h{font-size:13px;font-weight:700;color:var(--ink);margin-bottom:var(--s-3);display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.gw-note{font-size:11px;color:var(--sub);line-height:1.6;margin-bottom:var(--s-3)}
.gw-row{display:flex;gap:var(--s-2);align-items:center;flex-wrap:wrap;margin-bottom:var(--s-3)}
.gw-in,.gw-ta,.gw-sel{font:inherit;font-size:12px;padding:var(--s-2) var(--s-3);border:1px solid var(--stroke);border-radius:var(--r-1-5);background:var(--card);color:var(--ink);box-sizing:border-box;width:100%}
.gw-ta{resize:vertical;min-height:52px}
.gw-in:focus,.gw-ta:focus,.gw-sel:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 2px rgba(var(--blue-rgb),.25)}
.gw-btn{font:inherit;font-size:12px;padding:var(--s-2) var(--s-4);border:1px solid rgba(var(--blue-rgb),.28);border-radius:var(--r-1-5);background:rgba(var(--blue-rgb),.10);color:var(--blue-fg);cursor:pointer}
.gw-btn:hover{background:rgba(var(--blue-rgb),.18)}
.gw-btn.ghost{background:transparent;color:var(--sub);border-color:var(--line)}
.gw-btn.ghost:hover{background:rgba(var(--blue-rgb),.08);color:var(--ink)}
.gw-btn.primary{background:rgba(var(--blue-rgb),.18);border-color:rgba(var(--blue-rgb),.45);color:var(--blue-fg);font-weight:700}
.gw-btn.warn{background:rgba(var(--orange-rgb),.10);border-color:rgba(var(--orange-rgb),.30);color:var(--orange-fg)}
.gw-btn.danger{background:rgba(var(--red-rgb),.10);border-color:rgba(var(--red-rgb),.30);color:var(--red-fg)}
.gw-btn:disabled{opacity:.45;cursor:not-allowed}
.gw-scroll{overflow:auto;-webkit-overflow-scrolling:touch}
.gw-subj{max-height:230px;border:1px solid var(--line);border-radius:var(--r-1-5)}
.gw-subj-it{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--line);font-size:12px;color:var(--ink);cursor:pointer}
.gw-subj-it:last-child{border-bottom:none}
.gw-subj-it:hover{background:var(--row-hover)}
.gw-subj-it.no-rost{color:var(--disabled);cursor:not-allowed}
.gw-subj-no{font-size:11px;color:var(--sub);font-family:ui-monospace,Menlo,Consolas,monospace;min-width:72px;word-break:break-all}
.gw-subj-nm{font-size:12px;color:var(--ink);word-break:break-all}
.gw-subj-dp{font-size:11px;color:var(--sub);margin-left:auto;word-break:break-all}
.gw-ck{width:14px;height:14px;flex:none;accent-color:var(--blue);cursor:pointer}
.gw-tabs{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-bottom:var(--s-3)}
.gw-tab{display:flex;align-items:center;gap:var(--s-1);padding:var(--s-1) var(--s-2);border:1px solid var(--line);border-radius:var(--r-1-5);background:transparent}
.gw-tab.on{border-color:rgba(var(--blue-rgb),.45);background:rgba(var(--blue-rgb),.10)}
.gw-tab-go{font:inherit;font-size:12px;border:none;background:transparent;color:var(--ink);cursor:pointer;padding:0 var(--s-1)}
.gw-tab.on .gw-tab-go{color:var(--blue-fg);font-weight:700}
.gw-scope{max-height:250px;border:1px solid var(--line);border-radius:var(--r-1-5)}
.gw-tbl{width:100%;min-width:320px;border-collapse:collapse;font-size:12px}
.gw-tbl th{position:sticky;top:0;z-index:1;background:var(--card-hd);color:var(--sub);font-size:11px;font-weight:600;text-align:left;padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--line)}
.gw-tbl td{padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--line);color:var(--ink);vertical-align:middle;word-break:break-word}
.gw-tbl tbody tr:hover td{background:var(--row-hover)}
.gw-tbl td.gw-c{text-align:center;width:64px}
.gw-mini{font:inherit;font-size:11px;padding:var(--s-1) var(--s-2);border:1px solid var(--line);border-radius:var(--r-1);background:var(--card);color:var(--sub);cursor:pointer}
.gw-mini:hover{background:rgba(var(--blue-rgb),.10);color:var(--ink)}
.gw-prev{max-height:190px;border:1px solid var(--line);border-radius:var(--r-1-5);background:var(--card)}
.gw-v{padding:var(--s-2) var(--s-3);border-bottom:1px solid var(--line);font-size:11px;line-height:1.6}
.gw-v:last-child{border-bottom:none}
.gw-v .gw-vh{font-size:12px;font-weight:700;color:inherit}
.gw-v .gw-vm{font-size:11px;color:inherit;opacity:.88;white-space:pre-line}
.gw-v.ok{background:rgba(var(--green-rgb),.10);color:var(--green-fg)}
.gw-v.dup{background:var(--row-alt);color:var(--sub)}
.gw-v.warn{background:rgba(var(--orange-rgb),.12);color:var(--orange-fg)}
.gw-v.hard{background:rgba(var(--red-rgb),.12);color:var(--red-fg)}
.gw-diff{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-2);max-height:150px;overflow:auto}
.gw-diff-c{background:var(--row-alt);border:1px solid var(--line);border-radius:var(--r-1-5);padding:var(--s-2);min-width:110px}
.gw-diff-h{font-size:11px;font-weight:700;color:var(--ink);margin-bottom:var(--s-2)}
.gw-diff-it{font-size:11px;line-height:1.6;color:var(--sub);word-break:break-word;border-bottom:1px solid var(--line);padding:var(--s-1) 0}
.gw-diff-it:last-child{border-bottom:none}
.gw-diff-it.add{color:var(--green-fg)}
.gw-diff-it.rm{color:var(--red-fg)}
.gw-result{border:1px solid var(--line);border-radius:var(--r-1-5);padding:var(--s-3);background:var(--card);font-size:11px;line-height:1.7;color:var(--sub);white-space:pre-line;max-height:160px;overflow:auto}
/* ⚠ 三栏在窄屏上下堆叠；宽表 / 宽列表保原宽 + 容器横向滚动，⛔ 不缩字号 */
@media (max-width:900px){
  .gw-3{grid-template-columns:1fr}
  .gw-box{width:96vw}
}
/* 行内编辑单元格 */
.ecell-row{cursor:text;outline:none;display:inline-block;text-align:left;min-width:40px;padding:1px 4px;border-radius:var(--r-1);transition:box-shadow .15s;border:1px solid transparent;max-width:100%;white-space:normal;overflow-wrap:anywhere;vertical-align:middle}
.ecell-row:focus{box-shadow:0 0 0 2px var(--blue);background:var(--pure);border-color:var(--blue)}
.ecell-row.ro{cursor:not-allowed;opacity:.6;background:transparent!important}
.stage-sel{font:inherit;font-size:12px;padding:2px 3px;border:1px solid #cbd5e1;border-radius:6px;background:var(--pure);cursor:pointer;width:100%;max-width:100%;min-width:0;box-sizing:border-box}
.stage-sel:focus{border-color:#2f6fed;box-shadow:0 0 0 2px rgba(47,111,237,.25);outline:none}
/* D376 负责人下拉：与 .stage-sel 同款尺寸 / 边框 / 背景。
   ⛔ 不加「定宽 + white-space:nowrap」组合（越框叠字的老坑），沿用弹性宽度 + 正常换行。 */
.mgr-sel{font:inherit;font-size:12px;padding:2px 3px;border:1px solid var(--stroke);border-radius:6px;background:var(--pure);cursor:pointer;width:100%;max-width:100%;min-width:0;box-sizing:border-box;white-space:normal;overflow-wrap:anywhere}
.mgr-sel:focus{border-color:#2f6fed;box-shadow:0 0 0 2px rgba(47,111,237,.25);outline:none}
/* 历史姓名对不上工号：所有角色（含只读态）都能看到的弱化橙色小标记 */
.mgr-miss{display:inline-block;margin-left:4px;font-size:10px;line-height:1.4;color:var(--orange-fg);white-space:normal;overflow-wrap:anywhere;vertical-align:middle}
/* 新增项目面板：无任命权限的角色（sub 等）看到的可见说明，⛔ 不用 nowrap + 定宽 */
.mgr-add-hint{display:block;margin-top:4px;font-size:11px;line-height:1.4;color:var(--orange-fg);white-space:normal;overflow-wrap:anywhere}
.ecell-row.ro:focus{box-shadow:none;border-color:transparent}
/* 姓名删除按钮 */
.nlrow{display:flex;align-items:center;gap:4px}
.nlrole{flex-shrink:0;width:52px;padding:1px 2px;border:1px solid #cdd6e4;border-radius:var(--r-1);font-size:11px;color:#4a5568;background:var(--pure);cursor:pointer;outline:none}
.nlrole:focus{border-color:#3b82f6;box-shadow:0 0 0 2px rgba(59,130,246,.15)}
/* 内援的「来源部门」下拉：部门名通常 3~5 个汉字，比「组长 / 成都」宽，单独放宽。
   ⚠ 用 .nlrole.nldept 两个类提权，才压得过手机端 @media 里的 .nlrole{width:66px}。 */
.nlrole.nldept{width:76px}
.nldel{flex-shrink:0;width:20px;height:20px;border:1px solid #e0594f;color:#e0594f;background:var(--pure);border-radius:50%;font-size:11px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1;user-select:none;transition:all .15s;opacity:0;pointer-events:none}
.nlrow:focus-within .nldel{opacity:1;pointer-events:auto}
.nldel:hover{background:#e0594f;color:var(--pure)}
/* 「⇄ 调往」按钮（2026-10-02 用户定夺）：正式工 / 内援在编辑态每人一个，点开选同部门的目标行，
   一键原子移动（免「先删 A 再录 B」）。与 .nldel 共用「悬停才现身」的规矩（同一批理由）。 */
.nlmove{flex-shrink:0;width:20px;height:20px;border:1px solid #3f7fd6;color:var(--blue-fg);background:var(--pure);border-radius:50%;font-size:11px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1;user-select:none;transition:all .15s;opacity:0;pointer-events:none}
.nlrow:focus-within .nlmove{opacity:1;pointer-events:auto}
.nlmove:hover{background:#3f7fd6;color:var(--pure)}
/* 「⇄ 调往」目标行浮层：fixed 定位挂在按钮下方，点外部 / Esc 收起
   ⚠ 2026-10-05 第一百一十三轮：改成**弹性列**布局（hd / 筛选条 / 列表 / ft 四段），
     列表段自己滚、筛选条与页脚常驻。原先整个浮层 overflow:auto，列表一长就把
     筛选条一起顶出可视区 —— 手机端尤其明显（浮层贴顶后更放不下）。 */
.nlmove-pop{position:fixed;z-index:960;min-width:240px;max-width:320px;max-height:min(70vh,360px);display:flex;flex-direction:column;overflow:hidden;background:var(--pure);border:1px solid #d6deea;border-radius:10px;box-shadow:0 10px 28px rgba(16,42,80,.18);padding:6px}
.nlmove-hd{font-size:12px;font-weight:700;color:#28354a;padding:4px 6px 8px;flex:none}
.nlmove-it{display:flex;width:100%;align-items:center;justify-content:space-between;gap:10px;border:none;background:none;text-align:left;padding:var(--s-3) 8px;border-radius:var(--r-2);cursor:pointer;font-size:12px;color:#28354a;font-family:inherit}
.nlmove-it:hover{background:#eef4fd}
/* ---- 筛选条（车间 / 负责人 / 设备）：2026-10-05 用户要求「增加搜索框方便筛选」 ----
   ⚠ 为什么是三个输入框而不是一个大关键词框：这三个维度都是**受控枚举**
     （车间 76 种、负责人 32 种、设备 139 种，见 state.D 去重统计），
     同一个部门下候选行往往只有几行，但用户记不清是「C11-2F」还是「C11-2F-GL-FX」，
     关键词模糊匹配会漏；分维度输入 = 每个维度各自模糊匹配，容错高且不用背全量清单。
   ⚠ 不做 <datalist> 下拉：手机端原生下拉在 fixed 浮层里会被视口裁掉，
     反而比手输更难用（这是 fixed-overlay 系列的通病）。 */
.nlmove-filter{flex:none;display:flex;flex-wrap:wrap;gap:4px;padding:0 6px 6px}
.nlmove-filter input{flex:1 1 84px;min-width:0;width:84px;height:24px;padding:0 var(--s-3);font-size:12px;font-family:inherit;color:#28354a;background:#f7f9fc;border:1px solid #dde5f0;border-radius:6px;-webkit-appearance:none;appearance:none}
.nlmove-filter input::placeholder{color:#9aa4b4}
.nlmove-filter input:focus{outline:none;background:var(--pure);border-color:#3f7fd6;box-shadow:0 0 0 2px #e3edfb}
.nlmove-x{flex:none;height:24px;padding:0 8px;font-size:11px;font-family:inherit;color:var(--sub);background:#f1f4f9;border:1px solid #e2e8f2;border-radius:6px;cursor:pointer}
.nlmove-x:hover{background:#e6ebf3}
.nlmove-cnt{flex:none;font-size:12px;color:var(--muted);padding:0 6px 4px}
.nlmove-cnt b{color:var(--blue-fg);font-weight:700}
.nlmove-list{flex:1 1 auto;min-height:64px;overflow:auto;overscroll-behavior:contain}
/* ⚠⚠ 2026-10-05 第一百一十二轮（用户报「手机端的调往无法使用」）：
   原来 `white-space:nowrap` + `ellipsis` ⇒ 加上车间前缀后一行放不下就被截断，
   而截掉的恰恰可能是车间或线体 —— 用户看到两条长得一样的「MCH / 7AM / OMS / 5X…」，
   无法判断要挪到哪一行，等于功能不可用。
   改成**允许换行**（不再 nowrap），行高由内容决定、浮层本身有 max-height + overflow:auto 兜底。
   ⚠ 真正的根因是行定位串缺车间维度（已由 rowLocLabel() 补上）；这里只是让它不被二次截断。
   ⚠ min-height 保证**车间那一行**至少有一行的高度（两行时不会挤成两行都半截）。 */
.nlmove-it .t{min-width:0;white-space:normal;overflow-wrap:anywhere;line-height:1.45}
.nlmove-it .t .m{display:block;font-style:normal;font-size:12px;color:var(--muted);margin-top:1px}
.nlmove-it .n{flex:none;font-size:11px;color:var(--muted);background:#f1f4f9;border-radius:6px;padding:2px var(--s-3)}
.nlmove-ft{flex:none;font-size:12px;line-height:1.4;color:var(--muted);padding:6px 6px 2px;border-top:1px solid #eef1f6;margin-top:4px}
.nlmove-none{padding:14px 8px;text-align:center;font-size:12px;color:var(--muted)}
/* ===== 批量调往面板（D226，2026-10-26）=====
   「⇄ 调往」是逐人的；用户报「一个一个人调比较麻烦，能不能像支援那样一次调多人」。
   本面板＝「① 勾人 → ② 选目标行 → 提交」，一次把 N 个人搬到同部门的另一个行。
   ⚠ 不复用 .nlmove-pop：那个浮层被 check-nlmove-filter 的静态判据钉着结构
     （hd / 筛选 / 列表 / ft 四段），多塞两段会同时动到它和它的守卫。
   与 .nlmove-pop 保持同一套视觉语言（白底 / 12px 圆角 / 阴影 / 蓝色主色），
   这样用户不会觉得是两个不相干的功能。 */
.rmb-pop{position:fixed;z-index:966;display:flex;flex-direction:column;
  width:min(440px,calc(100vw - 16px));max-height:min(82vh,560px);
  background:var(--pure);border:1px solid #d6deea;border-radius:12px;
  box-shadow:0 12px 32px rgba(28,45,72,.22);overflow:hidden}
.rmb-hd{flex:none;display:flex;align-items:flex-start;gap:8px;padding:10px 10px 8px 12px;
  border-bottom:1px solid #eef1f6;background:#f7faff}
.rmb-hd .t{font-size:13px;font-weight:800;color:#28354a;line-height:1.35}
.rmb-hd .s{font-size:12px;color:var(--muted);margin-top:2px;line-height:1.45}
.rmb-x{flex:none;margin-left:auto;width:26px;height:26px;border:1px solid #e2e8f2;background:var(--pure);
  border-radius:8px;color:var(--sub);font-size:12px;cursor:pointer}
.rmb-x:hover{background:#eef4fd;border-color:#b9d4f5}
.rmb-body{flex:1 1 auto;min-height:0;overflow:auto;overscroll-behavior:contain;padding:var(--s-4) 10px 4px}
.rmb-sec{margin-bottom:var(--s-5)}
.rmb-sechd{display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;color:#28354a;margin-bottom:var(--s-2)}
.rmb-sechd b{font-weight:700}
.rmb-cnt{font-size:11px;color:var(--blue-fg);font-weight:700}
.rmb-mini{height:22px;padding:0 8px;font-size:11px;font-family:inherit;border:1px solid #e2e8f2;
  background:#f1f4f9;border-radius:6px;color:var(--sub);cursor:pointer}
.rmb-mini:hover{background:#e6ebf3}
.rmb-q{width:100%;height:30px;padding:0 8px;margin-bottom:6px;font-size:12px;font-family:inherit;
  color:#28354a;background:#f7f9fc;border:1px solid #dde5f0;border-radius:8px}
.rmb-q::placeholder{color:#9aa4b4}
.rmb-q:focus{outline:none;background:var(--pure);border-color:#3f7fd6;box-shadow:0 0 0 2px #e3edfb}
/* ① 段的「用工类型」筛选芯片（D227）。⛔ 不与姓名搜索合并成一个框：
   类型是**枚举**、搜索是**任意串**，混在一起用户不知道能填什么。 */
.rmb-cats{display:flex;gap:var(--s-2);flex-wrap:wrap;margin-bottom:6px}
.rmb-cat{height:22px;padding:0 var(--s-4);font-size:11px;font-family:inherit;border:1px solid #dde5f0;
  background:#f7f9fc;border-radius:var(--r-3);color:var(--sub);cursor:pointer;white-space:nowrap}
.rmb-cat:hover{background:#eef4fd;border-color:#c3d8f2}
.rmb-cat.on{background:#3f7fd6;border-color:#3f7fd6;color:var(--pure);font-weight:700}
.rmb-cat .c{margin-left:4px;font-weight:700;opacity:.75}
.rmb-cat.on .c{opacity:.9}
.rmb-picks,.rmb-targets{max-height:186px;overflow:auto;overscroll-behavior:contain;
  border:1px solid #eef1f6;border-radius:8px}
.rmb-pick{display:flex;align-items:flex-start;gap:var(--s-3);width:100%;padding:6px 8px;border:none;
  border-bottom:1px solid #f4f7fb;background:none;text-align:left;cursor:pointer;font-family:inherit}
.rmb-pick:last-child{border-bottom:none}
.rmb-pick:hover{background:#f4f9ff}
.rmb-pick.on{background:#eaf2fe}
.rmb-ck{flex:none;width:16px;height:16px;margin-top:1px;border:1.5px solid #b9c6d8;border-radius:4px;
  background:var(--pure);color:transparent;font-size:11px;line-height:13px;text-align:center;font-weight:700}
.rmb-pick.on .rmb-ck{background:#3f7fd6;border-color:#3f7fd6;color:var(--pure)}
.rmb-nm{flex:1 1 auto;min-width:0;font-size:12.5px;color:#28354a;line-height:1.35;overflow-wrap:anywhere}
.rmb-nm .no{display:inline-block;margin-left:var(--s-2);font-style:normal;font-size:11px;color:var(--muted)}
.rmb-where{flex:0 0 100%;font-size:12px;color:var(--muted);line-height:1.35;margin-top:1px;
  overflow-wrap:anywhere}
.rmb-pick .rmb-mid{flex:1 1 auto;min-width:0;display:flex;flex-wrap:wrap}
.rmb-tg{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;width:100%;
  border:none;background:none;text-align:left;padding:var(--s-3) 8px;border-radius:var(--r-2);
  cursor:pointer;font-family:inherit;border-bottom:1px solid #f4f7fb}
.rmb-tg:last-child{border-bottom:none}
.rmb-tg:hover{background:#f4f9ff}
.rmb-tg.on{background:#eaf2fe;box-shadow:inset 0 0 0 1px #9dc0ee}
/* D227：目标行前面也放一个小方块（用户要求「跟姓名一样」）。
   ⚠ 行为仍是**单选** —— 点另一行会自动换过去（checkbox 只是形态，
     多选会引出"每行各分几个人"的口径，用户没提，不做） */
.rmb-tg .rmb-ck{margin-top:2px}
.rmb-tg.on .rmb-ck{background:#3f7fd6;border-color:#3f7fd6;color:var(--pure)}
.rmb-tg .t{flex:1 1 auto;min-width:0;font-size:12.5px;color:#28354a;line-height:1.45;white-space:normal;overflow-wrap:anywhere}
.rmb-tg .t .m{display:block;font-style:normal;font-size:12px;color:var(--muted);margin-top:1px}
.rmb-tg .n{flex:none;align-self:center;font-size:11px;color:var(--muted);background:#f1f4f9;border-radius:6px;padding:2px var(--s-3)}
.rmb-ft{flex:none;display:flex;align-items:center;gap:8px;padding:8px 10px;
  border-top:1px solid #eef1f6;background:#fbfdff}
.rmb-sum{flex:1 1 auto;min-width:0;font-size:12px;color:var(--muted);line-height:1.4}
.rmb-sum b{color:var(--blue-fg)}
.rmb-go{flex:none;height:32px;padding:0 14px;font-size:12.5px;font-weight:700;font-family:inherit;
  color:var(--pure);background:#2f6fed;border:none;border-radius:8px;cursor:pointer}
.rmb-go:hover{background:#255fcb}
.rmb-go:disabled{background:#c6d3e4;cursor:not-allowed}
/* 「⇄ 批量」：长在名单浮层的标题行里（.nlhead-title 内），比逐人的 ⇄ 小一号，
   因为它属于「这一整行」而不是某个人。 */
.nlbatchmv{flex:none;margin-left:6px;height:19px;padding:0 6px;font-size:11px;line-height:17px;
  font-family:inherit;color:var(--blue-fg);background:#f2f7ff;border:1px solid #b9d4f5;border-radius:var(--r-2);
  cursor:pointer;white-space:nowrap;vertical-align:middle}
.nlbatchmv:hover{background:#e3edfb;border-color:#9dc0ee}
/* ⚠⚠ 主题覆盖（D226 实测发现）：全局深色主题有一条
   `body:not(.view-report) input[type=text]{background-color:var(--bg-2);…}`（specificity 0,2,1），
   它比 `.nlmove-filter input`(0,1,1) / `.rmb-q`(0,1,0) 都高 ⇒ **白底浮层里的输入框会变成深色块**，
   跟浮层本身的白底打架（手机截图上就是三块深色空洞，placeholder 几乎看不清）。
   浮层是一块**独立的白底表面**，里面的控件必须跟着浮层走 ⇒ 这里显式抬回浅色。
   ⚠ 顺带把逐人调往的 .nlmove-pop 一起修：它一直有同样的毛病，只是没人提。 */
body:not(.view-report) .nlmove-pop .nlmove-filter input,
body:not(.view-report) .rmb-pop .nlmove-filter input,
body:not(.view-report) .rmb-pop .rmb-q{background:#f7f9fc;color:#28354a;border-color:#dde5f0}
body:not(.view-report) .nlmove-pop .nlmove-filter input::placeholder,
body:not(.view-report) .rmb-pop .nlmove-filter input::placeholder,
body:not(.view-report) .rmb-pop .rmb-q::placeholder{color:#7f8ba0}
/* 外协 → 正式工「转正」按钮（2026-09-22 第二十一轮）。
   ⚠ 与 .nldel 共用「悬停才现身」的规矩：浮层本来就窄（姓名 88px + 后缀 + 两个按钮），
     转正按钮**常显**会把姓名框挤到没法用。同理用 :focus-within 触发，
     这样「鼠标移到这一行 / 键盘 Tab 进来」都会亮出来，触屏端另有常显规则（见 @media）。
   ⚠ 文案两字「转正」比圆形 ✕ 宽，单独给 padding，不做成圆形。 */
.nltf{flex-shrink:0;padding:0 6px;height:20px;border:1px solid #2f7d4f;color:#2f7d4f;background:var(--pure);border-radius:10px;font-size:11px;line-height:18px;cursor:pointer;user-select:none;transition:all .15s;opacity:0;pointer-events:none;white-space:nowrap}
.nlrow:focus-within .nltf{opacity:1;pointer-events:auto}
.nltf:hover{background:#2f7d4f;color:var(--pure)}
/* 强制改密弹窗 */
.pwchange-overlay{position:fixed;inset:0;background:rgba(15,23,42,.55);display:flex;align-items:center;justify-content:center;z-index:100}
.pwchange-box{background:var(--pure);border-radius:14px;padding:24px 28px;width:380px;max-width:92vw;box-shadow:0 16px 48px rgba(0,0,0,.3)}
.pwchange-box h3{margin:0 0 16px;font-size:17px;color:#1f2733}
.pwchange-box .pwfield{display:flex;flex-direction:column;gap:4px;margin-bottom:12px;font-size:12px;color:var(--sub)}
.pwchange-box .pwfield input{font-size:14px;padding:var(--s-4) 12px;border:1px solid #cfe0f4;border-radius:8px}
.pwchange-box .pwfield input:focus{outline:none;border-color:#2f6fed;box-shadow:0 0 0 2px #dbe7ff}
.pwchange-btns{display:flex;gap:10px;margin-top:6px}
.pwchange-err{color:var(--red-fg);font-size:12px;min-height:16px;margin-top:8px}
/* 周五更新提醒横幅 */
.fri-reminder{margin:8px 0 0;padding:var(--s-4) 14px;border-radius:10px;font-size:13.5px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.fri-reminder.warn{background:#fff4e5;border:1px solid #f0b66b;color:#9a5a12}
.fri-reminder.ok{background:#eafaf0;border:1px solid #9ad8b0;color:#1f7a45}
.fri-reminder.info{background:#eef4ff;border:1px solid #b9d2ff;color:#2c5aa8}
.fri-btn{margin-left:4px;border:1px solid #f0b66b;background:var(--pure);color:#9a5a12;border-radius:8px;padding:4px 12px;cursor:pointer;font-size:12px}
.fri-btn:hover{background:#f0b66b;color:var(--pure)}
/* 邮箱设置弹窗 */
.modal-overlay{position:fixed;inset:0;background:rgba(15,23,42,.5);display:flex;align-items:center;justify-content:center;z-index:90}
.modal-box{background:var(--pure);border-radius:14px;padding:22px 24px;box-shadow:0 16px 48px rgba(0,0,0,.28);max-height:90vh;overflow-y:auto}
.modal-title{font-size:16px;font-weight:600;color:#1f2733;margin-bottom:4px}
.modal-sub{font-size:12px;color:var(--muted);margin-bottom:14px}
.email-list{display:flex;flex-direction:column;gap:var(--s-3);max-height:52vh;overflow:auto;margin-bottom:14px}
.email-row{display:flex;align-items:center;gap:10px;font-size:13px}
.email-name{flex:1;color:#33404f;display:flex;align-items:center;gap:6px}
.email-role{font-style:normal;font-size:11px;color:#2c5aa8;background:#eef4ff;border:1px solid #b9d2ff;border-radius:6px;padding:0 6px}
.email-input{flex:1.4;min-width:180px;font-size:13px;padding:6px var(--s-4);border:1px solid #cfe0f4;border-radius:8px}
.email-empty{color:var(--muted);font-size:13px;padding:8px 0}
.modal-btns{display:flex;gap:10px;justify-content:flex-end}
/* 管理员重置密码 */
.pwdadmin-list{display:flex;flex-direction:column;gap:6px;max-height:56vh;overflow:auto;margin-bottom:14px;min-width:460px}
.pwdadmin-row{display:flex;align-items:center;gap:10px;font-size:13px;padding:var(--s-3) 10px;border:1px solid #e7f0fb;border-radius:8px;background:var(--card)}
.pwdadmin-role{font-style:normal;font-size:11px;color:#2c5aa8;background:#eef4ff;border:1px solid #b9d2ff;border-radius:6px;padding:1px var(--s-3);flex-shrink:0}
.pwdadmin-name{flex:1;color:#33404f}
.pwdadmin-status{font-size:12px;flex-shrink:0}
.pwdadmin-status.own{color:#1f7a45}
.pwdadmin-status.def{color:var(--muted)}
.pwdadmin-reset{flex-shrink:0;border:1px solid #e0594f;color:#e0594f;background:var(--pure);border-radius:8px;padding:3px 10px;cursor:pointer;font-size:12px}
.pwdadmin-reset:hover{background:#e0594f;color:var(--pure)}
.pwdadmin-empty{color:var(--muted);font-size:13px;padding:8px 0}
/* D286：授权总览里「名册里有同名的人」的标记。
   ⚠ 用伪元素画，⛔ 不写进单元格文本 —— 姓名列一旦多出字，
     「同名不同工号要分成两行」的判据是按 textContent 取人的，当场失效（D284 的教训）。 */
#permRosterBody td[data-dupn]{cursor:help}
#permRosterBody td[data-dupn]::after{content:" 同名×" attr(data-dupn);font-size:10px;color:var(--orange);white-space:nowrap}
/* D215：重置密码面板内的「员工自评账号」搜索区。
   ⚠ 与上方 .pwdadmin-row 同族，但输入框走 CSS 变量，深色主题靠段末覆盖补齐（见 pwdadmin-sect 那条）。 */
.pwdadmin-sect{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;font-size:12px;font-weight:600;color:#2c5aa8;margin:6px 0 8px;padding-top:12px;border-top:1px dashed #d7e2f0}
.pwdadmin-sect-note{font-weight:400;color:var(--muted);font-size:12px}
.pwd-selfsearch{display:flex;gap:8px;margin-bottom:8px;min-width:460px}
.pwd-selfsearch input{flex:1;min-width:0;border:1px solid #d7e2f0;border-radius:8px;padding:var(--s-3) 10px;font-size:13px;background:var(--pure);color:#33404f;outline:none}
.pwd-selfsearch input:focus{border-color:#7aa7f0}
.pwd-selfsearch .btn{flex-shrink:0}
/* 二级管理员管理 */
.subadmin-card{border:1px solid #d9e8fa;border-radius:10px;padding:12px 14px;margin-bottom:10px;background:var(--card)}
.subadmin-head{font-weight:600;font-size:13.5px;color:#1f2733;margin-bottom:10px;display:flex;align-items:center;gap:8px}
.subadmin-head .tag{font-size:11px;font-weight:500;color:#2c5aa8;background:#eef4ff;border:1px solid #b9d2ff;border-radius:6px;padding:1px var(--s-3)}
.sa-field{display:flex;flex-direction:column;gap:3px;font-size:12px;color:var(--sub);margin-right:14px}
.sa-field input{font-size:13px;padding:6px var(--s-4);border:1px solid #cfe0f4;border-radius:8px;width:150px}
.sa-field input:focus{outline:none;border-color:#2f6fed}
.sa-row{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px;flex-wrap:wrap}
.sa-group-label{font-size:12px;color:var(--sub);font-weight:600;min-width:60px;padding-top:var(--s-2)}
.sa-checkbox{display:flex;flex-wrap:wrap;gap:var(--s-2) 12px;flex:1}
.sa-checkbox label{display:flex;align-items:center;gap:4px;font-size:12px;color:#33404f;cursor:pointer;white-space:nowrap}
.sa-checkbox input{cursor:pointer}
.sa-dept-wrap{display:flex;flex-wrap:wrap;gap:var(--s-2) 12px;flex:1}
.sa-dept-wrap label{display:flex;align-items:center;gap:4px;font-size:12px;color:#33404f;cursor:pointer;white-space:nowrap}
.sa-dept-wrap input{cursor:pointer}
/* 综合部人员管理 */
.general-form{background:var(--card-hd);border:1px solid #d9e8fa;border-radius:10px;padding:14px;margin-bottom:14px}
.general-grid{display:grid;grid-template-columns:1.2fr 1fr 1fr auto;gap:10px 12px;align-items:end}
.general-grid label{display:flex;flex-direction:column;font-size:12px;color:var(--sub);gap:4px}
.general-grid input,.general-grid select{font-size:13px;padding:var(--s-3) var(--s-4);border:1px solid #cfe0f4;border-radius:8px;background:var(--pure);height:36px;box-sizing:border-box}
.general-grid input:focus,.general-grid select:focus{outline:none;border-color:#2f6fed}
.general-summary{font-size:12px;color:var(--sub);margin-bottom:10px;line-height:1.7}
.general-summary b{color:#1f2733}
.general-list{display:flex;flex-direction:column;gap:6px;max-height:48vh;overflow:auto;margin-bottom:12px}
.general-row{display:flex;align-items:center;gap:14px;font-size:13px;padding:10px 14px;border:1px solid #ddeaf9;border-radius:var(--r-2);background:var(--card)}
.general-row:hover{background:var(--row-hover)}
.general-name{font-weight:600;color:#1f2733;width:110px;flex-shrink:0}
.general-pos{font-size:11px;border-radius:6px;padding:3px 8px;background:#eef4ff;border:1px solid #b9d2ff;color:#2c5aa8;white-space:nowrap;margin-left:auto}
.general-region{font-size:11px;border-radius:6px;padding:3px 8px;background:#eafaf0;border:1px solid #9ad8b0;color:#1f7a45;white-space:nowrap}
.general-region.ALL{background:#e9f1fb;border-color:#cfe0f4;color:var(--sub)}
.general-actions{display:flex;gap:6px}
.general-del{width:22px;height:22px;border:1px solid #e0594f;color:#e0594f;background:var(--pure);border-radius:50%;font-size:11px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1}
.general-del:hover{background:#e0594f;color:var(--pure)}
.general-empty{color:var(--muted);font-size:13px;padding:14px 0;text-align:center}
.general-err{color:var(--red-fg);font-size:12px;min-height:16px}
/* 综合部视图切换：只留 #generalPanel。
   ⚠ 2026-09-16 之前这里还额外放行 #mgrBarRow，是为了让用户能点顶部标签切回别的部门；
   标签行下线后不再需要 —— 离开综合部的入口改成左侧边栏「售后调试部门」下的部门条目，
   而侧边栏是 <nav>，不在 .wrap 之内，不受本条规则影响，永远可点。 */
#generalPanel{display:none}
.wrap.general-mode > *:not(#generalPanel){display:none!important}
.wrap.general-mode > #generalPanel{display:block}
@media(max-width:640px){
  .general-grid{grid-template-columns:1fr 1fr}
  .general-grid label:first-child{grid-column:1/-1}
  .general-row{flex-wrap:wrap;gap:10px;padding:var(--s-4) var(--s-5)}
  .general-name{width:auto;min-width:80px}
  .general-pos{margin-left:0}
  .general-actions{width:100%;justify-content:flex-end;margin-top:2px}
}
/* 工伤人员汇报与台账 */
.injury-form{background:var(--card-hd);border:1px solid #d9e8fa;border-radius:10px;padding:14px;margin-bottom:14px}
.injury-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px 12px;align-items:start}
.injury-grid label{display:flex;flex-direction:column;font-size:12px;color:var(--sub);gap:4px;min-height:0}
.injury-grid label.full{grid-column:1/-1}
.injury-grid input,.injury-grid select{font-size:13px;padding:var(--s-3) var(--s-4);border:1px solid #cfe0f4;border-radius:8px;background:var(--pure);height:36px;box-sizing:border-box;line-height:1.2}
.injury-grid input::placeholder{color:var(--muted)}
.injury-grid input[type="date"]{padding:var(--s-2) var(--s-4)}
.injury-grid textarea{font-size:13px;padding:var(--s-3) var(--s-4);border:1px solid #cfe0f4;border-radius:8px;background:var(--pure);resize:vertical;min-height:60px;box-sizing:border-box}
.injury-grid .req{font-style:normal;color:#e0594f}
.injury-form-btns{display:flex;align-items:center;gap:12px;margin-top:12px}
.injury-err{color:var(--red-fg);font-size:12px;min-height:16px}
.injury-summary{font-size:12px;color:var(--sub);margin-bottom:10px;line-height:1.7}
.injury-summary b{color:#1f2733}
.injury-list{display:flex;flex-direction:column;gap:6px;max-height:46vh;overflow:auto;margin-bottom:12px}
.injury-row{display:flex;align-items:center;gap:10px;font-size:13px;padding:var(--s-4) var(--s-5);border:1px solid #ddeaf9;border-radius:var(--r-2);background:var(--card)}
.injury-row:hover{background:var(--row-hover)}
.injury-main{flex:1;min-width:0}
.injury-title{font-weight:600;color:#1f2733}
.injury-meta{font-size:12px;color:#7a8598;margin-top:2px;line-height:1.5}
.injury-tag{flex-shrink:0;font-size:11px;border-radius:6px;padding:2px var(--s-3);white-space:nowrap}
.injury-sev-轻伤{color:#8a6d00;background:#fff7e0;border:1px solid #f2d98a}
.injury-sev-重伤{color:var(--red-fg);background:#ffecec;border:1px solid #f2b8b0}
.injury-st-治疗中{color:#8a4d00;background:#fff2e6;border:1px solid #f2c89a}
.injury-st-已康复{color:#1e7f4f;background:#e8f8ee;border:1px solid #b0e0c3}
.injury-st-已结案{color:var(--sub);background:#e9f1fb;border:1px solid #cfe0f4}
.injury-actions{flex-shrink:0;display:flex;gap:6px;align-items:center}
.injury-actions select{font-size:12px;padding:3px 6px;border:1px solid #cfe0f4;border-radius:6px}
.injury-del{flex-shrink:0;width:22px;height:22px;border:1px solid #e0594f;color:#e0594f;background:var(--pure);border-radius:50%;font-size:11px;cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:1}
.injury-del:hover{background:#e0594f;color:var(--pure)}
.injury-empty{color:var(--muted);font-size:13px;padding:14px 0;text-align:center}
.injury-card-table{width:100%;border-collapse:collapse;font-size:13px}
.injury-card-table th{background:var(--card-hd);color:var(--sub);text-align:left;padding:8px 10px;font-weight:600;border-bottom:1px solid #d9e8fa;position:sticky;top:0;z-index:1}
.injury-card-table td{padding:8px 10px;border-bottom:1px solid #e7f0fb;color:#33404f}
.injury-card-table tr:last-child td{border-bottom:none}
#injuryCardBody{max-height:370px;overflow-y:auto;border:1px solid #ddeaf9;border-radius:8px}
.injury-filterbar{display:flex;align-items:center;gap:12px;margin-bottom:10px;flex-wrap:wrap}
.injury-filterbar label{font-size:12px;color:var(--sub);display:flex;align-items:center;gap:var(--s-2)}
.injury-filterbar select{font-size:12px;padding:var(--s-2) 8px;border:1px solid #cfe0f4;border-radius:var(--r-2);background:var(--pure)}
.injury-filterbar .spacer{flex:1}
.injury-attach-zone{margin-top:4px}
.injury-attach-stage{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
.injury-file-chip{display:inline-flex;align-items:center;gap:4px;font-size:12px;padding:3px 8px;background:#eef4ff;border:1px solid #c9dcff;border-radius:var(--r-2);color:var(--blue-fg);max-width:230px}
.injury-file-chip.pending{background:#eef5fe;border-color:#d9e8fa;color:var(--sub)}
.injury-file-chip .fname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:150px}
.injury-file-chip a{color:var(--blue,#2f6fed);text-decoration:none;display:inline-flex;align-items:center;gap:3px}
.injury-file-chip a:hover{text-decoration:underline}
.injury-file-chip .fsize{color:var(--muted);font-size:11px}
.injury-file-chip .fdel{color:#e0594f;cursor:pointer;font-weight:700;margin-left:2px;user-select:none}
.injury-file-chip .fdel:hover{color:#b23a30}
.injury-file-add{cursor:pointer;color:var(--blue-fg);font-size:12px;display:inline-flex;align-items:center;gap:3px;padding:3px 6px}
.injury-file-add:hover{text-decoration:underline}
.injury-attach-list{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-top:6px}
.injury-trendwrap{margin-bottom:14px}
/* ===== 移动端适配 ===== */
@media(max-width:640px){
  body{font-size:13px}
  header{padding:12px 14px;gap:10px}
  header>div{flex-wrap:wrap;gap:10px}
  header h1{font-size:17px;letter-spacing:0}
  .brand-logo{padding:3px var(--s-3);height:34px}
  .brand-logo img{height:26px}
  .brand-text{padding:0 12px;font-size:16px;height:34px}
  .co-badge{font-size:18px!important;padding:3px 10px!important}
  .co-logo{height:28px!important}
  header .meta{font-size:11px;line-height:1.6}
  .viewall-btn{padding:6px 12px;font-size:12px}
  /* 小屏：侧栏改为顶部横向 */
  body{padding-left:0}
  .sidebar{position:static;width:auto;flex-direction:row;flex-wrap:wrap;box-shadow:none;border-bottom:1px solid rgba(255,255,255,.08);padding:8px 10px;background:#222b3d}
  .sb-app{display:none}
  .sb-scroll{flex:0 1 auto;flex-direction:row;flex-wrap:wrap;overflow:visible;padding:0;gap:4px}
  .sb-sec{display:none!important}
  .sb-item{width:auto;flex:0 0 auto;padding:var(--s-3) 10px;font-size:12px}
  /* 仓库二级导航：横向侧栏里改「横排胶囊」，避免被压成窄竖列（2026-09-14） */
  #wareSubNav{flex-direction:row;flex-wrap:wrap;padding-left:0;margin:1px 0 3px;border-left:none;gap:4px}
  #wareSubNav .sb-subbtn{width:auto;flex:0 0 auto;border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:4px var(--s-4)}
  #wareSubNav .sb-subbtn::before{display:none}
  /* 员工驻厂信息二级导航（2026-09-19）：与仓库子导航同一处理 —— 横排胶囊 */
  #empSubNav{flex-direction:row;flex-wrap:wrap;padding-left:0;margin:1px 0 3px;border-left:none;gap:4px}
  #empSubNav .sb-subbtn{width:auto;flex:0 0 auto;border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:4px var(--s-4)}
  #empSubNav .sb-subbtn::before{display:none}
  /* 现场问题收集二级导航（2026-09-22 第二十三轮）：与仓库 / 驻厂子导航同一处理 —— 横排胶囊 */
  #probSubNav{flex-direction:row;flex-wrap:wrap;padding-left:0;margin:1px 0 3px;border-left:none;gap:4px}
  #probSubNav .sb-subbtn{width:auto;flex:0 0 auto;border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:4px var(--s-4)}
  #probSubNav .sb-subbtn::before{display:none}
  /* ⚠ 「外派支援」二级导航的响应式规则已随 #xsupSubNav 一并删除（2026-09-28 第七十五轮）：
     该模块不再是「按部门分栏的独立视图」—— 顶级入口 = 只读汇总页，部门来自左侧部门列表。 */
  .sb-logout{margin:0 0 0 auto;border-top:none;padding:var(--s-3) 10px;flex:0 0 auto}
  .statusbar{gap:8px}
  .statusbar .btn{padding:var(--s-3) 12px;font-size:12px}
  .wrap{padding:14px 12px 50px}
  .kpis{grid-template-columns:repeat(2,1fr);gap:10px}
  .kpi{padding:10px 12px 12px}
  .kpi .v{font-size:21px}
  .kpi .l{font-size:12px}
  .kpi .s{font-size:11px}
  .grid2{gap:12px}
  .chart{height:260px}
  #cInjuryDept.chart{height:200px}
  .card{padding:12px 12px 6px}
  .card h3{font-size:14px}
  .card .hint{font-size:11px}
  .addgrid{grid-template-columns:1fr}
  .toolbar{flex-direction:column;align-items:stretch;gap:6px}
  .tb-filters,.tb-actions{width:100%}
  .tb-actions{margin-left:0;padding-left:0;border-left:none;justify-content:flex-start}
  .toolbar input{min-width:0!important;width:100%}
  .toolbar select{width:100%}
  .tablewrap{padding:10px 8px}
  .table-scroll{max-height:440px}
  table{font-size:12px}
  th,td{padding:6px var(--s-3)}
  .mgrtable{font-size:12px}
  .mgrtable th,.mgrtable td{padding:6px var(--s-3)}
  .modalbox{padding:18px 16px}
  .modal-box{padding:16px 14px}
  .email-row{flex-wrap:wrap}
  .email-input{min-width:100%;width:100%}
  .pwdadmin-list{min-width:0}
  .sa-field{width:100%;margin-right:0}
  .sa-field input{width:100%}
  .injury-grid{grid-template-columns:1fr}
  .injury-grid label{grid-column:1/-1}
  .injury-row{flex-wrap:wrap}
  #injuryCardBody{overflow:auto}
}

/* ================= 视觉规范升级层（2026-09-05）· 统一圆角/阴影/过渡/留白 ================= */
:root{
  --shadow-sm:0 1px 2px rgba(16,24,40,.05),0 1px 3px rgba(16,24,40,.06);
  --shadow-md:0 6px 16px -4px rgba(16,24,40,.10),0 2px 6px -2px rgba(16,24,40,.06);
  --shadow-lg:0 20px 50px -12px rgba(16,24,40,.28);
  --ring:0 0 0 3px rgba(47,111,237,.16);
}
body{background:
  radial-gradient(1100px 380px at 85% -60px, rgba(26,166,183,.12), transparent 60%),
  radial-gradient(900px 340px at 8% -80px, rgba(47,111,237,.14), transparent 60%),
  var(--bg);}
header{background:linear-gradient(120deg,#1d4ed8 0%,#2f6fed 48%,#0C8391 100%);padding:20px 28px;box-shadow:0 8px 28px -8px rgba(29,78,216,.5)}
header .meta{opacity:.88}
/* 按钮：统一高度/圆角/动效 */
.btn{border-radius:var(--r-2);padding:8px var(--s-8);font-weight:600;letter-spacing:.2px;transition:transform .15s ease,box-shadow .15s ease,background .15s ease,border-color .15s ease}
.btn:hover{transform:translateY(-1px);box-shadow:0 5px 12px -3px rgba(16,24,40,.22)}
.btn:active{transform:translateY(0);box-shadow:none}
.btn:focus-visible{outline:none;box-shadow:var(--ring)}
.btn.primary{background:linear-gradient(135deg,#2f6fed,#137F8D);border:none;color:var(--pure)}
.btn.primary:hover{background:linear-gradient(135deg,#2a63d4,#17919f)}
.btn.ghost{background:var(--pure);color:var(--sub);border:1px solid #cfe0f4;box-shadow:none}
/* 危险操作（仓库一键清除）：白底红字，与主蓝按钮区分，避免误点成普通操作 */
.btn.danger{background:var(--pure);color:var(--red-fg);border:1px solid #f0c9c4;box-shadow:none}
.btn.danger:hover{background:#fff4f2;border-color:#e0a49c}
.btn.ghost:hover{background:#eef6ff;color:#33404f;transform:translateY(-1px);box-shadow:0 3px 8px -2px rgba(16,24,40,.12)}
.btn.active{background:#1e7f4f;border-color:#1e7f4f}
/* 输入控件：聚焦光环 */
select,input[type=text],input[type=password],input[type=number],input[type=date],input[type=email]{
  transition:border-color .15s ease,box-shadow .15s ease}
select:focus,input[type=text]:focus,input[type=password]:focus,input[type=number]:focus,input[type=date]:focus,input[type=email]:focus{
  outline:none;border-color:#2f6fed;box-shadow:var(--ring)}
/* KPI 卡片：顶部彩条 + 悬浮抬升 */
.kpi{border:none;border-radius:14px;box-shadow:var(--shadow-sm);position:relative;overflow:hidden;transition:transform .18s ease,box-shadow .18s ease}
/* 彩条的非颜色冗余编码（2026-10-03 补齐 · 界面评审第 12 项「KPI 顶部彩条纯靠颜色区分」）：
   7 条彩条只靠色相区分，红绿色觉障碍（≈8% 男性）在屏幕上无法分辨谁是谁。
   这里给每条叠一层 **周期性斜纹**（repeating-linear-gradient），
   用「纹路疏密」而非「颜色」再编码一次 —— 灰度打印、黑白截图、色弱模式下都还能区分。
   ⚠ 纹路是叠加在原渐变**之上**（background-image 多层，background-color 落底），
     不改原色值 ⇒ 视觉基调与原设计一致，只是多了一层 3px 高的细纹。
   ⚠ 周期按 nth-child 递增（4/6/8/10/12/14/16px）而非随机：
     相邻两卡的纹路密度必然不同，7 张卡两两可辨。
   ⚠ 只用 currentColor 之外的两层 rgba 黑/白叠色，不新增不可读的色值；
     3px 条太窄，图案必须极轻（alpha ≤ .18）否则会显脏。 */
.kpi::before{
  content:'';position:absolute;left:0;right:0;top:0;height:3px;
  background-color:var(--blue);
  background-image:
    repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 4px),
    linear-gradient(90deg,var(--blue),var(--cyan));
}
.kpi:nth-child(2)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 6px),linear-gradient(90deg,#2f6fed,#1aa6b7)}
.kpi:nth-child(3)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 8px),linear-gradient(90deg,#f0972b,#f6b93d)}
.kpi:nth-child(4)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 10px),linear-gradient(90deg,#23a06b,#3fc78d)}
.kpi:nth-child(5)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 12px),linear-gradient(90deg,#8b5cf6,#a78bfa)}
.kpi:nth-child(6)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 14px),linear-gradient(90deg,#e0594f,#f08a80)}
.kpi:nth-child(7)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 16px),linear-gradient(90deg,#0e9fb0,#38bdf8)}
.kpi:hover{transform:translateY(-3px);box-shadow:var(--shadow-md)}
/* 内容卡片 */
.card{border:none;border-radius:14px;box-shadow:var(--shadow-sm)}
.card h3,.card-title{letter-spacing:.3px}
/* 表格：斑马纹柔和 + 悬浮高亮 + 表头描边 */
table{font-size:13px}
th{background:var(--card-hd);border-bottom:2px solid #dbe9f8;color:var(--sub);font-weight:600}
th,td{border-bottom:1px solid #e7f0fb}
tbody tr{transition:background .12s ease}
tbody tr:hover{background:var(--row-hover)}
tbody tr:nth-child(even){background:var(--row-alt)}
tbody tr:hover:nth-child(even){background:var(--row-hover)}
.mgrtable th{background:var(--card-hd);border-bottom:2px solid #cfe0f4}
/* 联系人删除按钮（电话簿） */
.phdel{padding:3px 10px;border:1px solid #f3c6c1;color:var(--red-fg);background:var(--pure);border-radius:6px;font-size:12px;cursor:pointer;transition:background .15s,color .15s}
.phdel:hover{background:#fdeceb;border-color:#e0594f}
/* 弹窗 */
.modalbox,.modal-box{border-radius:16px;box-shadow:var(--shadow-lg)}
.modal-overlay{background:rgba(15,23,42,.55);backdrop-filter:blur(2px)}
/* 状态徽标 */
#connStat{font-size:12px}
/* 滚动条 */
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-thumb{background:#b8cfe8;border-radius:8px;border:2px solid var(--bg)}
::-webkit-scrollbar-thumb:hover{background:#9dc0e4}
::-webkit-scrollbar-track{background:transparent}
/* 可编辑单元格聚焦统一光环 */
.ecell:focus,.ecell-row:focus{box-shadow:var(--ring);border-color:#2f6fed;background:var(--pure)}
/* 编辑行：行内控件底色跟着行高亮走。
   否则「人数」列一排白底输入框 + 聚焦格的白底会把蓝色切成好几段，看起来就不像"整行变蓝"；
   聚焦的那一格改用更浅的蓝（而不是纯白），既能定位当前在改哪一格，又不打断整行的蓝色。 */
tbody tr.row-editing .ecell,
tbody tr.row-editing .ecell-row,
tbody tr.row-editing .stage-sel,
tbody tr.row-editing .nlname,
tbody tr.row-editing .nlrole,
tbody tr.row-editing .nlhead-qty input[type=number]{background:#D9E9FB;border-color:#b9d4f5}
tbody tr.row-editing .ecell:focus,
tbody tr.row-editing .ecell-row:focus,
tbody tr.row-editing .stage-sel:focus,
tbody tr.row-editing .nlname:focus,
tbody tr.row-editing .nlrole:focus,
tbody tr.row-editing .nlhead-qty input[type=number]:focus{background:#EDF5FF;border-color:#2f6fed}
/* 行内小按钮（删除手动行等） */
.manualtag,.rowdel{border-radius:6px;transition:background .15s}
@media(max-width:640px){
  .mgrow{flex-wrap:wrap}
  .mgrow span{width:100%}
  .mgrow input{width:100%}
  .namelist{position:static;width:auto;min-width:0;max-width:100%;box-shadow:none;border-radius:8px}
}

/* ================= 主题突出优化层（2026-09-05）· 标题锚点 / 信息降噪 / 玻璃拟态 ================= */
header{
  background:
    radial-gradient(900px 320px at 90% -60px, rgba(255,255,255,.16), transparent 62%),
    radial-gradient(720px 280px at 2% 130%, rgba(26,166,183,.38), transparent 62%),
    linear-gradient(120deg,#0f2a66 0%,#1d4ed8 46%,#0C8391 100%);
  padding:24px 28px 20px;
}
header h1{font-size:26px;font-weight:800;text-shadow:0 2px 12px rgba(4,18,54,.45);letter-spacing:1px}
/* 品牌徽标文字与主题同源渐变，避免蓝白撞色 */
.brand-text{background:linear-gradient(135deg,#1d4ed8,#0C8391)}
/* 辅助说明降级：小字 + 低透明度，不再与主题抢视觉 */
header .meta{font-size:12px;opacity:.55;margin-top:6px}
.statusbar{margin-top:6px;gap:10px}
.impstat{opacity:.75;font-size:12px}
/* 头部操作按钮：玻璃拟态次级按钮，视觉让位给标题 */
.viewall-btn{background:rgba(255,255,255,.14);color:var(--pure);border:1px solid rgba(255,255,255,.42);box-shadow:none;backdrop-filter:blur(4px)}
.viewall-btn:hover{background:rgba(255,255,255,.30);transform:none;box-shadow:none}
/* 登录横幅：深蓝实色块 → 玻璃卡片，融入主题渐变 */
.loginbanner{
  background:rgba(255,255,255,.13);
  border:1px solid rgba(255,255,255,.28);
  backdrop-filter:blur(6px);
  -webkit-backdrop-filter:blur(6px);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.14);
  display:flex;flex-wrap:wrap;align-items:center;gap:8px;
  margin:12px 0 0;padding:10px 14px;border-radius:12px;
}
.loginbanner b{color:#ffe08a}
.loginbanner .btn{
  margin:0!important;   /* 清掉各按钮参差的 inline margin-left，统一用 gap */
  background:rgba(255,255,255,.16);color:var(--pure);
  border:1px solid rgba(255,255,255,.36);
  box-shadow:none;padding:4px 12px;font-size:12px;white-space:nowrap;
}
.loginbanner .btn:hover{background:var(--pure);color:var(--blue-fg);transform:none;box-shadow:none}
/* KPI 卡片：7 张卡各配专属主题色条，数值用主题渐变字
   ⚠ 这 7 条只换配色（background-image 的**第二层**），
   第一层的斜纹冗余沿用基础层 .kpi::before —— 见该处「界面评审第 12 项」说明。
   绝不能退回 `background:` 简写：那会把 background-image 整个重置，斜纹就没了。 */
.kpi:nth-child(1)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 4px),linear-gradient(90deg,#1d4ed8,#3b82f6)}
.kpi:nth-child(2)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 6px),linear-gradient(90deg,#2f6fed,#1aa6b7)}
.kpi:nth-child(3)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 8px),linear-gradient(90deg,#f0972b,#f6b93d)}
.kpi:nth-child(4)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 10px),linear-gradient(90deg,#23a06b,#3fc78d)}
.kpi:nth-child(5)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 12px),linear-gradient(90deg,#8b5cf6,#a78bfa)}
.kpi:nth-child(6)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 14px),linear-gradient(90deg,#e0594f,#f08a80)}
.kpi:nth-child(7)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 16px),linear-gradient(90deg,#0e9fb0,#38bdf8)}
.kpi .v{
  background:linear-gradient(135deg,#12275e,#1d4ed8);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:var(--blue-fg);
}
/* ================= 布局与导航优化层（2026-09-05）· 头部通栏固定 / 侧栏紧凑下移 / 登录前置 ================= */
:root{--sbw:123px;--hh:170px}
body{padding-left:var(--sbw)}
/* 头部固定顶部、横向通栏铺满（抵消 body 左侧让位侧栏的内边距） */
header{position:sticky;top:0;z-index:65;margin-left:calc(-1*var(--sbw));width:calc(100% + var(--sbw));box-shadow:0 6px 18px -6px rgba(6,20,60,.45)}
/* 功能栏：下移到固定头部之下，深色导航保持全高 */
.sidebar{top:var(--hh);width:var(--sbw);box-shadow:2px 0 12px rgba(10,18,35,.35)}
/* 侧栏再收窄 1/3（184px→123px）后的内部紧凑化：字号整体下调，保证条目单行显示不折行 */
.sidebar{font-size:11px}
.sb-app{font-size:12px;letter-spacing:0;padding:12px 8px var(--s-4);gap:var(--s-2)}
.sb-app-ico{font-size:14px;flex:none}
.sb-scroll{padding:6px var(--s-2) 8px;gap:1px}
.sb-sec{font-size:9.5px;letter-spacing:1px;padding:var(--s-4) var(--s-2) 3px}
.sb-item{padding:6px var(--s-2);font-size:11px;gap:var(--s-2);line-height:1.3;white-space:normal;word-break:break-word;text-align:left}
.sb-subnav{padding-left:var(--s-2);gap:1px;margin:1px 0 3px 6px}
.sb-subbtn{padding:var(--s-2) 6px;font-size:11px;gap:4px}
.sb-subbtn::before{margin-right:4px}
/* 部门分区子项：侧栏仅 123px 宽，再收一档缩进与字号（2026-09-16） */
#deptSubNav .sb-subsec{margin-left:14px;padding-left:6px}
#deptSubNav .sb-subsec-btn{padding:4px 6px;font-size:11px}
/* 仓库二级导航：侧栏仅 123px 宽，名称可省略号、数量不换行（2026-09-14） */
#wareSubNav .sb-subbtn>span:first-child{overflow:hidden;text-overflow:ellipsis}
#wareSubNav .sb-n{padding-left:4px;font-size:9.5px}
/* 员工驻厂信息二级导航（2026-09-19，同仓库）：名称可省略号、数量不换行 */
#empSubNav .sb-subbtn>span:first-child{overflow:hidden;text-overflow:ellipsis}
#empSubNav .sb-n{padding-left:4px;font-size:9.5px}
.sb-logout{padding:var(--s-5) 6px;font-size:11px}
/* 表头吸附修正：页面级表格（电话簿/负责人构成表等）表头不随滚动吸附，
   始终固定在表格自身顶部；仅滚动容器内的表格保留吸顶 */
th{position:static;top:auto;z-index:auto}
.mgrtable th{position:static;top:auto}
.table-scroll th{position:sticky;top:0;z-index:2}
#injuryCardBody .injury-card-table th{position:sticky;top:0}
/* 电话簿：列表自身为滚动容器（#phoneTable max-height 280px），滚动时仅数据行滚动、表头钉在容器顶部 */
#phoneTable .mgrtable th{position:sticky;top:0;z-index:2;box-shadow:0 2px 6px -2px rgba(31,56,102,.18)}
/* 部门明细表格列数多，收紧表头/字号/内边距，让 14 列尽量一屏完整显示 */
#tableWrapSec .table-scroll table{table-layout:fixed;width:100%;font-size:11.3px}
#tableWrapSec .table-scroll th,
#tableWrapSec .table-scroll td{padding:var(--s-2) 6px;white-space:normal;vertical-align:middle;line-height:1.35}
#tableWrapSec .table-scroll th{font-size:11px;font-weight:600;background:var(--card-hd);border-right:1px solid rgba(207,224,244,.6)}
#tableWrapSec .table-scroll td{border-right:1px solid rgba(231,240,251,.7);overflow-wrap:anywhere}
/* 阶段下拉框跟随单元格宽度，不再按固定 90px 溢出到右侧「设备数」列 */
#tableWrapSec .table-scroll td select,
#tableWrapSec .table-scroll td input,
#tableWrapSec .table-scroll td .ecell,
#tableWrapSec .table-scroll td .ecell-row{max-width:100%;box-sizing:border-box}
#tableWrapSec .table-scroll td select.stage-sel{width:100%;max-width:100%;font-size:11px;padding:2px 2px}
#tableWrapSec .table-scroll th:last-child,
#tableWrapSec .table-scroll td:last-child{border-right:none}
/* 头部卡片紧凑化：整体高度约减半，给主内容留更多空间 */
header{padding:10px 28px 10px;gap:8px}
header h1{font-size:20px}
.brand-pill{border-radius:8px}
.brand-logo{height:32px;padding:3px 8px}
.brand-logo img{height:24px}
.brand-text{height:32px;padding:0 12px;font-size:16px;letter-spacing:1px}
.viewall-btn{padding:var(--s-2) 12px;font-size:12px;border-radius:8px}
header .meta{font-size:11px;margin-top:3px;line-height:1.4}
/* 「数据格式」与「当前数据/连接状态」合并为一行（窄屏放不下时自动换行） */
header .meta{width:auto;max-width:56%;margin-top:0;flex:0 1 auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.statusbar{margin-top:0;flex:1 1 0;min-width:0;flex-wrap:nowrap}
.statusbar .impstat{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.statusbar #connStat{flex-shrink:0}
.impstat{font-size:12px}
#connStat{padding:1px 8px;font-size:11px}
.loginbanner{margin-top:8px;padding:6px 12px;gap:6px;border-radius:10px}
.loginbanner .btn{padding:3px 10px;font-size:12px}
@media(max-width:640px){
  header{position:static;margin-left:0;width:auto;padding:14px 14px 14px}
  header h1{font-size:17px;letter-spacing:0}
  .loginbanner{padding:var(--s-4) var(--s-5);gap:6px}
  .loginbanner .btn{padding:3px 10px}
}
/* ================= 手动添加卡片优化（2026-09-05）· 标题栏可关闭 / 间距放松 ================= */
.addpanel{padding:14px 18px 16px;margin:6px 0 14px;box-shadow:0 4px 14px -6px rgba(31,56,102,.18)}
.addpanel-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid #e6ebf2}
.addpanel-head .t{font-size:13.5px;font-weight:700;color:#1f3866;letter-spacing:.5px}
.addpanel-close{border:1px solid #cfe0f4;background:var(--pure);color:var(--sub);font-size:12px;padding:3px 10px;border-radius:var(--r-2);cursor:pointer}
.addpanel-close:hover{background:#eef2f8;color:#1f3866;border-color:#b9c4d4}
.addgrid{gap:var(--s-5) 20px}
.addgrid label{font-size:12px;gap:var(--s-2)}
.addgrid input{padding:8px 10px}
.addbtns{margin-top:var(--s-6);gap:14px}
/* 侧栏按钮：窄栏下允许两行显示，不再截断省略，消除拥挤感 */
.sidebar .btn{white-space:normal;text-align:center;line-height:1.3;padding:6px 4px;word-break:keep-all}
/* ================= 手动添加：浮动卡片（2026-09-05）· 居中浮层 + 遮罩，不再挤在头部 ================= */
.addpanel{position:fixed;inset:0;z-index:85;display:none;background:rgba(15,23,42,.5);backdrop-filter:blur(2px);overflow:auto;padding:0;margin:0;border:0;border-radius:0;box-shadow:none}
.addpanel-box{width:min(680px,94vw);margin:7vh auto 40px;background:var(--pure);border:1px solid #d8e7f9;border-radius:14px;padding:18px 22px 20px;box-shadow:0 18px 50px rgba(0,0,0,.28)}
.addpanel-box .addpanel-head .t{font-size:15px}
.addpanel-box .addgrid label{font-size:13px}
.addpanel-box .addgrid input{padding:var(--s-4) var(--s-5)}
@media(max-width:640px){
  .addpanel-box{margin-top:3vh}
}
/* ================= 表格吸底平移条 + 工具栏一行化（2026-09-05）================= */
/* 吸底横向滚动条：sticky 常驻可视区底部，页面无需拉到底即可左右平移宽表 */
.hscroll{position:sticky;bottom:2px;display:none;height:15px;overflow-x:auto;overflow-y:hidden;z-index:25;margin-top:6px;background:rgba(248,250,253,.96);border:1px solid #d8e7f9;border-radius:var(--r-2);box-shadow:0 3px 10px -4px rgba(31,56,102,.25)}
.hscroll.on{display:block}
.hscroll::-webkit-scrollbar{height:11px}
.hscroll::-webkit-scrollbar-track{background:transparent}
.hscroll::-webkit-scrollbar-thumb{background:#b9c6dc;border-radius:6px;border:2px solid rgba(248,250,253,.96)}
.hscroll::-webkit-scrollbar-thumb:hover{background:#8fa5c8}
.hscroll-inner{height:1px;width:100px}
/* 手机端明细「加载更多」（配合增量渲染，见 renderTable 注释） */
.morerows{display:flex;align-items:center;gap:10px;justify-content:center;padding:10px 4px 4px;flex-wrap:wrap}
.morerows .btn{background:var(--pure)}
.morerows .legend{font-size:12px;color:var(--muted)}
/* 工具栏一行显示（宽屏时禁止换行，控件压缩让出一行高度） */
/* 操作日志：变更明细展开按钮 */
.oplog-det{border:1px solid #cfe0f4;background:var(--pure);color:#2c5aa8;font-size:12px;padding:2px var(--s-4);border-radius:6px;cursor:pointer}
.oplog-det:hover{background:#eef4ff;border-color:#b9d2ff}
/* 操作日志：明细区与各列的语义样式。
   2026-10-04：原先这些色值全部写在 renderOplog 生成的**内联 style** 里
   （color:#3f4a5a / #5a6472 / #8a93a6 / 动作色表），内联优先级高于任何类选择器，
   深色层就算写了 .oplog-det-box{color:var(--sub)} 也压不住 ——
   实测变更明细文字在深底上只有 1.75:1，几乎不可读（用户截图反馈）。
   改为类承载后，浅/深两套值都在 CSS 里，深色层覆写同一批类，两套主题都保证对比度。
   浅色值按 WCAG AA 4.5:1 对 #EAF3FE 实底重新标定（灰字统一走项目既有的 --sub 口径，
   动作色在原色基础上压深一档以过线）。 */
.oplog-det-box{color:#3f4a5a;max-height:420px;overflow:auto;overscroll-behavior:contain}
.oplog-det-more{color:#5b6b80}
.oplog-time{white-space:nowrap;color:#5b6b80;font-size:12px}
.oplog-rev{color:#5b6b80;font-size:11px}
.oplog-detail{color:#4a5768;white-space:normal}
.oplog-act{font-weight:600}
.oplog-act-ok{color:#1a6b46}
.oplog-act-bad{color:#b3281d}
.oplog-act-info{color:#1f5bc4}
.oplog-act-warn{color:#9a5b00}
.oplog-act-move{color:#6d3fd4}
.oplog-act-mute{color:#5b6b80}
.oplog-empty{padding:24px;text-align:center;color:#5b6b80}
/* ================= 阶段筛选（2026-09-17）=================
   阶段是仅次于部门的第二常用维度，此前工具栏没有它 —— 想筛「只看 OSS」只能在搜索框里打字，
   而「OSS」这个字样同时出现在项目名/设备名里，筛出来并不准。
   这里做成「按钮 + 多选浮层」：原生 <select multiple> 在手机上是灾难，复选框面板才可点。 */
.fstage-wrap{position:relative;display:inline-flex;flex:none}
#fStageBtn{background:var(--pure);border:1px solid var(--line);color:var(--ink);
  border-radius:8px;padding:6px 10px;font-size:12px;white-space:nowrap;cursor:pointer}
#fStageBtn:hover{background:var(--card-hd)}
#fStageBtn.on{background:var(--blue);border-color:var(--blue);color:var(--pure)}
#fStageMenu{position:absolute;top:calc(100% + 6px);left:0;z-index:80;
  width:min(236px,88vw);max-height:min(64vh,460px);overflow:auto;
  background:var(--card);border:1px solid var(--line);border-radius:10px;
  box-shadow:0 14px 34px -10px rgba(31,56,102,.4);padding:8px}
#fStageMenu[hidden]{display:none}
.fstage-presets{display:flex;flex-wrap:wrap;gap:6px;padding:2px 2px 8px;
  border-bottom:1px solid var(--line);margin-bottom:6px}
.fstage-presets button{font-size:12px;padding:3px 8px;border-radius:6px;
  border:1px solid var(--line);background:var(--pure);color:var(--ink);cursor:pointer}
.fstage-presets button:hover{background:var(--card-hd)}
.fstage-item{display:flex;align-items:center;gap:8px;padding:var(--s-2) 6px;border-radius:var(--r-2);
  cursor:pointer;font-size:13px;color:var(--ink)}
.fstage-item:hover{background:var(--row-alt)}
.fstage-item .n{margin-left:auto;color:var(--sub);font-size:12px;font-variant-numeric:tabular-nums}
.fstage-item input{margin:0;flex:none}
.fstage-tip{font-size:12px;color:var(--sub);padding:4px 6px 2px;line-height:1.5}
@media(min-width:981px){
  .toolbar{flex-wrap:nowrap;gap:8px}
  .toolbar label{font-size:12px;white-space:nowrap;flex:none}
  .toolbar select{padding:var(--s-2) 6px;font-size:12px;flex:none}
  .toolbar input[type=text]{padding:var(--s-2) var(--s-4);font-size:12px;min-width:0}
  .tb-filters{flex-wrap:nowrap;gap:8px}
  .tb-actions{margin-left:auto;padding-left:14px;border-left:1px solid #e7f0fb;flex:none}
  #fSearch{flex:1 1 150px;width:auto;min-width:120px!important}
  #fName{flex:0 1 140px;width:auto}
  #rowCount{white-space:nowrap;font-size:12px;flex:none}
  #tableEditBtn{margin-left:0!important;padding:var(--s-2) 10px;font-size:12px;white-space:nowrap;flex:none}
}
/* ================= 手机浏览体验升级层（2026-09-07）· 小屏也能顺畅操作 =================
   放在所有样式之后，同特异性下后写的规则优先生效，避免被前面的桌面规则覆盖。        */
@media(max-width:640px){
  html{-webkit-text-size-adjust:100%}
  /* 关键修正：桌面层 body{padding-left:112px} 写在小屏规则之后，会把手机屏挤压出 112px 空白 */
  body{padding-left:0!important;padding-bottom:env(safe-area-inset-bottom)}
  /* ---- 顶部：压缩留白、隐藏次要说明 ---- */
  header{position:static;margin-left:0;width:auto;padding:10px 12px 10px}
  header h1{font-size:15.5px;letter-spacing:0}
  header .meta{display:none}
  .brand-pill{border-radius:8px}
  .brand-logo{height:30px;padding:2px var(--s-3)}
  .brand-logo img{height:22px}
  .brand-text{height:30px;padding:0 10px;font-size:14px;letter-spacing:1px}
  .viewall-btn{padding:6px 10px;font-size:12px;white-space:nowrap}
  .statusbar{gap:6px;margin-top:6px;flex-wrap:wrap}
  .impstat{font-size:11px;white-space:normal;line-height:1.4}
  #connStat{font-size:11px;padding:1px var(--s-3)}
  .loginbanner{font-size:12px;padding:8px 10px;line-height:1.5}
  .loginbanner .btn{padding:var(--s-2) 10px;font-size:12px}
  .fri-reminder{font-size:12px;padding:8px 10px}
  /* ---- 功能栏：吸顶横向滚动，滑到页面任何位置都能点，不再占多行 ---- */
  .sidebar{position:sticky;top:0;z-index:66;width:auto;flex-direction:row;flex-wrap:nowrap;
    overflow-x:auto;overflow-y:hidden;padding:8px 10px;gap:6px;background:#222b3d;
    border-bottom:1px solid rgba(255,255,255,.08);box-shadow:0 4px 12px -6px rgba(10,18,35,.5);
    -webkit-overflow-scrolling:touch;scrollbar-width:none}
  .sidebar::-webkit-scrollbar{display:none}
  .sb-app{display:none}
  .sb-scroll{flex:0 0 auto;flex-direction:row;flex-wrap:nowrap;overflow:visible;padding:0;gap:6px}
  .sb-sec{display:none!important}
  .sb-item{width:auto;flex:0 0 auto;white-space:nowrap;padding:8px var(--s-5);font-size:12px;border-radius:var(--r-2)}
  /* 仓库二级导航：横向侧栏里改「横排胶囊」（2026-09-14） */
  #wareSubNav{flex-direction:row;flex-wrap:nowrap;padding-left:0;margin:1px 0 3px;border-left:none;gap:var(--s-2)}
  #wareSubNav .sb-subbtn{width:auto;flex:0 0 auto;border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:var(--s-2) 10px}
  #wareSubNav .sb-subbtn::before{display:none}
  /* 员工驻厂信息二级导航（2026-09-19，同仓库） */
  #empSubNav{flex-direction:row;flex-wrap:nowrap;padding-left:0;margin:1px 0 3px;border-left:none;gap:var(--s-2)}
  #empSubNav .sb-subbtn{width:auto;flex:0 0 auto;border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:var(--s-2) 10px}
  #empSubNav .sb-subbtn::before{display:none}
  /* 现场问题收集二级导航（2026-09-22 第二十三轮）
     ⚠ 这里用 flex-wrap:wrap 而**不是** emp 的 nowrap：本模块的分栏是「全部部门」，
       部门数可能到 7~10 个，nowrap 会把侧栏横向撑破（emp 只有 2 个站点才敢 nowrap）。 */
  #probSubNav{flex-direction:row;flex-wrap:wrap;padding-left:0;margin:1px 0 3px;border-left:none;gap:var(--s-2)}
  #probSubNav .sb-subbtn{width:auto;flex:0 0 auto;border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:var(--s-2) 10px}
  #probSubNav .sb-subbtn::before{display:none}
  /* ⚠ 「外派支援」二级导航的横排胶囊规则已删（2026-09-28 第七十五轮）：容器 #xsupSubNav 已不存在 */
  /* 部门二级导航：横向侧栏里改「横排胶囊」（2026-09-16）
     原先是纵向列 —— 在 flex-direction:row 的 .sb-scroll 里，7 个部门的纵向列会把
     整条侧栏撑到 ~345px 高，默认 align-items:stretch 又把激活项拉成一大块蓝色矩形。
     改横排后侧栏回到单行高度，与上面仓库二级导航的处理保持一致。 */
  #deptSubNav{flex-direction:row;flex-wrap:nowrap;padding-left:0;margin:1px 0 3px;border-left:none;gap:var(--s-2)}
  #deptSubNav .sb-subbtn{width:auto;flex:0 0 auto;border:1px solid rgba(255,255,255,.16);border-radius:999px;padding:var(--s-2) 10px}
  #deptSubNav .sb-subbtn::before{display:none}
  #deptSubNav .sb-sub-txt{flex-direction:row;align-items:baseline;gap:var(--s-2)}
  #deptSubNav .sb-sub-o{margin-top:0;opacity:.72}
  #deptSubNav .sb-subsec{flex-direction:row;flex-wrap:nowrap;margin:0;padding-left:0;border-left:none;gap:var(--s-2)}
  #deptSubNav .sb-subsec-btn{width:auto;flex:0 0 auto;border:1px solid rgba(255,255,255,.22);border-radius:999px;padding:4px var(--s-4)}
  #deptSubNav .sb-sub-sep{width:1px;height:auto;margin:var(--s-2) 3px;background:rgba(255,255,255,.16)}
  .sb-logout{margin:0 0 0 auto;border-top:none;padding:8px var(--s-5);flex:0 0 auto}
  /* ---- 主体容器与卡片 ---- */
  .wrap{padding:10px 10px 28px;max-width:none}
  .kpis{grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px}
  .kpi{padding:8px 10px 10px;border-radius:12px;transform:none!important}
  .kpi .v{font-size:19px}
  .kpi .l{font-size:11px;white-space:normal}
  .kpi .s{font-size:10px}
  .grid2{grid-template-columns:1fr;gap:12px}
  .card{padding:var(--s-5) var(--s-5) 6px;border-radius:12px}
  .card h3{font-size:14px}
  .card .hint{font-size:11px;line-height:1.5}
  .chart{height:240px}
  #cInjuryDept.chart{height:200px}
  .note{font-size:12px;padding:10px 12px;line-height:1.6}
  #deptAddBtn{font-size:12px;padding:6px 10px}
  #batchMoveBtn{font-size:12px;padding:6px 10px}
  /* ---- 输入控件统一 16px：避免 iOS 聚焦时整页自动放大 ---- */
  input,select,textarea,.nlname,.combosel{font-size:16px!important}
  .addgrid input,.addgrid select.combosel{padding:var(--s-4) 10px}
  .nlhead-qty input[type=number]{font-size:14px!important;width:64px}
  .toolbar input[type=text]{font-size:16px}
  /* ---- 筛选工具栏：两列网格 + 隐藏文字标签（用 placeholder/首选项表意） ---- */
  .toolbar{display:grid;grid-template-columns:1fr 1fr;gap:8px;align-items:center;margin:4px 0 10px}
  .toolbar label{display:none}
  .toolbar select,.toolbar input[type=text]{width:100%;min-width:0!important;padding:8px var(--s-4)}
  .toolbar #rowCount{grid-column:1/-1;font-size:12px;color:var(--muted)}
  .toolbar #tableEditBtn{margin-left:0!important;width:100%;padding:var(--s-4) 0;font-size:13px}
  .toolbar #viewToggleBtn{margin-left:0!important;grid-column:1/-1;width:100%;padding:var(--s-4) 0;font-size:13px}
  .tb-filters{grid-column:1/-1}
  .tb-actions{grid-column:1/-1;margin-left:0;padding-left:0;border-left:none;justify-content:flex-start}
  /* ---- 触摸目标放大 ---- */
  .btn{padding:var(--s-4) 12px}
  .cntbadge{min-width:34px;padding:4px 12px;font-size:14px}
  /* 手机上的徽标原本 min-width:34px / padding:4px 12px / 14px，四个挤一行会把行高顶到 39px；
     收紧到 28px / 2px 8px / 13px，一行四个才排得下，单个点击区仍有 28×23px 不至于误触 */
  body:not(.tv-table) .tablewrap .cntbadge{min-width:28px;padding:2px 8px;font-size:13px}
  .rowdel,.manualtag,.rowsave{padding:4px var(--s-4);font-size:12px}
  .ecell,.ecell-row{min-width:44px;min-height:26px;padding:3px var(--s-2);display:inline-block}
  .nldel{width:26px;height:26px;font-size:13px;opacity:1;pointer-events:auto}  /* 触屏无 hover，删除键常显 */
  .nltf{height:26px;line-height:24px;font-size:13px;padding:0 var(--s-4);opacity:1;pointer-events:auto}  /* 同上：触屏常显 */
  /*⚠⚠ .nlmove（「⇄ 调往」）原先漏在这条常显规则外 —— 用户报「手机端的调往无法使用」。
     后遗症比"看不见"更重：基线是 opacity:0 + **pointer-events:none**，
     按钮根本不接收点击；而桌面端是靠 :focus-within（鼠标移到该行 / Tab 聚焦）才显身的，
     触屏既没有 hover、点开后焦点也在别处 ⇒ 永远点不到。
     ⚠ 别只加 opacity:1：漏掉 pointer-events:auto 按钮照样点不动，
       而且会误判成"加了没用"（这类半截修复最容易让人以为规则没生效）。 */
  .nlmove{width:26px;height:26px;font-size:13px;opacity:1;pointer-events:auto}
  /* 调往浮层的筛选条（2026-10-05 第一百一十三轮）：手机上 24px 高的输入框太难点，
     且 3 个框 + 清空按钮在 320px 宽里会折行。放大点击区并让浮层可用更宽。 */
  .nlmove-pop{max-width:calc(100vw - 16px);max-height:min(74vh,420px)}
  /* 批量调往（D226）：手机上两块列表各给 150px —— 再大就会把「目标行」挤到只剩半行，
     用户得在面板里再滚一次才看得见目标（实测 390×844 就是这个形状）。 */
  .rmb-pop{max-height:min(84vh,600px)}
  .rmb-picks{max-height:150px}
  .rmb-targets{max-height:150px}
  .nlmove-filter input{height:32px;font-size:13px;flex:1 1 96px;width:96px}
  .nlmove-x{height:32px;font-size:12px;padding:0 10px}
  .nlmove-it{padding:var(--s-4) 8px}
  .nlrole{width:66px;font-size:13px;padding:3px 4px}
  .namelist{position:static;max-width:100%;width:100%;min-width:0;box-shadow:none}
  .nlname,.nlname.tonly,.nlname.rohalf{flex:1 1 auto;width:auto;min-width:0}
  .nlboxes{max-height:260px}
  /* ---- 明细表默认切换为「卡片视图」（可在工具栏按钮切回表格） ---- */
  body:not(.tv-table) .tablewrap .hscroll{display:none!important}
  body:not(.tv-table) .tablewrap .table-scroll{max-height:none;overflow:visible}
  body:not(.tv-table) .tablewrap table{display:block;width:100%}
  body:not(.tv-table) .tablewrap thead{display:none}
  body:not(.tv-table) .tablewrap tbody{display:block}
  /* 卡片体 = 4 列底层网格：正文字段各跨 2 列（一行两项），四个用工数各占 1 列（一行四项）。
     14 个字段从 14 行压到 7 行，卡高 455px → 实测 200~205px（360~430px 宽），一屏约 4 张卡 */
  /* 底层是 4 列：正文字段各占 2 列（一行两个），四个用工数各占 1 列（一行四个） */
  body:not(.tv-table) .tablewrap tr{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));column-gap:8px;row-gap:2px;
    background:var(--card);border:1px solid #e6ebf2;border-left:4px solid var(--blue);
    border-radius:12px;margin-bottom:10px;padding:var(--s-4) var(--s-5);box-shadow:0 1px 3px rgba(20,30,60,.06)}
  body:not(.tv-table) .tablewrap td{grid-column:span 2;display:flex;gap:6px;align-items:baseline;flex-wrap:wrap;min-width:0;
    border:none;padding:2px 0;font-size:13px;white-space:normal;line-height:1.35}
  body:not(.tv-table) .tablewrap td.cntcell,
  body:not(.tv-table) .tablewrap td.totcell{grid-column:span 1;gap:4px}
  body:not(.tv-table) .tablewrap td::before{content:attr(data-l);flex:0 0 auto;color:#8a94a6;font-size:11px;padding-top:1px}
  body:not(.tv-table) .tablewrap td[data-l="线体/设备"]::before{content:"设备"}
  body:not(.tv-table) .tablewrap td[data-l="设备/线体数"]::before{content:"数量"}
  body:not(.tv-table) .tablewrap td[data-l="内部支援调动"]::before{content:"内援"}
  body:not(.tv-table) .tablewrap td[data-l="外部支援"]::before{content:"外援"}
  body:not(.tv-table) .tablewrap td[data-l="正式工"]::before{content:"正式"}
  body:not(.tv-table) .tablewrap td[data-l="在册合计"]::before{content:"合计"}
  body:not(.tv-table) .tablewrap td[data-l="在册合计"]{background:#f6f9ff;border-radius:8px;padding:2px 8px;margin-top:2px;justify-self:start}
  body:not(.tv-table) .tablewrap td[data-l="在册合计"]::before{color:var(--blue-fg);font-weight:700}
  /* 「来源」自带内联 white-space:nowrap，这里必须 !important 才能放行换行（否则 edits 里的按钮会被挤出卡片） */
  body:not(.tv-table) .tablewrap td[data-l="来源"]{justify-content:flex-end;font-size:12px;white-space:normal!important}
  body:not(.tv-table) .tablewrap td.cntcell .namelist{flex:1 1 100%;width:100%;max-width:none}
  /* 展开姓名录入时占满整行：半列宽放不下人名输入框（由 JS 加 .nl-open，见 syncOpenNamelists） */
  body:not(.tv-table) .tablewrap td.cntcell.nl-open{grid-column:1/-1}
  /* 表格视图：保留横向滚动，但限高避免与页面滚动打架 */
  body.tv-table .tablewrap .table-scroll{max-height:64vh}
  body.tv-table .tablewrap{padding:10px 8px}
  /* 手机表格视图：**长内容换行**（2026-09-22 第二十五轮重做）
     ── 三版实测数据，别回退 ──────────────────────────────────────────────
     ① 全局 `#tableWrapSec table{table-layout:fixed;width:100%}`（桌面密排）：
        14 列硬压进 ~352px ⇒ 每列恒 25px、「一字一列」、表头竖排（2026-09-18 的坑，M0-9）。
     ② 2026-09-18 的修法 `{table-layout:auto;width:max-content}` + `th,td{nowrap}`：
        不再「一字一列」，但表格被撑到 **1326px**（列宽 = 该列**最长**内容宽度），
        横滑 974px；用户反馈「跟电脑端不一样，太长的没换行」（本轮需求）。
     ③ 本轮（当前）：`table-layout:auto` + `width:100%` + `white-space:normal`
        + `overflow-wrap:anywhere`。实测 **表宽 1326→587px、横滑 974→235px、被裁单元格 0**。
     实测对照（390px 视口，14 列）：
       · fixed + width:100%          → 表宽 352、横滑 0，但**每列 25px、150 个单元格被裁**（不可用）
       · auto  + width:max-content + nowrap（旧） → 表宽 1326、横滑 974、裁 0
       · auto  + width:100% + 换行（本版）        → 表宽 587、**横滑 235**、**裁 0**  ← 采用
     要点：`auto` 布局下 `width:100%` 只是**弹性目标**，浏览器仍按内容分配 ⇒ 不会塌成 1 字；
        真正把列压窄的是 `nowrap` 的消失 —— 换行后列宽由「最长单词」而非「最长整串」决定。
        ⚠ 不要改成 `table-layout:fixed`：试过 4 组（含百分比分栏、表头缩写），
          只要 fixed+100%，14 列恒被均分成 25px，一定「一字一列」。
     ⚠ `.table-scroll` 的 `overflow:auto` 保留：列宽之和仍略超屏宽（235px），横滑作兜底。
        `min-width` 只给必须可读的几列，防止被超长文本挤成 2~3 字。 */
  body.tv-table #tableWrapSec .table-scroll table{table-layout:auto;width:100%;min-width:0}
  body.tv-table #tableWrapSec .table-scroll th,
  body.tv-table #tableWrapSec .table-scroll td{white-space:normal;overflow-wrap:anywhere;word-break:break-word}
  /* ⚠ 2026-09-23 第二十六轮：**数量类列必须 nowrap**。
     用户报「设备数和合计这两列不用换行啊，换了没法看」：设备数 20X / 合计 17 这种短值
     被折成 2~3 行（实测设备数列仅 17.9px 宽、`20X` 占 3 行）。
     根因：`table-layout:auto` 下**没写 min-width 的列会塌到 min-content**，
     而这两列的 min-content 只有单个数字宽（7~13px）；表头「设备数/合计」反而更宽。
     实测各列「单行所需宽度」(360px 档，含 padding)：设备数 39 / 正式工 39 / 内援 28 /
     外援 28 / 外协 28 / 合计 28；全部 14 列都单行则共 632px > 可用 602px，
     所以**只给数量类列 nowrap，长文本列（楼层/专案/线体/来源）继续换行**。 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(7),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(7),
  body.tv-table #tableWrapSec .table-scroll th:nth-child(9),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(9),
  body.tv-table #tableWrapSec .table-scroll th:nth-child(10),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(10),
  body.tv-table #tableWrapSec .table-scroll th:nth-child(11),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(11),
  body.tv-table #tableWrapSec .table-scroll th:nth-child(12),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(12),
  body.tv-table #tableWrapSec .table-scroll th:nth-child(13),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(13){white-space:nowrap;overflow-wrap:normal;word-break:keep-all}
  /* 关键列的最小宽度（列序见 thead 14 列）。⚠ 手机档字号同时下调，否则表头自己就撑宽列。 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(1),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(1){min-width:52px}   /* 部门 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(2),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(2){min-width:44px}   /* 区域 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(3),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(3){min-width:46px}   /* 楼层 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(4),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(4){min-width:48px}   /* 专案 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(5),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(5){min-width:50px}   /* 线体/设备 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(7),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(7){min-width:39px}   /* 设备数 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(8),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(8){min-width:38px}   /* 负责人 */
  /* 四类用工 + 合计：纯数字，min-width 取表头宽度即可（31~32px 起，28px 会让「正式工」折行） */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(9),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(9),
  body.tv-table #tableWrapSec .table-scroll th:nth-child(12),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(12){min-width:34px}  /* 正式工 / 外协 */
  body.tv-table #tableWrapSec .table-scroll th:nth-child(10),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(10),
  body.tv-table #tableWrapSec .table-scroll th:nth-child(11),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(11),
  body.tv-table #tableWrapSec .table-scroll th:nth-child(13),
  body.tv-table #tableWrapSec .table-scroll td:nth-child(13){min-width:29px}  /* 内援 / 外援 / 合计 */
  body.tv-table #tableWrapSec .table-scroll th{font-size:11px;padding:var(--s-2) 3px;line-height:1.25}
  body.tv-table #tableWrapSec .table-scroll td{font-size:11px;padding:var(--s-2) 3px;line-height:1.3}
  /* ---- 电话簿等内嵌表格 ---- */
  #phoneTable{max-height:320px}
  .mgrtable{font-size:12px}
  .mgrtable th,.mgrtable td{padding:6px 8px}
  /* ---- 弹窗 / 浮动卡片：限高可滚、贴边留白收窄 ---- */
  .modal{padding:12px;align-items:flex-start}
  .modalbox,.modal-box,.pwchange-box{max-height:88vh;overflow-y:auto;-webkit-overflow-scrolling:touch}
  .modalbox,.pwchange-box{width:auto;min-width:0;max-width:94vw;padding:16px 14px;border-radius:14px}
  #oplogModal .modalbox,#emailModal .modal-box{width:auto}
  .modaltitle{font-size:15px}
  .email-row,.pwdadmin-row{flex-wrap:wrap}
  .pwdadmin-list{min-width:0}
  .pwd-selfsearch{min-width:0}
  .mgrow{flex-wrap:wrap}
  .mgrow span{width:100%}
  .mgrow input{width:100%}
  .general-grid,.sa-row{grid-template-columns:1fr}
  .general-grid label:first-child{grid-column:auto}
  .sa-field{width:100%;margin-right:0}
  .sa-field input{width:100%}
  .injury-grid{grid-template-columns:1fr}
  .injury-grid label{grid-column:1/-1}
  .injury-form-btns{flex-wrap:wrap}
  .addpanel-box{width:min(680px,96vw);margin:2vh auto 24px;padding:14px 14px 16px;max-height:94vh;overflow:auto}
  .addgrid{grid-template-columns:1fr;gap:10px}
  .addbtns{gap:10px}
  .addhint{display:none}
  .comboback{padding:6px 10px;font-size:12px}
  .addpanel-close{padding:var(--s-2) 12px;font-size:12px}

  /* ================= 手机端「横向溢出」根治层（2026-09-16）=================
     问题：390px 的手机上，文档宽度被撑到 544px（溢出 154px）→ 整页可左右拖、
     所有视图都错位、文字被迫缩得很小。两处真因，都必须在本层修复：            */
  /* ① 顶部状态条。桌面优化层（本文件更早处）写的是
        `.statusbar .impstat{white-space:nowrap}` 与 `.statusbar #connStat{flex-shrink:0}`，
        特异性分别是 (0,2,0) / (1,1,0)，**高于**本层早先的 `.impstat` / `#connStat`（0,1,0 / (1,0,0)），
        所以那两条桌面色一直被沿用：#connStat 不可收缩 + 不换行 → 单它就要 223px，
        加上 logo 与标题把 header 撑到 544px。
        这里用**同特异性 + 后写**覆盖（媒体查询同在文件末尾，后者胜）。
      ⚠ 注意不是简单地放开换行：状态文案会从「⏳ 连接中…」变成
        「已连接 · 已载入最新数据 rev 6190 · 04:52:11」，一旦允许折行，
        顶部栏高度就会随文案长短变化 —— 实测 header 先长高 139px 再缩回 77px，
        整页内容跟着上下跳（CLS 0.54）。所以改为**保持单行 + 省略号 + 允许收缩**：
        高度恒定，宽度不够就截断，既不溢出也不跳动。全文仍可用悬停 title 查看。 */
  .statusbar{flex-wrap:nowrap;min-width:0}
  .statusbar .impstat{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
  .statusbar #connStat{flex-shrink:1;min-width:0}
  /* ⚠ 2026-09-23 第二十六轮根治「顶栏连接状态胶囊超出屏幕 3px」（用户报障）。
     症状：360px 档 `#connStat` 盒子只有 16px、右边界 362.9（超出视口 2.9px），整页可左右拖。
     真因（实测 computed style 链，见 `_why-over.js`）：
       ① `.statusbar{flex:1 1 0px}` —— 这条桌面规则让状态条**基准宽度为 0**，
          在手机档（header 已 flex-wrap:wrap，状态条独占一行）它就被压成 27px；
       ② `#connStat{min-width:0}` 允许它继续缩到 16px，而内部是 `nowrap` 的 223px 文本；
       ③ 它的 `overflow:hidden` 只裁**自身盒内**，但因为 flex 项的
          min-content 参与行布局计算，溢出宽度仍计入**行** ⇒ 视口被撑到 363px。
     修法：给它一个**不小于自己内容最小宽度**的下限，同时**允许收缩+省略号**。
     不要把 min-width 设成具体像素（会把状态条顶回 223px 重新撑破整页）——
     用 `min-width:auto` 的**反向**：显式给出 `flex-basis:auto` + `flex-shrink:1`
     + `min-width:min-content`，让浏览器在「不至于溢出」的前提下尽量多地保留文字。 */
  .statusbar{flex:0 1 auto;min-width:0}
  .statusbar #connStat{flex:0 1 auto;min-width:min-content;max-width:100%}
  header{flex-wrap:wrap}
  header>*{min-width:0}
  /* ② CSS Grid 子项默认 min-width:auto：子项无法收缩到内容最小宽度以下，
        于是被内部宽表（负责人表 504px、电话簿表 420px）顶大 → 卡片 526px。
        置 0 后卡片回到容器宽，宽表改由 #deptMgrTable / #phoneTable 自身横向滚动承担。 */
  .grid2>*{min-width:0}
  .kpis>*{min-width:0}
  .wrap{min-width:0}
  /* ③ 兜底：真因已修，这里只保证「将来新增元素再撑破」不会立刻变成整页横拖。
        只写在 body 上（不写 html）：body 的 overflow 会被提升到视口，
        body 自身仍算 visible，因此不会破坏 .sidebar 的 position:sticky。 */
  body{overflow-x:hidden;max-width:100%}
  /* ④ 分级部门视图：.lvl-cards 用 minmax(360px,1fr)，而 390px 屏扣掉内边距只剩 344px,
        卡片被硬撑到 360px → 内部溢出 16px（原先被 header 的大溢出掩盖，修完 header 才暴露）。
        窄屏改单列自适应；logged 行的负外边距（用于放大 hover 高亮）在窄屏收进来。 */
  .lvl-cards{grid-template-columns:minmax(0,1fr);margin-left:0;margin-right:0}
  .lvl-stage-row.logged{margin-left:0;margin-right:0;padding-left:6px;padding-right:6px}

  /* ⑤ 首屏 KPI 区「先空后满」造成的整页跳动（2026-09-16 逐帧实测定位）：
        renderKPIs() 依赖登录态，登录校验完成前 #kpis 是空的（高度 0）；约 1.9 秒后
        卡片填上（高度 287px）→ 下面所有卡片整体下移 287px（手机端 CLS 0.32 的来源）。
        注意这是「首屏画得早」带来的副作用：改造前 echarts 阻塞首屏、FCP 在 2.6 秒，
        这次跳动发生在首屏绘制之前所以不计入 CLS。
        修法 = 在 #kpis 标签里直接写死同构骨架（见 HTML 中 `<div class="kpis" id="kpis">`），
        首屏就有最终高度，数据到达时只换文字不换几何 → 位移为 0，且各宽度自适应。
        ⚠ 曾试过 `body:not(.authed) #kpis{min-height:288px}`：**无效**。逐帧实测 body 在
        559ms 就带上 .authed（早于数据到达的 1790ms），规则恰好在最需要它的那段窗口失效；
        且固定像素在 320/430px 屏上高度对不齐，反会造出二次位移。故废弃该写法。 */
}
/* 超窄屏进一步收紧 */
@media(max-width:380px){
  .kpi .v{font-size:17px}
  .kpi .l{font-size:11px}
  .chart{height:210px}
  /* 超窄屏：卡片两列再挤一点，靠缩字号而不是加宽标签（标签固定宽度会让值换行，白压高度） */
  body:not(.tv-table) .tablewrap tr{column-gap:10px}
  body:not(.tv-table) .tablewrap td{font-size:12px;gap:var(--s-2)}
  body:not(.tv-table) .tablewrap td::before{font-size:11px}
}
/* ===== OSS 汇报（页面底部台账）：OSS 到期前 7 天自动提醒「总负责人 + 负责人」 ===== */
#ossSec{margin:20px 0 6px}
/* 「华南售后总览」为统计看板，不展示 OSS 台账；只在「售后调试部门」「分级部门」视图底部显示 */
body.view-overview #ossSec{display:none!important}
/* ⚠ 这里原有「部门分页模式隐藏 OSS 台账」的 CSS（2026-09-16 随「部门页」下线删除）——
   它配合 renderOss() 里那句对应的短路 return，会让「明细编辑」分区（deptsec-detail）
   的 OSS 台账也整块消失。具体选择器已随功能一并移除，勿再引入。 */
.oss-card{border:1px solid var(--line);border-radius:12px;background:var(--card);overflow:hidden;box-shadow:0 1px 3px rgba(31,56,102,.06)}
.oss-hd{display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;padding:12px 16px;border-bottom:1px solid var(--line);background:var(--card-hd)}
.oss-hd .oss-t{font-size:15px;font-weight:800;color:var(--ink-head)}
.oss-hd .oss-sub{font-size:12px;color:var(--muted);margin-top:3px;line-height:1.6}
.oss-hd .oss-sub b{color:#2f54b0}
.oss-acts{margin-left:auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.oss-acts .btn{padding:6px 12px;font-size:12px}
.oss-stat{width:100%;font-size:12px;color:var(--muted);padding:var(--s-3) 16px;background:var(--card);border-bottom:1px solid var(--line)}
.oss-stat b{color:#25324a}
.oss-stat .warn{color:var(--red-fg);font-weight:700}
.oss-stat .soon{color:#c77800;font-weight:700}
.oss-stat .muted{color:var(--muted)}
.oss-scroll{overflow-x:auto}
/* min-width 只是「窄屏兜底」：≥800px 时表格恒定 width:100% 铺满容器、列宽按比例分配（fixed 布局多出的百分比会自动摊到各列），
   不再撑出左右滑动条；真到手机宽度（见下方 max-width:640px 媒体查询）才允许横向滚动，避免列被挤到不可读 */
table.oss-table{width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;min-width:800px}
table.oss-table th{background:var(--card-hd);color:#41506b;font-weight:700;font-size:12px;padding:var(--s-4) 10px;border-bottom:1px solid var(--line);text-align:left;white-space:nowrap}
table.oss-table td{padding:8px 10px;border-bottom:1px solid var(--line);color:#25324a;vertical-align:middle;white-space:normal;overflow-wrap:anywhere}
/* ⚠ 同 table.wh-table：不写 white-space:normal 的话，本页第 264 行的全局 `th,td{white-space:nowrap}`
   会把下面的 overflow-wrap:anywhere 一起架空（nowrap 下永不换行）→ 长文本横向压到相邻列。
   已带 nowrap 的日期/数字/操作列（td.oss-date / td.oss-num / td.oss-op）特异性更高，不受影响。 */
/* 窄视口下表头不硬撑：列宽不够放标题时允许折行，避免表头压表头 */
@media(max-width:1100px){
  table.oss-table th{white-space:normal;overflow-wrap:anywhere}
}
table.oss-table tbody tr:last-child td{border-bottom:none}
table.oss-table tbody tr:hover{background:var(--row-hover)}
table.oss-table td.oss-date{white-space:nowrap;font-variant-numeric:tabular-nums}
table.oss-table th.oss-num-h{text-align:center}
table.oss-table td.oss-num{text-align:center;white-space:nowrap;font-variant-numeric:tabular-nums;font-weight:700;color:var(--ink-head)}
table.oss-table td.oss-num.none{font-weight:400;color:var(--muted)}
table.oss-table td.oss-dept{font-weight:600;color:#2f54b0}
/* ⭐ 2026-10-12（D182）「OSS 时长」列：与日期列同一套数字排版（nowrap + 等宽数字）。
   ⛔ 不能不写 nowrap —— 「132 天」在这种窄列里会被折成「132 / 天」两行、行高翻倍。 */
table.oss-table td.oss-dur{white-space:nowrap;font-variant-numeric:tabular-nums}
table.oss-table td.oss-op{text-align:right;white-space:nowrap}
/* ⚠ 必须显式 white-space:normal（2026-09-22 第二十一轮回归发现）：
   `td.oss-date{white-space:nowrap}` 是给**上面那行日期本身**用的（日期不能被折断），
   但它会**沿继承链漏到这一行的副标题上** —— 到期时间单元格里正是一个 `<span class="oss-sub2">`。
   而副标题文案「剩余 5 天 · 进入提醒窗口」（11 字）在窄视口下需要 124px，
   到期时间列在 1180/1240/1280 只有 116~128px、内容区仅 96px ⇒ 被 nowrap 顶出 18px，
   整张表随即出现横向滚动条、末列被截（check-oss-table-fit 实测 [1180,1240,1280]）。
   1440 下该列到 146px、内容区 126px ≥ 124px 才侥幸不出事 —— 所以这是个**只在中窄屏发作**的坑。
   副标题是「说明性文字」，本来就该折行；这里把继承来的 nowrap 关掉即可，不影响日期行。 */
.oss-sub2{display:block;font-size:11px;color:var(--muted);margin-top:2px;white-space:normal}
.oss-due-ok{color:#25324a}
.oss-due-soon{color:#c77800;font-weight:700}
.oss-due-over{color:var(--red-fg);font-weight:700}
/* 到期日已过：状态显示「OSS 服务结束」（2026-09-17 用户要求，不再显示「已过期 N 天」）。
   用次级文本色弱化 —— 服务结束是**终态事实**而非待办告警，不应与「今天到期 / 进入提醒窗口」
   的红橙告警抢注意力；红橙只留给还需要动作的那两类。 */
.oss-due-ended{color:var(--sub);font-weight:600}
.oss-due-none{color:var(--muted)}
.oss-op .btn{padding:3px var(--s-4);font-size:12px;margin-left:4px}
/* OSS 台账操作列 4 颗按钮（2026-09-28 新增「🔁 续期 / ✅ 结案」）：
   ⚠ 必须限定在 #ossSec 的 OSS 台账上 —— `.oss-op .btn` 是**三张表共用**的
     （驻厂信息表的删除按钮、现场问题表的编辑/删除/打印也都挂 td.oss-op）。
   实测（_probe-oss-opwidth）：操作列 13% 时 4 颗按钮在 1180px 需 166px、列只有 131px
   ⇒ 整表横向溢出 35px 且末列被截；收到 `3px 6px` 内边距 + 操作列 15%（见 thead 内联宽度）
   后，1180 / 1280 / 1366 / 1440 / 1600 五档全部归零。 */
#ossSec table.oss-table td.oss-op .btn{padding:3px 6px}
/* 已结案：整行弱化。服务已终止且人已确认过 —— 它不再需要动作，视觉上就该退到背景里。
   与 `.oss-due-ended`（到期日已过的终态）同一套语言，只是更强一档（连行一起降饱和）。
   ⚠ 背景必须写在 td 上：`td` 有自己的 background 就会盖掉 `tr:hover` 那层。 */
table.oss-table tbody tr.oss-closed td{color:var(--muted);background:#fafbfd}
table.oss-table tbody tr.oss-closed td.oss-dept{color:var(--muted)}
table.oss-table tbody tr.oss-closed td.oss-num{color:var(--muted)}
table.oss-table tbody tr.oss-closed:hover td{background:var(--surface2)}
/* 已结案的到期状态：比 `.oss-due-ended`（服务结束 —— 结束了但没人处理）再弱一档。
   这是「已经处理完、归档了」，不该再带任何催促感。 */
.oss-due-closed{color:var(--muted);font-weight:600}
/* ⚠ 续期次数**不做独立徽标**：开始/到期两列都是 10%，窄屏内容区只有 ~78px，
   而日期本身已占 66px —— 再塞一个 inline-block 徽标必然横向溢出
   （同 check-oss-table-fit 记的那族坑）。改为并入到期列的副标题（.oss-sub2 可折行）。 */
.oss-empty{padding:20px 16px;color:var(--muted);font-size:13px;line-height:1.7}
.oss-form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px 14px}
.oss-form-grid label{display:flex;flex-direction:column;gap:4px;font-size:12px;color:#41506b;font-weight:600}
.oss-form-grid label.full{grid-column:1/-1}
.oss-form-grid input,.oss-form-grid select{padding:6px var(--s-4);border:1px solid #d0d5dd;border-radius:6px;font-size:13px;background:var(--pure);font-weight:400;color:#25324a;width:100%;box-sizing:border-box}
.oss-form-grid .oss-lb{display:block;line-height:1.4}
.oss-form-grid .oss-dept-hint{font-size:12px;font-weight:500;color:var(--muted);line-height:1.4}
.oss-form-grid .oss-dept-hint.warn{color:#c9772a}
.oss-form-grid .oss-dept-hint.auto{color:#2f54b0}
.oss-form-grid .req{color:var(--red-fg);font-style:normal}
.oss-tip{font-size:12px;color:var(--muted);background:var(--card-hd,#EAF3FE);border:1px dashed #cdd6e4;border-radius:8px;padding:8px 10px;margin:0 0 12px;line-height:1.7}
.oss-tip b{color:#2f54b0}
.oss-err{color:var(--red-fg);font-size:12px;min-height:16px}
@media(max-width:640px){
  .oss-form-grid{grid-template-columns:1fr}
  .oss-hd{align-items:stretch}
  .oss-acts{margin-left:0;width:100%}
  table.oss-table{min-width:980px}
}

/* ===== 「OSS 汇报」独立导航视图（侧边栏功能导航，2026-09-13）：汇总查看各部门 OSS 台账，只读 =====
   整页只保留 OSS 台账：隐藏 .wrap 下除 #ossSec 外的所有区块（KPI 卡、图表、人力明细表、工伤卡等），
   且该视图内不提供「新增 / 编辑 / 删除」入口。 */
body.view-oss #ossSec{display:block!important;margin:0}
body.view-oss .wrap > *:not(#ossSec){display:none!important}
#ossRoTip{display:none}
body.view-oss #ossRoTip{display:block}
/* 只读：隐藏「新增 OSS」，并整列隐藏「操作」（编辑 / 删除） */
body.view-oss #ossAddBtn{display:none!important}
body.view-oss th.oss-op-h,body.view-oss td.oss-op{display:none!important}

/* ===== 「仓库管理」独立导航视图（2026-09-14）：赣州仓库 / 观澜仓库 =====
   整页只保留 #wareSec：隐藏 .wrap 下所有其它区块；库存由「入库流水 - 出库流水」自动算出，
   因此库存页不提供直接编辑，只能通过入库 / 出库登记来改变库存。 */
#wareSec{display:none}
body.view-ware #wareSec{display:block;margin:0}
body.view-ware .wrap > *:not(#wareSec){display:none!important}
/* ===== 「员工驻厂信息管理」独立导航视图（2026-09-19 第八轮）：赣州 / 深圳 =====
   与「仓库管理」同构：整页只保留 #empSec，其余 .wrap 子块一律隐藏。
   ⚠ 站点名是「赣州 / 深圳」两个**纯站点名**，与仓库的「赣州仓库 / 观澜仓库」不是同一套
     （仓库是存放地点，站点是人员驻场地），两套独立维护、互不影响。 */
#empSec{display:none}
body.view-emp #empSec{display:block;margin:0}
body.view-emp .wrap > *:not(#empSec){display:none!important}
/* ===== 「现场问题收集」独立导航视图（2026-09-22 第二十三轮）=====
   与「员工驻厂信息管理」同构：整页只保留 #probSec，其余 .wrap 子块一律隐藏。
   ⚠ 三条规则**一条都不能漏**（#probSec 默认隐藏 + 进入视图显示 + 其它块隐藏），
     漏掉第三条会让「总览的 KPI 卡」在 prob 视图下多长出来一张（铁律 3b）。 */
#probSec{display:none}
body.view-prob #probSec{display:block;margin:0}
body.view-prob .wrap > *:not(#probSec){display:none!important}
/* ===== 「外派支援」独立导航视图（2026-09-28 第七十四轮）=====
   与「现场问题收集」同构：整页只保留 #xsupSec，其余 .wrap 子块一律隐藏。
   ⚠ 三条规则**一条都不能漏**（#xsupSec 默认隐藏 + 进入视图显示 + 其它块隐藏），
     漏掉第三条会让「总览的 KPI 卡」在 xsup 视图下多长出来一张（铁律 3b）。 */
#xsupSec{display:none}
body.view-xsup #xsupSec{display:block;margin:0}
body.view-xsup .wrap > *:not(#xsupSec){display:none!important}
/* ⚠ 2026-09-28（第七十五轮）：#xsupSec 从此有**两个出口**，别只改一处 ——
   ① 顶级「外派支援」（body.view-xsup）= **只读汇总页**：汇总所有可见部门的记录，
      不渲染「➕ 新增外派」与操作列（用户定稿：「只做汇总显示」）；
   ② 部门视图 → 「外派支援」分区（body.view-dept.deptsec-xsup，见上方部门分区段）
      = **本部门**的可编辑台账，跟随左侧选中的部门。
   ⇒ 「当前是哪种模式」的唯一判据是 curView（见 renderXsup / xsupEditCtx），
     不要在两个渲染入口里各判一次，否则两处口径迟早漂移。 */

/* ===== 「👤 华南售后员工信息」独立视图隔离（2026-09-28 第八十二轮）=====
   ⚠⚠ 三条规则**一条都不能漏**（默认隐藏 + 进入视图显示 + 其它块隐藏）：
     漏掉第三条 ⇒ 总览的 KPI 卡会在本视图下多长出来一张（铁律 3b）；
     第三条**必须带 !important** —— JS 常给兄弟卡写内联 display:block，内联值赢过任何选择器。 */
#staffSec{display:none}
body.view-staff #staffSec{display:block;margin:0}
body.view-staff .wrap > *:not(#staffSec){display:none!important}

/* ===== 「华南售后员工信息」二级栏：在职 / 离职 两块互斥（2026-10-10 第一百六十一轮）=====
   ⚠ 与部门视图的 deptsec-* 同一做法：类名在 body 上**成对**写（staffsec-on / staffsec-off），
     缺一个就会出现「点了二级子项，上一块不收、新块也不出」两个同时显示。
   ⚠⚠ 默认（没有 staffsec-* 类，例如首帧 switchView 还没跑到）显示**在职**那一块，
     两个都不显示等于页面空白 —— 这里刻意让「在职」是缺省态，不是「两个都藏」。 */
body.view-staff #staffOffBlock{display:none}
body.view-staff #staffOutsBlock{display:none}
body.view-staff.staffsec-off #staffOnBlock{display:none}
body.view-staff.staffsec-off #staffOffBlock{display:block}
/* ★ 第一百七十三轮：第三块「外协人员信息」（#staffOutsBlock）—— 三块**互斥**。
   ⚠ 三个类名必须**成对**增删（见 applyStaffSection）：漏一个就会出现「点的子项不收、
     上一块还挂着」两块同框（部门视图 deptsec-* 那套的注释写着同一个坑）。
   ⚠ 缺省态（body 上没有任何 staffsec-* 类，例如首帧还没跑到）依旧显示「在职」：
     outs 这一档把 on 压住、off 本来就被上面的缺省规则压着，三条合起来才是三选一。 */
body.view-staff.staffsec-outs #staffOnBlock{display:none}
body.view-staff.staffsec-outs #staffOffBlock{display:none}
body.view-staff.staffsec-outs #staffOutsBlock{display:block}

/* ===== 「⚡ 快捷入口」（手机端）+「🩺 数据健康分」（总览）（2026-09-28 第七十轮 · D10）=====
   ⚠⚠ 这两个块**不是独立视图**：不参与 switchView 的 view-* 名单，
      也**不需要**加进 lockGate / switchView 那两处 `classList.remove` —— 它们不带 view 类。
   · #mQuickSec —— **仅手机端**（桌面侧边栏本就是纵向列表，再加一排入口是重复）。
     默认 display:none，靠 @media(max-width:640px) 打开。
     ⚠ 这里**故意不加 !important**：让「今日待办 / 周报 / 专案全景 / 人力趋势」那几个
       `.wrap > *:not(#xxxSec){display:none!important}` 的隔离规则正常把它藏掉 ——
       那几个是整页独占视图，顶部再挂一排入口会把主体挤下去。
   · #healthSec —— **已移入 #reportSec 内**（2026-09-28 第七十一轮，用户要求：
     「数据健康分在周报月报里面显示，总览里面不显示」）。
     ⚠⚠ 因此这里**不能**再写 `#healthSec{display:none}` + `body.view-overview #healthSec{display:block}`：
        它的显隐**完全跟随父容器** #reportSec（`body.view-report #reportSec{display:block}`）——
        多写一条规则就多一个「两边不一致」的机会（铁律 3b：显隐赋值必须双向；这里干脆不需要赋值）。
     只补一条与「一页纸」对齐的宽度规则（见下）。
   ⚠ 配色一律走 :root 变量（护眼主题检查 smoke-eyecare-theme 会抓硬编码灰蓝值）。 */
#mQuickSec{display:none}
/* ⚠ 与 .rpt-paper 同宽居中（一页纸是 max-width:1100px;margin:0 auto），否则这张卡会横跨整个 wh-card。 */
#healthSec{max-width:1100px;margin:12px auto 0}
.mq-card,.hc-card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:var(--s-5) var(--s-6);margin-bottom:12px;box-shadow:0 1px 3px rgba(20,30,60,.04)}
.mq-hd,.hc-hd{display:flex;align-items:baseline;gap:8px;margin-bottom:var(--s-4);flex-wrap:wrap}
.mq-t,.hc-t{font-size:14px;font-weight:800;color:var(--ink);flex:none}
.mq-sub,.hc-sub{font-size:12px;color:var(--sub);flex:1 1 auto;min-width:0}
/* ⚠⚠ 下面这几条 min-height 曾是**首屏 CLS 的命根子**（2026-09-28 实测 0.2741 > 0.05 被打回）：
   本模块的两块内容全部由 JS 渲染，而 #mQuickSec 仍在 .wrap 顶部、又在 KPI 骨架**之上** ——
   渲染前（HTML 静态空壳）与渲染后（3 个按钮 / 5 个维度）高度差 ~84px，足够把整屏推下去。
   对策与本项目 KPI 骨架（#kpis 里写死 7 张卡）同源：**让占位高度 = 渲染后高度**。
   ⚠ 第七十一轮起 #healthSec 已移出总览（进了 #reportSec）⇒ 它对**首屏** CLS 的影响消失；
     这里保留 .hc-* 的骨架尺寸，是为了「切到周报视图时块高不跳」。
   ⚠ 动 .mq-* 的 min-height 仍必须同步跑 check-mobile.js 的 H5（4G+4x CPU，CLS<0.05）。 */
.mq-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;min-height:78px}
.mq-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;background:var(--card-hd);border:1px solid var(--line);border-radius:10px;padding:12px 6px;font-family:inherit;font-size:12px;font-weight:700;color:var(--ink);cursor:pointer;position:relative;min-height:78px;min-width:0;overflow:hidden}
.mq-btn:active{background:var(--row-hover)}
.mq-ico{font-size:22px;line-height:1}
.mq-nm{line-height:1.25;text-align:center;max-width:100%;overflow:hidden;text-overflow:ellipsis}
.mq-badge{position:absolute;top:5px;right:5px;min-width:19px;height:19px;border-radius:999px;background:var(--red);color:var(--pure);font-size:11px;font-weight:700;line-height:19px;text-align:center;padding:0 var(--s-2);font-variant-numeric:tabular-nums}
.hc-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.hc-score{display:flex;align-items:baseline;gap:var(--s-2);flex:none}
.hc-num{font-size:30px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums}
.hc-unit{font-size:12px;color:var(--sub);font-weight:600}
.hc-dims{display:flex;flex-wrap:wrap;gap:6px;flex:1 1 auto;min-width:0;min-height:24px;align-content:flex-start}
.hc-dim{display:inline-flex;align-items:center;gap:4px;background:var(--card-hd);border:1px solid var(--line);border-radius:999px;padding:3px var(--s-4);font-size:12px;color:var(--ink);cursor:pointer;font-family:inherit;white-space:nowrap}
.hc-dim.bad{border-color:var(--red);color:var(--red-fg);font-weight:700}
.hc-dim.na{opacity:.5;cursor:default}
.hc-note{font-size:12px;color:var(--sub);margin-top:8px;line-height:1.6;min-height:20px}
@media(max-width:640px){
  #mQuickSec{display:block}
  /* ⚠ 手机档的 min-height 必须 ≥ 渲染后实际高度（见上方 CLS 注释）：
     按钮 84px、5 个维度胶囊约占 3 行 ≈ 56px、note 长文案约占 3 行 ≈ 56px。
     ⚠ 改这三个数就要复跑 check-mobile.js 的 H5。 */
  /* ── D293 · 三件事：3 张大卡 → 一行胶囊（实测 138px → 约 76px）────────────
     原来的 84px 方格按钮对「每天都要点一遍」的用户是重复导航，占掉首屏 16%。
     ⚠ 高度必须与 .mq-grid/.mq-btn 的 min-height 同步，且**占位骨架同步**：
       HTML 里预置的 3 个 disabled 按钮走同一套 CSS，静态高度仍等于渲染后高度。
     ⚠ 改这几个数必须复跑 check-mobile-248 的 H5（CLS < 0.05）。 */
  .mq-card{padding:8px 10px;margin-bottom:10px}
  .mq-hd{margin-bottom:0;align-items:center}
  .mq-sub{display:none}
  .mq-grid{display:flex;gap:6px;min-height:34px;margin-top:var(--s-3);overflow-x:auto;scrollbar-width:none}
  .mq-grid::-webkit-scrollbar{display:none}
  .mq-btn{flex:1 1 0;min-width:0;min-height:34px;flex-direction:row;gap:var(--s-2);padding:var(--s-2) 6px;border-radius:var(--r-2);font-size:12px}
  .mq-ico{font-size:13px}
  .mq-nm{flex:0 1 auto;min-width:0;white-space:nowrap}
  /* badge 从「右上角绝对定位」改成「跟在名字后面」：34px 高的胶囊里浮一个 19px 圆点会压字 */
  .mq-badge{position:static;min-width:15px;height:16px;line-height:16px;font-size:10px;padding:0 3px;flex:none}
  .hc-num{font-size:26px}
  .hc-row{gap:var(--s-4)}
  /* ⚠⚠ 88 不是拍脑袋：探针实测 5 个真实维度胶囊在 390px 下占 **3 行 = 88px**，
     而初始骨架占 2 行 = 57px ⇒ 差 31px 直接把下面推下去。min-height 必须取**渲染后**的高度。 */
  .hc-dims{min-height:88px}
  .hc-note{min-height:56px}
}

/* ===== 「项目转移」（2026-09-24 第四十一轮）=====
   与「现场问题收集」同构：整页只保留 #projXferSec，其余 .wrap 子块一律隐藏。
   ⚠ 三条一条都不能漏；第三条必须带 !important —— JS 会给兄弟卡片写**内联**
     display:block，内联赢任何选择器，漏了就会在别的视图里多长出一张卡（铁律 3b）。 */
#projXferSec{display:none}
body.view-projxfer #projXferSec{display:block;margin:0}
body.view-projxfer .wrap > *:not(#projXferSec){display:none!important}

/* ===== 「人力趋势」（2026-09-26 第四十三轮复上线）=====
   与「项目转移」同构：整页只保留 #weeklyTrendSec，其余 .wrap 子块一律隐藏。
   ⚠ 三条一条都不能漏；第三条必须带 !important —— JS 会给兄弟卡片写**内联**
     display:block，内联赢任何选择器，漏了就会在别的视图里多长出一张卡（铁律 3b）。 */
#weeklyTrendSec{display:none}
body.view-weeklytrend #weeklyTrendSec{display:block;margin:0}
body.view-weeklytrend .wrap > *:not(#weeklyTrendSec){display:none!important}
/* ===== 「专案全景」（2026-09-28 第五十九轮 · B4）=====
   与「人力趋势」同构：整页只保留 #projSec。⚠ 三条一条都不能漏，第三条必须 !important
   （JS 会给兄弟卡片写内联 display:block，内联赢任何选择器 ⇒ 漏了就在别的视图里多长出一张卡）。 */
#projSec{display:none}
body.view-proj #projSec{display:block;margin:0}
body.view-proj .wrap > *:not(#projSec){display:none!important}
/* 一览表交互（2026-10-02）：行可点（联动上方图表）、筛选钮、表头排序箭头 */
.pjt-row{cursor:pointer}
.pjt-row:hover td{background:rgba(76,141,255,.10)}
#projSec th[data-psort]{cursor:pointer;user-select:none;white-space:nowrap}
#projSec th[data-psort]:hover{color:var(--fg,#e8eef6)}
/* 可排序表头的键盘可达性（2026-10-03 补齐 · 界面评审第 9 项「表格排序仅鼠标可达」）：
   原实现只有 cursor:pointer + click 委托，th 不可聚焦、无 aria-sort，
   键盘与读屏用户完全无法触发排序。
   这里补:focus-visible 焦点环；颜色用 currentColor + outline，不新增硬编码色
   ⇒ 自动跟随三套主题，不触发硬编码灰蓝扫描。
   ⚠ 用 :focus-visible 而非 :focus —— 鼠标点击排序后表头会获得焦点，
   若用 :focus 会留下一圈常驻焦点框（鼠标用户也要付出视觉代价）。 */
#projSec th[data-psort]:focus-visible{outline:2px solid var(--blue,#4C8DFF);outline-offset:-2px;border-radius:4px}
.pjt-arrow{color:var(--sub);font-size:11px}
.pjt-ft{border:1px solid rgba(128,140,160,.35);background:transparent;color:var(--sub,#aab6c8);border-radius:16px;padding:4px 12px;font-size:12px;cursor:pointer;font-family:inherit;transition:all .15s}
.pjt-ft:hover{border-color:#4C8DFF;color:var(--fg,#e8eef6)}
/* 筛选钮的键盘焦点态（2026-10-03 补齐 · 界面评审第 11 项「输入框 outline:none 无兜底」）：
   实测发现这一组是全站唯一「可聚焦但聚焦后毫无视觉变化」的控件 ——
   .pjt-ft 只有 :hover 与 .pjt-ft-on，没有 :focus-visible，
   键盘用户 Tab 到「全部阶段/进行中/…」时不知道焦点在哪。
   ⚠ 用 :focus-visible 而非 :focus：鼠标点击后按钮会 retain 焦点，
   用 :focus 会让点过的按钮一直挂着焦点框。 */
.pjt-ft:focus-visible{outline:2px solid var(--blue,#4C8DFF);outline-offset:2px}
.pjt-ft.pjt-ft-on{background:var(--blue-solid);border-color:var(--blue-solid);color:var(--pure);font-weight:600}
/* ===== 「今日待办」（2026-09-28 第六十二轮 · C7）=====
   与「专案全景」同构：整页只保留 #todoSec。⚠ 三条一条都不能漏，第三条必须 !important
   （JS 会给兄弟卡片写内联 display:block，内联赢任何选择器 ⇒ 漏了就会在别的视图里多长出一张卡）。
   ⚠ 本视图**刻意不做侧栏子导航**：它的分组在页内（按来源分段、按紧急度排序），
     不像 ware / emp / prob 那样「每个站点 / 部门一个子按钮」—— 待办是「今天要做什么」，
     按站点或部门切分只会让人漏掉另一边的急事（与 §4 的响应式子导航规则无关，因为压根没有）。 */
#todoSec{display:none}
body.view-todo #todoSec{display:block;margin:0}
body.view-todo .wrap > *:not(#todoSec){display:none!important}
/* 待办条目：左侧一道 4px 紧急度色条 + 内容 + 右侧「去处理」。
   ⚠⚠ 紧急度<b>同时</b>用三种方式表达：色条颜色、徽标文字（「已过期 12 天」/「今天到期」）、
     **排序位置**。红色只是辅助 —— 色盲、黑白打印、投影仪都会把颜色丢掉，
     所以真正传达信息的是文字与「排在前面」这件事（与 C6 超期标红同一原则）。 */
.todo-row{display:flex;align-items:flex-start;gap:10px;padding:8px 10px;border:1px solid #e6eaf2;border-left-width:4px;border-radius:var(--r-2);background:var(--pure);margin-bottom:var(--s-3)}
.todo-row.lv0{border-left-color:#dc2626;background:#fff7f7}
.todo-row.lv1{border-left-color:#ea580c;background:#fffaf5}
.todo-row.lv2{border-left-color:#d97706}
.todo-row.lv3{border-left-color:#2563eb}
.todo-row.lv4{border-left-color:#64748b}
.todo-row .td-main{flex:1;min-width:0}
.todo-row .td-t{font-size:13.5px;font-weight:700;color:var(--ink);word-break:break-word;line-height:1.6}
.todo-row .td-s{font-size:12px;color:#475467;margin-top:2px;line-height:1.65;word-break:break-word}
.todo-row .td-act{flex:0 0 auto;white-space:nowrap;padding-top:1px}
.todo-lv{display:inline-block;padding:1px var(--s-3);border-radius:999px;font-size:12px;font-weight:700;white-space:nowrap;margin-right:6px}
.todo-lv.lv0{background:#fee2e2;color:#b91c1c;border:1px solid #fca5a5}
.todo-lv.lv1{background:#ffedd5;color:#c2410c;border:1px solid #fdba74}
.todo-lv.lv2{background:#fef3c7;color:var(--orange-fg);border:1px solid #fcd34d}
.todo-lv.lv3{background:#e8f0ff;color:var(--blue-fg);border:1px solid #c3d8ff}
.todo-lv.lv4{background:#eef2f7;color:#475467;border:1px solid #d6dde8}
.todo-sum{display:flex;gap:10px;flex-wrap:wrap;margin:2px 0 14px}
.todo-sum .ts-card{flex:1 1 132px;border:1px solid #e6eaf2;border-radius:10px;padding:var(--s-4) var(--s-5);background:#fbfcfe}
.todo-sum .ts-n{font-size:21px;font-weight:800;line-height:1.15;color:var(--ink);font-variant-numeric:tabular-nums}
.todo-sum .ts-l{font-size:12px;color:var(--sub);margin-top:2px}
.todo-src{margin-top:16px}
.todo-src .ts-hd{font-size:13px;font-weight:800;color:#344054;margin-bottom:8px;display:flex;align-items:center;gap:var(--s-3)}
.todo-empty{color:#98a2b3;font-size:13px;padding:14px 2px;line-height:1.8}
/* 「去处理」跳到 OSS 台账后，把那一行闪一下 —— 否则用户只知道「跳到 OSS 了」，
   却不知道要看哪一行（OSS 没有搜索框，这是唯一的定位手段）。 */
#ossTbody tr.todo-hit > td{box-shadow:inset 0 0 0 2px #f59e0b}
/* 「去处理」跳到仓库库存明细后，把那一行闪一下（与 OSS 台账同款橙色描边，保持同一套语言）。 */
#whTbody tr.todo-hit > td{box-shadow:inset 0 0 0 2px #f59e0b}
/* ★ D229：从「🔍 与台账比对」面板点人名跳到外协名册那一条时，把那一行闪一下。
   ⚠ 与上面两条**同款橙色描边**（同一套「我把你带到这了」的语言）；
   ⛔ 不铺整行底色 —— 那会和批量选中态 .outs-batch-sel 的红底打架，
      而且表格行背景走斑马纹变量，铺底色换主题就露馅（同 .wh-safe 那条注释）。 */
#outsTbody tr.todo-hit > td{box-shadow:inset 0 0 0 2px #f59e0b}
/* ====== 安全库存（C8）======
   ⚠ 标红刻意**只改字色 + 首列一条内阴影红条**，不铺整行底色：
     仓库表格的行背景走 --row-alt / --row-hover 两个主题变量（斑马纹 + 悬停），
     铺底色要么被它们盖掉、要么得把两个变量都覆盖一遍 —— 换个主题就露馅。
     字色 + 左侧红条在任何主题下都读得出来。 */
table.wh-table td.wh-safe{text-align:right;font-variant-numeric:tabular-nums;font-weight:700;cursor:pointer;color:var(--sub)}
table.wh-table td.wh-safe-none{color:var(--sub)}
table.wh-table td.wh-safe-set{color:#1f3866}
table.wh-table td.wh-safe-low{color:var(--red-fg);background:rgba(217,72,59,.07)}
/* 低库存行的首列红条：用 inset box-shadow 而不是 border-left ——
   table-layout:fixed 下加 border 会挤掉 1px 内容宽，13 位物料编码那列本来就不富余。 */
table.wh-table tbody tr.wh-row-low > td:first-child{box-shadow:inset 3px 0 0 #d9483b}
table.wh-table td.wh-num-low{color:var(--red-fg)}
.wh-low-tag{color:var(--red-fg);font-weight:700}
.wh-safe-warn{color:var(--red-fg);font-weight:700;font-size:12px}
/* 阶段推进时间轴：阶段有固定的先后（入职培训 → NPI → P1 → P2 → EVT → SAT → MP → OSS），
   所以**每一段固定一个颜色**，且按 STAGES 的先后发色 —— 不能沿用折线图那种「按人数排序发色」，
   否则同一个阶段在不同专案里颜色不同、看着像另一个阶段。 */
.wkt-chart-area{height:260px;width:100%}
/* 环比摘要卡：本周总人力 + 与上周的增减。
   颜色语义 = 「涨红 / 跌绿」：本看板里红是「需要关注」的约定色（与 KPI 的
   内援/外援强调色同族），不沿用股票口径 —— 人力上涨通常意味着要补配套。 */
.wkt-cards{display:flex;gap:12px;flex-wrap:wrap;margin:12px 0 4px}
.wkt-card{flex:1 1 170px;min-width:148px;background:var(--card-hd);border:1px solid var(--line);border-radius:10px;padding:10px var(--s-6)}
.wkt-card .k{font-size:12px;color:var(--sub)}
.wkt-card .v{font-size:20px;font-weight:600;color:var(--ink-head);font-variant-numeric:tabular-nums;line-height:1.35}
.wkt-card .d{font-size:12px;font-variant-numeric:tabular-nums}
.wkt-up{color:var(--red-fg)}
.wkt-down{color:#15803d}
.wkt-flat{color:var(--sub)}
/* D297 · 「持平不等于没变化」：总量没变、内部口径换了位时补的一行说明。
   颜色用琥珀 —— 它不是涨也不是跌（涨红跌绿都不能用），而是一条「这里有替换发生」的提示。 */
.wkt-struct{color:#8a5a00;font-size:12px}
.wkt-struct b{font-variant-numeric:tabular-nums}
/* 独占一行（flex-wrap 里 100% 基线）—— 它是整排卡的补充说明，不属于任何单张卡 */
.wkt-struct-row{flex:1 1 100%;margin:-2px 0 2px;padding:6px 10px;border-radius:var(--r-2);
  background:#fff6e5;border:1px solid #f0dcb4;color:#8a5a00;font-size:12px;line-height:1.55}
.wkt-struct-row b{font-variant-numeric:tabular-nums;color:#6b4400}
/* D297 · 「一句话结论」条：把算得出来的事写出来（近 N 周变化 + 最大贡献部门）。
   ⚠ 数字用 <b> 而不是整条加粗 —— 整条粗体会让它看起来像告警，它只是摘要。 */
.wkt-insight{margin:10px 0 2px;padding:8px 12px;border-radius:8px;
  background:#f2f6ff;border:1px solid #dbe4f3;border-left:3px solid #2f6fed;
  font-size:12.5px;line-height:1.65;color:#3d4a5c}
.wkt-insight b{color:#1f3866;font-variant-numeric:tabular-nums}
.wkt-insight .up{color:var(--red-fg);font-weight:600}
.wkt-insight .down{color:#15803d;font-weight:600}
/* D297 · 专案一览分页：默认 30 行，按需加页（76 行 ≈ 2771px，裸长表扫不动） */
.pjt-more{display:flex;align-items:center;justify-content:center;gap:10px;padding:10px 0 2px}
.pjt-more-note{font-size:12px;color:var(--sub);font-variant-numeric:tabular-nums}
.wkt-chart{width:100%;height:300px}
.wkt-chart-sm{width:100%;height:250px}
/* 趋势明细表：数字列右对齐 + 等宽数字，逐周对齐才好扫读 */
.wkt-table td.wkt-num,.wkt-table th.wkt-num{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.wkt-table td.wkt-week{white-space:nowrap;font-variant-numeric:tabular-nums}
.wkt-table tr.wkt-cur{background:#fffaea}
/* ===== 「📄 周报 / 月报」（2026-09-28 第六十四轮 · D9）=====
   三条隔离行与其它独立视图同构，第三条必须 !important（铁律 3b：JS 会给兄弟卡片写内联 display）。
   ⚠ 本视图**刻意不做侧栏子导航**：期间切换（周 / 月 + 上一期 / 下一期）就在页内工具栏 ——
     它没有「按站点 / 按部门」那种天然切分，硬做子导航只是多一层点击。
   ⚠⚠ 「一页纸」刻意**写死浅色**（白纸黑字），不跟主题变量。理由：同一份 HTML 要喂三个出口 ——
     屏幕预览、🖨 打印 / 存 PDF、🖼 导出 PNG。跟主题走的话，深色主题下导出的图会变成黑底、
     与打印结果不一致，用户截图对照时必然来问。而「一页纸」本来就是「一张纸」，浅色是它的正确形态。 */
#reportSec{display:none}
body.view-report #reportSec{display:block;margin:0}
body.view-report .wrap > *:not(#reportSec){display:none!important}
.rpt-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:10px 0 14px}
.rpt-seg{display:inline-flex;border:1px solid var(--line,#d6dde8);border-radius:8px;overflow:hidden}
.rpt-seg button{border:0;background:transparent;padding:var(--s-2) var(--s-8);font-size:13px;cursor:pointer;color:#475467}
.rpt-seg button.on{background:#2f6fed;color:var(--pure);font-weight:700}
.rpt-win{font-size:13px;color:#475467;font-variant-numeric:tabular-nums;min-width:140px;text-align:center}
.rpt-msg{font-size:12px;color:var(--sub)}
.rpt-msg.err{color:var(--red-fg)}
/* 「一页纸」本体：屏幕上是「一张纸」，打印 / 导出时同一个类名复用（不做第二份样式）。 */
.rpt-paper{background:var(--pure);color:#1f2937;border:1px solid #e3e8f0;border-radius:10px;
  box-shadow:0 2px 14px rgba(23,43,77,.07);padding:26px 30px 22px;max-width:1100px;margin:0 auto}
.rpt-paper h1{margin:0;font-size:22px;font-weight:800;color:#16233a;letter-spacing:.5px}
.rpt-paper .rp-hd{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;
  border-bottom:2px solid #16233a;padding-bottom:10px;margin-bottom:14px}
.rpt-paper .rp-sub{font-size:12px;color:#5b6b85;margin-top:var(--s-2);line-height:1.7}
.rpt-paper .rp-gen{font-size:12px;color:var(--rpt-muted);text-align:right;line-height:1.7;white-space:nowrap}
.rpt-paper h2{font-size:14px;font-weight:800;color:#16233a;margin:18px 0 8px;padding-left:var(--s-4);
  border-left:4px solid #2f6fed;line-height:1.45}
.rpt-paper h2 .mut{font-weight:500;color:var(--rpt-muted);font-size:12px}
.rp-kpis{display:grid;grid-template-columns:repeat(5,1fr);gap:var(--s-4)}
.rp-kpi{border:1px solid #e3e8f0;border-radius:var(--r-2);padding:var(--s-4) var(--s-5);background:#fbfcfe}
.rp-kpi .k{font-size:12px;color:var(--rpt-sub);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rp-kpi .v{font-size:25px;font-weight:800;color:var(--rpt-fg);line-height:1.25;font-variant-numeric:tabular-nums}
.rp-kpi .d{font-size:12px;font-variant-numeric:tabular-nums;margin-top:1px}
.rp-up{color:var(--red-fg)}.rp-down{color:#15803d}.rp-flat{color:var(--rpt-muted)}
.rp-tbl{width:100%;border-collapse:collapse;font-size:12px}
.rp-tbl th,.rp-tbl td{border-bottom:1px solid #eef1f6;padding:var(--s-2) var(--s-3);text-align:left;font-variant-numeric:tabular-nums}
.rp-tbl th{background:#f4f7fc;color:#3d4c66;font-weight:700;white-space:nowrap}
.rp-tbl td.n,.rp-tbl th.n{text-align:right;white-space:nowrap}
.rp-tbl tr.rp-sum td{font-weight:800;background:#fafcff;border-top:1px solid #dbe3ef}
.rp-bars{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s-4)}
.rp-box{border:1px solid #e3e8f0;border-radius:var(--r-2);padding:10px 12px;background:#fbfcfe}
.rp-box .t{font-size:12px;color:var(--rpt-sub)}
.rp-box .n{font-size:23px;font-weight:800;line-height:1.2;font-variant-numeric:tabular-nums;color:var(--rpt-fg)}
.rp-box .n.warn{color:var(--red-fg)}
.rp-box .s{font-size:12px;color:var(--rpt-sub);line-height:1.65;margin-top:2px}
.rp-foot{margin-top:16px;border-top:1px dashed #dbe3ef;padding-top:var(--s-4);font-size:12px;color:var(--rpt-muted);line-height:1.85}
.rp-empty{color:var(--rpt-muted);font-size:12px;padding:8px 2px;line-height:1.7}
/* ---- 打印（周报 / 月报 + 领料单 / 入库单 / 问题单）----
   ⚠⚠ 用「整页只留 #printRoot」的做法（与视图隔离同构）：比给每个组件加 print-only 类可靠得多 ——
     浮层、侧栏、toast、子导航全都不用逐个处理。
   ⚠ #printRoot 必须是 body 的**直接子节点**（见 HTML 里那一行，紧挨 </body>）。
   ⚠ 颜色一律写死浅色：黑白打印机也会丢掉背景色，所以**信息不能只靠颜色**
     （红字「超期 12 天」旁边必须同时有「超期」两个字 —— 与 C6/C8 同一原则）。 */
#printRoot{display:none}
/* ─────────────────────────────────────────────────────────────────────────
   P2-5 · 报告域 CSS 变量（2026-09-30 优化轮）
   「屏幕深色 / 打印浅色 / 导出图浅色」三态要同时成立（附录 C 第 7 条，不许统一成单一主题），
   但此前每加一个报告块都要在 @media screen 与 @media print 各写一份硬编码色，漏一处就翻车。
   现在立一条**变量契约**：报告域（.rpt-paper / .rp-* 家族）新增样式只允许消费 --rpt-* 变量：
     · :root                    → 浅色（= 打印 / 导出基准值，非报告视图也安全）；
     · body.view-report（屏幕） → 在 @media screen 闸门内被覆盖成深色 token（见覆盖层 G12）。
   ⇒ 新报告块只写一次 var(--rpt-*)，屏幕自动深、打印自动浅，三态保真从「纪律」变成「机制」。
   ⚠ 原生 @media print 段（1930 行起）保持硬编码不动 —— 它是三态保真的既有测试覆盖区；
     变量在这里只是**备好**，不改变任何既有规则的求值结果（没有既有规则读它们）。 */
:root{
  --rpt-bg:#FBFCFE;        /* 纸面底 */
  --rpt-fg:#16233A;        /* 纸面主文字 */
  --rpt-sub:#5B6B85;       /* 次级文字 / 说明 */
  --rpt-muted:#5B6B80;     /* 弱化文字（生成时间、脚注） */
  --rpt-line:#E3E8F0;      /* 分隔线 / 表格线 */
  --rpt-card:#FFFFFF;      /* 卡片底（KPI 小卡） */
  --rpt-head:#F1F5FB;      /* 表头底 */
  --rpt-accent:#2F6FED;    /* 标题强调条 / 链接 */
  --rpt-up:#C0392B;        /* 上升（报告域自己的涨色，与屏幕的 --up 解耦） */
  --rpt-down:#15803D;      /* 下降 */
}
@media print{
  html,body{background:var(--pure)!important;margin:0!important;padding:0!important}
  body.printing-doc > *{display:none!important}
  body.printing-doc #printRoot{display:block!important}
  #printRoot *{-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .rpt-paper{border:0!important;box-shadow:none!important;padding:0!important;
    max-width:none!important;margin:0!important;border-radius:0!important}
  @page{size:A4;margin:12mm}
}
#printRoot .pr-paper{color:#111;background:var(--pure)}
#printRoot .pr-doc{color:#111;background:var(--pure);font-size:12px;line-height:1.6}
#printRoot .pr-doc h1{font-size:19px;font-weight:800;text-align:center;margin:0 0 2px;letter-spacing:1px}
#printRoot .pr-doc .pr-no{text-align:center;font-size:12px;color:#444;margin-bottom:12px}
#printRoot .pr-doc table{width:100%;border-collapse:collapse;font-size:12px;margin-bottom:4px}
#printRoot .pr-doc th,#printRoot .pr-doc td{border:1px solid #8c8c8c;padding:4px 6px;text-align:left;vertical-align:top}
#printRoot .pr-doc th{background:#eeeeee;font-weight:700}
#printRoot .pr-doc td.n,#printRoot .pr-doc th.n{text-align:right;white-space:nowrap}
#printRoot .pr-doc .pr-kv{display:grid;grid-template-columns:repeat(3,1fr);gap:3px 16px;margin-bottom:10px;font-size:12px}
#printRoot .pr-doc .pr-kv b{color:#000}
#printRoot .pr-doc .pr-sign{display:flex;gap:30px;margin-top:26px;font-size:12px}
#printRoot .pr-doc .pr-sign > div{flex:1;border-top:1px solid #333;padding-top:6px;color:#333}
#printRoot .pr-doc .pr-foot{margin-top:14px;font-size:11px;color:#666;line-height:1.7}
.wkt-legend{font-size:12px;color:var(--muted);margin-top:4px}
/* 范围 / 口径 切换（2026-09-26 第四十六轮）：只放两个下拉，观感跟着 .wh-filter 走，不抢视线。
   ⚠ select 的 max-width 用 min(52vw,300px)：手机上部门名最长 5 个汉字，给足宽度就不会被截断，
     但也不能顶满一行把「口径」挤到换行。 */
.wkt-ctl{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin:12px 0 2px}
.wkt-ctl label{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--sub);white-space:nowrap}
.wkt-ctl select{font-size:12px;padding:4px 8px;border:1px solid var(--line);border-radius:var(--r-2);background:var(--pure);color:var(--ink-head);max-width:min(52vw,300px);font-family:inherit}
/* 「旧口径」小标签：只标记、不占列，所以塞在周次单元格里 */
.wkt-tag{display:inline-block;font-size:11px;line-height:1.5;color:var(--orange-fg);background:#fffaea;border:1px solid #f0d9a8;border-radius:var(--r-1);padding:0 var(--s-2);margin-left:var(--s-2);vertical-align:1px}
/* 脚注：口径说明 / 旧快照说明 / 无负责人维度说明 */
.wkt-note{font-size:12px;color:var(--orange-fg);margin-top:6px;line-height:1.6}
/* ===== 🔀 人力异动流水（2026-09-27 第五十五轮 · A2）===== */
.emv-ctl{display:flex;gap:14px;align-items:center;flex-wrap:wrap;margin:12px 0 2px}
.emv-ctl label{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--sub);white-space:nowrap}
.emv-ctl select{font-size:12px;padding:4px 8px;border:1px solid var(--line);border-radius:var(--r-2);background:var(--pure);color:var(--ink-head);max-width:min(52vw,220px);font-family:inherit}
.emv-sum{font-size:12px;color:#475467;margin:8px 0 2px;line-height:1.7}
.emv-sum b{color:var(--ink-head)}
.emv-table td{font-size:12px;vertical-align:middle}
.emv-table td.emv-when{white-space:nowrap;color:var(--muted);font-variant-numeric:tabular-nums}
.emv-table td.emv-what{color:#475467;word-break:break-word;min-width:120px}
.emv-table td.emv-by{white-space:nowrap;color:#475467}
/* 原因徽章：一张卡里最多 7 类，颜色只用于「扫一眼分组」，不承担语义强弱 */
.emv-badge{display:inline-block;font-size:12px;line-height:1.7;border-radius:99px;padding:0 8px;border:1px solid transparent;white-space:nowrap}
.emv-b-e{background:#eef4fd;color:var(--blue-fg);border-color:#d8e6fb}
.emv-b-m{background:#f4f0ff;color:#6d4bd8;border-color:#e3dbfb}
.emv-b-s{background:#e9f7f8;color:#137f8d;border-color:#cbeaee}
.emv-b-a{background:#eaf7ef;color:var(--green-fg);border-color:#cfe9da}
.emv-b-d{background:#fdedeb;color:var(--red-fg);border-color:#f7d8d4}
.emv-b-v{background:#fff6e6;color:#b7791f;border-color:#f5e2c0}
.emv-b-r{background:#f2f4f7;color:var(--sub);border-color:#e4e7ec}
/* 变化量：**不用股市那套红绿**（这里不是价格）。增=绿、减=红，
   且正负号本来就写在数字里 ⇒ 色觉障碍下照样读得出方向。 */
.emv-d{display:inline-block;font-size:12px;line-height:1.7;border-radius:6px;padding:0 6px;margin:1px 4px 1px 0;white-space:nowrap;font-variant-numeric:tabular-nums}
.emv-d.up{background:#eaf7ef;color:var(--green-fg)}
.emv-d.dn{background:#fdedeb;color:var(--red-fg)}
.emv-d.mv{background:#f2f4f7;color:var(--sub)}
.emv-arrow{color:#98a2b3;padding:0 2px}
/* 多张 wh-card 上下堆叠：wh-card 自身没写下间距（原本都是单卡场景），这里补上 */
#weeklyTrendSec .wh-card+.wh-card{margin-top:12px}
/* 手机：卡片占满一行，避免两列挤压后数字换行 */
@media (max-width:720px){ .wkt-card{flex:1 1 100%} }
/* 转移候选表：楼层 / 专案名可能较长，允许换行；勾选列窄且居中 */
.pxf-table{table-layout:auto}
.pxf-table td.pxf-name{white-space:normal;word-break:break-word;min-width:130px}
.pxf-table td.pxf-line{white-space:nowrap}
.pxf-table th.pxf-check,.pxf-table td.pxf-check{width:38px;text-align:center;padding-left:6px;padding-right:6px}
.pxf-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:12px 0 4px}
.pxf-bar .pxf-cnt{font-size:12px;color:#475467}
.pxf-bar .pxf-cnt b{color:var(--blue-fg);font-size:14px}
.pxf-empty{color:#98a2b3;font-size:13px;padding:16px 2px;text-align:center}
.pxf-warn{color:var(--orange-fg);font-size:12px;margin-top:6px}
.pxf-form{display:flex;gap:16px;flex-wrap:wrap;align-items:center}
.pxf-form label{display:flex;align-items:center;gap:6px;font-size:12px;color:#475467;white-space:nowrap}
.pxf-form select{min-width:132px}
/* 新负责人输入框（2026-09-24 第四十二轮）：与同排 select 视觉一致。
   ⚠ 用 input + datalist 而**不是** select —— 目标部门可能还没有任何负责人候选
     （新建部门 / 该部门首条项目），纯 select 会让人无从下手，datalist 允许直接手输。 */
.pxf-form #pxfManager{min-width:180px;height:30px;padding:0 8px;border:1px solid #cfd6e4;border-radius:var(--r-2);font-size:13px;font-family:inherit;color:#1f2937;background:var(--pure);box-sizing:border-box}
.pxf-form #pxfManager:focus{outline:none;border-color:#2f6fed;box-shadow:0 0 0 3px rgba(47,111,237,.12)}
.pxf-form #pxfManager::placeholder{color:#98a2b3}
/* 现场问题表：问题点 / 回复对策两列内容较长，给最小宽度并允许换行 */
.prob-table{table-layout:auto}
.prob-table td.prob-issue,.prob-table td.prob-reply{white-space:normal;word-break:break-word;min-width:170px;max-width:320px}
.prob-table td.prob-device{white-space:normal;word-break:break-word;min-width:110px}
/* 专案列（2026-09-28 第六十轮 · B5）：取值是业务方自定的专案名（V63/64、UTG 7X打样 …），
   长短差很多 ⇒ 允许换行、给个下限，别把行高撑跳也别截断（与设备列同款）。 */
.prob-table td.prob-subproject{white-space:normal;word-break:break-word;min-width:90px}
/* 车间列（2026-09-24 第四十轮）：取值为楼层式短名（如 C07-5F），**不换行**以免行高被撑跳 */
.prob-table td.prob-workshop{white-space:nowrap}
/* 问题划分徽标：电控（蓝）/ 机构（橙）。两色区分是**信息编码**，不只装饰 */
.prob-kind{display:inline-block;padding:1px 8px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap}
.prob-kind.k-ec{background:#e8f0ff;color:var(--blue-fg);border:1px solid #c3d8ff}
.prob-kind.k-mc{background:#fff2e6;color:#c2410c;border:1px solid #ffd9b8}
/* 状态徽标：待解决（橙，需跟进）/ 已解决（绿，终态） */
.prob-status{display:inline-block;padding:1px 8px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap}
.prob-status.s-open{background:#fff4e5;color:var(--orange-fg);border:1px solid #fcd9a8}
.prob-status.s-done{background:#e7f7ee;color:#15803d;border:1px solid #bfe8d0}
/* 时效（C6）：状态格里的「用时 N 天 / 已 N 天」小字 + 超期行的标红。
   ⚠ 标红是**辅助**，真正的信号是那行文字里的「超期」二字
     （色盲 / 黑白打印 / 投影仪都会把颜色丢掉，文字不会）。
   ⚠ 超期行不整行铺底色（会和表格斑马纹打架），只在首列打一道左侧红条。
   ⚠⚠ 这一格是**两行堆叠**（徽标在上、时效小字在下），所以整格的宽度需求 =
     max(徽标行, 时效行) + 单元格 padding（**不是**两行相加，也不是 textContent 拼接）。
     2026-09-28（第八十一轮）把超期文案从「超期 · 已 N 天」压成「超期 N 天」，
     时效行真实宽 76 → ~55px，整格需求 93 → ~71px —— 这才塞得进 7~8% 的列宽。
     量这一格**必须用真实几何**（子元素 rect.width / scrollWidth 取大），
     用离屏 ruler 重算会同时犯两个错：徽标低估（只量文字 36 vs 真实盒 54）、
     小字虚高（报 113，实测 93）—— 见 `_probe-prob-opfit.js` 注释。 */
.prob-table td.prob-status-cell{white-space:nowrap}
.prob-dur{font-size:12px;line-height:1.5;color:var(--sub);font-variant-numeric:tabular-nums}
.prob-dur.od{color:#b91c1c;font-weight:700}
/* 操作列（2026-09-28 第八十轮）：三颗按钮（编辑 / 删除 / 🖨）必须**排在同一行**。
   ⚠⚠ 单元格用 `wh-op` 而**不是** `oss-op` —— `oss-op` 是 OSS / 驻厂表的共用类，
     它的 nowrap 规则写死在 `table.oss-table td.oss-op` 上，本表（`wh-table prob-table`）
     匹配不到；而 `table.wh-table td{white-space:normal;word-break:break-all}`（第 2160 行）
     又盖掉了全局 nowrap ⇒ 三颗按钮各占一行、**连按钮自身的文字也竖向折**（「编辑」实测 30×38），
     行高被顶到 113px（改前实测见 _probe-prob-opfit）。
     `table.wh-table td.wh-op`（第 2185 行）= nowrap + 居中 + 按钮内边距 2px 6px，
     是仓库台账早就趟对的写法，直接复用，不再给 `oss-op` 打补丁。
   ⚠ 按钮间距用 `+` 相邻选择器**统一给**，不写行内 `margin-left` ——
     既避免第一颗按钮白拿一个左边距（行首多出一截空隙），也不用逐个按钮去记该写几 px。
     4px 与 OSS 操作列同款；`2px 6px` 的按钮内边距**不能再收**（6px 是触屏点击区下限，见第 2190 行）。 */
#probSec table.prob-table td.wh-op .btn + .btn{margin-left:4px}
.prob-table tr.prob-overdue > td:first-child{box-shadow:inset 3px 0 0 #ef4444}
.prob-overdue-tag{color:#b91c1c;font-weight:600}
.prob-reply-txt{white-space:pre-wrap}
.prob-reply-empty{color:#98a2b3;font-size:12px}
.prob-time{white-space:nowrap;font-variant-numeric:tabular-nums}
/* 回复对策行内编辑区（编辑态展开）：输入框 + 保存/取消 */
.prob-reply-edit{display:flex;flex-direction:column;gap:6px;min-width:190px}
.prob-reply-edit textarea{width:100%;min-height:52px;resize:vertical;padding:var(--s-2) var(--s-3);font-size:12px;line-height:1.5;border:1px solid #cfd6e4;border-radius:6px;background:var(--pure);font-family:inherit}
.prob-reply-edit .prob-reply-acts{display:flex;gap:6px}
.prob-reply-edit .prob-reply-acts .btn{padding:2px var(--s-4);font-size:12px}
/* ===== 「外派支援」表样式（2026-09-28 第七十四轮）=====
   视觉语言与 .prob-* **逐条对齐**（同一套配色 / 圆角 / 字号），两页并排看不出是从两个年代做的。 */
.xsup-table{table-layout:auto}
/* 姓名 / 工号 / 日期列都是短而定长的值 ⇒ 不换行（换行只会把行高撑跳） */
.xsup-table td.xsup-name{white-space:nowrap}
/* 负责人（2026-09-28 第七十八轮）：**允许折行，不用 ellipsis 截断**。
   ⚠ 为什么不用截断：姓名一旦被截，「欧阳小武」和「欧阳小X」在页面上长得一样 —— 静默损失信息。
     实测全公司 756 个正式工姓名里 2 字 23% / 3 字 75% / 4 字以上仅 1.3%，
     所以按 7% 给宽后 3 字姓名一定单行，只有那 1.3% 的长名（含「李康龙 05232」这种带工号的）
     才会折成两行 —— 宁可那一行高几像素，也不让名字看不出是谁。 */
.xsup-table td.xsup-owner{white-space:normal;word-break:break-word;min-width:44px;max-width:130px}
.xsup-table td.xsup-no{white-space:nowrap;font-variant-numeric:tabular-nums}
.xsup-table td.xsup-date{white-space:nowrap;font-variant-numeric:tabular-nums}
/* 外派去向是「客户 / 站点 / 专案」这类可长可短的自由文本 ⇒ 允许换行并给下限 */
.xsup-table td.xsup-dest{white-space:normal;word-break:break-word;min-width:120px;max-width:260px}
.xsup-table td.xsup-note{white-space:normal;word-break:break-word;min-width:110px;max-width:280px}
/* 状态徽标：进行中（橙，还没回来）/ 已结束（绿，终态）。
   ⚠ 与 prob 的 .prob-status 同款：颜色是**辅助**，文字才是信号（色盲 / 黑白打印丢不掉文字）。 */
.xsup-status{display:inline-block;padding:1px 8px;border-radius:999px;font-size:12px;font-weight:600;white-space:nowrap}
.xsup-status.s-open{background:#fff4e5;color:var(--orange-fg);border:1px solid #fcd9a8}
.xsup-status.s-done{background:#e7f7ee;color:#15803d;border:1px solid #bfe8d0}
/* 「外派时长」小字（派生列）：未结束 = 已外派 N 天（随时间增长）；已结束 = 共 N 天。 */
.xsup-table td.xsup-durcell{white-space:nowrap}
.xsup-dur{font-size:12px;line-height:1.5;color:var(--sub);font-variant-numeric:tabular-nums}
/* 外派超过 30 天仍未回来：与 prob 的「超期」同一处理 —— 只在首列打一道左侧橙条，不整行铺底
   （整行铺底会和表格斑马纹打架）。⚠ 只在**进行中**的行上出现，已结束的再长也不再提示。 */
.xsup-table tr.xsup-long > td:first-child{box-shadow:inset 3px 0 0 #f59e0b}
.xsup-long-tag{color:var(--orange-fg);font-weight:600}
.xsup-dur.warn{color:var(--orange-fg);font-weight:700}
/* 弹窗里的「外派时长」实时预览条：深色底会被护眼主题检查判为硬编码 ⇒ 一律走主题变量 */
.xsup-calc{margin:10px 0 0;padding:8px 12px;border-radius:8px;background:var(--card-hd,#EAF3FE);
  border:1px solid var(--line,#D2E3F7);font-size:13px;line-height:1.7;color:var(--ink,#1f2733)}
.xsup-calc b{font-size:14px}
.xsup-calc .xsup-calc-warn{color:var(--orange-fg)}
.xsup-calc .xsup-calc-muted{color:#98a2b3;font-size:12px}
/* 弹窗内的多行文本框复用 .injury-grid textarea 的既有样式（678-685 行），此处不再重复定义，
   只补一条：标签不换行（「问题划分」「汇报时间」在窄列下会被折成两行，看起来像少了字段） */
.ij-label{white-space:nowrap}
/* 外派支援弹窗的「负责人」是**派生字段**（由「部门」自动带出，第七十九轮用户澄清口径：
   「这里的负责人就是各部门里面的负责人」）——只读样式要**一眼看出不是给人填的**：
   灰底 + 默认光标 + 不进 Tab 焦点顺序（HTML 里 tabindex="-1"）。
   ⚠ 用 id 选择器限定，别写成 `.injury-grid input[readonly]`：别的弹窗（现场问题 / OSS）
     若将来出现只读输入框，不该被这条样式顺带改掉。 */
#xsupFOwner[readonly]{background:#f2f5f9;color:#41505f;cursor:default;border-color:#dde6f0}
#xsupFOwner[readonly]::placeholder{color:#a8b2bf}
.ij-label .auto{font-style:normal;font-weight:400;color:#8a95a4;font-size:11px;margin-left:3px}
/* 部门没设总负责人时的**指路**提示（有值时 textContent 为空 ⇒ :empty 收起，不占高度） */
.xsup-ro-tip{font-size:12px;line-height:1.4;color:var(--orange-fg)}
.xsup-ro-tip:empty{display:none}

/* ============================================================
   「👤 华南售后员工信息」（2026-09-28 第八十二轮）：staff-table 的单元格样式。
   ============================================================
   ⚠⚠ **操作列直接复用 `td.wh-op`（wh-table 家族自己的类），不要再造一个。**
      这张表的 table 类是 `wh-table staff-table`，而 `td.wh-op` 的三件套
      （第 2185/2191 行的 `table.wh-table td.wh-op`）挂的正是 `wh-table` 前缀 ⇒ 天然命中。
      第八十轮「现场问题收集」就是因为操作格借了跨表共用的 `oss-op`
      （它的 nowrap 写死在 `table.oss-table td.oss-op` 上）⇒ 三颗按钮各占一行、
      连按钮文字都竖向折。**教训：借来的类名会失灵，认表类前缀。**
   ⚠ 姓名列**绝不 ellipsis**（截断后「欧阳小武」「欧阳小X」长得一样）⇒ 用 nowrap 让它把列撑开，
      列宽预算里已按 4 字名（≈48px）留足；真出现超长名宁可让整表宽一点。
   ⚠ 工号 / 入职时间都是「定长数字」⇒ nowrap + 等宽数字（tabular-nums），
      否则各行的数字宽度不一致，整列看起来是歪的。 */
.staff-table td.staff-no,.staff-table td.staff-date{white-space:nowrap;font-variant-numeric:tabular-nums}
.staff-table td.staff-name{white-space:nowrap;font-weight:600;color:var(--ink-head)}
.staff-table td.staff-dept{white-space:normal;word-break:break-word}
/* ---- 「🗂 离职员工信息」子页（2026-10-10 第一百六十一轮）----
   与「在职员工信息」共用 staff-table 的列样式，这里只补「状态」列两种胶囊。
   ⚠ 状态是**结构位**（td[data-k="_st"] 里的 span），不是「这段文字看着像」——
     测试要判断它，必须判这个结构位，别只搜文本。 */
.stf-lv-badge,.stf-lv-ok{display:inline-block;padding:1px 8px;border-radius:999px;font-size:11px;
  font-weight:600;white-space:nowrap;border:1px solid transparent}
.stf-lv-badge{background:rgba(47,111,237,.15);color:var(--blue-fg);border-color:rgba(47,111,237,.35)}
.stf-lv-ok{background:rgba(138,149,164,.16);color:var(--muted);border-color:rgba(138,149,164,.32)}
tr.stf-lv-restored>td{opacity:.6}
/* 性别 / 年龄：窄列，居中显示；空值统一由渲染层写成「—」 */
.staff-table td.staff-ctr{white-space:nowrap;text-align:center;font-variant-numeric:tabular-nums}
.staff-table tr.staff-imported > td:first-child{box-shadow:inset 3px 0 0 #2f6fed}
/* 学历用胶囊，和「状态」类徽标同一套视觉语言（背景浅、字号 12、不换行） */
.staff-edu{display:inline-block;padding:1px 8px;border-radius:999px;font-size:12px;
  background:#eef4fb;color:#3b5b7a;border:1px solid #d8e6f5;white-space:nowrap}
.staff-empno{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}
/* 员工弹窗里的「部门」在非管理员时是只读的（负责人只能维护本部门）——
   灰底 + 默认光标，让「不能改」看得见，而不是提交时才报错。
   ⚠ 用 id 选择器限定，别写成 `.injury-grid input[readonly]`：别的弹窗（工伤 / 外派支援）
     也有只读输入框，不该被这条样式顺带改掉（与 #xsupFOwner[readonly] 同一条纪律）。 */
#staffFDept[readonly]{background:#f2f5f9;color:#41505f;cursor:default;border-color:#dde6f0}
#staffFDept[readonly]::placeholder{color:#a8b2bf}
/* 导入弹窗里「本次可见范围」那一行（负责人导了别的部门的文件时要能一眼看懂为什么被跳过） */
.staff-imp-scope{font-size:12px;color:#5b6b85;margin:6px 0 2px}
/* 被跳过的行清单：与「汇总」同款但用警示色 —— 它是**需要注意**的信息，不是统计 */
.wh-imp-skip{font-size:12px;color:var(--orange-fg);background:#fff8ec;border:1px solid #fcd9a8;
  border-radius:8px;padding:6px 10px;margin-top:6px;line-height:1.7}

/* ===== 👤 员工信息 · 两张分析表（年龄 / 在职时间）2026-09-28 第八十二轮续 =====
   口径见 STAFF_AGE_BANDS / STAFF_TEN_BANDS 的注释；这里是纯样式。
   ⚠ 条形宽度按**该组最大值**归一（常规条形图 y 轴自动缩放的等价物），
     真实占比一律以右侧文字写出 —— 别让读者从条长反推比例，条长只表达「相对多少」。
   ⚠ 两张卡并排；窄屏（<1100px）落成上下单列，否则两列的档位标签都会被挤折行。 */
.staff-an{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
@media (max-width:1100px){ .staff-an{grid-template-columns:1fr} }
.staff-an-card{background:var(--pure);border:1px solid var(--line,#D2E3F7);border-radius:12px;padding:10px 12px 8px}
.staff-an-hd{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;font-size:13.5px;font-weight:800;
  color:var(--ink-head);padding-bottom:6px;border-bottom:1px dashed #dbe7f6;margin-bottom:6px}
.staff-an-hd .staff-an-sub{font-size:12px;font-weight:600;color:var(--sub)}
.staff-an-hd .staff-an-tot{margin-left:auto;font-size:12px;font-weight:600;color:var(--sub)}
.staff-an-row{display:grid;grid-template-columns:92px 1fr 96px;align-items:center;gap:8px;padding:var(--s-1) 0}
.staff-an-lb{font-size:12px;color:#41505f;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.staff-an-track{background:#eef3fa;border-radius:999px;height:12px;overflow:hidden}
.staff-an-track > i{display:block;height:100%;border-radius:999px;background:var(--blue,#2f6fed);transition:width .25s}
.staff-an-val{font-size:12px;color:#5b6b85;text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.staff-an-val b{color:var(--ink-head);font-weight:700}
/* 在职卡用绿色区分两张图；只有末档（现为「5 年及以上」）单独上橙。
   ⚠ 第九十六轮把「3 年及以上」拆成 3~4 / 4~5 / 5 年及以上后，**没有**给三档都上橙 ——
     橙色是整屏里唯一的「需要盯一眼」信号色，同时出现三条橙条会让它失去指示作用；
     判据仍是「i === bands.length-1」，故以后增删档位时橙色自动跟着末档走，无需改这里。 */
.staff-an-card.ten .staff-an-track > i{background:var(--green,#23a06b)}
.staff-an-card.ten .staff-an-row.staff-an-last .staff-an-track > i{background:var(--orange,#f0972b)}
/* 0 人的档位整体退灰：既保留档位（说明「这一档确实没人」），又不与有数据的档抢注意力 */
.staff-an-zero .staff-an-lb,.staff-an-zero .staff-an-val{color:var(--sub)}
.staff-an-foot{margin-top:6px;padding-top:6px;border-top:1px dashed #dbe7f6;font-size:12px;color:var(--sub);line-height:1.75}
@media (max-width:640px){ .staff-an-row{grid-template-columns:80px 1fr 84px} }
.wh-card{background:var(--card,#F8FBFF);border:1px solid var(--line,#D2E3F7);border-radius:14px;padding:14px 16px;box-shadow:0 2px 10px rgba(47,111,237,.06)}
/* 页头一行：标题（左）/ 操作按钮（右）。align-items:stretch → 标题块与按钮自动等高，
   顶边/底边完全平齐，标题文字再靠内部 align-items:center 与按钮文字同线 */
.wh-hd{display:flex;align-items:stretch;justify-content:space-between;gap:10px 12px;flex-wrap:wrap}
.wh-t{display:flex;align-items:center;font-size:15px;font-weight:800;color:var(--ink-head)}
.wh-sub{font-size:12px;color:var(--muted);margin-top:6px;line-height:1.75}
.wh-acts{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-left:auto}
/* 按钮等高：白底按钮（ghost / danger）自带 1px 边框、且个别符号字形行盒更高，
   会让顶边差 1~3px → 统一 1px 透明边框盒 + 固定行高（颜色仍各自保留） */
.wh-acts .btn{border:1px solid transparent;line-height:1.25}
.wh-acts .btn.ghost{border-color:#cfe0f4}
.wh-acts .btn.danger{border-color:#f0c9c4}
/* 页头里的「范围」下拉（员工驻厂信息管理 → 数据导出，2026-09-23 第三十二轮）。
   ⚠ 高度必须与 .wh-acts .btn 齐平：按钮是 1px 边框 + line-height:1.25，
     下拉若用默认 line-height 会高出 2~3px，一行按钮里突兀地鼓出来。 */
.wh-acts-sel{display:inline-flex;align-items:center;gap:var(--s-2);font-size:12px;color:#5b6b85;font-weight:600}
.wh-acts-sel select{border:1px solid #cfd6e4;border-radius:6px;padding:var(--s-2) 8px;font-size:12px;
  background:var(--pure);color:var(--ink-head);line-height:1.25;font-family:inherit;cursor:pointer;max-width:190px}
.wh-title-wh{margin-left:6px;font-size:15px;color:var(--blue-fg)}
.wh-subtabs{display:flex;gap:2px;flex-wrap:wrap;border-bottom:1px solid var(--line,#D2E3F7);margin:14px 0 10px}
.wh-stab{border:none;background:transparent;color:#5b6b85;font-size:13px;font-weight:600;padding:var(--s-3) 14px;cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px}
.wh-stab:hover{color:var(--blue-fg)}
.wh-stab.on{color:var(--blue-fg);border-bottom-color:#2f6fed}
.wh-stat{font-size:12px;color:#5b6b85;margin-bottom:var(--s-4);line-height:1.8}
.wh-stat b{color:var(--ink-head)}
.wh-stat .muted{color:var(--muted)}
.wh-impstat{font-size:12px;color:#5b6b85;margin:-5px 0 var(--s-4);line-height:1.8}
.wh-impstat b{color:var(--ink-head)}
.wh-impstat .wh-chip{margin-right:4px}
/* ===== 驻厂证到期状态（2026-09-21 第十三轮）=====
   色阶与 OSS 到期状态同构：终态（已过期）用次级文本色弱化、窗口内/今天用告警色。
   ⚠ 「已到期」是**终态事实**而非待办告警，不该与「进入提醒窗口」抢视觉 —— 与 OSS 的
     「OSS 服务结束」同一处理原则（见 ossDueText 注释）。
   ⚠ 字号 12px + 显示在日期下方的第二行：主信息是日期本身，状态是注解。
     （2026-10-03 批次C · C4：原为 11.5px，随「12.5/11.5 → 12」的档位收敛一并改为 12px） */
.emp-due{font-size:12px;line-height:1.5;font-weight:600}
.emp-due-ok{color:var(--muted)}
.emp-due-soon{color:var(--orange-fg)}
.emp-due-today{color:var(--red-fg)}
.emp-due-over{color:var(--muted)}
.emp-due-none{color:var(--sub);font-weight:400}
/* 驻厂名册「从员工信息带出的工号」（2026-10-01）：派生值 ⇒ 弱化显示（次级文字 + 更小字号），
   与「本记录自带」的工号在视觉上一眼可分，避免把档案推测值当成已核对值。 */
.emp-no-derive{color:var(--muted,#8794a8);font-size:12px}
/* 批量删除模式（2026-10-01 离职更新轮）：不进「批量模式」时零影响；进入后行可点选，
   选中行整行着色 —— 不加勾选列，避免动 EMP_COLS 列宽配平与列序号消费方。 */
#empTbody.emp-batching tr{cursor:pointer}
#empTbody.emp-batching tr:hover td{background:rgba(120,140,180,.10)}
#empTbody tr.emp-batch-sel td{background:rgba(217,72,59,.16)!important}
/* 员工信息表「驻厂证件」列（2026-10-01）：一行三值小字 —— 数字等宽不折行，
   列宽 14.5% 内放得下；空值跳过。 */
.staff-empids{font-size:11px;white-space:nowrap;letter-spacing:0}
/* 出入库登记：仓库不再下拉选择，直接显示当前仓库（跟随左侧二级导航） */
.wh-curwh{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 12px;padding:8px 12px;border:1px solid var(--line,#D2E3F7);border-radius:8px;background:var(--card-hd,#EAF3FE)}
.wh-curwh .wh-lb{margin:0}
.wh-curwh b{font-size:14.5px;color:#1f3866}
.wh-curwh-note{font-size:12px;color:#7c8aa0}
.wh-src{display:inline-block;padding:0 6px;border-radius:4px;font-size:11px;font-weight:700;background:#fff2d9;color:#9a6210;border:1px solid #f0d9a8;margin-left:var(--s-2);vertical-align:1px}
.sb-subempty{padding:6px 10px 6px 14px;font-size:12px;line-height:1.6;color:rgba(255,255,255,.62)}
.sb-subempty span{font-size:11px;color:rgba(255,255,255,.45)}
/* ---- 仓库数据导入弹窗 ---- */
.wh-imp-bar{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end;margin:4px 0 10px}
.wh-imp-lb{display:flex;flex-direction:column;gap:4px;font-size:12px;color:#5b6b85;font-weight:600}
.wh-imp-lb select{padding:var(--s-2) 8px;font-size:12px;border:1px solid #cfd6e4;border-radius:6px;background:var(--pure)}
.wh-imp-status{font-size:12px;color:#5b6b85;background:var(--card-hd,#EAF3FE);border:1px solid var(--line,#D2E3F7);border-radius:8px;padding:8px var(--s-5);line-height:1.8}
.wh-imp-status.error{color:var(--red-fg);background:#fdeceb;border-color:#f5c6c0}
.wh-imp-status.success{color:var(--green-fg);background:#e8f7ee;border-color:#bfe6cf}
.wh-imp-preview{max-height:290px;overflow:auto;margin-top:10px;font-size:12px}
/* 迁移前体检（D256）：三档分组。⚠ 颜色是固定值不是 CSS 变量 —— 本块在三个视图下都要可读，
   变量版在浅色/深色切换时曾出现「绿字压浅底」看不清（体检结论不能靠猜颜色）。 */
.mpc-sec{margin:0 0 14px}
.mpc-h{font-size:13px;font-weight:700;padding:6px 2px;border-bottom:1px solid rgba(128,140,160,.18);display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}
.mpc-h-safe{color:#1a7f37}
.mpc-h-manual{color:var(--orange-fg)}
.mpc-h-block{color:var(--red-fg)}
.mpc-hint{font-weight:400;font-size:12px;color:var(--muted)}
.mpc-row{padding:var(--s-3) 4px;border-bottom:1px solid rgba(128,140,160,.10);font-size:13px;line-height:1.6}
.mpc-tag{display:inline-block;font-size:11px;color:#5b6678;background:rgba(128,140,160,.12);border-radius:4px;padding:1px 6px;margin-right:6px}
.mpc-key{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;background:rgba(128,140,160,.10);border-radius:4px;padding:1px var(--s-2)}
.mpc-arrow{color:var(--muted);margin:0 var(--s-2)}
.mpc-new{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;color:#1a7f37}
.mpc-who{color:var(--muted);font-size:12px;margin-left:6px}
.mpc-why{font-size:12px;color:var(--muted);margin-top:3px;line-height:1.55}
.mpc-fix{color:var(--muted)}
.wh-imp-preview table{width:100%;border-collapse:collapse;margin-top:6px}
.wh-imp-preview th,.wh-imp-preview td{border:1px solid var(--line,#D2E3F7);padding:4px var(--s-3);text-align:left;white-space:nowrap}
.wh-imp-preview th{background:var(--card-hd,#EAF3FE);color:#31517f;font-size:12px;position:sticky;top:0}
.wh-imp-preview tr.bad td{background:#fdeceb;color:#a83226}
/* ⚠⚠⚠ 导入弹窗说明折叠（2026-10-07 第一百二十二轮，用户报障「这个很多显示界面都有」）。
 * 起因：驻厂导入弹窗的 modal-sub 有 903 字，是次长（612 字）的 1.5 倍、全库 29 个弹窗里唯一超过 500 的。
 *   而用户真正要做的只有「选文件 → 点开始导入」⇒ 说明把操作区挤到首屏之外。
 *   ⚠ 改法**不是删**（这些规则是真需求：三种导入方式 + 同名判定），而是**首屏只留决策必需**、
 *     细节折叠。⛔ 别把规则删掉换"简洁"—— 用户按错导入方式会丢数据。
 * ⚠ 用 <details>/<summary> 原生折叠：不引依赖、键盘可达、可被打印。 */
.wh-imp-details{margin:2px 0 8px;font-size:12px;line-height:1.65;color:var(--sub,#5b6b85)}
.wh-imp-details>summary{
  cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:var(--s-2);
  padding:4px var(--s-4);border:1px solid var(--line,#D2E3F7);border-radius:8px;
  background:var(--card-hd,#EAF3FE);color:#31517f;font-weight:600;
}
.wh-imp-details>summary::-webkit-details-marker{display:none}   /* Chrome 自带三角与自绘箭头会重叠 */
/* ⚠ 展开箭头用 CSS 画（⛔ 不用内联 svg，本项目规矩：图标一律 CSS mask） */
.wh-imp-details>summary::before{
  content:'';width:0;height:0;flex:none;
  border-left:5px solid currentColor;border-top:4px solid transparent;border-bottom:4px solid transparent;
  transition:transform .15s ease;
}
.wh-imp-details[open]>summary::before{transform:rotate(90deg)}
.wh-imp-details>summary:hover{background:#DCEBFB}
.wh-imp-details .wh-imp-detail-body{margin-top:var(--s-3);padding:2px 2px 0}
/* 首屏那 3 行：只留「支持什么格式 / 哪些列 / 空值规则」—— 决定用户能不能顺利填表 */
.wh-imp-quick{font-size:12px;line-height:1.7;color:var(--sub,#5b6b85);margin:0 0 8px}
.wh-imp-sum{font-size:12px;color:#31517f;line-height:1.9;margin-top:8px}
.wh-imp-sum b{color:#1f3866}
/* ---- 仓库权限分配弹窗 ---- */
.wh-perm-sec{border:1px solid var(--line,#D2E3F7);border-radius:var(--r-2);padding:var(--s-5) 12px;margin:10px 0;background:var(--card,#F8FBFF)}
.wh-perm-hd{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:var(--s-4);font-size:13.5px;color:#1f3866}
.wh-perm-hd b{font-weight:800}
.wh-perm-note{font-size:12px;color:#7c8aa0;font-weight:400}
.wh-perm-tools{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:8px}
.wh-perm-tools input[type=text],.wh-perm-tools input:not([type]){flex:1;min-width:170px;padding:var(--s-2) 8px;font-size:12px;border:1px solid #cfd6e4;border-radius:6px}
.wh-perm-chk{font-size:12px;color:#5b6b85;display:flex;align-items:center;gap:var(--s-2);white-space:nowrap;cursor:pointer}
.wh-perm-list{max-height:260px;overflow:auto}
.wh-perm-list table{width:100%;border-collapse:collapse;font-size:12px}
.wh-perm-list th,.wh-perm-list td{border-bottom:1px solid var(--line,#D2E3F7);padding:6px 8px;text-align:left;vertical-align:middle}
.wh-perm-list th{background:var(--card-hd,#EAF3FE);color:#31517f;font-size:12px;position:sticky;top:0;z-index:1}
.wh-perm-list td.wh-perm-dept{color:#31517f;font-weight:600;white-space:nowrap}
.wh-perm-list td.wh-perm-wh{white-space:nowrap}
.wh-perm-list input[type=text],.wh-perm-list input[type=password]{width:100%;padding:4px var(--s-3);font-size:12px;border:1px solid #cfd6e4;border-radius:6px;background:var(--pure)}
.wh-perm-list .wh-perm-del{padding:2px 8px;font-size:12px}
.wh-perm-empty{color:var(--muted);font-size:12px;padding:12px 4px;text-align:center}
.wh-perm-fixed{color:var(--muted);font-size:12px}
/* 仓库管理员：侧边栏只保留「仓库管理」「员工驻厂信息管理」（各含其下的子项）、个人中心与退出登录
   ⚠ 若该账号被单独授予「驻厂信息管理」权限（empAccess），也保留同一组入口 —— 见 initSidebarAuth */
/* D322：允许 [data-act="selfperf"]（填写我的绩效自评）—— 否则被这条「只留仓库」的规则连内联样式一起压掉 */
body.authed.is-ware .sb-scroll > *:not([data-view="ware"]):not(#wareSubNav):not([data-view="emp"]):not(#empSubNav):not(.sb-sec-profile):not([data-act="profile"]):not([data-act="selfperf"]):not(#sbRoleSwitchSec):not(#sbRoleSwitchList){display:none!important}
/* 驻厂信息管理员（2026-09-20 第十轮）：独立账号，侧边栏**只**保留「员工驻厂信息管理」
   （含其下的站点子项）、个人中心与退出登录 —— 它连仓库管理都不该看到。
   ⚠ 这条必须写在 is-ware 那行**之后**：同一元素可能同时命中两个类（不会，但顺序一致更安全）。 */
body.authed.is-emp .sb-scroll > *:not([data-view="emp"]):not(#empSubNav):not(.sb-sec-profile):not([data-act="profile"]):not([data-act="selfperf"]):not(#sbRoleSwitchSec):not(#sbRoleSwitchList){display:none!important}
/* 员工自评账号（D205，2026-10-19）：这个角色**只有**年终绩效考评一件事。
   ⚠ 它的数据面在服务端也同样是白的（/api/data 与 /api/stream 对它显式 403），
     这里的侧栏裁剪只是**让界面不撒谎** —— 不是权限本身。
     两边都给，是因为「只有一个入口」和「只有一个接口」是两件不同的事。 */
body.authed.is-self .sb-scroll > *:not([data-view="perf"]):not(.sb-sec-profile):not([data-act="profile"]){display:none!important}
/* 年终绩效考评入口（D205）：只对 管理员 / 总负责人 / 行负责人 显示。
   ⚠ 与 admin-item 同一套写法（两条都带 !important，压掉任何内联值）：
     完全由角色决定 ⇒ 不需要 applyXxxNavVisibility 那种内联 display 的显隐函数。 */
.sb-item.perf-item{display:none!important}
body.authed.can-perf .sb-item.perf-item{display:flex!important}
/* 表格滚动区：纵向内部滚动（表头 sticky 吸顶）+ 横向滚动。
   ⚠ max-height 要留足「卡片头 + 子标签 + 筛选 + 统计 + 吸底平移条」的高度余量，
     否则平移到不了视口内（2026-09-15 实测：余量不足会让吸底平移条掉到首屏外）。
   ⚠ 560px 下限 = 「15 行」高度：行高 34.5px + 表头 31px → 15×34.5+31 = 548.5，取 560 留余量。
     原因：原先纯 calc(100vh - 420px) 在小屏本（视口 1366×660）只算出 240px ≈ 6 行，
     用户只能一屏看 6 条流水（2026-09-15 截图报障）。改用 max()：小屏按 15 行保底
     （卡片因此高于视口、由整页滚动承载），大屏仍按视口余量自适应、能显示更多行。 */
.wh-scroll{overflow:auto;max-height:max(560px,calc(100vh - 420px));min-height:200px}
table.wh-table{width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;min-width:960px}
/* ⚠ 必须显式写 white-space:normal —— 本页第 264 行有一条全局 `th,td{white-space:nowrap}`，
   仓库表格若只写 word-break:break-all 是**完全不生效**的（nowrap 下根本不换行）：
   线上实测「规格型号」值长 80 字符、单元格 161px、实际需要 806px → 溢出 645px，
   直接压在「单位/数量/申请人/单号/入库时间」五列的文字上（2026-09-15 用户截图报障）。
   overflow-wrap:anywhere 兜底超长无空格串（物料编号/型号），line-height 让多行可读。 */
table.wh-table th,table.wh-table td{border-bottom:1px solid var(--line,#D2E3F7);padding:var(--s-3) 8px;text-align:left;vertical-align:middle;white-space:normal;word-break:break-all;overflow-wrap:anywhere;line-height:1.5}
table.wh-table thead th{background:var(--card-hd,#EAF3FE);color:#31517f;font-weight:700;font-size:12px;white-space:nowrap;line-height:1.35;position:sticky;top:0;z-index:3;box-shadow:inset 0 -1px 0 var(--line,#D2E3F7)}
/* ── 员工档案表头排序（D267，2026-11-01）──────────────────────────────────────
   932 行 × 12 列，用户最常问的「谁年纪最大 / 谁入职最久 / 哪几个证快到期」
   原来只能靠眼睛扫。
   ⚠⚠ 箭头必须是 ::after **绝对定位**、且默认 opacity:0 ——本表 table-layout:fixed、
     列宽是百分比硬分，「性别」列只有 4%（960px 下 ≈38px，两个 12px 汉字 + 左右 8px 内边距
     已经吃满）。塞一个**占位**的箭头进去，那一列立刻折行、整行高翻倍，
     而且不报任何错，只在截图里看得见。
   ⚠ th 本身就是 position:sticky ⇒ 已经在定位上下文里，::after 的 right/top 直接相对它。
   ⚠ 颜色跟两套主题走：默认（报告视图）深蓝 #31517f 表头配 #2f6fed；
     看板暗色下用浅蓝 #A9C6F2（与那条 body:not(.view-report) 表头色同源）。 */
table.wh-table.staff-table thead th.th-sort{cursor:pointer;user-select:none}
table.wh-table.staff-table thead th.th-sort::after{content:'▲';position:absolute;right:2px;top:50%;
  transform:translateY(-50%);font-size:8px;line-height:1;color:var(--blue-fg);opacity:0;
  transition:opacity .12s;pointer-events:none}
table.wh-table.staff-table thead th.th-sort:hover::after{opacity:.4}
table.wh-table.staff-table thead th.th-sort[data-dir="1"]::after{content:'▲';opacity:1}
table.wh-table.staff-table thead th.th-sort[data-dir="-1"]::after{content:'▼';opacity:1}
body:not(.view-report) table.wh-table.staff-table thead th.th-sort::after{color:#A9C6F2}
/* ── 仓库表表头排序（D270，2026-11-01）──────────────────────────────────────
   与员工档案同一套做法（见上面那段）：箭头 ::after 绝对定位、默认透明、不占位。
   ⚠⚠ 这张表的列宽更容易被挤爆：入库表「仓位」只有 8%、出库「规格型号」只有 9%，
     而且这张表的注释里写着「改任何一列之前先量一遍每列单行需要多少 px」。
     所以箭头**绝不可以**占位 —— 一旦占位，11656 行的表会有整列折行，行高一翻，一屏从 16 行掉到 11 行。 */
table.wh-table thead th.wh-sort{cursor:pointer;user-select:none}
table.wh-table thead th.wh-sort::after{content:'▲';position:absolute;right:2px;top:50%;
  transform:translateY(-50%);font-size:8px;line-height:1;color:var(--blue-fg);opacity:0;
  transition:opacity .12s;pointer-events:none}
table.wh-table thead th.wh-sort:hover::after{opacity:.4}
table.wh-table thead th.wh-sort[data-dir="1"]::after{content:'▲';opacity:1}
table.wh-table thead th.wh-sort[data-dir="-1"]::after{content:'▼';opacity:1}
body:not(.view-report) table.wh-table thead th.wh-sort::after{color:#A9C6F2}
/* 窄视口下表头不硬撑：列宽被压到不够放标题时允许折行，避免表头文字压到相邻表头。
   ⚠ 只在「放不下」时生效（宽屏下各列都够宽，观感与 nowrap 完全一致）。 */
@media(max-width:1100px){
  table.wh-table thead th{white-space:normal;overflow-wrap:anywhere}
}
/* 宽屏（容器宽 ≥960px 表宽，即视口 ≥1160px）：表格去掉内部纵向滚动，改为
   「自然高度 + 整页滚动 + 表头吸附在顶部栏之下」。
   为什么：内部限高时小屏本（1366×660）一屏只能看 6 行；自然高度 + 整页滚动后，
   同一台机器一屏能看到 16~17 行（2026-09-15 用户报障「显示多几行，15 行左右」）。
   ⚠ 必须写在基础规则 `table.wh-table thead th{...top:0...}` **之后**（同优先级下后者胜），
     否则会被基础层的 top:0 覆盖 → 表头吸到视口顶、被顶部栏压住（2026-09-15 实测踩过）。
   ⚠ overflow 必须是 visible：只要有一轴是 auto/scroll/hidden，本容器就成了 sticky 的
     滚动祖先，表头会退化成「固定在容器顶」，随整页一起滚走。
   ⚠ 表头 top 用 --whHeadTop（header 自身是 sticky top:0，表头必须让开它），
     值由 JS syncWhHeadTop() 实测写入，这里 52px 只是兜底默认值。 */
@media(min-width:1160px){
  .wh-scroll{overflow:visible;max-height:none;min-height:0}
  table.wh-table thead th{top:var(--whHeadTop,52px);z-index:30}
}

/* ---- 仓库表格：常驻纵向滚动条（2026-10-05）----
   用户报障：「物料太多没有滚动拉条，只能靠鼠标滑动」。
   实测根因（Playwright，1440×900 / 1280×660）：上面那条 @media(min-width:1160px) 把
   .wh-scroll 改成 overflow:visible —— 表格**自身不滚动**（offsetHeight==scrollHeight==2140px），
   靠**整页滚动**带动。这是 2026-09-15 为了「笔记本一屏看 16~17 行」刻意做的，不能回退。
   而 Windows 10/11 的 Chrome/Edge 滚动条默认「自动隐藏」⇒ 静止时整页滚动条也不可见。
   两者叠加 = 屏幕上不存在任何可抓的滑块，只能滚轮。

   ⚠ 为什么不用 position:sticky（第一版踩过，实测拦下）：控件放在表格**末尾**，
     自然位置 y≈2466（表格 2140px 高），而 `top` 型 sticky 的语义是「元素顶不低于 top」，
     **不会把视口下方的元素拉上来**（只有 `bottom` 型才会）。结果滑块停在屏幕外，
     拖不动、点不到 —— 正是"加了等于没加"。改用 position:fixed 贴视口右侧。
   ⚠ 为什么 fixed 不会「切走视图后还悬着」：它在 #wareSec 内，而 #wareSec 默认
     display:none，离开仓库页整棵子树不渲染（display:none 祖先会一并隐藏 fixed 子元素）。
     这与 .hscroll 当年担心的「fixed 跨视图残留」不同，故此处可放心用 fixed。
   ⚠ 右侧留白：仓库视图常驻 30px 右内边距把表格让出来，避免滚动条压住最右侧的
     「入库时间」列。留白是**常驻**的（不随控件显隐变化）⇒ 显隐切换时表格不横向跳动。 */
body.view-ware #wareSec{padding-right:30px}
.wh-vscroll{position:fixed;right:5px;top:calc(var(--whHeadTop,52px) + 8px);z-index:70;
  display:none;align-items:center;gap:var(--s-2);width:max-content;
  padding:6px var(--s-2);border-radius:999px;
  background:var(--card);border:1px solid var(--line);box-shadow:0 4px 14px -6px rgba(20,40,80,.42)}
.wh-vscroll.on{display:flex}
.wh-vtrack{position:relative;width:11px;border-radius:999px;background:var(--surface2,#EAF1FB);
  box-shadow:inset 0 0 0 1px var(--line,#D2E3F7);overflow:hidden;flex:none;
  height:var(--whVH,180px)}
.wh-vthumb{position:absolute;left:0;right:0;top:0;border-radius:999px;
  background:linear-gradient(180deg,#7ea4f5,#4c7ae0);box-shadow:0 0 0 1px rgba(28,64,140,.35);
  cursor:grab;touch-action:none;min-height:26px;transition:background .15s}
.wh-vthumb:hover{background:linear-gradient(180deg,#93b4f8,#5b86e6)}
.wh-vthumb.drag{cursor:grabbing;background:linear-gradient(180deg,#a9c4fa,#6d95ea)}
.wh-vthumb:focus-visible{outline:2px solid var(--blue,#2f6fed);outline-offset:2px}
.wh-vlabel{font-size:11px;color:var(--sub,#5b6b85);font-variant-numeric:tabular-nums;
  white-space:nowrap;letter-spacing:.2px;user-select:none;
  writing-mode:vertical-rl;text-orientation:mixed}
.wh-vbtns{display:flex;flex-direction:column;gap:2px;flex:none}
.wh-vbtn{width:20px;height:18px;line-height:1;border:1px solid var(--line,#D2E3F7);border-radius:6px;
  background:var(--card);color:var(--sub,#5b6b85);font-size:11px;font-weight:700;cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:0;font-family:inherit}
.wh-vbtn:hover{background:var(--card-hd,#EAF3FE);color:var(--ink,#1f3866)}
.wh-vbtn:focus-visible{outline:2px solid var(--blue,#2f6fed);outline-offset:1px}
/* 窄屏：表格本身是滚动容器（.wh-scroll overflow:auto），滚动条依然有用，压窄一点 */
@media(max-width:640px){
  body.view-ware #wareSec{padding-right:22px}
  .wh-vscroll{right:3px;padding:var(--s-2) 4px;gap:4px}
  .wh-vtrack{width:10px}
  .wh-vlabel{display:none}
}
/* 打印/存 PDF：滚动条是屏幕交互件，不进纸面 */
@media print{.wh-vscroll{display:none!important}}
table.wh-table tbody tr:nth-child(even){background:var(--row-alt,#F2F8FF)}
table.wh-table tbody tr:hover{background:var(--row-hover,#DBEAFB)}
table.wh-table td.wh-num{text-align:right;font-variant-numeric:tabular-nums;font-weight:700;color:#2f54b0}
table.wh-table td.wh-code{font-family:ui-monospace,Consolas,monospace;font-size:12px}
table.wh-table td.wh-op{white-space:nowrap;text-align:center}
/* 按钮内边距收紧：早先「操作」列只有约 61px，两个按钮若按 2px 8px 会要 69px
   → 溢出 8px 压到左邻列（2026-09-15 窄屏实测）。
   ⚠ 2026-09-28（D9）：行里多了第三颗 🖨 ⇒ 三颗在表格最小宽度 960px 下要 89px，
     `操作` 列已由 WARE_COLS 从 8% 加到 **10%**（96px，留 7px 余量）。
     **不要再往下收这个 padding** —— 6px 已是触屏点击区下限，再收修好溢出却换来误触。 */
table.wh-table td.wh-op .btn{padding:2px 6px;font-size:12px}
/* 驻厂名册「删除」按钮：加大点击区 + 与左邻列拉开安全间距（2026-09-27 第五十六轮）
   ⚠ 原来它是**行内样式** padding:2px 8px;font-size:12px ⇒ 实测点击区只有 42×22px，
     不到触屏推荐值（44×44）的一半：手指按偏一点就落到隔壁行/隔壁列。
     更要命的是**行内样式优先级高于 @media 里的 .btn**，手机端「加大按钮」的规则被整个压掉，
     所以触屏上永远是这个 22px 高的小目标 —— 用户报的「容易误触」就出在这里。
   ⇒ 行内样式改为类；尺寸分桌面 / 手机两档；操作列左侧留 16px 安全间距。
   ⚠ 选择器必须写到 `td.oss-op .emp-del-btn` 这一级：稍早那条 `.oss-op .btn{padding:3px 9px…}`
     同权重且在前，写 `.emp-del-btn` 单类会被它盖回去（改了像没改）。 */
table.wh-table td.emp-del-cell{padding-left:16px}
td.oss-op .emp-del-btn{margin-left:0;padding:var(--s-2) 14px;font-size:12px;line-height:1.25;min-height:30px}
@media(max-width:640px){
  td.oss-op .emp-del-btn{padding:var(--s-4) 14px;font-size:13px;min-height:38px;min-width:58px}
}
/* 「✏️ 编辑」按钮（第121 轮）：与删除**并排**，故必须有独立尺寸与间距。
   ⚠⚠⚠ 选择器同样要写到 `td.oss-op .emp-edit-btn` 这一级：
     `.oss-op .btn{padding:3px 9px…}` 与下面这些**同权重且在前**，
     只写 `.emp-edit-btn` 单类会被盖回去（改了像没改，见 2654 行的教训）。
   ⚠ 触屏尺寸跟着删除按钮一档（min-height 30/38px）——
     这两个按钮在同一格里，若编辑按钮更小，手机上就又是一个「容易误触」的小目标。 */
td.oss-op .emp-edit-btn{
  margin-left:0; margin-right:6px;
  padding:var(--s-2) 12px; font-size:12px; line-height:1.25; min-height:30px;
}
@media(max-width:640px){
  td.oss-op .emp-edit-btn{padding:var(--s-4) 12px;font-size:13px;min-height:38px;min-width:46px}
}
/* 2026-09-27 第五十八轮：驻厂名册的「工号」列已按用户要求**撤掉**
   （原话：「驻厂名册无需工号，还是按之前即可，因为证件号是唯一的」）
   ⇒ 原先为它写的 td.emp-no 样式一并删除，表格恢复改造前的样子。 */
/* 删除后的可见反馈（2026-09-27 第五十六轮）：原来删完**一声不响**——
   行没了，但用户不知道自己删的是谁、还能不能找回来，这正是「误触焦虑」的来源。
   现在给一条绿色提示条（含姓名 + 回收站入口），5 秒后自动收起。 */
.emp-flash,.outs-flash{display:none;margin:0 0 10px;padding:var(--s-4) 12px;border-radius:var(--r-2);font-size:12px;line-height:1.7;
  background:#eef8f1;border:1px solid #bfe3cc;color:var(--green-fg)}
.emp-flash b,.outs-flash b{color:#14663c}
/* 外协名册「批量删除模式」（第一百七十五轮）：与驻厂那套（#empTbody.emp-batching）同族 ——
   不进批量模式时**零影响**；进入后行可点选、选中行整行着色。
   ⚠ 与驻厂刻意不同的一点：外协**允许**勾选后逐条取消（再点一次即取消），
     所以这里没有「一进模式就禁用 ⋯ 按钮」那类限制 —— 行内本来也没有别的按钮。
   ⚠ 选中态的 !important 是必需的：表格行有斑马纹 / hover 底色，不压过它们就看不出来。 */
#outsTbody.outs-batching tr{cursor:pointer}
#outsTbody.outs-batching tr:hover td{background:rgba(120,140,180,.10)}
#outsTbody tr.outs-batch-sel td{background:rgba(217,72,59,.16)!important}
/* ⚠ 批量模式下**不**给操作列做「变灰」—— 与驻厂那套一致：那两颗按钮仍然可用
   （用户可能顺手改一条再删），把可点的东西画成不可点才是更坏的误导。 */
.wh-empty{display:none;text-align:center;color:var(--muted);font-size:13px;padding:26px 10px;line-height:1.9}
.wh-zero{color:var(--red-fg);font-weight:700}
.wh-pos{font-weight:600;color:#31517f}
.wh-chip{display:inline-block;padding:1px 8px;border-radius:999px;font-size:12px;font-weight:700;background:var(--card-hd,#EAF3FE);color:#31517f}
.wh-chip.in{background:#e2f3e8;color:var(--green-fg)}
.wh-chip.out{background:#fdeaea;color:var(--red-fg)}
.wh-filter{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px}
.wh-filter input,.wh-filter select{border:1px solid var(--line,#D2E3F7);border-radius:8px;padding:6px 10px;font-size:13px;background:var(--pure);color:var(--ink-head)}
.wh-filter input{min-width:190px}
.wh-form-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.wh-form-grid label{display:flex;flex-direction:column;gap:var(--s-2);font-size:12px;color:#4a5568}
.wh-form-grid label.full{grid-column:span 2}
.wh-lb{font-weight:700;color:#54617a}
.wh-lb .req{color:var(--red-fg);font-style:normal}
.wh-form-grid input,.wh-form-grid select{border:1px solid #cdd6e4;border-radius:8px;padding:var(--s-3) 10px;font-size:13px;background:var(--pure);color:#1f3866}
/* 「仓位 / 物料编码」的原生下拉防护（两道）：
   ① 这两个框**已经不挂 datalist**（第五十三轮，用户要求点进去别弹候选，见 HTML 处注释）；
   ② 这里再兜一道：万一将来给它们加回 `list`，框内右侧那个小三角也不会冒出来
      （Chromium 给 input[list] 画的小三角，Chrome 96+ 的伪元素叫 calendar-picker-indicator，
      旧版叫 list-button，两条都写）。 */
#whFPos::-webkit-calendar-picker-indicator,#whFCode::-webkit-calendar-picker-indicator,
#whFPos::-webkit-list-button,#whFCode::-webkit-list-button{display:none!important}
.wh-tip{font-size:12px;color:var(--muted);background:var(--card-hd,#EAF3FE);border:1px dashed #cdd6e4;border-radius:8px;padding:8px 10px;margin:0 0 12px;line-height:1.7}
/* ===== 「个人全景」抽屉（2026-09-27 第五十七轮 · 优化清单 B3）=====
   背景：同一个人的信息散在五个模块里 —— 明细行（部门/专案/阶段/用工性质）、驻厂证件、
   内援调动、转正、工伤。要回答一句最日常的话「张三现在在哪个专案、驻厂证什么时候到期、
   有没有没结的工伤」，得开四个模块、按姓名搜四遍。这正是「人手一个看板，却还要靠微信问」的根源。
   做法：姓名旁边一个 👤，从右侧滑出一张卡，把**我本来就看得见**的那几段并到一处。

   ⚠⚠ 权限：本抽屉**不新建任何权限**。每一段都复用该模块自己的可见性闸门，
     看不到的模块**整段不拼**（不是灰掉，是根本不渲染）。
     聚合面板最容易变成越权口子 —— 这里漏判一处，就等于给所有人开了一个「一次看全」的后门。
   ⚠ 用 position:fixed 全屏覆盖（不用 100vh 算高度）：手机上 100vh 是「大视口」，
     会被系统栏挡住底部按钮（见 rules 50/51）。 */
.pdp-mask{position:fixed;inset:0;background:rgba(15,26,45,.34);z-index:96;display:none}
.pdp-mask.on{display:block}
.pdp{position:fixed;top:0;right:0;bottom:0;width:min(480px,96vw);background:var(--pure);z-index:97;
  display:none;flex-direction:column;box-shadow:-10px 0 30px rgba(15,26,45,.18);
  border-left:1px solid var(--line,#D2E3F7)}
.pdp.on{display:flex}
.pdp-hd{flex:0 0 auto;padding:12px 14px;border-bottom:1px solid var(--line,#D2E3F7);
  background:var(--card-hd,#EAF3FE);display:flex;align-items:center;gap:var(--s-4)}
.pdp-hd .t{font-weight:800;color:#1f3866;font-size:15px;display:flex;align-items:center;gap:8px;
  min-width:0;flex-wrap:wrap}
.pdp-hd .t .no{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;
  color:#31517f;background:#dbe9fb;border-radius:6px;padding:1px var(--s-3);font-weight:700}
.pdp-x{margin-left:auto;flex:0 0 auto;border:1px solid #cdd6e4;background:var(--pure);border-radius:8px;
  width:34px;height:34px;font-size:17px;line-height:1;color:#54617a;cursor:pointer;padding:0}
.pdp-x:hover{background:#f2f6fc}
.pdp-bd{flex:1 1 auto;overflow:auto;-webkit-overflow-scrolling:touch;padding:12px 14px 26px}
.pdp-sec{margin:0 0 var(--s-8)}
.pdp-sec>h4{margin:0 0 var(--s-3);font-size:13px;color:#31517f;display:flex;align-items:center;gap:var(--s-3)}
.pdp-sec>h4 .n{font-weight:400;color:var(--muted);font-size:12px}
.pdp-card{border:1px solid var(--line,#D2E3F7);border-radius:10px;padding:var(--s-4) var(--s-5);background:#fbfdff}
.pdp-card+.pdp-card{margin-top:8px}
.pdp-kv{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;font-size:12px;line-height:1.8}
.pdp-kv .k{color:var(--muted);white-space:nowrap}
.pdp-kv .v{color:#1f3866;word-break:break-word}
.pdp-lock{font-size:12px;color:var(--muted);background:#f6f8fb;border:1px dashed #d6deea;
  border-radius:var(--r-2);padding:var(--s-4) var(--s-5);line-height:1.7}
.pdp-empty{font-size:12px;color:var(--muted);padding:3px 0 var(--s-2)}
.pdp-tag{display:inline-block;padding:1px var(--s-3);border-radius:999px;font-size:11px;font-weight:700;
  background:#eef3fb;color:#31517f;margin:0 var(--s-2) 3px 0}
.pdp-tag.on{background:#e2f3e8;color:var(--green-fg)}
.pdp-tag.no{background:var(--surface2);color:var(--muted)}
/* 姓名旁的 👤 触发钮：点击区必须够大（触屏 44px 的取舍见 emp-del-btn 的教训），
   但它是**次要**动作、不能把姓名挤掉 ⇒ 桌面 20px 高、手机加到 30px。 */
.pdp-btn{flex:0 0 auto;margin-left:6px;border:1px solid #cdd6e4;background:var(--pure);border-radius:6px;
  padding:1px var(--s-3);font-size:12px;line-height:1.7;color:#31517f;cursor:pointer}
/* ⚠⚠ 👤 必须由 **CSS 伪元素**画出来，绝不能在按钮里写文本节点：
   姓名单元格的 textContent 会被这个 emoji 污染（`李四` → `李四👤`），
   任何「取单元格文本」的消费方（复制、导出预期、外部脚本）都会拿到脏值。
   实测代价：smoke-emp-data 的「导出数据里部门值真的写出来了」直接判红。
   用 ::before 后按钮 textContent 为空串，视觉一字不差；可访问性靠 aria-label/title。 */
.pdp-btn::before{content:'\1F464'}
.pdp-btn:hover{background:#eef3fb;border-color:#9dc0ee}
/* ⚠ 只加 `white-space:nowrap`，**不**把 td 改成 flex：flex 单元格的下限会变成
   min-content，手机窄屏上会顶穿表格（见 mobile M0-9/M0-11 的教训）。 */
table.wh-table td.emp-name{white-space:nowrap}
@media(max-width:640px){
  .pdp{width:100%;border-left:0}
  .pdp-hd{padding:10px 12px}
  .pdp-bd{padding:10px 12px 30px}
  .pdp-btn{padding:4px var(--s-4);font-size:13px;line-height:1.4}
}
.wh-tip b{color:#2f54b0}
.wh-tip .warn{color:var(--red-fg)}
.wh-err{color:var(--red-fg);font-size:12px;min-height:16px}
.wh-stock-hint{font-size:12px;color:var(--muted);margin-top:4px}
.wh-stock-hint b{color:var(--green-fg)}
.wh-stock-hint .bad{color:var(--red-fg)}
/* 仓位候选（2026-09-26 第五十轮）：同一物料编码在多个仓位有货时全部列出，由用户点选。
   出库扣错仓位在总数上看不出来（总数还是对），所以「多个候选时一个都不猜」是硬约定。 */
.wh-pos-pick{grid-column:span 2;display:flex;flex-direction:column;gap:var(--s-3);border:1px dashed #cdd6e4;border-radius:8px;padding:8px 10px;background:#F7FAFF}
.wh-pos-pick.ok{border-color:#b6ddc6;background:#F3FBF6}
.wh-pos-pick.warn{border-color:#f0c9c4;background:#FEF6F5}
.wh-pos-pick-hd{font-size:12px;color:var(--muted);line-height:1.6}
.wh-pos-pick-hd b{color:#2f54b0}
.wh-pos-pick.ok .wh-pos-pick-hd b{color:var(--green-fg)}
.wh-pos-pick.warn .wh-pos-pick-hd b{color:var(--red-fg)}
.wh-pos-chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.wh-pos-chip{border:1px solid #cdd6e4;background:var(--pure);color:#1f3866;border-radius:999px;padding:var(--s-2) var(--s-5);font-size:12px;line-height:1.25;cursor:pointer;font-family:inherit}
.wh-pos-chip:hover{border-color:#8fb6e8;background:#F0F6FF}
.wh-pos-chip.on{border-color:#2f54b0;background:#E3EDFC;color:#1a3f8f;font-weight:700}
.wh-pos-chip b{color:var(--green-fg)}
.wh-pos-chip.z{color:#98a2b3;cursor:default;background:#FAFBFD;border-style:dashed}
.wh-pos-chip.z:hover{border-color:#cdd6e4;background:#FAFBFD}
.wh-pos-note{font-size:12px;color:#98a2b3;line-height:1.6}
/* 连续登记（2026-09-26）：一次领/收多个物料时，弹窗内列出「本次已录入」的条目。
   ⚠ 显隐靠 JS 双向写 style.display（与 #whPosPickWrap 同款），这里**不写 display**，
     避免内联与 class 打架（铁律 3b）。 */
.wh-sess{margin-top:14px;border-top:1px dashed #cdd6e4;padding-top:10px}
.wh-sess-hd{font-size:12px;color:var(--muted);display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px}
.wh-sess-hd b{color:#2f54b0}
.wh-sess-hd .no{color:var(--green-fg);font-weight:700}
.wh-sess-list{display:flex;flex-direction:column;gap:6px;max-height:140px;overflow-y:auto}
.wh-sess-row{display:flex;align-items:center;gap:8px;font-size:12px;background:#F7FAFF;border:1px solid #e3ebf6;border-radius:8px;padding:6px 10px}
.wh-sess-row .c{color:#1f3866;font-weight:700}
.wh-sess-row .p{color:var(--muted)}
.wh-sess-row .q{margin-left:auto;color:var(--green-fg);font-weight:700;white-space:nowrap}
.wh-sess-row .x{flex:0 0 auto;border:1px solid #cdd6e4;background:var(--pure);color:var(--muted);border-radius:6px;padding:2px 8px;font-size:12px;cursor:pointer;font-family:inherit}
.wh-sess-row .x:hover{border-color:#e0a49c;color:var(--red-fg);background:#FEF6F5}
@media(max-width:640px){
  .wh-form-grid{grid-template-columns:1fr}
  .wh-form-grid label.full{grid-column:span 1}
  .wh-pos-pick{grid-column:span 1}
  .wh-sess-list{max-height:110px}
  .wh-hd{flex-direction:column;align-items:stretch}
  .wh-acts{width:100%;margin-left:0;justify-content:flex-start}
  /* 手机端同样给足高度：窄屏行高会因长型号折行变大，460px 约当 8~10 行 */
  .wh-scroll{max-height:max(460px,calc(100vh - 340px))}
  /* ⚠ 不要再把 min-width 调小：仓库表最多 10 列，表格窄于 960px 后
     短值列（仓位/物料编码/日期/单位）会被迫折行撕开（如「G4-4-」/「6」），
     观感比横向滚动差得多 —— 窄屏就让它横向滚动（配吸底平移条）。 */
}

/* ============================================================
   视觉精修层（2026-09-18）· 全局美观/合理性统一
   原则：不动护眼主题锁定的变量锚点（--bg/--card/--card-hd/--row-*）、
   白底约定（弹窗/按钮/输入框）与业务配色，只做层次、节奏、一致性精修。
   ============================================================ */
:root{
  /* D301：旧名改成别名（值逐字节相同，别名只是去掉重复定义）。
     ⚠ 别顺手把 8/12/16 改成 4/8/12/16 四档 —— 那是批二的视觉变更，不是本批的事。 */
  --r-sm:var(--r-2); --r-md:var(--r-3); --r-lg:var(--r-4);   /* 圆角三级：控件 / 卡片 / 弹窗 */
  --ink-2:#3d4a5c;                        /* 二级正文 */
  /* --ink-3 已删除（D302）：深色层它与 --sub 同值重复、浅色层 #6B7686 只有 4.44:1 不达 AA。
     7 处引用改指 --sub —— 深色零变化，浅色 5.24:1。说明/注脚一律 --muted。 */
}
/* --- 1) KPI 卡：数值等宽对齐、主卡强调、悬浮微抬 --- */
.kpi{padding:12px 14px var(--s-6);box-shadow:var(--shadow-sm)}
.kpi .l{font-weight:600;letter-spacing:.2px}
.kpi .v{font-variant-numeric:tabular-nums}
/* 「在册总人力」是首屏主指标：字号由第二批的 .kpi.pri .v 统一接管（:first-child 写法特异性更高会压过 .pri，故不再在这里设） */
@media(hover:hover) and (min-width:641px){
  .kpi:hover{transform:translateY(-2px);box-shadow:var(--shadow-md)}
}
@media(min-width:641px){
  /* 部门视图长标签「在册总人力（调试一部）」原先单行截断 → 放开两行 */
  .kpi .l{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
}
@media(max-width:640px){ /* 恢复手机端紧凑内边距（M0-8 口径，勿丢） */
  .kpi{padding:8px 10px 10px}
}
/* --- 2) 卡片说明文字：段落灰字不再挤成一行 --- */
.card{box-shadow:var(--shadow-sm)}
.hint{font-size:12px;line-height:1.7;margin:0 0 10px}
@media(max-width:640px){.hint{font-size:11px;line-height:1.6;margin-bottom:8px}}
/* --- 3) 表格：表头字距、数值等宽（总计行内联样式见 deptMgrTable 渲染处） --- */
th{letter-spacing:.4px;color:#33415c;font-weight:600}
td{font-variant-numeric:tabular-nums}
/* --- 4) 负责人×阶段蓝卡：深蓝大色块头 → 浅蓝头 + 左侧蓝条，降低整页压迫感 --- */
.lvl-card{border:1px solid #c9d9f2;box-shadow:var(--shadow-sm)}
.lvl-card-hd{background:linear-gradient(135deg,#e7f0ff,#f2f7ff);color:#2f54b0;border-bottom:1px solid #d5e2f7;box-shadow:inset 3px 0 0 #2f6fed}
.lvl-card-hd .lvl-mgrname{color:var(--blue-fg);font-weight:700}
.lvl-cardmeta{color:var(--sub)}
/* --- 5) 按钮：细描边增加界定 + 悬浮微抬（白底约定不动） --- */
.btn{border:1px solid #dbe6f6;box-shadow:var(--shadow-sm)}
@media(hover:hover){
  .btn:hover{transform:translateY(-1px);box-shadow:var(--shadow-md)}
}
/* --- 6) 弹窗圆角/阴影统一到三级令牌 --- */
.modalbox,.modal-box,.pwchange-box{border-radius:var(--r-lg);box-shadow:var(--shadow-lg)}
/* --- 7) 侧栏：条目过渡 + active 微渐变 --- */
.sb-item{transition:background .15s ease,color .15s ease}
.sb-item.active{background:linear-gradient(135deg,#2f6fed,#3b82f6)}
/* --- 8) 滚动条收敛（10px → 8px，更轻） --- */
::-webkit-scrollbar{width:8px;height:8px}
::-webkit-scrollbar-thumb{background:#c3d5ec;border-radius:8px;border:2px solid var(--bg)}
::-webkit-scrollbar-thumb:hover{background:#9fbce2}

/* ============================================================
   第一批 · 结构重设计（2026-09-18）
   A 上下文条 / B 阶段徽标 / C 零值弱化 / D 数值列右对齐
   E 筛选条独立成区 / F 8pt 间距节奏
   ⚠ 第二批「KPI 主次分级」要改 #kpis 骨架同构，另做，本批不碰。
   ⚠ 同上所述：护眼主题锚点（--bg/--card/--card-hd/--row-*）、
      白底约定（弹窗/按钮/输入框）、业务配色一律不动。
   ============================================================ */
/* --- A) 上下文条：第一眼回答「我在看什么 / 当前口径 / 数据多新」 --- */
.ctxbar{display:flex;align-items:center;justify-content:space-between;gap:4px 16px;flex-wrap:wrap;
  margin:0 0 14px;padding:var(--s-4) 14px;border-radius:var(--r-md);border:1px solid #d8e5f8;
  background:linear-gradient(90deg,#e9f1ff 0%,var(--card) 55%);box-shadow:var(--shadow-sm)}
.cb-l{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;min-width:0}
.cb-title{font-size:15px;font-weight:700;color:var(--blue-fg);letter-spacing:.4px;white-space:nowrap}
/* ⚠ cb-sub/cb-r 一律单行：文字从「加载中…」换成真实口径时高度不得变化 ——
   手机端 CLS 防线的一部分（check-mobile H5，阈值 <0.05） */
.cb-sub{font-size:12px;color:var(--ink-2);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cb-r{font-size:12px;color:var(--sub);font-variant-numeric:tabular-nums;white-space:nowrap}
/* 已有专属标题区的视图（OSS 台账 / 仓库 / 工伤）不再重复占位，避免两条标题叠着看 */
body.view-oss #ctxBar,body.view-ware #ctxBar,body.view-emp #ctxBar,body.view-injury #ctxBar,
.wrap.general-mode #ctxBar{display:none}
@media(max-width:640px){
  .ctxbar{margin:0 0 10px;padding:8px 10px;gap:2px 8px}
  .cb-title{font-size:13.5px}
  .cb-sub,.cb-r{font-size:12px}
  .cb-r{display:none}   /* 手机上「更新于/rev」让位给口径行，同时保证单行高度恒定 */
}
/* --- B) 只读阶段徽标（stageTd 只读分支输出，编辑态仍是 <select>） --- */
.stage-tag{display:inline-block;padding:2px var(--s-4);border-radius:999px;font-size:12px;line-height:1.6;
  white-space:nowrap;background:#e9f0fb;color:#2f54b0;border:1px solid #d3e0f5}
.stage-tag[data-stage="MP"]{background:#e2f5ea;color:#1f7a45;border-color:#c1e6d1}
.stage-tag[data-stage="OSS"]{background:#e6f2ff;color:#1668a5;border-color:#c7e2f7}
.stage-tag[data-stage="待定"]{background:#f4f0ff;color:#7c4dbe;border-color:#e2d8f7}
.stage-tag[data-stage="暂停"]{background:#fdf1e3;color:var(--orange-fg);border-color:#f3d9b4}
/* --- C) 零值弱化：数量为 0 的格子不再用满强度胶囊抢视线（点击展开仍照常可用） --- */
td[data-z="1"] .cntbadge{opacity:.42;box-shadow:none}
td[data-z="1"] .cntbadge.edit:hover{opacity:.9}
/* --- D) 全列居中（2026-09-19 第五轮，用户：「这两个图片的字体居中显示」） ---
   原先的设计是「文本列左对齐 + 数值列右对齐」，用户要求整表**居中**。
   ⚠ 这对「按位对齐、方便竖着比大小」是有损的 —— 数字改成居中后，个位/十位数不再对齐成列。
     用户看图后明确要求居中，故按此执行；如果他之后想恢复右对齐，删掉 D 段这两条即可。
   ⚠ 表头与数据格必须**同时**居中，否则又会出现上一轮那种「表头与列内数字错位」的问题。
   ⚠⚠ `.cntcell` 里装的是 `inline-block` 的 `.cntbadge` 胶囊 —— **不要给它加 `display:flex`！**
     我第一版图省事写成 `display:flex;justify-content:center`，结果**整表行高从 34px 爆到 139px**：
     `display:flex` 会让这个 `<td>` 不再是 `table-cell`，从而退出表格的「同一行等高」计算，
     行高改由其它单元格撑（实测 138.7px），同时 `.namelist`（隐藏的姓名浮层）被 flex 拉到
     与胶囊同行，视觉上多出一竖列幻影胶囊。
     ⇒ 对 `table-cell` 来说，**`text-align:center` 本来就能居中 `inline-block` 子元素**，
     不需要动 `display`。下面只留 text-align，且不再区分 cntcell/totcell。
   ⚠ 必须限 `min-width:641px`：本目录里的规则**不限于表格视图**，而 640px 以下手机端走「卡片视图」
     （`body:not(.tv-table)`，见文件上方 ~1086 行）。卡片视图里每个 `<td>` 本来就是 `display:flex`
     的「标签 + 值」横排条，`text-align:center` 在 flex 容器上会命中 `::before` 的标签文本，
     把「合计」二字从左侧标签位移到中间糊在数字上。居中只对「真表格」有意义。 */
@media(min-width:641px){
  #tableWrapSec .table-scroll th{text-align:center}
  #tableWrapSec .table-scroll td{text-align:center}
}
/* --- E) 筛选条独立成区：筛选项是「操作」，不该和表格数据同底 --- */
.toolbar{background:var(--card-hd);border:1px solid #d8e5f8;border-radius:var(--r-md);
  padding:10px 12px;margin:0 0 12px;gap:10px}
.tb-actions{border-left-color:var(--line)}
@media(max-width:640px){
  .toolbar{padding:8px 10px;margin-bottom:10px}
  .tb-actions{padding-left:0;border-left:none}
}
/* --- F) 8pt 节奏：卡片与网格的间距统一到 8 的倍数 --- */
.kpis{gap:12px;margin-bottom:16px}
.card{padding:16px 16px 12px}
@media(max-width:640px){ .card{padding:12px 12px 8px} }

/* ============================================================
   第二批 · KPI 主次分级（2026-09-18）
   1 张主卡（在册总人力，跨 2 列 + 左侧蓝条 + 32px 数值）+ 6 张次卡（18px 数值、弱化标签）。
   ⚠ 列数 8/4/2 + 主卡 span 2 是刻意的：各断点下总格数都是 8，行数与改造前完全一致
     （桌面 1 行 / ≤980 两行 / ≤640 四行）→ 骨架高度不变，手机端 CLS 不受影响。
   ⚠ 主卡的任何几何改动（类、跨列、字号）都必须同步 #kpis 的内联骨架，否则 CLS 复发。
   ============================================================ */
.kpi.pri{grid-column:span 2;background:linear-gradient(135deg,#eaf2ff,#f8fbff);border-color:#cfdff5;
  padding-left:18px;box-shadow:var(--shadow-sm),inset 4px 0 0 #2f6fed}
.kpi.pri::before{display:none}      /* 顶部彩条让位给左侧强调条，避免两条并存 */
.kpi.pri .l{color:var(--blue-fg);font-size:13px}
.kpi.pri .v{font-size:32px;letter-spacing:.3px}
/* 次卡降级：数值与标签各收一档，让主卡成为首屏唯一视觉锚点 */
.kpi .v{font-size:18px}
.kpi .l{color:var(--sub)}
@media(max-width:980px){
  .kpi.pri .v{font-size:28px}
}
@media(max-width:640px){
  .kpi.pri{padding-left:14px;box-shadow:var(--shadow-sm),inset 3px 0 0 #2f6fed}
  .kpi.pri .v{font-size:24px}
  .kpi.pri .l{font-size:12px}
  .kpi .v{font-size:19px}
  /* ── D293 · 窄屏说明行 2 行 → 1 行 ────────────────────────────────────────
     6 张副卡各让出约 18px，合计约 108px —— 首屏的图表才有机会露头。
     ⚠ 只在 ≤640 收一行：桌面 ≥641 仍是 3 行（1024 档「正式工」要 5 行，那是既有窄档问题）。
     ⚠ 信息不能真丢：点卡片展开全文，见 #kpis 的事件委托（kpiExpandOnTap）。 */
  .kpi:not(.pri) .s{display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical;
    overflow:hidden;cursor:pointer}
  .kpi.s-open .s{display:block;-webkit-line-clamp:unset;overflow:visible}
}
/* ============================================================
   第三批 · 框架层重构（2026-09-18）
   I 顶栏精简 / J 侧栏重构 / K 内容区节奏 / L 视图切换动效
   原则同上：护眼锚点与业务配色不动，只改框架与节奏。
   ============================================================ */
/* --- I) 顶栏：只留品牌与状态；状态胶囊化，口径一眼可读 --- */
header .statusbar{justify-content:flex-end;gap:8px}
header .impstat{padding:4px var(--s-5);border-radius:999px;font-size:12px;line-height:1.5;opacity:1;
  white-space:nowrap;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.2)}
@media(max-width:640px){
  header .statusbar{justify-content:flex-start;gap:6px}
  header .impstat{padding:3px var(--s-4);font-size:11px}
}
/* --- 构建版本号胶囊（2026-10-05）---
   ⚠ 选择器必须写成 `.statusbar .impstat.verchip`（0,3,0）而不是 `.verchip`（0,1,0）：
     手机端根治层有 `.statusbar .impstat{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}`
     （0,2,0，见 2026-09-23 第二十六轮），版本号正好要被它截掉。
     用三级特异性 + flex:none 把它从"可压缩"名单里摘出来，任何断点下都完整可读。
   ⚠ flex:none 的代价是它会挤占状态条宽度 —— 这正是我们要的：宁可让 #impStat 的
     「当前数据：内置解析…」先被省略，也不能让版本号缺字（用户看不到版本号 = 本次改动白做）。 */
.statusbar .impstat.verchip{flex:none;overflow:visible;white-space:nowrap;min-width:0;
  cursor:pointer;user-select:none;font-variant-numeric:tabular-nums;letter-spacing:.2px}
.statusbar .impstat.verchip:hover{background:rgba(255,255,255,.28)}
.statusbar .impstat.verchip:focus-visible{outline:2px solid var(--pure);outline-offset:1px}
/* 数字部分用高对比色：版本号是「我现在看的是哪一版」的唯一凭据，不能和状态文案一样灰 */
.statusbar .verchip-no{font-weight:800;color:var(--pure)}
.statusbar .verchip-at{opacity:.72;margin-left:var(--s-2);font-weight:500}
/* 版本号取不到时（未连服务器 / file:// 直开）：弱化显示，别让用户以为有版本可查 */
.statusbar .impstat.verchip.verchip-na{opacity:.55;cursor:default}
@media(max-width:640px){
  /* 手机上只留序号，时间挪进 title —— 360px 档状态条只剩约 40px 余量 */
  .statusbar .verchip-at{display:none}
  .statusbar .impstat.verchip{padding:3px 8px}
}
/* 迁到页脚的「数据格式说明」：改做提示条，不再是顶栏里的半透明白字。
   ⚠ 只在数据型视图显示 —— 仓库/工伤/OSS/综合部不需要它，且它会挤掉仓库表的一屏可见行数
     （check-ware-rows A8 断言「一屏完整可见 ≥15 行」，实测被顶掉 1 行）。 */
#metaNote{display:none}
body.view-overview #metaNote,body.view-dept #metaNote,body.view-level #metaNote{display:block}
.fmt-note{margin:0 0 12px;padding:var(--s-4) 14px;border-radius:var(--r-md);
  background:var(--card);border:1px solid var(--line);color:var(--sub);font-size:12px;line-height:1.7}
/* --- J) 侧栏：窄屏维持 124px，宽屏（≥1500px）放宽到 184px ---
   ⚠ 为什么不能无条件加宽：侧栏每宽 1px，内容区就少 1px，而内容区的横向开销是固定的 78px
     （.wrap 左右各 22px + .wh-card 左右各 16px + 1px 描边）。仓库表是 table-layout:fixed，
     表宽一缩，「规格型号」这类长文本就会从 1 行变 2 行（行高 34.5→54px），把整表往下顶，
     1366×660 下实测「一屏完整可见」从 15 行掉到 14 行（check-ware-rows A8）。
     实测扫描（1366×660）：sbw 128 → 表宽 1160 → 15 行；sbw 132 → 表宽 1156 → 14 行。**阈值就在 128px**。
     故 1366 这类常见笔记本分辨率不参与加宽；加宽断点取 1500px —— 该宽度下即使切到 184px 仍有 16 行
     （1440px 处只有刚好 15 行、等于贴着断言下限，故不取），留给数据变化一点余量。
     ⚠ 123px 时代的紧凑化仍在下面放宽，两套宽度共用同一份排版。 */
:root{--sbw:124px}
@media(min-width:1500px){ :root{--sbw:184px} }
.sidebar{font-size:13px}
.sb-app{font-size:14px;letter-spacing:.3px;padding:16px 14px var(--s-6);gap:var(--s-4)}
.sb-scroll{padding:8px 8px 12px;gap:2px}
.sb-sec{font-size:11px;letter-spacing:1.6px;padding:14px 10px 6px;color:#8b97ad}
.sb-item{padding:var(--s-4) 10px;font-size:13px;gap:var(--s-4);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;word-break:normal}
.sb-subnav{padding-left:8px;gap:2px;margin:2px 0 var(--s-2) 14px}
#deptSubNav .sb-subsec{margin-left:16px;padding-left:10px}
#deptSubNav .sb-subsec-btn{padding:var(--s-2) 8px;font-size:12px}
#wareSubNav .sb-n{font-size:11px;padding-left:6px}
.sb-logout{padding:14px 12px;font-size:13px}
/* 视图入口：左侧指示条 + 更高对比，和功能型入口明确分层 */
.sb-scroll .sb-item.sb-view{position:relative;padding-left:14px;font-weight:600}
.sb-scroll .sb-item.sb-view::before{content:'';position:absolute;left:4px;top:50%;transform:translateY(-50%);
  width:3px;height:0;border-radius:2px;background:#5b8cf7;transition:height .16s ease;opacity:0}
.sb-scroll .sb-item.sb-view:hover::before{height:52%;opacity:.7}
.sb-scroll .sb-item.sb-view.active::before{height:60%;opacity:1}
.sb-scroll .sb-item.sb-view.active{color:var(--pure)}
@media(max-width:640px){ /* 手机横排导航：指示条改为下划线，不占横向空间 */
  .sb-scroll .sb-item.sb-view{padding-left:10px}
  .sb-scroll .sb-item.sb-view::before{left:8px;right:8px;top:auto;bottom:3px;width:auto;height:2px;transform:none}
  .sb-scroll .sb-item.sb-view:hover::before,.sb-scroll .sb-item.sb-view.active::before{height:2px}
}
/* --- L) 视图切换动效：消除 display 硬切的突兀感（降级：系统减弱动效时不播） --- */
/* 电话簿折叠条：标题变点击区（右侧箭头指示），收起时只隐藏卡片内容、外壳保留 */
#cardPhone h3{cursor:pointer;display:flex;align-items:center;gap:8px;user-select:none;-webkit-user-select:none}
/* 折叠指示箭头紧贴标题文字（gap 8px），不再用 margin-left:auto 甩到卡片右边缘 ——
   卡片最宽可到 628px，箭头飘在十几厘米外，用户根本看不出它属于这个标题。 */
#cardPhone h3::after{content:'▾';font-size:11px;color:var(--sub);flex:none;transition:transform .16s ease}
#cardPhone.folded h3::after{transform:rotate(-90deg)}
#cardPhone.folded > *:not(h3){display:none!important}
/* ⚠ 折叠后必须去掉卡片在 grid 里被撑开的多余高度（2026-09-18）：
   #gridPhoneTop 是两列 grid，右列是 414px 高的 Top15 卡；即使用 align-items:start 让左卡
   按内容定高，grid **行高** 仍等于最高卡（414px），而 .card 会在行内被拉伸到行高
   （align-items:start 只阻止拉伸，但本页 .card 另有背景/描边，视觉上仍是一条 414px 的空壳，
   实测折叠后 #cardPhone 仍渲染成 414px，与右卡等高、内部全空 → 看着像空滚动条轨道）。
   用 align-self:start 把折叠卡钉在行首，行高归内容，露出真实高度（57px）。 */
#cardPhone.folded{align-self:start;padding-bottom:12px}
/* ⚠ grid 默认 stretch：兄弟卡片会把折叠后的电话簿又拉回整行高度，折叠就白省了。
   改 align-items:start，让每张卡按自身内容定高 ── 折叠后才真的是一条矮样。 */
#gridPhoneTop,#gridConstitute{align-items:start}
/* 展开态「下端对齐」（2026-09-19 第三轮，用户要求「这里下端对齐啊」）：
   两列 grid 里右卡「项目 / 线体 人力 Top 15」是固定 414px 高（h3 23 + hint 20 + 图表 330 + 内距/padding），
   左卡「负责人电话号码查询」展开后只有 298px（h3 23 + 工具行 31 + 电话表 200 + padding 12×2）
   → 底边差 117px，左卡右下留白、两张卡像没对齐。修法两个方向：
     ① 让电话表吃掉富余高度（flex:1 1 auto + 抬 max-height）—— 多显示几行联系人；
     ② 卡内最后一个子元素换成 auto 收缩（margin-bottom:0），避免 flex 富余高度在卡底留一条 12px 空带。
   ⚠ 只在**展开态**拉伸：折叠态仍由 #cardPhone.folded 的 align-self:start 钉住（57px），
      绝不能改成 align-items:stretch —— 那会把折叠卡又拉回 414px 的空壳（见上方注释）。
   ⚠ 只在**两列**（min-width:981px，与 .grid2 的对开断点一致）生效 —— 单列下没有「并排的另一张卡」，
      拉伸只会让矮的那张被拉长、底部留一大片空白（手机 / 部门视图都属单列）。
   ⚠ 高度必须守恒：本行 grid 行高由**右卡**（固定 414px）决定，左卡拉伸不改变行高，
      故下方内容不会位移，CLS 不受影响；不要给 .chart 加成 flex 子项，否则图表会被压扁。 */
@media(min-width:981px){
  body:not(.view-dept) #gridPhoneTop > #cardPhone:not(.folded){display:flex;flex-direction:column;align-self:stretch}
  body:not(.view-dept) #gridPhoneTop > #cardTop15{align-self:stretch}
}
/* ⚠ max-height 必须带 !important：#phoneTable 的 200px 是**内联** style（见 HTML 骨架），
   内联优先级高于本选择器 —— 不带就完全不起作用（实测表格仍停在 200px，卡底留 129px 空白）。 */
body:not(.view-dept) #cardPhone:not(.folded) > #phoneTable{flex:1 1 auto;max-height:340px!important}
body:not(.view-dept) #cardPhone:not(.folded) > *:last-child{margin-bottom:0}
body:not(.view-dept) #gridPhoneTop > #cardTop15{align-self:stretch}
body:not(.view-dept) #cardPhone:not(.folded) > *:last-child{margin-bottom:0}
@media(hover:hover){ #cardPhone h3:hover{color:var(--blue)} }
@media(prefers-reduced-motion:no-preference){
  /* ⚠ 只做 opacity 淡入，不加 transform：.wrap 内有仓库表 sticky 表头，祖先位移会把它一起推下去，
       1366×660 下实测少一整行（2026-09-18）。起步透明度留 .45 而非 0，避免大体量表格闪白。 */
  .wrap.view-enter{animation:viewFade .2s ease both}
  @keyframes viewFade{from{opacity:.45}to{opacity:1}}
}

/* ============================================================
   设计升级层（2026-09-28）· 全系统统一精修 →「现代数据看板」
   方向：更大留白 · 弱化描边 · 靠阴影与背景分层 · 数字可扫读

   ⚠ 不可越线（由 smoke-eyecare-theme.js 静态扫描整个 <style> 守护）：
     1) 护眼四色锚点 --bg / --card / --card-hd / --row-* 的**值**不动；
     2) 「卡片非纯白」「弹窗与按钮白底」两根约定保留 —— .btn 的 background 一律不许改；
     3) ⛔ 本层（⚠ 连注释里也不行）不得出现被禁的「旧灰底色」十六进制字面量 ——
        清单见 smoke-eyecare-theme.js 第 4 节最后三条断言。该守护是**静态扫描整段
        CSS 文本**、不剥注释，把颜色名原样写进注释会诬告自己（本轮亲历：写出清单后
        PASS 43 直接掉到 39）。

   ⚠⚠ 横向留白有「列宽下界」红线：.wrap / .card / .tablewrap 的**左右** padding
       一旦加大，所有表格的可用宽度同步变小 —— 此前定稿的短列（申请人 95 / 时间 90 /
       仓位 79 / 操作 96）都是**下界**，压 1px 就整表折行。故：
       横向留白只在 ≥1360px 加（那时 .wrap 已被 max-width 封顶，加留白不会压缩内容区）。
   ============================================================ */
:root{
  /* D301：原有的 --s2…--s6（8/12/16/20/24）已删除 —— 它们**从未被任何规则引用**，
     且命名与标尺的 --s-2(4px) 只差一个连字符、语义完全不同（--s2 是"第 2 档=8px"，
     --s-2 是"2px 档=4px"）。两套并存是迟早写错一次的坑，标尺统一在顶部 :root 定义。 */
  /* 三级阴影：用「浮起」取代「重描边」—— 现代看板分层的主要手段 */
  --el1:0 1px 2px rgba(16,24,40,.05);
  --el2:0 2px 8px -2px rgba(16,24,40,.10), 0 1px 3px rgba(16,24,40,.05);
  --el3:0 16px 40px -12px rgba(16,24,40,.20), 0 4px 12px -4px rgba(16,24,40,.08);
  /* 分隔线:极淡「透明黑」而非蓝线 —— 让线条中性退场，不再与数据配色抢注意力 */
  --hair:rgba(24,36,58,.08);
  --hair2:rgba(24,36,58,.14);
  /* 面板内二级面：用于「卡中卡」分区底，与 --card 拉开一档明度 */
  --surface2:#EDF2F9;
  /* D301 删 --r-xs；D302 把 --r-sm2(10)/--r-md2(14)/--r-lg2(18) 折到标尺档位后删除：
     10→--r-2(8)、14→--r-3(12)、18→--r-4(16)。原注释写「批二一并收敛」，这就是批二。 */
  --ease:cubic-bezier(.22,.61,.36,1);
}

/* --- A) 全局：滚动条 / 选区 / 焦点可见性 --- */
::-webkit-scrollbar{width:10px;height:10px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:rgba(24,36,58,.16);border:2px solid transparent;background-clip:content-box;border-radius:8px}
::-webkit-scrollbar-thumb:hover{background:rgba(24,36,58,.28);background-clip:content-box}
::selection{background:rgba(47,111,237,.16)}
:focus-visible{outline:2px solid rgba(47,111,237,.45);outline-offset:2px}

/* --- B) 骨架留白：内容更宽，横向留白只在封顶后才加（见上方红线） --- */
.wrap{max-width:1440px;padding-bottom:80px}
@media(min-width:1360px){ .wrap{padding-left:30px;padding-right:30px} }

/* --- C) 卡片体系：弱描边 + 二级阴影，靠「浮起」分离而非靠蓝框 --- */
.card,.kpi,.tablewrap,.viewbar,.oss-card,.lvl-dept,.lvl-stage,.dh-btn,.general-row,.injury-row{
  border-color:var(--hair);
}
.card,.kpi,.tablewrap,.viewbar{border-radius:var(--r-3);box-shadow:var(--el2)}
/* ⚠⚠ 刻意**不改 .card 的 padding**：卡片内边距直接决定卡片高度，而
   smoke-card-align 有一条「折叠卡仍是 57px 矮条」的高度字面量断言 ——
   上下各加 2~4px 就会把它推成 61px（实测）。2px 的留白收益不值得打破高度契约。 */

/* --- D) 表头与表格：去掉表头那条 2px 蓝线，行分隔线转中性 --- */
th{border-bottom:1px solid var(--hair2);color:var(--sub);letter-spacing:.5px}
td{border-bottom:1px solid var(--hair)}

/* --- E) 按钮体系：统一圆角与过渡；⚠ 不改 background（白底约定）--- */
.btn{border-color:var(--hair2);border-radius:var(--r-2);
  transition:transform .18s var(--ease),box-shadow .18s var(--ease),background .18s var(--ease),color .18s var(--ease)}
.btn.primary{border-radius:var(--r-2);box-shadow:0 2px 10px -3px rgba(47,111,237,.5)}

/* --- F) 弹窗：更大的圆角与投影，与卡片层次区分 --- */
.modalbox,.pwchange-box,.modal-box,.addpanel-box{border-radius:var(--r-4);box-shadow:var(--el3)}

/* --- G) 输入控件：统一圆角 + 统一焦点环（⚠ 不改 background）--- */
select,input[type=text],input[type=number],input[type=date],input[type=password],input[type=search],textarea{
  border-radius:var(--r-2);transition:border-color .18s var(--ease),box-shadow .18s var(--ease)
}
select:focus,input[type=text]:focus,input[type=number]:focus,input[type=date]:focus,
input[type=password]:focus,input[type=search]:focus,textarea:focus{
  outline:none;border-color:var(--blue);box-shadow:var(--ring)
}

/* --- H) 纵向节奏：卡片与网格之间的「呼吸」（⚠ 仍只动纵向，横向见上方红线）--- */
.grid2{gap:18px 16px;margin-bottom:20px}
.kpis{margin-bottom:20px}
/* ⚠⚠ 也**不改 .card h3 的下边距**（原 6px）：卡片内任何纵向尺寸都会传导到卡片高度，
   6→10px 恰好把 smoke-card-align 的折叠卡从 57px 推成 61px、左卡从 298 推成 302。 */

/* --- I) 「同概念不同样式」收敛 ①：待办页统计格 ---
   原先 .ts-card 自带一套近白底 + 独立灰边（#fbfcfe / #e6eaf2），与系统卡片不是同一族。
   并入 --card + --hair + --el1 后，它和 KPI 卡、内容卡读起来才是「同一套设计语言」。 */
.todo-sum{gap:12px;margin:4px 0 18px}
.todo-sum .ts-card{border:1px solid var(--hair);border-radius:var(--r-3);padding:12px 14px;
  background:var(--card);box-shadow:var(--el1)}
.todo-sum .ts-n{font-size:22px;color:var(--ink);letter-spacing:-.2px}
.todo-sum .ts-l{color:var(--sub)}

/* --- J) 表格分隔线收敛 ---
   同一个「行分隔线」在系统里有 6 种颜色（--line / #eef1f5 / #e7f0fb / #eef1f6 /
   #d9e8fa / #cfe0f4），表头 2px、正文 1px 也不统一 —— 单看一页看不出，
   连着翻页就会觉得「每张表的精致度不一样」。这里收敛到 --hair / --hair2 两档。
   ⚠ 用「列举 + 同等特异性」而不是 !important：以后要改回来不必再打 !important 擂台。
   ⚠ 刻意**不动** #printRoot .pr-doc：打印出口要的是高对比 #8c8c8c，不是屏幕上的轻盈感。 */
.mgrtable th,table.oss-table th,.injury-card-table th,.xfer-list th,.wh-perm-list th,.wh-imp-preview th{
  border-bottom-color:var(--hair2);border-bottom-width:1px
}
.mgrtable td,table.oss-table td,.injury-card-table td,.xfer-list td,.wh-perm-list td,.wh-imp-preview td,.rp-tbl th,.rp-tbl td{
  border-bottom-color:var(--hair)
}
/* KPI 主卡阴影对齐次卡：.kpi.pri 比 .kpi 多一个类，会绕过上面那条 --el2，
   结果主卡反而比次卡「浅」—— 层级颠倒。这里把两者拉齐（左侧蓝条是主卡锚点，保留）。 */
.kpi.pri{box-shadow:var(--el2),inset 3px 0 0 #2f6fed}

/* --- K) 侧栏：每天第一眼的面，条目更「软」、激活态给一个明确的锚点 ---
   ⚠ 深色底 #222b3d 是守护断言的一部分（.sidebar），本层只改条目自身。
   ⚠⚠ 刻意**不动**待办条目的动作按钮：check-todo / check-todo-push 断言它是
      「单个 primary 按钮 + 同尺寸内联样式 ×N」，改样式会把老断言打红。 */
.sb-item{border-radius:10px;padding:var(--s-4) var(--s-5);
  transition:background .16s var(--ease),color .16s var(--ease)}
.sb-item:hover{background:rgba(255,255,255,.09)}
.sb-item.active{background:linear-gradient(135deg,#2f6fed,#0D6EFD);
  box-shadow:0 3px 10px -2px rgba(47,111,237,.5)}
.sb-sec{font-size:11px;letter-spacing:1.6px;color:#8d99af}

/* --- L) 「同概念不同样式」收敛 ②：周报数字块 ---
   .rp-kpi 与 .ts-card 是**同一个模式**（独立近白底 + 细灰边，自己一套），
   同样并入 --card / --hair / 统一圆角 —— 这样「一个数字块」在全系统只有一种长相。
   ⚠ 限定 @media screen：周报还要「打印 / 存 PDF / 导出图片」，打印态**必须保持原样**
     （#printRoot 里仍是浅白底 + 细灰边）—— 别把屏幕上的淡蓝底带进打印。
     check-report-d9 只断言「#printRoot 里有 .rp-kpis、结构是同一份一页纸」，不锁颜色，
     所以这里只改屏幕表现是安全的。 */
@media screen{
  .rp-kpi{border-color:var(--hair);border-radius:var(--r-3);background:var(--card)}
}

/* ===== M) 侧栏导航图标（2026-09-28 第八十四轮）：22 个条目的 emoji → 统一单色线性图标 =====
   用户原话：「侧栏 emoji 图标：风格确实杂乱（混用），……这个做吧」。
   杂乱的三个具体来源：
     ① 混了两套渲染机制 —— 彩色 emoji 与「跟随文字色的符号」并存；
     ② 语义重复 —— 铃铛同时指「今日待办」和「发送提醒通知」；人形同时指「员工信息」和「登录」；
     ③ 颜色不可控 —— 彩色 emoji 在深色侧栏上冷暖不一，且不同系统渲染出的样子各不相同。
   ⇒ 统一为 24×24 viewBox / stroke-width 2 的线性图标，用 mask + background:currentColor 上色。

   ⚠⚠ **为什么用 CSS 伪元素，而不是在 HTML 里塞内联 `<svg>`**：
     ① `.sb-view::before` 已被「激活指示条」占用 ⇒ 图标只能用 `::after`，再用 `order:-1`
        把它提到文字之前（flex 容器里 ::after 默认排最后一名）；
     ② **#loginBtn 的文本是 JS 用 textContent 写的**（'退出登录（xx）' / '负责人登录'，见 applyLoginUI）——
        textContent 会把元素子节点**整个替换**，若图标是内联 `<svg>`，每次登录/登出都会把它抹掉。
        伪元素不是 DOM 节点，textContent 碰不到它。这是本方案最先要保住的一条。
     ③ 颜色自动跟随 currentColor：普通 / :hover / .active / .sb-logout 红 —— 一处都不用重写。

   ⚠ 尺寸受**列宽硬约束**，改动前先跑 `_probe-sidebar.js`：
     条目 box 只有 108px（--sbw 124px），实测原「emoji + 空格」占 20.8px
     ⇒ 取「图标 15px + gap 5px = 20px」恰好持平，这样「导入导出 & 备份恢复」等
       长条目的截断程度不会比改前更差。
     ⛔ 别放大尺寸（124px 是下界）；⛔ 别改用 em 单位（手机端字号不同会两级放大）。 */
@media(min-width:641px){
  /* 桌面端把三类条目（视图 / 功能 / 退出登录）的左内边距**统一成同一个值**，
     让三组图标从同一个 x 起画 —— 否则视图组 14px、其余 11px，扫视时差 3px。
     ⚠ 取 11px，而**不是**把非视图组抬到 14px：
       抬到 14 会吃掉 3px 文字空间，「删除回收站」这类**原本刚好放得下**的条目开始裁字
       （实测：文字需 65px，14px 下只剩 63px；11px 下有 66px）。
       降到 11px 则全部条目只改善不退化（视图组文字可用 62.2 → 66）。
     ⚠ 视图组原本是 14px，那是**给左侧指示条让位** ⇒ 指示条必须跟着左移（下面第三条）。
     ⚠ 优先级：原规则是 `.sb-scroll .sb-item.sb-view`（0,3,0），
       只写 `.sb-item`（0,1,0）**盖不住**，必须用同深度选择器。
     ⚠ 限定 min-width:641px：手机横排另有一套 `.sb-item{padding:...}` 和下划线指示条。 */
  .sb-item{padding-left:var(--s-5)}
  .sb-scroll .sb-item.sb-view{padding-left:var(--s-5)}
  .sb-scroll .sb-item.sb-view::before{left:2px}
}
.sb-item{gap:var(--s-2)}
/* ⚠ 只在「明确登记了图标」的条目上生成 ::after。
   若写成 `.sb-item::after` 通配，而某个条目忘了定义 --nav-ico，
   `mask:var(--nav-ico)` 整条声明失效 ⇒ 没有遮罩 ⇒ 显示成 15×15 的**实心色块**。
   ⚠⚠ 2026-10-05 实测修正：这里的门控**不能只靠 mask 失败**。
     `content:''` / `content:var(--nav-ico)` 这类写法在漏定义时**照样**会生成 ::after
     （--nav-ico 未定义 ⇒ mask 声明失效 ⇒ 剩下 background-color:currentColor
     ⇒ 就是那个灰方块，用户截图里见到的就是它）。
     所以门控必须用**一个与 --nav-ico 配对定义的标记变量**：漏写 --nav-ico 时
     标记也没了 ⇒ content 为 normal ⇒ ::after 不生成 ⇒ 图标只是不显示，一眼能看见。 */
.sb-item[data-view]::after,.sb-item[data-act]::after,#loginBtn::after{
  content:var(--nav-ico-has);order:-1;flex:0 0 15px;width:15px;height:15px;align-self:center;
  background-color:currentColor;opacity:.8;
  -webkit-mask:var(--nav-ico) center/15px 15px no-repeat;
  mask:var(--nav-ico) center/15px 15px no-repeat;
  transition:opacity .16s var(--ease);
}
.sb-item:hover::after,.sb-item.active::after{opacity:1}

/* 图标本体：统一模板，只换 path。stroke 用 black（mask 只取 alpha 通道，
   background:currentColor 才决定实际颜色）—— 写成 #000 要转义成 %23，没必要。 */
.sb-item[data-view="overview"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 3h7v7H3zM14 3h7v7h-7zM3 14h7v7H3zM14 14h7v7h-7z'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="todo"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="dept"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18ZM6 12H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2M18 9h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-2M10 6h4M10 10h4M10 14h4M10 18h4'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="oss"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2ZM8 2v4M16 2v4M3 10h18'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="injury"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M11 2h2a2 2 0 0 1 2 2v5h5a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-5v5a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-5H4a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2h5V4a2 2 0 0 1 2-2Z'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="ware"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16ZM3.3 7l8.7 5 8.7-5M12 22V12'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="emp"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2M9 2h6a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1ZM12 11h4M8 11h.01M12 16h4M8 16h.01'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="staff"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2M12 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8Z'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="prob"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="xsup"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 2 11 13M22 2l-7 20-4-9-9-4Z'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="projxfer"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 3 4 7l4 4M4 7h16M16 21l4-4-4-4M20 17H4'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="weeklytrend"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M16 7h6v6M22 7l-8.5 8.5-5-5L2 17'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="proj"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 2 2 7l10 5 10-5ZM2 12l10 5 10-5M2 17l10 5 10-5'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-view="report"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7ZM14 2v5h6M10 9H8M16 13H8M16 17H8'/%3E%3C/svg%3E");--nav-ico-has:''}
/* D205 年终绩效考评：夹板 + 对勾（「评分表」的直觉形状）。⚠ 新增侧栏条目必须在这里配一条，
   否则就是一个没有图标的灰方块（check-nav-ico 的 A3 专门守这个）。 */
.sb-item[data-view="perf"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 2h6a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1ZM9 4H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-2M9 13l2 2 4-4'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-act="notify"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 11l18-5v12L3 14v-3ZM11.6 16.8a3 3 0 1 1-5.8-1.6'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-act="datahub"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 16l4 4 4-4M7 20V4M21 8l-4-4-4 4M17 4v16'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-act="bkdir"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6'/%3E%3C/svg%3E");--nav-ico-has:''}

/* D372：「邮件服务设置」从权限管理 hub 移到侧栏（它与权限无关，放在权限入口里会误导）。
   信封图标，与「发送提醒通知」的喇叭区分开。
   ⚠ 新增侧栏条目必须同时给 --nav-ico 与 --nav-ico-has（漏前者会少一个图标位，check-nav-ico A3 会红）。 */
.sb-item[data-act="mailcfg"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='4' width='20' height='16' rx='2'/%3E%3Cpath d='m2 7 10 6 10-6'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-act="oplog"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-act="trash"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M10 11v6M14 11v6'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-act="perm"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 3a4 4 0 1 0 0 8 4 4 0 0 0 0-8ZM22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-act="profile"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15.5 7.5l3 3L22 7l-3-3M21 2l-9.6 9.6M7.5 10a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11Z'/%3E%3C/svg%3E");--nav-ico-has:''}
/* D366：补两条漏登记的 data-act 图标 —— projcode（专案代码速查，本轮搬进主导航区）
   与 selfperf（填写我的绩效自评）。它们是 check-nav-ico A3 的既有红灯（漏：act:selfperf、act:projcode）：
   ::after 的 content 取 var(--nav-ico-has)，标记变量未定义时 content 回落 normal ⇒ 压根不生成伪元素 ⇒
   这两个条目比别的少一个图标位，文字左边缘对不齐（不是灰方块，灰方块是没有 content 门控时的老症状）。
   projcode 本轮从「个人中心」搬到「外派支援」后面，与 24 个有图标的兄弟条目并排，必须补齐才不突兀。
   图标本体：lucide tag（吊牌 —— 读作「代号 / 编号」，与已有的 proj / projxfer 的叠层与箭头区分开）；
   selfperf 用 lucide pencil-line（笔，与本条目的语义「填本人的绩效自评」一致）。
   ⚠ 本注释刻意不写反引号：check-staffoutsfilter-178 A6 守着「CSS 段里不许出现反引号」（本项目铁律）。 */
.sb-item[data-act="projcode"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20.6 13.4 13.4 20.6a2 2 0 0 1-2.8 0l-8.2-8.2A2 2 0 0 1 1.8 11V3.8A2 2 0 0 1 3.8 1.8H11a2 2 0 0 1 1.4.6l8.2 8.2a2 2 0 0 1 0 2.8Z'/%3E%3Ccircle cx='7' cy='7' r='1.2'/%3E%3C/svg%3E");--nav-ico-has:''}
.sb-item[data-act="selfperf"]{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z'/%3E%3C/svg%3E");--nav-ico-has:''}
/* 「使用手册」图标（2026-10-05 用户反馈：前面那个小方块能换成翻开的书本吗）
   ⚠⚠ 这条是**唯一**漏登记的条目 —— 漏了不会像注释设想的那样「图标不显示」，
      而是渲染成 15×15 的**实心灰方块**。原因见下面那条 content 门控补丁：
      门控靠 `content: var(--nav-ico-has)`，而 --nav-ico-has 与 --nav-ico 是**两条独立声明**，
      只写 --nav-ico 不会带上标记 ⇒ 门控失效 ⇒ mask:none 退化成实心块。
      （实测：用户截图里的灰方块就是它。）
   图标本体：lucide `book-open`（翻开的书），与其他 21 个条目同一套单色线性风格。 */
.sb-item[data-act="manual"]{
  --nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z'/%3E%3C/svg%3E");
  --nav-ico-has:''}
/* ⚠ 退出登录条目：图标随登录态切换（走 body.authed，不必改 JS —— textContent 只管文字） */
#loginBtn{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M15 12H3'/%3E%3C/svg%3E");--nav-ico-has:''}
body.authed #loginBtn{--nav-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9'/%3E%3C/svg%3E")}

/* ============================================================
   第九十四轮（2026-09-29）：「问题划分」可手输 + 「问题点」自动增高 + 草稿恢复提示条
   ============================================================
   ⚠⚠ 视觉层一律「CSS 末段追加覆盖」，不改上面任何既有规则。
   ⚠⚠ 不动任何列宽（铁律 3d）：本次只补「自定义值的兜底样式」，
       「电控 / 机构」两个既有值的渲染结果应逐像素不变（下方逐条给了理由）。 */

/* 1) 问题划分徽标：新增**中性色**兜底 k-other。
   ⚠ 为什么必须有：原渲染是 `s==='电控'?'k-ec':'k-mc'` —— 任何非「电控」的值
     都会落到**橙色（机构）**，等于给自定义值贴错标签。颜色在本表是**信息编码**，
     不是装饰（蓝=电控 / 橙=机构），三态之后语义才自洽。
   ⚠ 灰蓝与「待解决」的橙、「已解决」的绿都不撞色。 */
.prob-kind.k-other{background:#eef1f6;color:#475569;border:1px solid #d5dce6}

/* 2) 徽标允许折行。
   ⚠ 原 `.prob-kind{white-space:nowrap}` 在 `table-layout:fixed` 的 7% 列里，
     手输的长值会**溢出到相邻列**（视觉重叠、看不出属于哪一格）⇒ 必须能折。
   ⚠ 对既有值**渲染不变**：「电控」「机构」各 2 字，7% 列（960px 下 67.2px）内容区约
     55px，徽标文字区约 37px > 2×12px=24px ⇒ 根本触发不了换行，逐像素一致。
   ⚠ 用 `#probSec` 提特异性（0-1-0 → 1-1-0）压过上面那条 `.prob-kind`，无需 !important。
   ⚠⚠ **只改 `.prob-kind`，不碰 `.prob-status`** —— 状态格是「徽标 + 时效小字」两行堆叠，
     列宽余量已被记录为 6.5px 的极限，顺带改它就会引发列宽回归。
   ⚠ 不加 `line-height`：徽标高度直接参与行高，改它等于动行高（铁律：行高是敏感项）。 */
#probSec .prob-kind{white-space:normal;word-break:break-word;text-align:center}

/* 3) 「问题点」改 textarea 后的起始高度。
   ⚠ 复用 `.injury-grid textarea` 的既有样式（边框 / 内边距 / 字号 / resize:vertical），
     只把**起始高度**从 60px 收到 44px：问题点多是一两行的短描述，60px 会让空表单
     凭空多一块空白。而那条 60px 是「回复对策 / 外派备注」的合理高度，**不能动共用规则**。
   ⚠ 用 id 选择器（1-0-0）压过 `.injury-grid textarea`（0-1-1）—— 但**只点名这一个框**。
   ⚠ resize:vertical 保留 ⇒ 用户仍可手动拖大（自动增高只是替他省掉这一步）。 */
#probFIssue{min-height:44px;line-height:1.55}

/* 4) 草稿恢复提示条。
   ⚠ 用「提示」语义的蓝，**不要**用错误红：误关后还能找回内容是**好消息**，不是报错。
   ⚠ 内联 style="display:none" 管初始隐藏（内联赢一切，铁律 3b）；
      JS 显隐**双向**赋值 'flex' / 'none'，别写 ''（'' 会回落到本条的 flex，看似能用但语义含糊）。 */
.prob-draft-tip{margin:10px 0 0;padding:8px 12px;border-radius:8px;font-size:12px;line-height:1.7;
  background:#eef4ff;border:1px solid #b9d2ff;color:#1e40af;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.prob-draft-tip .prob-draft-drop{margin-left:auto;flex-shrink:0;font-size:12px;padding:2px 10px;font-family:inherit;
  border:1px solid #b9d2ff;border-radius:6px;background:var(--pure);color:#1e40af;cursor:pointer}
.prob-draft-tip .prob-draft-drop:hover{background:#dce9ff}

/* ===== 员工信息：以【工号】为唯一识别依据（2026-09-29 第九十六轮）=====
   1) 导入预览表最后一列「配对结果」的标记色。
   ⚠ 只用**颜色**区分，不加 padding / 不改字号行高 —— 预览区是 max-height:290px 的滚动框，
     撑高任何一行都会让它少显示几行（行高是敏感项）。
   ⚠ 类名带 `staff-imp-` 前缀（本模块自己的命名空间）：借别的模块的类名会失灵
     （第八十轮「现场问题收集」借 oss-op 那次的教训）。
   ⚠ 用 `.wh-imp-preview td.X`（0-2-1）压过 `.wh-imp-preview td`（0-1-1），无需 !important。 */
.wh-imp-preview td.staff-imp-add,.wh-imp-preview td.outs-imp-add{color:var(--green-fg);font-weight:600}
.wh-imp-preview td.staff-imp-upd,.wh-imp-preview td.outs-imp-upd{color:#1e40af;font-weight:600}
.wh-imp-preview td.staff-imp-same,.wh-imp-preview td.outs-imp-same{color:var(--muted)}
.wh-imp-preview td.staff-imp-skip,.wh-imp-preview td.outs-imp-skip{color:var(--red-fg);font-weight:600}

/* 2) 汇总条上的「工号重复」告警 —— 只在唯一键被破坏时出现（当前线上 0 组）。
   ⚠ 用琥珀色而不是错误红：它是「需要看一眼」的数据质量问题，不是操作失败。
   ⚠ cursor:help + title：告警必须能点开看到**是哪几个人**，只报组数等于让人自己去翻 906 行。 */
.staff-dupno{color:var(--orange-fg);background:#fff8ec;border:1px solid #fcd9a8;border-radius:var(--r-1);
  padding:0 6px;cursor:help}

/* ===== 台账人员工号（2026-09-29 第九十八轮）=====
   ⚠ 遵守本项目的视觉约定：一律「CSS 末段追加覆盖」，且**不动行高与卡片纵向尺寸** ——
     下面几个胶囊都是 inline-block + 10~11px 字号 + 小 line-height，靠 vertical-align
     收在现有行高里；一旦加 margin-上下 / 调 padding-上下，明细表每行就会变高、
     一屏少显示好几行（第八十轮那次踩过）。
   ⚠ 这是本项目**唯一**的浅色主题（无深色模式分支），所以颜色直接写死，与既有做法一致。 */
/* 姓名浮层里的工号胶囊：紧贴姓名输入框右侧 */
.nlempno{display:inline-block;margin-left:2px;padding:0 4px;border-radius:4px;font-size:11px;
  line-height:15px;color:#1e40af;background:#eef4ff;border:1px solid #cfdffb;white-space:nowrap;
  cursor:help;vertical-align:middle;flex-shrink:0}
/* 「未对上」态 = 在「华南售后员工信息」里查无此名、或有同名多条无法定向。
   ⚠ 用琥珀色而不是错误红：它是「需要看一眼」的数据问题，不是操作失败。
   这批人正是同名管控里「无法证明是两位不同的人」的那些，标出来省得用户去翻报告。 */
.nlempno.missing{color:var(--orange-fg);background:#fff8ec;border-color:#fcd9a8}
/* 明细表「负责人」列旁的工号。
   ⚠⚠ 必须**与姓名落在同一行**。实测（1600px 视口）：负责人列宽 94.4px，可用内宽约 84px；
     名字「葛冰冰」约 34px，工号按 10px 字号量出来 47px ⇒ 34+3+47 = 84 已经顶格，
     实际渲染直接换行 ⇒ 行高从 36.2px 涨到 40.1px（一屏少显示 2 行）。
     本项目对行高极敏感（见上面「卡片的纵向尺寸一律不许动」那段），所以把字号收到 9px、
     左右 padding 收到 1px、再加一点字距收紧，让「3~4 字姓名 + 6 位工号」稳定同排。
     改完实测行高回到 36.2px（见 _t-rowheight98.js）。
   ⚠ 弹窗里的 .mgr-no 不受行高约束，保持 11px 不动。 */
.mgrno{display:inline-block;margin-left:2px;padding:0 1px;border-radius:var(--r-1);font-size:11px;
  line-height:12px;letter-spacing:-0.2px;color:#1e40af;background:#eef4ff;border:1px solid #cfdffb;
  white-space:nowrap;cursor:help;vertical-align:middle}
/* 「各部门总负责人设置」弹窗里每个人名右侧的工号 */
.mgr-no{font-style:normal;font-size:11px;color:#1e40af;background:#eef4ff;border:1px solid #cfdffb;
  border-radius:4px;padding:0 var(--s-2);margin-left:6px;white-space:nowrap}
.mgr-no.missing{color:var(--orange-fg);background:#fff8ec;border-color:#fcd9a8}
/* 人工确认的工号（2026-09-29 第九十九轮）：这些姓名在员工档案里有**同名多条**，
   自动规则不可能判定，工号是管理员人工指定到本人的（表见 ledgerAliasTable）。
   与自动定向的结果用**颜色**区分开，让看的人知道「这条的来历不同，可复核」。
   ⚠⚠ 只改颜色 —— 字号 / padding / 边框宽度 / line-height 一律不动：上面那段量行高的
     结论对这三只胶囊同样成立，动尺寸就会把明细表行高撑大（第八十轮踩过）。
   用紫色（不是红/绿）：它既不是错误、也不是「成功」，只是「来源不同」。 */
.nlempno.manual{color:#6d28d9;background:#f6f1ff;border-color:#ddd0fb}
.mgrno.manual{color:#6d28d9;background:#f6f1ff;border-color:#ddd0fb}
.mgr-no.manual{color:#6d28d9;background:#f6f1ff;border-color:#ddd0fb}
/* 「🔗 同步工号」按钮的**视图闸门**：只有「部门视图 · 明细编辑」分区才出（与 #deptAddBtn 同款）。
   ⚠ 写在末尾而不是去改第 319/320 行那两条老规则：本项目约定「视觉层一律 CSS 末段追加覆盖」，
     而且那两条老规则里没有本 id，末段追加即可生效（无 !important 争用）。
   ⚠ 权限闸门不在这里，在 applyLoginUI() 里（两者必须都在，只做一处会让无权限的人看见按钮）。 */
#syncLedgerNoBtn{display:none}
body.view-dept.deptsec-detail #syncLedgerNoBtn{display:inline-block}
/* 「📇 专案代码速查」（D349）：与上一颗同款视图闸门 —— 只在「部门视图 · 明细编辑」分区出现。
   ⚠ 它**没有**权限闸门（全员可查；编辑按钮另按 admin 控制），所以这里不需要去 applyLoginUI 补 showIf。 */
#projCodeBtn{display:none}
body.view-dept.deptsec-detail #projCodeBtn{display:inline-block}
/* <<<DARK-OVERLAY-BEGIN>>> */
/* ════════════════════════════════════════════════════════════════════════════
   深色视觉覆盖层 · hc-command · append-only（只追加，不改原 CSS 任何一行）
   色板来源：_design/hc-command/01-visual-direction.md §3.3 映射表 + §9 根变量块
   生效范围：两层闸门（缺一不可）
     ① 外层 @media screen —— 挡「打印 / 存 PDF / 导出图片」。
        原理：window.print()、Ctrl+P、浏览器菜单「另存为 PDF」统统切到 print 媒体，
        块内规则一律不生效；而媒体查询不参与特异性 ⇒ 屏幕态层叠结果逐字节不变。
        （沿用原 CSS 已确立的同款手法：报告页顶部 KPI 卡那条「限定 @media screen」注释。）
        ⚠ 不要改成逐个选择器补 :not(.printing-doc)：printing-doc 仅由 printDoc() 添加，
          用户直接 Ctrl+P 时不会有它 ⇒ 那条路径零保护（探针实测 A 组 28 / C 组 28，数字相同）。
     ② 内层 body:not(.view-report) —— 继续挡「周报一页纸」视图：该视图 body 带对应类，
        深色 token 与覆盖规则都不生效，报告内变量自动回落根级浅色值。
   本层为纯 CSS：不做任何节点增删；不写报告页 / 打印页的选择器
   （那两处由既有断言与打印保真约定保护，一律留白）。
   ════════════════════════════════════════════════════════════════════════════ */

/* ─────────────────────────────────────────────────────────────────────────
   G1 · 设计 token 重定义（浅色 → 深色；同特异性下后定义胜出）
   ───────────────────────────────────────────────────────────────────────── */
@media screen{  /* ← 深色覆盖层的打印闸门（块内不额外缩进，保持 diff 最小） */
body:not(.view-report){
  --bg:#0C1118; --card:#131A24; --card-hd:#1A2430;
  --row-alt:#1A2430; --row-hover:#212D3B; --row-edit:#22314A; --row-edit-bar:#4C8DFF;
  --ink:#E8EEF6; --ink-2:#C6D1E0;
  --sub:#A6B4C6; --line:#2A3846; --surface2:#1A2430;
  --blue:#4C8DFF; --green:#3ECF8E; --orange:#FFA53D; --red:#FF3D66;
  --purple:#A78BFA; --cyan:#2BD4E0;
  --hair:rgba(255,255,255,.08); --hair2:rgba(255,255,255,.14);
  /* 新增 token（§9） */
  --bg-0:#0C1118; --bg-1:#131A24; --bg-2:#1A2430; --bg-3:#212D3B; --bg-4:#29384A;
  /* 2026-10-03 批次A（界面评审第 9/11 项）· 三级描边与说明文字提亮，只改值不改名：
     --line 1.6:1（装饰分隔线） < --stroke 2.15:1（控件/表面描边） < --stroke-strong 3.14:1（悬停/强调）。
     --muted 5.2:1 → 6.5:1（过 AA 未达 AAA 的说明文字，承载口径解释，实测偏吃力）。
     三档均与 --bg #0C1118 比对；smoke-eyecare-theme 守的是硬编码值，改令牌值不触发。 */
  --stroke:#3A4C60; --stroke-strong:#4E6480;
  --muted:#8E9FB6; --disabled:#55657A;
  --amber:#FFD24A; --pink:#F97BB0;
  --up:#FF5A52; --down:#3ECF8E; --danger:#FF3D66; --warn:#FFA53D; --ok:#3ECF8E;
  --cat-1:#4C8DFF; --cat-2:#2BD4E0; --cat-3:#3ECF8E; --cat-4:#FFA53D; --cat-5:#A78BFA;
  --cat-6:#F97BB0; --cat-7:#FFD24A; --cat-8:#7C8DA4;
  --stage-1:#6E86A8; --stage-2:#4E8DF0; --stage-3:#3AA4DF; --stage-4:#29C0C6;
  --stage-5:#2FCE96; --stage-6:#7BD05C; --stage-7:#E9B43C; --stage-8:#F2793C;
  /* ── D301 · 令牌层 v1.1 屏显深色补齐（只补 G1 里没写的名字，上方已有值一字未改）──
     D302 已删除 --ink-3：它与 --sub 同值重复（深色都是 #A6B4C6、hc 都是 #CBD7E7），
       7 处引用改指 --sub（深色零变化、浅色 4.44→5.24:1）。真正的「说明/注脚」是 --muted。 */
  --pure:#FFFFFF;
  /* D303：报告域弱化文字补上屏显值。此前 --rpt-muted 只在 :root（浅色/打印）与
     body.view-report 里定义，屏显普通视图**回落到浅色值** —— 那些元素只在报告视图渲染
     所以看不见，但「令牌在三层里求值不同」这件事本身就是隐患。这里显式给它深色值，
     让四层各自明确：浅色=打印 #5B6B80 ｜ 深色=#8E9FB6 ｜ 高对比=#8E9FB6 ｜ 周报=#8E9FB6。 */
  --rpt-muted:#8E9FB6;
  --ink-head:var(--ink);            /* 深色标题与正文同色，层级靠字重不靠色阶 */
  --blue-fg:#9FC2FF;                /* 文字专用；对 --card 9.69:1 */
  --green-fg:#7CE0B0;               /* 10.95:1 */
  --orange-fg:#FFC178;              /* 10.95:1 */
  --red-fg:#FF8FA9;                 /* 8.12:1（涨红跌绿的「红」用它，⛔ 不用 --red 填充色） */
  --purple-fg:#C4B5FD;              /* 9.47:1 */
  /* tint 三元组：深色底上的派生底色/边框一律 rgba(var(--x-rgb), α) */
  --blue-rgb:76,141,255; --green-rgb:62,207,142; --orange-rgb:255,165,61;
  --red-rgb:255,61,102;  --purple-rgb:167,139,250; --cyan-rgb:43,212,224;
  /* 阴影 / 层级 / 焦点环 —— 深底改为「黑色投影 + 内高光」 */
  --shadow-sm:0 1px 2px rgba(0,0,0,.45);
  --shadow-md:0 6px 16px -4px rgba(0,0,0,.55);
  --shadow-lg:0 20px 50px -12px rgba(0,0,0,.65);
  --el1:inset 0 1px 0 rgba(255,255,255,.045);
  --el2:0 8px 24px rgba(0,0,0,.45);
  --el3:0 24px 64px rgba(0,0,0,.55);
  --ring:0 0 0 3px rgba(76,141,255,.28);
  --glow-blue:0 0 0 1px rgba(76,141,255,.35),0 0 24px rgba(76,141,255,.22);
  --glow-danger:0 0 0 1px rgba(255,61,102,.40),0 0 28px rgba(255,61,102,.28);
  color-scheme:dark;
}

/* ─────────────────────────────────────────────────────────────────────────
   G2 · 全局：页面底 / 滚动条 / 选区 / 焦点可见性
   ───────────────────────────────────────────────────────────────────────── */
body:not(.view-report){
  background:radial-gradient(900px 320px at 85% -60px,rgba(var(--blue-rgb),0.07),transparent 60%),
             radial-gradient(760px 300px at 6% -80px,rgba(var(--cyan-rgb),0.05),transparent 60%),
             var(--bg);
  color:var(--ink);
}
body:not(.view-report) ::-webkit-scrollbar{width:10px;height:10px}
body:not(.view-report) ::-webkit-scrollbar-track{background:transparent}
body:not(.view-report) ::-webkit-scrollbar-thumb{background:#33445A;border-radius:6px;border:2px solid transparent;background-clip:padding-box}
body:not(.view-report) ::-webkit-scrollbar-thumb:hover{background:#41546E;background-clip:padding-box}
body:not(.view-report) ::-webkit-scrollbar-corner{background:transparent}
body:not(.view-report) ::selection{background:rgba(var(--blue-rgb),0.34);color:var(--pure)}
body:not(.view-report) :focus-visible{outline:2px solid rgba(var(--blue-rgb),0.7);outline-offset:2px}

/* ─────────────────────────────────────────────────────────────────────────
   G3 · 骨架与卡片系统（头部 / 侧栏 / 卡片 / 面板 / 分区底）
   ───────────────────────────────────────────────────────────────────────── */
body:not(.view-report) header{
  background:radial-gradient(760px 300px at 90% -70px,rgba(var(--blue-rgb),0.18),transparent 62%),
             radial-gradient(620px 240px at 2% 130%,rgba(var(--cyan-rgb),0.16),transparent 62%),
             linear-gradient(120deg,#0E1930 0%,var(--card) 48%,#0C2027 100%);
  border-bottom:1px solid var(--stroke);
  box-shadow:0 10px 30px -14px rgba(0,0,0,.85);
}
body:not(.view-report) header .meta{color:#B7C3D4}
/* G3·品牌胶囊「衔接」修正（用户截图反馈：白底 logo 方块与蓝色字块硬拼一道竖缝）
   病根两条，都在素材侧，不在配色侧：
   ① sj-logo.png 是**不透明白底**图（实测 170×139、alpha 全 255）——浅色主题下胶囊本来就是
      白的，白底白胶囊自然融为一体；换成深色胶囊后，它立刻变成「贴在深色条上的一块纯白砖」。
   ② 该图自带**截图裁切残留的灰线**（实测首行 y=0 ≈ #AAAAAA、末行 y=138 ≈ #636363），
      在白底块边缘会显出一圈灰框（放大 8 倍可见）。灰线已由裁掉首末两行解决（见交付说明）。
   处置：不再假装白底是透明的，而是把它做成一颗**规整的白色圆角芯片**——渐变整体提到胶囊上，
   两半之间不再有竖向硬缝；芯片四周统一内缩 3px、圆角按同心半径 8−3=5px；
   logo 图与字块之间留 10px 匀称间隙。 */ 
body:not(.view-report) .brand-pill{background:linear-gradient(135deg,#2A4FC0,#0E7E8C);box-shadow:0 2px 10px rgba(0,0,0,.5)}
body:not(.view-report) .brand-logo{
  background:var(--pure);
  align-self:stretch;height:auto;
  margin:3px 0 3px 3px;padding:0 var(--s-2);
  border-radius:var(--r-1);
  box-shadow:0 1px 2px rgba(0,0,0,.3);
}
/* 18px 对桌面(胶囊 32px → 芯片 26px)与手机(胶囊 30px → 芯片 24px)都留得出匀称白边，
   所以刻意不按断点分档：省掉嵌套的断点媒体查询，也让四档尺寸表现一致。
   注意：源图 sj-logo.png 自带两处截图裁切残留（首末各一行满宽灰线、左缘一条全高淡灰带
   246~253），在白色芯片上会显出淡淡的方框。**已在素材侧清理**（裁掉首末行 + 把与边界
   连通的近白像素压成纯白，见 `_clean-logo-artifact.js`），此处不再用 clip-path 硬遮 ——
   上线时 `sj-logo.png` 需与 HTML 一并替换。 */
body:not(.view-report) .brand-logo img{height:18px;width:auto}
body:not(.view-report) .brand-text{background:none;padding-left:10px}
body:not(.view-report) #connStat{background:rgba(var(--green-rgb),0.14);color:var(--green-fg)}
body:not(.view-report) #connStat.off{background:rgba(var(--red-rgb),0.14);color:var(--red-fg)}
/* 版本号胶囊（2026-10-05）：深色下 #impStat 走的是半透明白底，而版本号要更醒目，
   否则会和状态文案一样糊在背景里。数字用蓝、时间用弱化白。 */
body:not(.view-report) .statusbar .impstat.verchip{background:rgba(var(--blue-rgb),0.18);border-color:rgba(124,168,255,.42);color:#DCE9FF}
body:not(.view-report) .statusbar .verchip-no{color:#8FBBFF}
body:not(.view-report) .statusbar .verchip-at{color:#9FB4CE}
body:not(.view-report) .import-status.error,body:not(.view-report) .import-status.success{color:var(--orange)}
body:not(.view-report) .import-status.success{color:var(--green)}
body:not(.view-report) .sidebar{background:#101722}

/* 卡片 / 面板（多数吃 var(--card)，此处只补硬编码者） */
body:not(.view-report) .kpi.pri{background:linear-gradient(135deg,var(--card-hd),var(--card));border-color:#2A3E58;box-shadow:var(--shadow-sm),inset 3px 0 0 #4C8DFF}
body:not(.view-report) .kpi.pri .l{color:#8FBBFF}
body:not(.view-report) .kpi.pri .v{background:none;-webkit-text-fill-color:currentColor;color:var(--ink)}
body:not(.view-report) .kpi .v{background:linear-gradient(135deg,#DCE9FF,#7FB0FF);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
/* 彩条斜纹冗余（2026-10-03 · 界面评审第 12 项）—— 见基础层 .kpi::before 的说明。
   ⚠⚠ 这里**必须写 background-image，不能写 background 简写**：
   `background` 简写会把 background-image 整个重置掉，
   基础层叠上去的斜纹会被这 7 条深色配色**全部抹掉**（同特异性时后者胜）。
   所以深色层只换第二个渐变色值（配色），斜纹层原样保留。 */
body:not(.view-report) .kpi:nth-child(1)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 4px),linear-gradient(90deg,#4C8DFF,#6EA6EF)}
body:not(.view-report) .kpi:nth-child(2)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 6px),linear-gradient(90deg,#4C8DFF,#2BD4E0)}
body:not(.view-report) .kpi:nth-child(3)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 8px),linear-gradient(90deg,#FFA53D,#FFD24A)}
body:not(.view-report) .kpi:nth-child(4)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 10px),linear-gradient(90deg,#3ECF8E,#7BD05C)}
body:not(.view-report) .kpi:nth-child(5)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 12px),linear-gradient(90deg,#A78BFA,#C4B5FD)}
body:not(.view-report) .kpi:nth-child(6)::before{background-image:repeating-linear-gradient(115deg, rgba(255,255,255,.18) 0 1px, rgba(0,0,0,0) 1px 14px),linear-gradient(90deg,#FF5A52,#FF8A80)}
body:not(.view-report) .kpi:nth-child(7)::before{background-image:repeating-linear-gradient(115deg, rgba(0,0,0,.16) 0 1px, rgba(0,0,0,0) 1px 16px),linear-gradient(90deg,#2BD4E0,#7BD5F5)}
body:not(.view-report) .lvl-card{background:var(--card);border-color:#2A3E58}
body:not(.view-report) .lvl-card-hd{background:linear-gradient(135deg,#1B2C46,#152238);color:#DCE9FF;border-bottom:1px solid #2A3E58;box-shadow:inset 3px 0 0 #4C8DFF}
body:not(.view-report) .lvl-card-hd .lvl-mgrname{color:#BBD3FF}
body:not(.view-report) .lvl-cardmeta{color:#9FB4D6}
body:not(.view-report) .lvl-stage{border-color:var(--line)}
body:not(.view-report) .lvl-meta,body:not(.view-report) .lvl-row-eq,body:not(.view-report) .lvl-stage-cnt{color:var(--muted)}
body:not(.view-report) .lvl-warn{color:var(--danger)}
body:not(.view-report) .lvl-headmgr,body:not(.view-report) .lvl-stage-badge{color:var(--blue)}
body:not(.view-report) .lvl-dept>summary::before,body:not(.view-report) .lvl-stage-none .lvl-stage-badge{color:var(--muted)}
body:not(.view-report) .lvl-row-loc{color:var(--ink)}
body:not(.view-report) .lvl-row-proj{color:var(--sub)}
body:not(.view-report) .lvl-row-nums{color:var(--blue)}
body:not(.view-report) .viewbar .vb-title,body:not(.view-report) .cb-title{color:var(--ink)}
body:not(.view-report) .ctxbar{background:linear-gradient(90deg,rgba(var(--blue-rgb),0.12) 0%,var(--card) 55%);border-color:var(--line)}
body:not(.view-report) .toolbar{border-color:var(--line)}
body:not(.view-report) .tb-actions{border-left-color:var(--line)}
body:not(.view-report) .toolbar #rowCount{color:var(--muted)}
body:not(.view-report) .note{background:rgba(var(--orange-rgb),0.1);border-color:rgba(var(--orange-rgb),0.32);color:#FFCB8A}
body:not(.view-report) .note b{color:#FFE0AE}
body:not(.view-report) .hscroll{background:rgba(19,26,36,.96);border-color:var(--line)}
body:not(.view-report) .addpanel{border-color:var(--line)}
body:not(.view-report) .addpanel-head{border-bottom-color:var(--line)}
body:not(.view-report) .addpanel-head .t{color:var(--ink)}
body:not(.view-report) .addpanel-close{background:var(--bg-2);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) .addpanel-close:hover{background:var(--bg-3);color:var(--ink);border-color:var(--stroke-strong)}
body:not(.view-report) .addpanel-box{background:var(--bg-1);border-color:var(--line)}
body:not(.view-report) .morerows .btn{background:var(--bg-2)}
body:not(.view-report) .morerows .legend{color:var(--muted)}

/* ── 卡片 / 表格 / 视图条描边补强（2026-10-03 批次A · 界面评审第 11 项）────────
   现状：.card/.kpi 在 L1029 被 `border:none` 抹掉描边，只剩 box-shadow 纯黑投影；
   深色下卡底 #131A24 与页底 #0C1118 明度差仅 1.08:1 ⇒ 卡片「浮不起来」，
   分区感靠运气而非靠描边（L2816「弱描边 + 二级阴影」的意图从未真正渲染）。
   ⚠ 用 inset box-shadow 而**不是**真 border：.card 高度是既有契约（smoke-card-align 有
     「折叠卡仍是 57px 矮条」的字面量断言），加 border 会让自动高度 +2px 打破它；
     box-shadow 不参与布局 ⇒ 零 CLS、零高度变化。
   ⚠ 必须写在深色层内：--stroke 只在深色层定义，放全局会让浅色域求值失效。
   注：.kpi.pri 特异性更高，保留它自己的渐变+蓝色左条，不被本规则覆盖。 */
body:not(.view-report) .card,body:not(.view-report) .kpi,
body:not(.view-report) .tablewrap,body:not(.view-report) .viewbar{
  box-shadow:var(--el2),inset 0 0 0 1px var(--stroke);
}

/* ── 非阻塞通知浮层（2026-10-03 批次A · 配套 alert 拦截器，界面评审第 4 项）────
   底部居中、栈式（最多 3 条）、3.5s 自动消失、点按可提前关闭、同文本 2s 合并。
   放在深色层内以复用 token（--bg-3/--ink/--stroke/--el3），不新增硬编码色 ⇒ 过
   smoke-eyecare-theme。容器带 role=status aria-live=polite（顺带补全站 aria-live 为 0
   的缺口）。@media print 隐藏：打印/导出路径不应出现浮层。 */
body:not(.view-report) .hn-toast-host{
  position:fixed;left:50%;transform:translateX(-50%);bottom:22px;z-index:9999;
  display:flex;flex-direction:column;gap:8px;align-items:center;
  pointer-events:none;max-width:92vw;
}
body:not(.view-report) .hn-toast{
  pointer-events:auto;cursor:pointer;
  background:var(--bg-3);color:var(--ink);
  border:1px solid var(--stroke);border-radius:10px;
  padding:var(--s-4) 16px;font-size:13px;line-height:1.55;
  box-shadow:var(--el3);max-width:92vw;word-break:break-word;
  opacity:0;transform:translateY(6px);
  animation:hnToastIn .18s ease forwards;
  transition:opacity .18s ease,transform .18s ease;
}
body:not(.view-report) .hn-toast.out{opacity:0;transform:translateY(6px)}
body:not(.view-report) .hn-toast-err{border-color:rgba(var(--red-rgb),0.55);box-shadow:var(--el3),inset 3px 0 0 var(--danger)}
/* ── 级别色条（2026-10-03 批次B · B3）────────────────────────────────────────
   alert 的字面文案本身带级别（「保存失败…」vs「已保存」），按文案推断后给左侧色条上色。
   ⚠ 一律用 inset box-shadow 而不是真 border-left：真 border 会改变盒模型宽度，
     .hn-toast 有 max-width:92vw，加边框在 390px 手机上会把文本挤成两行。
   ⚠ 颜色只取 token（--danger/--warn/--ok），不写死色值 ⇒ 过 smoke-eyecare-theme。 */
body:not(.view-report) .hn-toast-warn{box-shadow:var(--el3),inset 3px 0 0 var(--warn)}
body:not(.view-report) .hn-toast-ok{box-shadow:var(--el3),inset 3px 0 0 var(--ok)}
/* 长文案（拼接式 alert，实测最长 1.6k 字符）：限高 + 内部滚动 + 保留换行。
   原实现把整段丢进一个无上限的浮层，手机上会盖住半屏。 */
body:not(.view-report) .hn-toast-long{
  white-space:pre-line;text-align:left;max-height:34vh;overflow-y:auto;
  -webkit-overflow-scrolling:touch;line-height:1.6;
}

/* ── 确认弹窗 .hn-cfm（2026-11-01 · D271 · 界面评审批次B 遗留项）──────────────
   原生 confirm() 的替身：语义不变（等用户答复 → true/false），但可控外观、可长文案、
   危险操作默认聚焦「取消」、不可撤销的操作可要求输入指定文字才放行。
   ⚠ 颜色只取 --card/--line/--ink/--sub/--red/--blue/--row-hover —— 这几个**两套主题都定义**；
     不取 --danger：它只在 body.dark 里定义，浅色下取不到值会让整条 box-shadow 失效
     （.hn-toast-err 现在就有这个小毛病，此处不跟着踩）。
   ⚠ 不用 transform/opacity 做入场动画：确认框弹出时用户正在做决定，
     动画只会让「我点的是哪个按钮」多一层不确定。 */
.hn-cfm-mask{position:fixed;inset:0;z-index:10000;background:rgba(15,23,42,.5);
  display:flex;align-items:center;justify-content:center;padding:16px}
.hn-cfm{width:460px;max-width:100%;max-height:86vh;display:flex;flex-direction:column;
  background:var(--card,#F8FBFF);color:var(--ink,#1f2733);
  border:1px solid var(--line,#D2E3F7);border-radius:14px;
  box-shadow:0 20px 50px -12px rgba(16,24,40,.35);outline:none}
.hn-cfm-hd{display:flex;align-items:center;gap:8px;padding:16px 18px 0;
  font-size:15px;font-weight:700;line-height:1.4}
.hn-cfm-hd .ico{flex:none;font-size:15px}
.hn-cfm-bd{padding:10px 18px 2px;font-size:13px;line-height:1.65;color:var(--sub,#5b6b80);
  white-space:pre-line;word-break:break-word;overflow-y:auto}
.hn-cfm-bd b,.hn-cfm-bd strong{color:var(--ink,#1f2733);font-weight:600}
.hn-cfm-req{padding:10px 18px 0;font-size:12.5px;color:var(--sub,#5b6b80)}
.hn-cfm-req input{margin-top:6px;width:100%;box-sizing:border-box;font-size:13px;
  padding:8px 10px;border:1px solid var(--line,#D2E3F7);border-radius:8px;
  background:var(--card,#F8FBFF);color:var(--ink,#1f2733);font-family:inherit}
.hn-cfm-req input:focus{outline:none;border-color:var(--blue,#2f6fed);box-shadow:0 0 0 2px rgba(47,111,237,.18)}
.hn-cfm-ft{display:flex;gap:10px;justify-content:flex-end;padding:14px 18px 16px}
.hn-cfm-btn{font-size:13px;padding:8px 18px;border-radius:var(--r-2);cursor:pointer;
  border:1px solid var(--line,#D2E3F7);background:var(--card,#F8FBFF);color:var(--ink,#1f2733);
  font-family:inherit;line-height:1.4}
.hn-cfm-btn:hover{background:var(--row-hover,#DBEAFB)}
.hn-cfm-ok{background:var(--blue,#2f6fed);border-color:var(--blue,#2f6fed);color:var(--pure);font-weight:600}
.hn-cfm-ok:hover{background:#2560d8;border-color:#2560d8}
.hn-cfm-ok.dg{background:var(--red,#e0594f);border-color:var(--red,#e0594f)}
.hn-cfm-ok.dg:hover{background:#c9483f;border-color:#c9483f}
.hn-cfm-ok[disabled]{opacity:.45;cursor:not-allowed}
.hn-cfm-ok[disabled]:hover{background:var(--blue,#2f6fed)}
.hn-cfm-ok.dg[disabled]:hover{background:var(--red,#e0594f)}@media(max-width:560px){
  .hn-cfm-mask{padding:12px}
  .hn-cfm{width:100%;max-height:80vh}
  .hn-cfm-ft{padding:12px 14px 14px;gap:8px}
  .hn-cfm-btn{flex:1;padding:10px 12px}
  .hn-cfm-bd{max-height:52vh}
}

/* ── 字段级校验标记 .hn-fld-*（2026-11-01 · D271）────────────────────────────
   以前只在弹窗顶部写一句「请填写姓名」—— 表单有 12 个格子时，用户得自己找是哪一个。
   现在把提示挂到**那一格**下面，并给输入框描红；用户一改这一格，红标记立刻消失。
   ⚠ 用 !important 压过各表单自带的 `input{border:1px solid #cfe0f4}`（那条是属性选择器级别，
     不加 !important 会输给 .injury-grid input 这种更具体的选择器）。
   ⚠ 文案用 --red：两套主题都有定义；不写死色值，暗色下也读得清。 */
.hn-fld-bad{border-color:var(--red,#e0594f)!important;box-shadow:0 0 0 2px rgba(224,89,79,.15)!important}
.hn-fld-msg{display:block;width:100%;color:var(--red,#e0594f);font-size:12px;
  line-height:1.45;font-weight:400;margin-top:1px}

/* ── 弹窗层配套（2026-10-03 批次B · B1）────────────────────────────────────
   hnOpenModal 在「无人持有焦点」时会把焦点落到弹窗容器本身（tabindex=-1），
   容器是 div，不该显示焦点环（环只该出现在可交互控件上）。 */
.modal:focus,.modal-overlay:focus{outline:none}
@keyframes hnToastIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
/* ⚠ 打印/折行动画这两条**不能**写在这里：本行以上整段都在 `@media screen{…}` 闸门内，
   而 `@media print` 嵌进 @media screen 后条件恒不成立（screen AND print = ∅）⇒ 是死代码。
   批次A 曾把「打印隐藏 toast」写在这个位置（实际没生效），批次B 已移到闸门外，
   见 DARK-OVERLAY-END 之后的「屏幕外组件（打印 / 折叠）」。 */
@media (prefers-reduced-motion:reduce){ .hn-toast{animation:none;opacity:1;transform:none} }

/* 阶段标签 / 上下文条 */
body:not(.view-report) .stage-tag{background:rgba(var(--blue-rgb),0.14);color:var(--blue-fg);border-color:rgba(76,141,255,.30)}
body:not(.view-report) .stage-tag[data-stage="MP"]{background:rgba(var(--green-rgb),0.14);color:var(--green-fg);border-color:rgba(62,207,142,.30)}
body:not(.view-report) .stage-tag[data-stage="OSS"]{background:rgba(var(--orange-rgb),0.14);color:var(--orange-fg);border-color:rgba(255,165,61,.30)}
body:not(.view-report) .stage-tag[data-stage="待定"]{background:rgba(var(--purple-rgb),0.14);color:var(--purple-fg);border-color:rgba(167,139,250,.30)}
body:not(.view-report) .stage-tag[data-stage="暂停"]{background:rgba(255,210,74,.12);color:#FFD98A;border-color:rgba(255,210,74,.28)}

/* 周报 / 趋势卡（非 report 视图内的周快照卡） */
body:not(.view-report) .wkt-card .v,body:not(.view-report) .wkt-card .k{color:var(--ink)}
body:not(.view-report) .wkt-card .k{color:var(--muted)}
body:not(.view-report) .wkt-table tr.wkt-cur{background:rgba(255,210,74,.10)}
body:not(.view-report) .wkt-tag{color:var(--orange);background:rgba(var(--orange-rgb),0.12);border-color:rgba(255,165,61,.32)}
body:not(.view-report) .wkt-note,body:not(.view-report) .wkt-legend{color:var(--muted)}
/* D297：结构提示条 / 结论条在深色层要重新给色 —— 浅色那套 #f2f6ff 底 + #1f3866 字
   压在深色卡里会变成「一块发白的面板」，比没有还难看。 */
body:not(.view-report) .wkt-struct{color:var(--orange)}
body:not(.view-report) .wkt-struct-row{background:rgba(var(--orange-rgb),0.1);border-color:rgba(var(--orange-rgb),0.3);
  color:var(--orange)}
body:not(.view-report) .wkt-struct-row b{color:#FFD08A}
body:not(.view-report) .wkt-insight{background:rgba(var(--blue-rgb),0.09);border-color:rgba(var(--blue-rgb),0.26);
  border-left-color:var(--blue);color:var(--ink-2)}
body:not(.view-report) .wkt-insight b{color:var(--ink)}
body:not(.view-report) .pjt-more-note{color:var(--muted)}
body:not(.view-report) .wkt-up{color:var(--up)}
body:not(.view-report) .wkt-down{color:var(--down)}
body:not(.view-report) .wkt-flat{color:var(--muted)}
body:not(.view-report) .wkt-ctl label,body:not(.view-report) .emv-ctl label{color:var(--sub)}
body:not(.view-report) .wkt-ctl select,body:not(.view-report) .emv-ctl select{background-color:var(--bg-2);color:var(--ink);border-color:var(--stroke)}

/* 人力异动流水 */
body:not(.view-report) .emv-sum,body:not(.view-report) .emv-table td.emv-what,body:not(.view-report) .emv-table td.emv-by{color:var(--sub)}
body:not(.view-report) .emv-sum b{color:var(--ink)}
body:not(.view-report) .emv-table td.emv-when{color:var(--muted)}
body:not(.view-report) .emv-arrow{color:var(--muted)}
body:not(.view-report) .emv-b-e{background:rgba(var(--blue-rgb),0.14);color:var(--blue-fg);border-color:rgba(76,141,255,.30)}
body:not(.view-report) .emv-b-m{background:rgba(var(--purple-rgb),0.14);color:var(--purple-fg);border-color:rgba(167,139,250,.30)}
body:not(.view-report) .emv-b-s{background:rgba(var(--cyan-rgb),0.13);color:#7FE6EE;border-color:rgba(43,212,224,.30)}
body:not(.view-report) .emv-b-a{background:rgba(var(--green-rgb),0.14);color:var(--green-fg);border-color:rgba(62,207,142,.30)}
body:not(.view-report) .emv-b-d{background:rgba(var(--red-rgb),0.14);color:var(--red-fg);border-color:rgba(255,61,102,.30)}
body:not(.view-report) .emv-b-v{background:rgba(var(--orange-rgb),0.13);color:var(--orange-fg);border-color:rgba(255,165,61,.30)}
body:not(.view-report) .emv-b-r{background:var(--bg-2);color:var(--muted);border-color:var(--stroke)}
/* 变化量：本组件自定「增=绿 / 减=红」（非股市红涨绿跌），沿用其语义 */
body:not(.view-report) .emv-d.up{background:rgba(var(--green-rgb),0.14);color:var(--green)}
body:not(.view-report) .emv-d.dn{background:rgba(255,90,82,.14);color:#FF7A72}
body:not(.view-report) .emv-d.mv{background:var(--bg-2);color:var(--muted)}

/* 今日待办流 */
body:not(.view-report) .todo-row{background:var(--bg-1);border-color:var(--line)}
body:not(.view-report) .todo-row.lv0{background:rgba(var(--red-rgb),0.1);border-left-color:var(--red)}
body:not(.view-report) .todo-row.lv1{background:rgba(var(--orange-rgb),0.09);border-left-color:var(--orange)}
body:not(.view-report) .todo-row .td-t{color:var(--ink)}
body:not(.view-report) .todo-row .td-s{color:var(--sub)}
body:not(.view-report) .todo-lv.lv0{background:rgba(var(--red-rgb),0.16);color:var(--red-fg);border-color:rgba(255,61,102,.34)}
body:not(.view-report) .todo-lv.lv1{background:rgba(var(--orange-rgb),0.16);color:var(--orange-fg);border-color:rgba(255,165,61,.34)}
body:not(.view-report) .todo-lv.lv2{background:rgba(255,210,74,.14);color:#FFD98A;border-color:rgba(255,210,74,.32)}
body:not(.view-report) .todo-lv.lv3{background:rgba(var(--blue-rgb),0.15);color:var(--blue-fg);border-color:rgba(76,141,255,.32)}
body:not(.view-report) .todo-lv.lv4{background:var(--bg-2);color:var(--sub);border-color:var(--stroke)}
body:not(.view-report) .todo-sum .ts-card{background:var(--bg-1);border-color:var(--line)}
body:not(.view-report) .todo-sum .ts-n{color:var(--ink)}
body:not(.view-report) .todo-sum .ts-l,body:not(.view-report) .todo-src .ts-hd{color:var(--muted)}
body:not(.view-report) .todo-empty{color:var(--muted)}
body:not(.view-report) #ossTbody tr.todo-hit > td,body:not(.view-report) #whTbody tr.todo-hit > td,body:not(.view-report) #outsTbody tr.todo-hit > td{box-shadow:inset 0 0 0 2px #FFA53D}

/* 仓库 / 员工驻厂骨架 */
body:not(.view-report) .wh-card{background:var(--card);border-color:var(--line)}
body:not(.view-report) .wh-t,body:not(.view-report) .wh-stat b,body:not(.view-report) .wh-curwh b,body:not(.view-report) .wh-impstat b,body:not(.view-report) .wh-imp-sum b,body:not(.view-report) .wh-perm-hd,body:not(.view-report) .wh-pos,body:not(.view-report) .wh-card .wh-t{color:var(--ink)}
body:not(.view-report) .wh-sub,body:not(.view-report) .wh-stab,body:not(.view-report) .wh-stat,body:not(.view-report) .wh-impstat,body:not(.view-report) .wh-acts-sel,body:not(.view-report) .wh-imp-lb,body:not(.view-report) .wh-pos-pick-hd,body:not(.view-report) .wh-sess-hd,body:not(.view-report) .wh-stock-hint,body:not(.view-report) .wh-lb,body:not(.view-report) .wh-perm-chk{color:var(--sub)}
body:not(.view-report) .wh-stab.on,body:not(.view-report) .wh-stab:hover,body:not(.view-report) .wh-title-wh{color:var(--blue)}
body:not(.view-report) .wh-stab.on{border-bottom-color:var(--blue)}
body:not(.view-report) .wh-subtabs{border-bottom-color:var(--line)}
body:not(.view-report) .wh-stat .muted,body:not(.view-report) .wh-impstat .muted{color:var(--muted)}
body:not(.view-report) .wh-curwh{background:var(--card-hd);border-color:var(--line)}
body:not(.view-report) .wh-src{background:rgba(var(--orange-rgb),0.13);color:var(--orange-fg);border-color:rgba(255,165,61,.30)}
body:not(.view-report) .wh-imp-status{background:var(--card-hd);border-color:var(--line);color:var(--sub)}
body:not(.view-report) .wh-imp-status.error{background:rgba(var(--red-rgb),0.13);color:var(--red-fg);border-color:rgba(255,61,102,.30)}
body:not(.view-report) .wh-imp-status.success{background:rgba(var(--green-rgb),0.13);color:var(--green-fg);border-color:rgba(62,207,142,.30)}
body:not(.view-report) .wh-imp-preview th{background:var(--card-hd);color:#A9C6F2}
body:not(.view-report) .wh-imp-preview td{border-color:var(--line)}
body:not(.view-report) .wh-imp-preview tr.bad td{background:rgba(var(--red-rgb),0.12);color:#FF9FB4}
body:not(.view-report) .wh-imp-preview td.staff-imp-add{color:#7CE0B0}
body:not(.view-report) .wh-imp-preview td.staff-imp-upd{color:#9FC2FF}
body:not(.view-report) .wh-imp-preview td.staff-imp-same{color:var(--muted)}
body:not(.view-report) .wh-imp-preview td.staff-imp-skip{color:var(--danger)}
body:not(.view-report) .wh-imp-sum,body:not(.view-report) .wh-imp-sum b{color:var(--ink)}
body:not(.view-report) .wh-perm-sec{background:var(--card);border-color:var(--line)}
body:not(.view-report) .wh-perm-note,body:not(.view-report) .wh-curwh-note,body:not(.view-report) .wh-perm-empty,body:not(.view-report) .wh-perm-fixed,body:not(.view-report) .wh-empty{color:var(--muted)}
body:not(.view-report) .wh-perm-list th{background:var(--card-hd);color:#A9C6F2}
body:not(.view-report) .wh-perm-list th,body:not(.view-report) .wh-perm-list td{border-bottom-color:var(--line)}
body:not(.view-report) .wh-perm-list td.wh-perm-dept{color:#A9C6F2}
body:not(.view-report) .wh-perm-tools input,body:not(.view-report) .wh-perm-list input[type=text],body:not(.view-report) .wh-perm-list input[type=password]{background:var(--bg-2);border-color:var(--stroke)}
body:not(.view-report) table.wh-table thead th{background:var(--card-hd);color:#A9C6F2;box-shadow:inset 0 -1px 0 var(--line)}
body:not(.view-report) table.wh-table th,body:not(.view-report) table.wh-table td{border-bottom-color:var(--line)}
body:not(.view-report) table.wh-table td.wh-num{color:#9FC2FF}
body:not(.view-report) table.wh-table td.wh-safe{color:var(--muted)}
body:not(.view-report) table.wh-table td.wh-safe-none{color:var(--muted)}
body:not(.view-report) table.wh-table td.wh-safe-set{color:var(--ink)}
body:not(.view-report) table.wh-table td.wh-safe-low,body:not(.view-report) table.wh-table td.wh-num-low,body:not(.view-report) .wh-low-tag,body:not(.view-report) .wh-safe-warn,body:not(.view-report) .wh-zero,body:not(.view-report) .wh-err,body:not(.view-report) .wh-lb .req,body:not(.view-report) .wh-tip .warn{color:var(--danger)}
body:not(.view-report) table.wh-table tbody tr.wh-row-low > td:first-child{box-shadow:inset 3px 0 0 #FF3D66}
body:not(.view-report) table.wh-table td.wh-safe-low{background:rgba(255,61,102,.10)}
body:not(.view-report) .emp-flash,body:not(.view-report) .outs-flash{background:rgba(var(--green-rgb),0.13);border-color:rgba(var(--green-rgb),0.3);color:#7CE0B0}
body:not(.view-report) .emp-flash b,body:not(.view-report) .outs-flash b{color:#A7EBCC}
body:not(.view-report) .emp-due-ok,body:not(.view-report) .emp-due-over{color:var(--muted)}
body:not(.view-report) .emp-due-soon{color:var(--orange)}
body:not(.view-report) .emp-due-today{color:var(--danger)}
body:not(.view-report) .emp-due-none{color:var(--muted)}
body:not(.view-report) .wh-chip{background:var(--card-hd);color:#A9C6F2}
body:not(.view-report) .wh-chip.in{background:rgba(var(--green-rgb),0.14);color:#7CE0B0}
body:not(.view-report) .wh-chip.out{background:rgba(var(--red-rgb),0.13);color:#FF8FA9}
body:not(.view-report) .wh-filter input,body:not(.view-report) .wh-filter select,body:not(.view-report) .wh-form-grid input,body:not(.view-report) .wh-form-grid select,body:not(.view-report) .wh-acts-sel select,body:not(.view-report) .wh-imp-lb select{background-color:var(--bg-2);color:var(--ink);border-color:var(--stroke)}
body:not(.view-report) .wh-form-grid label,body:not(.view-report) .wh-imp-lb{color:var(--sub)}
body:not(.view-report) .wh-tip{background:var(--card-hd);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) .wh-tip b,body:not(.view-report) .wh-pos-pick-hd b,body:not(.view-report) .wh-sess-hd b{color:#9FC2FF}
body:not(.view-report) .wh-pos-pick{background:var(--bg-1);border-color:var(--stroke)}
body:not(.view-report) .wh-pos-pick.ok{background:rgba(var(--green-rgb),0.1);border-color:rgba(62,207,142,.32)}
body:not(.view-report) .wh-pos-pick.warn{background:rgba(var(--red-rgb),0.09);border-color:rgba(255,61,102,.32)}
body:not(.view-report) .wh-pos-pick.ok .wh-pos-pick-hd b{color:#7CE0B0}
body:not(.view-report) .wh-pos-pick.warn .wh-pos-pick-hd b{color:var(--danger)}
body:not(.view-report) .wh-pos-chip{background-color:var(--bg-2);color:var(--ink);border-color:var(--stroke)}
body:not(.view-report) .wh-pos-chip:hover{background:var(--bg-3);border-color:var(--stroke-strong)}
body:not(.view-report) .wh-pos-chip.on{background:rgba(var(--blue-rgb),0.2);border-color:var(--blue);color:#DCE9FF}
body:not(.view-report) .wh-pos-chip b{color:#7CE0B0}
body:not(.view-report) .wh-pos-chip.z{color:var(--muted);background:var(--bg-1)}
body:not(.view-report) .wh-pos-chip.z:hover{border-color:var(--stroke);background:var(--bg-1)}
body:not(.view-report) .wh-pos-note{color:var(--muted)}
body:not(.view-report) .wh-sess{border-top-color:var(--stroke)}
body:not(.view-report) .wh-sess-hd{color:var(--sub)}
body:not(.view-report) .wh-sess-hd .no{color:#7CE0B0}
body:not(.view-report) .wh-sess-row{background:var(--bg-1);border-color:var(--line)}
body:not(.view-report) .wh-sess-row .c{color:var(--ink)}
body:not(.view-report) .wh-sess-row .p{color:var(--sub)}
body:not(.view-report) .wh-sess-row .q{color:#7CE0B0}
body:not(.view-report) .wh-sess-row .x{background:var(--bg-2);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) .wh-sess-row .x:hover{border-color:rgba(var(--red-rgb),0.5);color:var(--danger);background:rgba(255,61,102,.10)}
body:not(.view-report) .wh-imp-skip,body:not(.view-report) .staff-dupno{color:var(--orange);background:rgba(var(--orange-rgb),0.12);border-color:rgba(255,165,61,.32)}

/* 商品详情抽屉 pdp-* */
body:not(.view-report) .pdp{background:var(--bg-1);border-left-color:var(--line)}
body:not(.view-report) .pdp-hd{background:var(--card-hd);border-bottom-color:var(--line)}
body:not(.view-report) .pdp-hd .t{color:var(--ink)}
body:not(.view-report) .pdp-hd .t .no{color:#A9C6F2;background:var(--bg-3)}
body:not(.view-report) .pdp-x{background:var(--bg-2);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) .pdp-x:hover{background:var(--bg-3)}
body:not(.view-report) .pdp-sec>h4{color:#A9C6F2}
body:not(.view-report) .pdp-sec>h4 .n,body:not(.view-report) .pdp-kv .k,body:not(.view-report) .pdp-empty,body:not(.view-report) .pdp-lock{color:var(--muted)}
body:not(.view-report) .pdp-card{background:var(--bg-1);border-color:var(--line)}
body:not(.view-report) .pdp-kv .v{color:var(--ink)}
body:not(.view-report) .pdp-lock{background:var(--bg-2);border-color:var(--stroke)}
body:not(.view-report) .pdp-tag{background:var(--bg-3);color:#A9C6F2}
body:not(.view-report) .pdp-tag.on{background:rgba(var(--green-rgb),0.14);color:#7CE0B0}
body:not(.view-report) .pdp-tag.no{color:var(--muted)}
body:not(.view-report) .pdp-btn{background:var(--bg-2);border-color:var(--stroke);color:#A9C6F2}
body:not(.view-report) .pdp-btn:hover{background:var(--bg-3);border-color:var(--stroke-strong)}

/* 员工档案 staff-an-* 与 staff-* */
body:not(.view-report) .staff-table td.staff-name{color:var(--ink)}
body:not(.view-report) .staff-an-card{background:var(--card);border-color:var(--line)}
body:not(.view-report) .staff-an-hd{color:var(--ink);border-bottom-color:var(--line)}
body:not(.view-report) .staff-an-hd .staff-an-sub,body:not(.view-report) .staff-an-hd .staff-an-tot,body:not(.view-report) .staff-an-foot,body:not(.view-report) .staff-an-val,body:not(.view-report) .staff-an-lb,body:not(.view-report) .staff-imp-scope{color:var(--muted)}
body:not(.view-report) .staff-an-val b{color:var(--ink)}
body:not(.view-report) .staff-an-track{background:var(--bg-3)}
body:not(.view-report) .staff-an-zero .staff-an-lb,body:not(.view-report) .staff-an-zero .staff-an-val{color:var(--muted)}
body:not(.view-report) .staff-an-foot{border-top-color:var(--line)}
body:not(.view-report) .staff-edu{background:rgba(var(--blue-rgb),0.12);color:#A9C6F2;border-color:rgba(76,141,255,.28)}
body:not(.view-report) #xsupFOwner[readonly],body:not(.view-report) #staffFDept[readonly]{background:var(--bg-3);color:var(--sub);border-color:var(--stroke)}
body:not(.view-report) #xsupFOwner[readonly]::placeholder,body:not(.view-report) #staffFDept[readonly]::placeholder{color:var(--disabled)}
body:not(.view-report) .ij-label .auto{color:var(--muted)}

/* OSS 台账 oss-* */
body:not(.view-report) .oss-hd .oss-t,body:not(.view-report) .oss-stat b{color:var(--ink)}
body:not(.view-report) .oss-hd .oss-sub,body:not(.view-report) .oss-stat,body:not(.view-report) .oss-form-grid .oss-dept-hint,body:not(.view-report) .oss-sub2,body:not(.view-report) .oss-due-none,body:not(.view-report) .oss-empty{color:var(--muted)}
body:not(.view-report) .oss-hd .oss-sub b,body:not(.view-report) .oss-tip b,body:not(.view-report) .oss-oss-dept,body:not(.view-report) table.oss-table td.oss-dept,body:not(.view-report) .oss-form-grid .oss-dept-hint.auto{color:#9FC2FF}
body:not(.view-report) .oss-stat .warn,body:not(.view-report) .oss-due-over,body:not(.view-report) .oss-err,body:not(.view-report) .oss-form-grid .req,body:not(.view-report) .oss-stat .soon{color:var(--danger)}
body:not(.view-report) .oss-stat .soon,body:not(.view-report) .oss-due-soon,body:not(.view-report) .oss-form-grid .oss-dept-hint.warn{color:var(--orange)}
body:not(.view-report) .oss-stat .muted{color:var(--muted)}
body:not(.view-report) .oss-tip{background:var(--card-hd);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) table.oss-table th{color:#A9C6F2}
body:not(.view-report) table.oss-table td{color:var(--sub)}
body:not(.view-report) table.oss-table td.oss-num{color:var(--ink)}
body:not(.view-report) table.oss-table td.oss-num.none{color:var(--muted)}
body:not(.view-report) table.oss-table tbody tr.oss-closed td{background:rgba(255,255,255,.02);color:var(--muted)}
body:not(.view-report) table.oss-table tbody tr.oss-closed td.oss-dept,body:not(.view-report) table.oss-table tbody tr.oss-closed td.oss-num,body:not(.view-report) .oss-due-closed{color:var(--muted)}
/* D305：这一行原来是 var(--disabled)，深色下 #55657A 压在 #1A2430 上只有 2.64:1 ——
   「已结案」的行是**要读的数据**，把它调到「禁用控件」那一档是误用：
   --disabled 允许低于 AA 的前提是「用户不需要读」，而结案名单恰恰要复盘。
   基础层（打印）本来就是 --muted，是深色层把它降级了；改回 --muted 后深色 6.48:1。 */
body:not(.view-report) .oss-due-ok{color:var(--sub)}
body:not(.view-report) .oss-form-grid label{color:var(--sub)}
body:not(.view-report) .oss-form-grid input,body:not(.view-report) .oss-form-grid select{background:var(--bg-2);border-color:var(--stroke);color:var(--ink)}

/* ─────────────────────────────────────────────────────────────────────────
   G4 · 表格
   ───────────────────────────────────────────────────────────────────────── */
body:not(.view-report) th{background:var(--card-hd);color:var(--sub);border-bottom:2px solid var(--line)}
body:not(.view-report) th,body:not(.view-report) td{border-bottom:1px solid var(--hair)}
body:not(.view-report) .mgrtable th{background:var(--card-hd);color:var(--sub);border-bottom:2px solid var(--line)}
body:not(.view-report) .tablewrap{background:var(--card)}
body:not(.view-report) .mgrtable tr:hover{background:var(--row-hover)}
body:not(.view-report) .mgrtable tbody tr:nth-child(even){background:var(--row-alt)}
body:not(.view-report):not(.tv-table) .tablewrap tr,body:not(.view-report) .tablewrap tr{border-color:var(--hair)}
body:not(.view-report):not(.tv-table) .tablewrap td::before{color:var(--muted)}
body:not(.view-report):not(.tv-table) .tablewrap td[data-l="在册合计"]{background:var(--bg-3)}
body:not(.view-report):not(.tv-table) .tablewrap td[data-l="在册合计"]::before{color:var(--blue)}

/* ─────────────────────────────────────────────────────────────────────────
   G4b · 补遗：版本横幅（#verBanner 内的两颗药丸）
   原为内联 near-white —— #verNow 底 rgba(255,255,255,.92)（近白）、
   #verBannerClose 底 rgba(255,255,255,.2)（偏亮），深色下都是刺眼亮块。
   横幅本身是红底(#c0392b) ⇒ 用中性暗药丸（--bg-2 / --bg-3）+ 浅字（--ink / --sub）
   + 细描边（--stroke），与红底横幅形成「暗灰 / 红」层次，且不与页内深色卡片冲突。
   ⚠ 元素带内联 style（优先级最高）⇒ 必须 !important 才能覆盖。
   ⚠ 元素仅由 showVerBanner() 在「检测到新版本」时动态插入，隔离副本平时不存在
     ⇒ 无法用截图验证视觉效果；此处仅代码层面保证覆盖（见交付说明）。
   ───────────────────────────────────────────────────────────────────────── */
body:not(.view-report) #verNow{background:var(--bg-2)!important;color:var(--ink)!important;border:1px solid var(--stroke)!important}
body:not(.view-report) #verNow:hover{background:var(--bg-3)!important}
body:not(.view-report) #verBannerClose{background:var(--bg-3)!important;color:var(--sub)!important;border:1px solid var(--stroke)!important}
body:not(.view-report) #verBannerClose:hover{background:var(--bg-2)!important;color:var(--ink)!important}
/* 可编辑单元格 / 下拉 */
body:not(.view-report) .ecell:focus,body:not(.view-report) .ecell-row:focus,body:not(.view-report) .ecell:focus,body:not(.view-report) .ecell-row:focus{background:var(--bg-3)}
body:not(.view-report) .ecell:focus,body:not(.view-report) .ecell-row:focus{border-color:var(--blue)}
body:not(.view-report) .stage-sel{background:var(--bg-2);border-color:var(--stroke);color:var(--ink)}
body:not(.view-report) .stage-sel:focus{border-color:var(--blue)}
body:not(.view-report) .mgr-sel{background:var(--bg-2);border-color:var(--stroke);color:var(--ink)}
body:not(.view-report) .mgr-sel:focus{border-color:var(--blue)}
body:not(.view-report) .mgr-miss{color:var(--orange)}
body:not(.view-report) .mgr-add-hint{color:var(--orange)}
body:not(.view-report) .nlrole{background:var(--bg-2);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) .nlrole:focus{border-color:var(--blue)}
body:not(.view-report) .namelist{background:var(--bg-1);border-color:var(--line)}
body:not(.view-report) .nlname{border-color:var(--stroke)}
body:not(.view-report) .nlname[readonly]{background:var(--bg-3);color:var(--sub)}
body:not(.view-report) .nlhead-qty input[type=number]{border-color:var(--stroke)}
body:not(.view-report) .nlroletag{color:var(--blue-fg);background:rgba(var(--blue-rgb),0.14);border-color:rgba(76,141,255,.28)}
body:not(.view-report) .nlempty{color:var(--muted)}
body:not(.view-report) .nlempno,body:not(.view-report) .mgrno,body:not(.view-report) .mgr-no{color:var(--blue-fg);background:rgba(var(--blue-rgb),0.13);border-color:rgba(76,141,255,.28)}
body:not(.view-report) .nlempno.missing,body:not(.view-report) .mgr-no.missing{color:var(--orange);background:rgba(var(--orange-rgb),0.12);border-color:rgba(255,165,61,.32)}
body:not(.view-report) .nlempno.manual,body:not(.view-report) .mgrno.manual,body:not(.view-report) .mgr-no.manual{color:var(--purple-fg);background:rgba(var(--purple-rgb),0.13);border-color:rgba(167,139,250,.30)}
/* 行编辑态（原规则用 !important + var，token 已接管；此处补单元格底色与描边） */
body:not(.view-report) tbody tr.row-editing .ecell,body:not(.view-report) tbody tr.row-editing .ecell-row,body:not(.view-report) tbody tr.row-editing .stage-sel,body:not(.view-report) tbody tr.row-editing .nlname,body:not(.view-report) tbody tr.row-editing .nlrole,body:not(.view-report) tbody tr.row-editing .nlhead-qty input[type=number]{background:var(--row-edit);border-color:#3A5A8C}
body:not(.view-report) tbody tr.row-editing .ecell:focus,body:not(.view-report) tbody tr.row-editing .ecell-row:focus,body:not(.view-report) tbody tr.row-editing .stage-sel:focus,body:not(.view-report) tbody tr.row-editing .nlname:focus,body:not(.view-report) tbody tr.row-editing .nlrole:focus,body:not(.view-report) tbody tr.row-editing .nlhead-qty input[type=number]:focus{background:#2A3C5A;border-color:var(--blue)}
/* 明细层级折叠卡 / 负责人构成 */
body:not(.view-report) .lvl-dept>summary:hover,body:not(.view-report) .lvl-stage>summary:hover,body:not(.view-report) .lvl-stage-row.logged:hover{background:var(--row-hover)}
body:not(.view-report) .phdel,body:not(.view-report) .rowdel,body:not(.view-report) .manualtag{background:rgba(var(--red-rgb),0.12);border-color:rgba(var(--red-rgb),0.32);color:#FF8FA9}
body:not(.view-report) .phdel:hover,body:not(.view-report) .rowdel:hover,body:not(.view-report) .manualtag:hover{background:rgba(255,61,102,.20)}
body:not(.view-report) .rowsave,body:not(.view-report) .phsave{background:rgba(var(--blue-rgb),0.14);border-color:rgba(var(--blue-rgb),0.3);color:#9FC2FF}
body:not(.view-report) .rowsave:hover,body:not(.view-report) .phsave:hover{background:rgba(76,141,255,.22)}
body:not(.view-report) .nldel{background:rgba(var(--red-rgb),0.12);border-color:rgba(var(--red-rgb),0.42);color:#FF8FA9}
body:not(.view-report) .nldel:hover{background:var(--danger);color:#1A0308}
body:not(.view-report) .nltf{background:rgba(var(--green-rgb),0.12);border-color:rgba(var(--green-rgb),0.42);color:#7CE0B0}
body:not(.view-report) .nltf:hover{background:var(--green);color:#08211A}
body:not(.view-report) .cntbadge.f{background:rgba(var(--blue-rgb),0.14);color:#9FC2FF}
body:not(.view-report) .cntbadge.s{background:rgba(var(--green-rgb),0.14);color:#7CE0B0}
body:not(.view-report) .cntbadge.t{background:rgba(var(--orange-rgb),0.14);color:#FFC178}
body:not(.view-report) .cntbadge.e{background:rgba(var(--purple-rgb),0.14);color:#C4B5FD}
body:not(.view-report) .cntbadge.warn{background:rgba(var(--red-rgb),0.16)!important;color:var(--red-fg)!important;border-color:rgba(255,61,102,.34)!important}
body:not(.view-report) .nlwarn,body:not(.view-report) .adderr,body:not(.view-report) .loginerr,body:not(.view-report) .pwchange-err,body:not(.view-report) .general-err,body:not(.view-report) .injury-err,body:not(.view-report) .oss-err{color:var(--danger)}
body:not(.view-report) .addhint{color:var(--muted)}

/* ─────────────────────────────────────────────────────────────────────────
   G5 · 按钮 / 弹窗 / 输入 / 标签 / 浮层
   ───────────────────────────────────────────────────────────────────────── */
/* 通用按钮 */
body:not(.view-report) .btn{background:var(--bg-2);border:1px solid var(--stroke);color:var(--ink);box-shadow:none}
body:not(.view-report) .btn:hover{background:var(--bg-3)}
body:not(.view-report) .btn.primary{background:linear-gradient(135deg,#3E7BF0,#3A77EE);color:#0B1220;border-color:#3E7BF0}
body:not(.view-report) .btn.primary:hover{background:linear-gradient(135deg,var(--blue),#3672E6)}
body:not(.view-report) .btn.ghost{background:var(--bg-2);color:var(--sub);border-color:var(--stroke)}
body:not(.view-report) .btn.ghost:hover{background:var(--bg-3);color:var(--ink)}
body:not(.view-report) .btn.danger{background:rgba(var(--red-rgb),0.12);color:var(--red-fg);border-color:rgba(255,61,102,.34)}
body:not(.view-report) .btn.danger:hover{background:rgba(255,61,102,.20)}
body:not(.view-report) .btn.active{background:linear-gradient(135deg,#2FB877,#23A06B);color:#08211A;border-color:#2FB877}
body:not(.view-report) .viewall-btn{background:rgba(255,255,255,.14);color:var(--pure)}
body:not(.view-report) .pf-red{background:var(--danger);color:#1A0308}
body:not(.view-report) .pf-red:hover{background:#FF6079}
body:not(.view-report) .phdel:hover{border-color:var(--danger)}

/* 输入控件 */
body:not(.view-report) select,body:not(.view-report) input[type=text],body:not(.view-report) input[type=password],body:not(.view-report) input[type=number],body:not(.view-report) input[type=date],body:not(.view-report) input[type=email],body:not(.view-report) textarea{background-color:var(--bg-2);color:var(--ink);border:1px solid var(--stroke)}
body:not(.view-report) select:focus,body:not(.view-report) input:focus,body:not(.view-report) textarea:focus{border-color:var(--blue);box-shadow:0 0 0 3px rgba(76,141,255,.14)}
body:not(.view-report) input::placeholder,body:not(.view-report) textarea::placeholder{color:var(--disabled)}
body:not(.view-report) .addgrid label,body:not(.view-report) .modalfield,body:not(.view-report) .modalfield label,body:not(.view-report) .pf-lab{color:var(--sub)}
body:not(.view-report) .addgrid input,body:not(.view-report) .addgrid select.combosel,body:not(.view-report) .modalfield select,body:not(.view-report) .modalfield input{background-color:var(--bg-2);color:var(--ink);border-color:var(--stroke)}
body:not(.view-report) .comboback{background:var(--bg-2);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) .comboback:hover{border-color:var(--blue);color:var(--blue)}
body:not(.view-report) .general-grid input,body:not(.view-report) .general-grid select,body:not(.view-report) .injury-grid input,body:not(.view-report) .injury-grid select,body:not(.view-report) .injury-grid textarea,body:not(.view-report) .injury-filterbar select,body:not(.view-report) .pxf-form #pxfManager,body:not(.view-report) .prob-reply-edit textarea,body:not(.view-report) .stage-sel,body:not(.view-report) .wh-filter input,body:not(.view-report) .wh-form-grid input{background-color:var(--bg-2);color:var(--ink);border-color:var(--stroke)}

/* 弹窗 / 浮层 */
body:not(.view-report) .modalbox,body:not(.view-report) .modal-box,body:not(.view-report) .pwchange-box{background:var(--bg-1);color:var(--ink);border:1px solid var(--stroke)}
body:not(.view-report) .modal .x{color:var(--muted)}
body:not(.view-report) .modal .x:hover{background:var(--bg-3);color:var(--ink)}
body:not(.view-report) .modal-title,body:not(.view-report) .pwchange-box h3,body:not(.view-report) .pf-t{color:var(--ink)}
body:not(.view-report) .modal-sub,body:not(.view-report) .pwchange-box .pwfield,body:not(.view-report) .pf-sub,body:not(.view-report) .sa-field,body:not(.view-report) .sa-group-label,body:not(.view-report) .general-grid label,body:not(.view-report) .injury-grid label{color:var(--muted)}
body:not(.view-report) .pwchange-box .pwfield input,body:not(.view-report) .sa-field input,body:not(.view-report) .email-input{border-color:var(--stroke);background:var(--bg-2)}
body:not(.view-report) .modal{background:rgba(3,7,14,.62)}
body:not(.view-report) .modal-overlay,body:not(.view-report) .pwchange-overlay{background:rgba(3,7,14,.66)}
body:not(.view-report) .addpanel{background:rgba(3,7,14,.62)}
body:not(.view-report) .pdp-mask{background:rgba(3,7,14,.55)}

/* 登录门禁 / 登录窗 */
body:not(.view-report).gate-locked{background:#0B1220}
body:not(.view-report).gate-locked #loginModal{background:radial-gradient(900px 420px at 78% -10%,rgba(76,141,255,.18),transparent 62%),linear-gradient(160deg,#0D1526 0%,var(--bg) 55%,#0B1B22 100%)}
body:not(.view-report) .lg-brand{border-bottom-color:var(--line)}
body:not(.view-report) .lg-brand-t{color:var(--ink)}
body:not(.view-report) .lg-brand-s{color:var(--muted)}
body:not(.view-report) .lghint{color:#9FC2FF}
body:not(.view-report) .loginbanner{background:rgba(76,141,255,.14)}
body:not(.view-report) .loginbanner b{color:#FFD98A}

/* 功能 hub / 个人中心 / 通知项 */
body:not(.view-report) .dh-btn{background:var(--card);border-color:var(--stroke)}
body:not(.view-report) .dh-btn b,body:not(.view-report) .pf-sec-t{color:var(--ink)}
body:not(.view-report) .dh-btn i,body:not(.view-report) .pf-sub{color:var(--muted)}
body:not(.view-report) .dh-btn:hover{border-color:var(--blue);background:var(--row-hover)}
body:not(.view-report) .dh-btn.off:hover{border-color:var(--stroke);background:var(--card)}
body:not(.view-report) .pf-row input,body:not(.view-report) .pf-in{background:var(--bg-2);border-color:var(--stroke)}
body:not(.view-report) .pf-divider{border-top-color:var(--line)}
body:not(.view-report) .pf-x{color:var(--muted)}
body:not(.view-report) .pf-x:hover{background:var(--bg-3);color:var(--ink)}
body:not(.view-report) .nt-radio{color:var(--sub)}
body:not(.view-report) .nt-pv-warn{color:var(--danger)}
body:not(.view-report) .nt-go:hover{background:var(--row-hover)}
body:not(.view-report) .nt-pv{background:var(--card-hd)}
body:not(.view-report) .nt-pv-mail{background:var(--card)}
body:not(.view-report) .fri-reminder.warn{background:rgba(var(--orange-rgb),0.12);border-color:rgba(var(--orange-rgb),0.32);color:#FFC178}
body:not(.view-report) .fri-reminder.ok{background:rgba(var(--green-rgb),0.12);border-color:rgba(var(--green-rgb),0.3);color:#7CE0B0}
body:not(.view-report) .fri-reminder.info{background:rgba(var(--blue-rgb),0.12);border-color:rgba(var(--blue-rgb),0.28);color:#9FC2FF}
body:not(.view-report) .fri-btn{background:var(--bg-2);border-color:rgba(var(--orange-rgb),0.34);color:#FFC178}
body:not(.view-report) .fri-btn:hover{background:var(--orange);color:#1A1200}
body:not(.view-report) .mq-badge{background:var(--danger);color:#1A0308}
body:not(.view-report) .mq-btn{background:var(--card-hd)}
body:not(.view-report) .mq-btn:active{background:var(--row-hover)}
body:not(.view-report) .hc-dim{background:var(--card-hd)}
body:not(.view-report) .hc-dim.bad{border-color:var(--danger);color:var(--danger)}

/* 权限 / 管理员 / 组织 / 工伤 / 问题 / 外派 标签族 */
body:not(.view-report) .email-role,body:not(.view-report) .pwdadmin-role,body:not(.view-report) .subadmin-head .tag,body:not(.view-report) .general-pos,body:not(.view-report) .injury-file-chip{background:rgba(var(--blue-rgb),0.14);color:var(--blue-fg);border-color:rgba(76,141,255,.30)}
body:not(.view-report) .general-region{background:rgba(var(--green-rgb),0.14);color:var(--green-fg);border-color:rgba(62,207,142,.30)}
body:not(.view-report) .general-region.ALL{background:var(--bg-2);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) .email-name,body:not(.view-report) .pwdadmin-name,body:not(.view-report) .subadmin-head,body:not(.view-report) .sa-checkbox label,body:not(.view-report) .sa-dept-wrap label,body:not(.view-report) .general-summary b,body:not(.view-report) .general-name,body:not(.view-report) .injury-summary b,body:not(.view-report) .injury-title{color:var(--ink)}
body:not(.view-report) .email-empty,body:not(.view-report) .pwdadmin-empty,body:not(.view-report) .general-empty,body:not(.view-report) .injury-empty,body:not(.view-report) .injury-file-chip .fsize{color:var(--muted)}
body:not(.view-report) .pwdadmin-status.own{color:#7CE0B0}
body:not(.view-report) .pwdadmin-status.def{color:var(--muted)}
/* D215：员工账号搜索区（深色主题）—— 输入的底/字/边必须跟着 token 走，
   否则在深色面板里是一个刺眼的白框（本项目其它输入框都走同一条规则）。 */
body:not(.view-report) .pwdadmin-sect{color:var(--blue-fg);border-top-color:var(--line)}
body:not(.view-report) .pwdadmin-sect-note{color:var(--muted)}
body:not(.view-report) .pwd-selfsearch input{background:var(--bg-2,var(--card));border-color:var(--stroke,#243247);color:var(--ink)}
body:not(.view-report) .pwd-selfsearch input::placeholder{color:var(--muted)}
body:not(.view-report) .injury-meta{color:var(--muted)}
body:not(.view-report) .pwdadmin-row,body:not(.view-report) .subadmin-card,body:not(.view-report) .general-row,body:not(.view-report) .injury-row{border-color:var(--line)}
body:not(.view-report) .general-form,body:not(.view-report) .injury-form,body:not(.view-report) #injuryCardBody{border-color:var(--line)}
body:not(.view-report) .injury-summary,body:not(.view-report) .general-summary,body:not(.view-report) .injury-filterbar label{color:var(--sub)}
body:not(.view-report) .injury-card-table th{color:var(--sub);border-bottom-color:var(--line)}
body:not(.view-report) .injury-card-table td{border-bottom-color:var(--hair);color:var(--sub)}
body:not(.view-report) .injury-sev-轻伤{color:#FFD98A;background:rgba(255,210,74,.12);border-color:rgba(255,210,74,.30)}
body:not(.view-report) .injury-sev-重伤{color:var(--red-fg);background:rgba(var(--red-rgb),0.13);border-color:rgba(255,61,102,.32)}
body:not(.view-report) .injury-st-治疗中{color:var(--orange-fg);background:rgba(var(--orange-rgb),0.13);border-color:rgba(255,165,61,.30)}
body:not(.view-report) .injury-st-已康复{color:var(--green-fg);background:rgba(var(--green-rgb),0.13);border-color:rgba(62,207,142,.30)}
body:not(.view-report) .injury-st-已结案{color:var(--muted);background:var(--bg-2);border-color:var(--stroke)}
body:not(.view-report) .injury-file-chip.pending{background:var(--bg-2);border-color:var(--stroke);color:var(--sub)}
body:not(.view-report) .injury-file-chip a,body:not(.view-report) .injury-file-add,body:not(.view-report) .prob-draft-tip .prob-draft-drop{color:var(--blue)}
body:not(.view-report) .injury-file-chip .fdel{color:var(--danger)}
body:not(.view-report) .injury-grid .req{color:var(--danger)}
body:not(.view-report) .general-del,body:not(.view-report) .injury-del{background:rgba(var(--red-rgb),0.12);color:var(--red-fg);border-color:rgba(255,61,102,.34)}
body:not(.view-report) .general-del:hover,body:not(.view-report) .injury-del:hover,body:not(.view-report) .pwdadmin-reset:hover,body:not(.view-report) .pf-red:hover{background:var(--danger);color:#1A0308}
body:not(.view-report) .pwdadmin-reset{background:rgba(var(--red-rgb),0.12);color:var(--red-fg);border-color:rgba(255,61,102,.34)}
body:not(.view-report) .sa-del{color:var(--danger)}

/* 问题单 / 外派支援 / 转移候选 */
body:not(.view-report) .prob-kind.k-ec{background:rgba(var(--blue-rgb),0.14);color:var(--blue-fg);border-color:rgba(76,141,255,.30)}
body:not(.view-report) .prob-kind.k-mc{background:rgba(var(--orange-rgb),0.14);color:var(--orange-fg);border-color:rgba(255,165,61,.30)}
body:not(.view-report) .prob-kind.k-other{background:var(--bg-2);color:var(--sub);border-color:var(--stroke)}
body:not(.view-report) .prob-status.s-open,body:not(.view-report) .xsup-status.s-open{background:rgba(var(--orange-rgb),0.14);color:var(--orange-fg);border-color:rgba(255,165,61,.32)}
body:not(.view-report) .prob-status.s-done,body:not(.view-report) .xsup-status.s-done{background:rgba(var(--green-rgb),0.14);color:var(--green-fg);border-color:rgba(62,207,142,.30)}
body:not(.view-report) .prob-dur,body:not(.view-report) .xsup-dur{color:var(--muted)}
body:not(.view-report) .prob-dur.od,body:not(.view-report) .prob-overdue-tag{color:var(--danger)}
body:not(.view-report) .prob-table tr.prob-overdue > td:first-child{box-shadow:inset 3px 0 0 #FF3D66}
body:not(.view-report) .xsup-table tr.xsup-long > td:first-child{box-shadow:inset 3px 0 0 #FFA53D}
body:not(.view-report) .xsup-long-tag,body:not(.view-report) .xsup-dur.warn,body:not(.view-report) .xsup-calc .xsup-calc-warn,body:not(.view-report) .xsup-ro-tip,body:not(.view-report) .pxf-warn{color:var(--orange)}
body:not(.view-report) .prob-reply-empty,body:not(.view-report) .pxf-empty,body:not(.view-report) .xsup-calc .xsup-calc-muted,body:not(.view-report) .wh-pos-note{color:var(--muted)}
body:not(.view-report) .xsup-calc{background:var(--card-hd);border-color:var(--line);color:var(--ink)}
body:not(.view-report) .pxf-bar .pxf-cnt,body:not(.view-report) .pxf-form label{color:var(--sub)}
body:not(.view-report) .pxf-bar .pxf-cnt b{color:var(--blue)}
body:not(.view-report) .pxf-form #pxfManager::placeholder{color:var(--disabled)}

/* 转移 / 调拨 xfer-* */
body:not(.view-report) .xfer-card{background:var(--card)}
body:not(.view-report) .xfer-row select,body:not(.view-report) .xfer-row input[type=text],body:not(.view-report) .xfer-pop .xfp-fld select,body:not(.view-report) .xfer-pop .xfp-prev,body:not(.view-report) .xfer-rows{background:var(--bg-2);color:var(--ink)}
body:not(.view-report) .xfer-chip{background:var(--bg-2);color:var(--sub)}
body:not(.view-report) .xfer-chip.on{background:var(--blue);border-color:var(--blue);color:#0B1220}
body:not(.view-report) .xfer-list th,body:not(.view-report) .xfer-ret>td{background:var(--card-hd)}
body:not(.view-report) .xfer-tag{background:rgba(var(--blue-rgb),0.14);color:#9FC2FF}
body:not(.view-report) .xfer-st.pending{background:rgba(var(--orange-rgb),0.14);color:#FFC178}
body:not(.view-report) .xfer-st.accepted{background:rgba(var(--green-rgb),0.14);color:#7CE0B0}
body:not(.view-report) .xfer-st.rejected{background:rgba(var(--red-rgb),0.14);color:#FF8FA9}
body:not(.view-report) .xfer-arrow{color:var(--blue)}
body:not(.view-report) .xfer-rows .xfr-item.on{background:var(--card-hd)}
body:not(.view-report) #xferAlert{background:linear-gradient(135deg,rgba(var(--orange-rgb),0.12),rgba(var(--orange-rgb),0.05));color:var(--orange-fg);border-color:rgba(255,165,61,.32)}
body:not(.view-report) #xferAlert .xa-item b{color:#FFD98A}

/* 操作日志 / 回收站 / 备份目录 */
body:not(.view-report) .oplog-det{background:var(--bg-2);border-color:var(--stroke);color:#9FC2FF}
body:not(.view-report) .oplog-det:hover{background:var(--bg-3);border-color:var(--stroke-strong)}
body:not(.view-report) .oplog-det-box{background:var(--bg-2);border-color:var(--line);color:var(--ink-2)}
body:not(.view-report) .oplog-det-more{color:var(--muted)}
body:not(.view-report) .oplog-time{color:var(--muted)}
body:not(.view-report) .oplog-rev{color:var(--muted)}
body:not(.view-report) .oplog-detail{color:var(--ink-2)}
/* 动作语义色（深底）：浅色那套 #1e7f4f/#2f6fed 等在 #1A2430 上只有 3.1~3.5:1，
   这里改用深色层既有的高亮令牌（--ok/--danger/--blue/--warn/--purple/--muted），
   实测全部 ≥4.5:1。--ink-2 而非 --sub：变更明细是审计证据，要比一般说明文字更亮。 */
body:not(.view-report) .oplog-act-ok{color:var(--ok)}
body:not(.view-report) .oplog-act-bad{color:var(--danger)}
body:not(.view-report) .oplog-act-info{color:var(--blue)}
body:not(.view-report) .oplog-act-warn{color:var(--warn)}
body:not(.view-report) .oplog-act-move{color:var(--purple)}
body:not(.view-report) .oplog-act-mute{color:var(--muted)}
body:not(.view-report) .oplog-empty{color:var(--muted)}

/* 标记：行内样式里最常见的浅底 / 深字（JS 生成，选择器打不过 ⇒ 用 !important） */
body:not(.view-report) [style*="background:#f4f9ff"],
body:not(.view-report) [style*="background:#f7fbff"],
body:not(.view-report) [style*="background:#eef3fe"],
body:not(.view-report) [style*="background:#f8fafc"],
body:not(.view-report) [style*="background:#f2f8ff"],
body:not(.view-report) [style*="background:#fbfcfe"],
body:not(.view-report) [style*="background:#ecf3fd"],
body:not(.view-report) [style*="background:#eef5fe"],
body:not(.view-report) [style*="background:#e9f1fb"]{background:var(--bg-2)!important}
body:not(.view-report) [style*="background:#e8f1ff"]{background:rgba(76,141,255,.14)!important}
body:not(.view-report) [style*="color:#1d4ed8"]{color:#8FBBFF!important}
body:not(.view-report) [style*="color:#1f3866"],
body:not(.view-report) [style*="color:#33404f"],
body:not(.view-report) [style*="color:#1b2a41"],
body:not(.view-report) [style*="color:#16233a"]{color:var(--ink)!important}
body:not(.view-report) [style*="color:#4a5568"],
body:not(.view-report) [style*="color:#5a6573"],
body:not(.view-report) [style*="color:#5a6472"],
body:not(.view-report) [style*="color:#8a93a6"],
body:not(.view-report) [style*="color:#5b6b85"],
body:not(.view-report) [style*="color:#475467"],
body:not(.view-report) [style*="color:#3c4a5e"],
body:not(.view-report) [style*="color:#3d4c66"],
body:not(.view-report) [style*="color:#41505f"]{color:var(--sub)!important}
/* 内联浅底表格分隔线 */
body:not(.view-report) [style*="border-bottom:1px solid #eef2f7"],
body:not(.view-report) [style*="border-bottom:1px solid #eef1f6"],
body:not(.view-report) [style*="border:1px solid #e3e8f0"],
body:not(.view-report) [style*="border-top:1px solid #e7f0fb"]{border-color:var(--hair)!important}

/* 端到端兜底：编辑器内联 white 底（保存/编辑等按钮） */
body:not(.view-report) .save-tip,body:not(.view-report) .toast{background:var(--bg-3);color:var(--ink)}

/* ─────────────────────────────────────────────────────────────────────────
   G6 · 补遗：实测截图后发现的写死白底（筛选条「阶段」按钮 / 预设按钮）
   ───────────────────────────────────────────────────────────────────────── */
body:not(.view-report) #fStageBtn{background-color:var(--bg-2);color:var(--ink);border-color:var(--stroke)}
body:not(.view-report) #fStageBtn:hover{background:var(--bg-3)}
body:not(.view-report) #fStageMenu{box-shadow:var(--el2)}
body:not(.view-report) .fstage-presets button{background-color:var(--bg-2);color:var(--ink);border-color:var(--stroke)}
body:not(.view-report) .fstage-presets button:hover{background:var(--bg-3)}
body:not(.view-report) .fstage-item .n,body:not(.view-report) .fstage-tip{color:var(--sub)}
/* 工具栏旁的图例/计数小字 */
body:not(.view-report) .morerows .legend,body:not(.view-report) .tb-actions{color:var(--sub)}

/* ─────────────────────────────────────────────────────────────────────────
   G7 · 交互可达性：触控目标 ≥44px（15:05 实测 .sb-item 仅 34.89px，低于
   Apple HIG / WCAG 2.5.8 的 44px 触控推荐值，手指容易点偏）
   只对粗指针设备（手机/平板）生效：@media (pointer:coarse) 嵌套在
   @media screen 闸门内 —— 桌面（pointer:fine）布局一字不动，打印态不受影响。
   button 元素在 Chromium 里内容默认垂直居中，min-height 即可，无需 flex。
   ───────────────────────────────────────────────────────────────────────── */
@media (pointer:coarse){
  body:not(.view-report) .sb-item{min-height:44px}
}

/* ─────────────────────────────────────────────────────────────────────────
   G8 · 名册行「姓名 + 👤钮」对齐（15:18/15:52 用户反馈「歪七扭八→整齐划一」「左右两侧都对齐」）
   两个方向都要齐：
   · 垂直 —— .pdp-btn 原本无 vertical-align（默认 baseline），钮内唯一内容是 👤 emoji，
     行内盒基线被彩色 emoji 字形盒拖着走，实测姓名文字中心与图标中心差
     +1.6px（手机 390）/ +2.27px（桌面 1440），肉眼看是一行行高低不齐。
   · 水平 —— 图标原本 margin-left:6px 紧跟姓名文字，姓名 2~4 字宽窄不一，
     图标起始 x 每行都不同，图标列左右参差（用户主诉）。
   td 不能改 flex/grid（L2411 历史教训 M0-9/M0-11：单元格下限变 min-content，手机窄屏顶穿），
   纯 CSS 也包不住裸文本节点 ⇒ 用**绝对定位**（td 布局零改动，最稳）：
   · 垂直：top:50% + translateY(-50%) 相对 td 居中（文字单行 + td 上下 padding 对称，中心重合）；
   · 水平：left:calc(4.2rem + 6px) —— rem 不随端字号变，桌面/手机同一坐标；
     4 字姓名(52px)后留 1 字余量不贴钮，5 字姓名(65px)仍不压钮（6 字以上极罕见才可能接触）。
   打印态：本段整层在 @media screen 闸门内，打印布局完全不受影响。
   ───────────────────────────────────────────────────────────────────────── */
body:not(.view-report) table.wh-table td.emp-name{position:relative}
body:not(.view-report) table.wh-table td.emp-name .pdp-btn{
  position:absolute;left:calc(4.2rem + 6px);top:50%;transform:translateY(-50%);margin-left:0}

/* ─────────────────────────────────────────────────────────────────────────
   ★ D192（2026-10-14）「在职员工信息」姓名格的 👤 钮（个人全景）
   与上面 G8 那条的关系先说清楚，免得下次有人「顺手统一」：
   · G8 管的是**驻厂名册**的 td.emp-name —— 那列 13%（≈125px），固定坐标绝对定位放得下，
     顺便把图标钉成一竖列。
   · 本条管**在职员工信息**的 td.staff-name —— 该列只有 10.5%（1280 档 ≈ 107.8px）。
     实测（.sbx/_probe-staffpdp.js，A/B 两姿势对照）：G8 那套绝对定位
     （left:calc(4.2rem+6px) = 73.2px）在 1280 / 1366 / 1440 / 1536 / 1920 **五档全部压到
     「性别」列**（钮右缘越过该列内容盒最宽 30px、1280 档 120 行里 90 行命中）
     ⇒ 那套**不能照抄**，这里走**流式**（钮跟着姓名文字排）。
   · 代价（认下并写明白）：图标起始 x 会随姓名 2 / 3 字略有参差；换来的是**永远不压隔壁列、
     也永远不出格**（改后实测：全档零压列，除 1 条姓名里混了工号的脏数据行）。
   ⚠ 垂直方向**不能**靠 vertical-align 摆平（实测调参，.sbx/_tune-staffpdp.js）：彩色 emoji 的
     字形盒不跟中文走 —— middle 1.98px、baseline 1.70px、text-bottom 3.70px、-0.1em 2.88px…
     全都在 1.7px 以上；「middle + 写死 top:2px」在桌面能压到 0.3px，但手机端钮更高
     （24.39px → 28.19px），同一补偿量反而变成 **3.98px**。
   ⚠ 所以垂直借 G8 那套「top:50% + translateY(-50%)」（钮盒中心 = 单元格中心），
     水平**不写 left**：`left:auto;right:auto` 让绝对定位元素落回它的**静态位置**
     ⇒ 仍然紧跟在姓名文字后面（实测间距恒为 4px），不是 G8 那个写死的 x=73.2px。
     一句话：**垂直照 G8、水平照流式**。两档实测（.sbx/_tune-staffpdp2.js）：
       桌面 1440 与手机 390，垂直最大差都是 **0.25px**，压列 0 行、出格 0 行。
   ⚠ margin-left 收到 4px（.pdp-btn 默认 6px）：4 字姓名带钮在 1280 档只剩约 5px 余量，能省则省；
     绝对定位下这个 margin 仍然生效（把钮从静态位置往右推），实测间距就是 4px。
   ⚠ 本段整层在 @media screen 闸门内，打印布局不受影响（与 G8 同一闸门）。 */
/* ★ D308（2026-11-03 用户要求「姓名左右对齐」）：名字靠左、👤 钮靠右，钮成一竖列。
   做法＝**格内 flex**：名字 flex:0 0 auto，钮 margin-left:auto 吃掉剩余空间。
   ⛔ 不再用上面 G8/G9 那套绝对定位：D192 五档实测证明 left:calc(4.2rem+6px) 会压「性别」列；
      而「静态位置」又让钮随姓名 2/3 字参差（那正是用户这次要消掉的）。
   ⚠ 没有余量时（4~5 字姓名 + 钮塞满该列）auto 边距自动归零 ⇒ 退化成原来的流式，绝不压隔壁列。
   ⚠ 垂直对齐从原来的 top:50%+translateY 换成 flex 的 align-items:center —— 同一个语义
      （钮盒中心＝行中心），但不再依赖 position:absolute。 */
table.staff-table td.staff-name .staff-name-in{display:flex;align-items:center;gap:4px}
table.staff-table td.staff-name .staff-name-txt{flex:0 0 auto}
table.staff-table td.staff-name .staff-name-in .pdp-btn{margin-left:auto}

/* ─────────────────────────────────────────────────────────────────────────
   G10 · 视觉最后一公里（16:49 用户「整体体验感」反馈的第 3 项）
   ① KPI 数值用等宽数字（tabular-nums）：数据刷新时位数相同的数字宽度恒定，
     不再左右跳动；对「在册总人力」这类会被实时改写的数字尤其明显。
   ② 原生下拉框自绘：覆盖层 411 行已给 select 深色配色，但「下拉箭头」仍是
     浏览器原生外观（深色胶囊上突兀），展开的 option 弹层也是白底黑字。
     这里去掉系统箭头换成 SVG 折线箭头、option 弹层一并深色。
   全部在 @media screen 闸门内，打印态不受影响。
   ───────────────────────────────────────────────────────────────────────── */
body:not(.view-report) .kpi .v{font-variant-numeric:tabular-nums}
body:not(.view-report) select{
  -webkit-appearance:none; appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23A6B4C6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 9px center; padding-right:26px}
body:not(.view-report) select option{background:var(--card-hd);color:#E8EEF6}
/* ③ 旧蓝残留实锤（深色态 computed 审计全站扫出的唯一露馅点）：电话簿「添加」钮
   #addPhoneBtn 把 background:#2f6fed 写死在**内联 style** 里，普通覆盖层压不过，
   必须加 !important；配色与 .btn.primary 深色款（L398）完全一致。 */
body:not(.view-report) #addPhoneBtn{
  background:linear-gradient(135deg,#3E7BF0,#3A77EE)!important;color:#0B1220!important;border:none}

/* ─────────────────────────────────────────────────────────────────────────
   G11 · 手机大表「粘性首列」（16:49 用户反馈的第 4 项）
   表格 min-width:960px 在手机上必须横滑，滑到右侧就看不到这一行是谁/什么仓位。
   首列（名册=姓名、仓库=仓位）sticky 吸附在左缘，横滑时始终可见。
   仅手机（≤640px）启用：桌面表格完整可见，无需吸附。
   细节：
   · thead 首列本就 sticky（top），叠加 left:0 即「左上角双轴吸附」，z-index 沿用 30；
   · 首列 td 需要实色背景，否则滑过的行内容会从下方穿透；
   · 低库存行首列本有 inset 红条（基础 CSS L1851），这里合并右分隔线，避免互相覆盖。
   ───────────────────────────────────────────────────────────────────────── */
@media(max-width:640px){
  body:not(.view-report) .wh-scroll table thead th:first-child{left:0}
  body:not(.view-report) .wh-scroll table tbody td:first-child{
    position:sticky;left:0;z-index:2;background:var(--bg-1);
    box-shadow:inset -1px 0 0 var(--stroke)}
  body:not(.view-report) .wh-scroll table tbody tr.wh-row-low>td:first-child{
    box-shadow:inset 3px 0 0 #d9483b,inset -1px 0 0 var(--stroke)}
}

/* ─────────────────────────────────────────────────────────────────────────
   G12 · 周报/月报视图屏幕深色（17:16 用户反馈「这个你没有换底色啊」）
   历史约定修订：本层头部原写「不写报告页选择器」，那是为「打印 / 导出保真」
   设的保守闸门。今日实测复核，两条安全前提均已独立成立：
     ① 打印：应用原生 print 媒体段颜色写死浅色、printing-doc 隔离依旧；
        本层整体在 @media screen 内 ⇒ 打印媒体下 G12 规则一律不匹配，
        周报页（body.view-report）Ctrl+P 打出来仍是原生浅色纸面。
     ② 导出 PNG：走 rptPngDraw 手写 canvas 重绘（零依赖，不读屏幕 CSS），
        颜色硬编码浅色 ⇒ 屏幕再深，导出图依然是浅色（探针采样画布像素验证）。
   ⇒ 「屏幕深色、打印浅色、导出图浅色」三态同时成立。
   纪律口径随之修订（append-only，上方既有规则一字不动）：
     - 打印根（printRoot）、print 媒体查询、报告容器（reportSec）、
       各视图类（pagemode / dep-page / dep-kpis / dep-lvl-t）
       仍保持不带选择器字面 ⇒ 全文计数 0；
     - rp- / rpt- 前缀自本段起允许出现，但每条选择器必须以 body.view-report
       开头（只在报告视图生效，且被 @media screen 闸门双重兜底）。
   配色来源：token = G1 的等值拷贝（body.view-report 上一份，var() 引用自动跟随）；
   组件值抄自本层既有深色规则（.btn=L396-404、select=G10、wh/todo=L199-206）；
   报告纸面 .rpt-paper/.rp-* 原生是硬编码浅色，逐条映射深色 token。
   ───────────────────────────────────────────────────────────────────────── */
body.view-report{
  --bg:#0C1118; --card:#131A24; --card-hd:#1A2430;
  --row-alt:#1A2430; --row-hover:#212D3B; --row-edit:#22314A; --row-edit-bar:#4C8DFF;
  --ink:#E8EEF6; --ink-2:#C6D1E0;
  --sub:#A6B4C6; --line:#2A3846; --surface2:#1A2430;
  --blue:#4C8DFF; --green:#3ECF8E; --orange:#FFA53D; --red:#FF3D66;
  --purple:#A78BFA; --cyan:#2BD4E0;
  --hair:rgba(255,255,255,.08); --hair2:rgba(255,255,255,.14);
  --bg-0:#0C1118; --bg-1:#131A24; --bg-2:#1A2430; --bg-3:#212D3B; --bg-4:#29384A;
  /* 2026-10-03 批次A（界面评审第 9/11 项）· 三级描边与说明文字提亮，只改值不改名：
     --line 1.6:1（装饰分隔线） < --stroke 2.15:1（控件/表面描边） < --stroke-strong 3.14:1（悬停/强调）。
     --muted 5.2:1 → 6.5:1（过 AA 未达 AAA 的说明文字，承载口径解释，实测偏吃力）。
     三档均与 --bg #0C1118 比对；smoke-eyecare-theme 守的是硬编码值，改令牌值不触发。 */
  --stroke:#3A4C60; --stroke-strong:#4E6480;
  --muted:#8E9FB6; --disabled:#55657A;
  --amber:#FFD24A; --pink:#F97BB0;
  --up:#FF5A52; --down:#3ECF8E; --danger:#FF3D66; --warn:#FFA53D; --ok:#3ECF8E;
  --shadow-sm:0 1px 2px rgba(0,0,0,.45);
  --shadow-md:0 6px 16px -4px rgba(0,0,0,.55);
  --shadow-lg:0 20px 50px -12px rgba(0,0,0,.65);
  --el1:inset 0 1px 0 rgba(255,255,255,.045);
  --el2:0 8px 24px rgba(0,0,0,.45);
  --el3:0 24px 64px rgba(0,0,0,.55);
  --ring:0 0 0 3px rgba(76,141,255,.28);
  /* ── D301 · 令牌层 v1.1 周报层补齐（与 G1 严格等值，别只改一边）── */
  --pure:#FFFFFF;
  --ink-head:var(--ink);
  --blue-fg:#9FC2FF; --green-fg:#7CE0B0; --orange-fg:#FFC178;
  --red-fg:#FF8FA9; --purple-fg:#C4B5FD;
  --blue-rgb:76,141,255; --green-rgb:62,207,142; --orange-rgb:255,165,61;
  --red-rgb:255,61,102;  --purple-rgb:167,139,250; --cyan-rgb:43,212,224;
  /* P2-5 报告域变量 · 深色侧（契约见上方 :root 注释 1930 行起）。
     每一项都与本层既有映射严格等值：bg=--card / fg=--ink / sub=--sub / muted=--muted /
     line=--line / card=--bg-1(KPI小卡) / head=--bg-1(表头) / accent=--blue / up=--up / down=--down。
     ⇒ 报告域规则消费 var(--rpt-*) 后，本层这些行的求值结果一个像素都不变。 */
  --rpt-bg:#131A24; --rpt-fg:#E8EEF6; --rpt-sub:#A6B4C6; --rpt-muted:#8E9FB6;
  --rpt-line:#2A3846; --rpt-card:#1A2430; --rpt-head:#1A2430; --rpt-accent:#4C8DFF;
  --rpt-up:#FF5A52; --rpt-down:#3ECF8E;
  color-scheme:dark;
  background:var(--bg);
}
body.view-report ::-webkit-scrollbar{width:10px;height:10px}
body.view-report ::-webkit-scrollbar-thumb{background:var(--bg-4);border-radius:var(--r-1)}
body.view-report ::selection{background:rgba(var(--blue-rgb),0.34);color:var(--pure)}
/* 按钮（值 = L396-404 深色款，周报页里 导出图片/打印/上一期/下一期 全是 .btn） */
body.view-report .btn{background:var(--bg-2);border:1px solid var(--stroke);color:var(--ink);box-shadow:none}
body.view-report .btn:hover{background:var(--bg-3)}
body.view-report .btn.primary{background:linear-gradient(135deg,#3E7BF0,#3A77EE);color:#0B1220;border-color:#3E7BF0}
body.view-report .btn.primary:hover{background:linear-gradient(135deg,var(--blue),#3672E6)}
body.view-report .btn.ghost{background:var(--bg-2);color:var(--sub);border-color:var(--stroke)}
body.view-report .btn.ghost:hover{background:var(--bg-3);color:var(--ink)}
body.view-report .btn.danger{background:rgba(var(--red-rgb),0.12);color:var(--red-fg);border-color:rgba(255,61,102,.34)}
body.view-report .btn.active{background:linear-gradient(135deg,#2FB877,#23A06B);color:#08211A;border-color:#2FB877}
/* 视图条 / 卡片骨架（原生硬编码色部分） */
body.view-report .vb-title{color:var(--ink)}
body.view-report .wh-card{background:var(--card);border-color:var(--line)}
body.view-report .wh-t{color:var(--ink)}
body.view-report .todo-empty{color:var(--muted)}
body.view-report .muted{color:var(--muted)}
/* 报告纸面：原生硬编码浅色 → 深色 token 逐条映射
   P2-5 后本家族改为消费 var(--rpt-*)（深浅两值见 :root 与上方 body.view-report 契约块），
   逐项与旧写法等值 ⇒ 视觉零变化；仅两处保留通用 token：
   · .rp-hd / tr.rp-sum 的 --stroke-strong：比 --rpt-line 更亮的分层线，报告域色板外；
   · tr.rp-sum 的 --bg-2：合计行底，与 --rpt-card 同值但语义独立。 */
body.view-report .rpt-paper{background:var(--rpt-bg);color:var(--rpt-fg);border-color:var(--rpt-line);box-shadow:var(--el2)}
body.view-report .rpt-paper h1{color:var(--rpt-fg)}
body.view-report .rpt-paper .rp-hd{border-bottom-color:var(--stroke-strong)}
body.view-report .rp-sub{color:var(--rpt-sub)}
body.view-report .rp-gen{color:var(--rpt-muted)}
body.view-report .rpt-paper h2{color:var(--rpt-fg);border-left-color:var(--rpt-accent)}
body.view-report .rpt-paper h2 .mut{color:var(--rpt-muted)}
body.view-report .rp-kpi{background:var(--rpt-card);border-color:var(--rpt-line)}
body.view-report .rp-kpi .k{color:var(--rpt-sub)}
body.view-report .rp-kpi .v{color:var(--rpt-fg)}
body.view-report .rp-up{color:var(--rpt-up)}
body.view-report .rp-down{color:var(--rpt-down)}
body.view-report .rp-flat{color:var(--rpt-muted)}
body.view-report .rp-tbl th{background:var(--rpt-head);color:var(--rpt-sub)}
body.view-report .rp-tbl th,body.view-report .rp-tbl td{border-bottom-color:var(--rpt-line)}
body.view-report .rp-tbl tr.rp-sum td{background:var(--bg-2);border-top-color:var(--stroke-strong)}
body.view-report .rp-box{background:var(--rpt-card);border-color:var(--rpt-line)}
body.view-report .rp-box .t{color:var(--rpt-sub)}
body.view-report .rp-box .n{color:var(--rpt-fg)}
body.view-report .rp-box .n.warn{color:var(--rpt-up)}
body.view-report .rp-box .s{color:var(--rpt-sub)}
body.view-report .rp-foot{border-top-color:var(--rpt-line);color:var(--rpt-muted)}
body.view-report .rp-empty{color:var(--rpt-muted)}
/* 工具条：周报|月报分段 / 周期窗口 / 提示 */
body.view-report .rpt-seg{border-color:var(--line)}
body.view-report .rpt-seg button{color:var(--sub)}
body.view-report .rpt-seg button.on{background:linear-gradient(135deg,#2F6FED,#2A63D4);color:var(--pure);font-weight:700}
body.view-report .rpt-win{color:var(--sub)}
body.view-report .rpt-msg{color:var(--muted)}
body.view-report .rpt-msg.err{color:var(--red-fg)}
/* 范围 / 口径下拉（值 = L169 + G10 箭头同款） */
body.view-report .wkt-ctl label{color:var(--sub)}
body.view-report .wkt-ctl select{background-color:var(--bg-2);color:var(--ink);border-color:var(--stroke)}
body.view-report .wkt-tag{color:var(--amber);background:rgba(255,210,74,.08);border-color:rgba(255,210,74,.25)}
body.view-report select{
  -webkit-appearance:none; appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23A6B4C6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 9px center; padding-right:26px}
body.view-report select option{background:var(--card-hd);color:var(--rpt-fg)}

/* ─────────────────────────────────────────────────────────────────────────
   G13 · 现场问题收集「待解决 / 超期」显示优化（2026-09-30 17:52 用户反馈）
   实测三处糙点（_probe-prob-badge.js，双端）：
     ① 汇总条「待解决 N」胶囊高 25.6px，比周围 16px 文字顶部凸出 4px —— 行内参差；
     ② 汇总条「超期 N 条 / 未回复 N 条」是裸红字（.prob-overdue-tag / .wh-zero），
        与同为警报语义的橙色胶囊风格不统一；
     ③ 状态格里徽标与「超期 N 天」小字间隙 0px，红字无衬底，挤在徽标正下方。
   修法（全部 scoped，不动原生 CSS / 打印态）：
     - 汇总条：三颗警报统计统一为「同规格胶囊族」——高度压到 20px、垂直居中；
       待解决=橙系（沿用既有深色款），未回复 / 超期=红系（rgba 衬底 + 细边）。
       事实类统计（部门 / 共 N 条 / 电控 / TOP）保持文字——警报=胶囊、事实=文字，
       一眼分层。
     - 状态格：徽标下加 3px 呼吸；超期小字加贴文红衬底（width:fit-content，
       桌面 65px ≤ 内宽 82px；960 窄表内宽 61px，把左右 padding 收到 3px 后 61px
       恰好贴住 —— 不用 border，避免 +2px 溢出）。C6 硬要求不变：「超期」二字
       仍然是文字信号，衬底只是辅助。
   ───────────────────────────────────────────────────────────────────────── */

/* ① 汇总条胶囊：压高度 + 行内垂直居中（状态格里的同款徽标不受影响 —— scoped 到 .wh-stat） */
body:not(.view-report) #probStat .prob-status{padding:0 8px;line-height:18px;vertical-align:middle}

/* ② 汇总条红系警报胶囊：未回复（.wh-zero 借用自仓库零库存的裸红粗体）/ 超期（裸红字）→ 与橙胶囊同规格 */
body:not(.view-report) #probStat .prob-overdue-tag,
body:not(.view-report) #probStat .wh-zero{
  display:inline-block;padding:0 8px;line-height:18px;border-radius:999px;
  font-size:12px;font-weight:600;white-space:nowrap;vertical-align:middle;
  background:rgba(var(--red-rgb),0.13);border:1px solid rgba(var(--red-rgb),0.32);color:var(--danger)}

/* ③ 状态格：徽标 → 时效小字 3px 呼吸；超期行贴文红衬底（fit-content 零列宽成本） */
body:not(.view-report) .prob-table td.prob-status-cell .prob-dur{margin-top:3px}
body:not(.view-report) .prob-table td.prob-status-cell .prob-dur.od{
  width:fit-content;padding:0 var(--s-2);border-radius:4px;
  background:rgba(255,61,102,.13)}
@media(max-width:640px){
  body:not(.view-report) .prob-table td.prob-status-cell .prob-dur.od{padding:0 3px}
}

/* ─────────────────────────────────────────────────────────────────────────
   G14 · 现场问题「回复对策编辑器」越界压制（17:58 用户截图：徽标叠在输入框右下角）
   根因（双端实测 _probe-prob-reply.js）：
     .prob-reply-edit{min-width:190px}（原生 L2061）比回复列的固定宽大 ——
     table.wh-table 是 table-layout:fixed、回复列 13%：
       桌面 1222 表 → 格内容宽 143px，编辑器被 min-width 撑到 190 → 右溢 39.1px；
       手机 960 表  → 格内容宽 109px，编辑器 190 → 右溢 73.2px。
     编辑器（textarea 白/深底）伸进状态格底下，徽标所在 td 在 DOM 靠后
     ⇒ 徽标+「超期 N 天」叠画在 textarea 右下角上（实测重叠 31.1 / 65.2px，
       手机端 54px 徽标整个压在框上）—— 用户截图即此。
   修法：min-width 归零让编辑器回自己家（width:auto = 填满本格内容宽）；
     按钮行允许折行（「保存回复」88.2px + 「清空」在 109px 格内放不下两颗，
     折行后清空钮落到第二行，不再越界）。垂直居中不动 —— 重叠消失后
     徽标在中轴上与编辑器并排，就是自然观感。
   ───────────────────────────────────────────────────────────────────────── */
body:not(.view-report) .prob-table td.prob-reply .prob-reply-edit{min-width:0}
body:not(.view-report) .prob-table td.prob-reply .prob-reply-acts{flex-wrap:wrap}

}  /* ← 关闭深色覆盖层的 @media screen 闸门 */
/* <<<DARK-OVERLAY-END>>> */

/* 跳转到主内容（2026-10-03 补齐 · 界面评审第 10 项）
   ──────────────────────────────────────────────────────────────────────────
   放在深色层闸门**之外**，与下面的折叠组件同区：这条链接是键盘/读屏专用，
   必须在一浅色域、二深色域、三报告域里都正确，不能只活在深色层内。
   ⚠ 隐藏方式必须是「移出视口」而不是 display:none / visibility:hidden：
      display:none 会把它从无障碍树里摘掉，键盘用户 Tab 不到焦点，
      链接等于不存在 —— 这是 skip link 最常见的实现错误。
   ⚠ 平时不可见但**仍在 Tab 序**里：第一次按 Tab 就显形，符合预期。
   ⚠⚠ 必须用 position:**fixed** 而不是 absolute：
      absolute 相对**文档**定位，top:0 指的是页面最顶端，而 :focus 时要的是
      「滑进**当前视口**顶部」。用 absolute 时链接会停在文档顶部 ——
      用户已往下滚了几屏时聚焦它等于什么都没发生（实测 top 卡在 -60 不动）。 */
.hn-skip{
  position:fixed;left:8px;top:-60px;z-index:10000;
  padding:8px 14px;border-radius:0 0 8px 8px;
  /* ⚠ D292：底色**不能**用 var(--blue)。深色主题下 --blue=#4C8DFF，
     白字压上去只有 3.20:1（WCAG AA 要 4.5）—— 实测来自 _audit-d293-contrast。
     这里写死 #2563EB：白字 5.17:1，浅色/深色两套主题下都过（这个链接打印时是隐藏的）。 */
  background:#2563EB;color:var(--pure);
  font-size:13px;font-weight:600;text-decoration:none;
  transition:top .16s ease;
}
.hn-skip:focus{top:0;outline:2px solid var(--pure);outline-offset:-2px}
/* 目标容器因跳转链接获得焦点时，不要在整片主内容上画焦点框 ——
   那里有几十行表格，画出来是一道横贯全屏的粗线，反而更难定位。
   ⚠ 只压 outline，**不能**写 display:none / visibility:hidden：
   那样跳转后焦点会真的落不到主内容上，skip link 等于白做。 */
#mainContent:focus,#mainContent:focus-visible{outline:none}
@media print{.hn-skip{display:none}}

/* ══════════════════════════════════════════════════════════════════════════
   屏幕外组件（不随深色覆盖层走：打印 / 折叠 · 2026-10-03 批次B · B2）
   ──────────────────────────────────────────────────────────────────────────
   ⚠ 必须放在 `@media screen{…}` 闸门**之外**：
      · 折叠块的正文在打印时必须**展开**（周报 / 一页纸导出要带走口径），
        而 @media print 嵌进 @media screen 后条件恒不成立（screen AND print = ∅）。
      · 同理，批次A 的「打印隐藏 toast」也落在闸门内、实际从未生效，这里一并修正。
   ⚠ 全条不写任何硬编码色值：折叠头用 `opacity` + `currentColor`，颜色继承所在容器
      ⇒ 浅色域 / 深色域 / 报告域三套主题都自动正确，也不会触发
        smoke-eyecare-theme 的「硬编码灰蓝」扫描。
   ══════════════════════════════════════════════════════════════════════════ */
.hn-fold-hd{
  display:inline-flex;align-items:center;gap:6px;cursor:pointer;
  font-size:12px;line-height:1.6;opacity:.78;padding:2px 0;margin:2px 0;
  -webkit-user-select:none;user-select:none;
}
.hn-fold-hd:hover{opacity:1;text-decoration:underline}
.hn-fold-hd:focus-visible{outline:2px solid currentColor;outline-offset:2px;border-radius:var(--r-1)}
.hn-fold-hd .hn-fold-caret{display:inline-block;transition:transform .15s ease}
.hn-fold.on > .hn-fold-hd .hn-fold-caret{transform:rotate(90deg)}
.hn-fold-bd{display:none}
.hn-fold.on > .hn-fold-bd{display:block}
@media (prefers-reduced-motion:reduce){ .hn-fold-hd .hn-fold-caret{transition:none} }
@media print{
  .hn-toast-host{display:none!important}        /* 通知浮层不该进打印件 */
  .hn-fold-hd{display:none!important}           /* 打印件里不要「ⓘ」这个交互件 */
  .hn-fold-bd{display:block!important}          /* 但口径正文必须完整带走 */
}

/* ===== 「🔍 与台账比对」面板（2026-10-07 第一百二十四轮）=====
   三行摘要（两边各多少人 / 差多少）+ 一张分类表。
   ⚠⚠ 视觉层一律在**末段追加覆盖**，不要回头改上面的 .wh-stat 等既有规则（见 MEMORY 视觉约定）。
   ⚠ 数字用等宽，避免比对表里两条数字不对齐看着像抄错。 */
.stf-recon{margin:10px 0 12px;padding:12px 14px;border:1px solid var(--line,#e3e8f0);
  border-radius:10px;background:rgba(47,111,237,.05);font-size:13px;line-height:1.75}
.stf-recon-hd{font-weight:600;margin-bottom:8px;color:var(--ink,#1f3866)}
.stf-recon-sum{display:flex;flex-wrap:wrap;gap:8px 18px;margin-bottom:10px;align-items:baseline}
.stf-recon-sum span{white-space:nowrap}
.stf-recon-num{font-variant-numeric:tabular-nums;font-weight:700}
.stf-recon-eq{font-variant-numeric:tabular-nums;color:var(--sub,#5b6b85)}
.stf-recon-tb{width:100%;border-collapse:collapse;font-size:12.5px;table-layout:fixed}
.stf-recon-tb th,.stf-recon-tb td{border:1px solid var(--line,#e3e8f0);padding:6px 8px;
  text-align:left;vertical-align:top;overflow-wrap:anywhere}
.stf-recon-tb th{background:rgba(47,111,237,.07);font-weight:600;white-space:nowrap}
/* ★ D231：「分类」列的宽度**从内联 style 挪到这里**（原来是 style="width:104px"，写死在两处表头）。
   ⚠ 为什么要 136px：最长的标签「同名同姓 · 独立的人」实测 116px，加单元格左右内边距 8+8 与边框 2
     ⇒ 内容宽至少 116 ⇒ 列宽 ≥ 134；取 136 留 2px 余量。
     改前 104px（内容 86）连 6 个字的标签都装不下（95px）。
   ⚠ 为什么必须挪出内联：宽屏要 136、窄屏要 96，**内联 style 压过一切 media 查询**，改不动。
   ⚠ table-layout:fixed 只认**第一行**的宽度，所以这条写在 th 上（⛔ 不要写到 td 上，不起作用）。 */
.stf-recon-tb th.stf-recon-cat{width:136px}
.stf-recon-tb td.n{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
/* ★ D231：分类胶囊**不许再 nowrap** —— 用户报「前面的字超出框了」。
   ⚠ 改前这里是 `white-space:nowrap`，而这一格是 `table-layout:fixed` 定宽 104px（内容宽 86px）：
     「同名同姓 · 独立的人」实测 **116px** ⇒ 溢出 21px，胶囊**跨过单元格竖线**压在「人数」列的 0 上；
     连 6 个字的「名册有未挂台账」(95px) 也把右内边距吃光、贴到边框上。
   ⇒ 三件套：① 允许自身折行；② `max-width:100%`（宽度永远 ≤ 单元格内容宽，⛔ 不可能再漏出去）；
     ③ `line-height:1.75` —— ⚠ 这个值**不是随手写的**：行内默认行高实测 1.748，
        写成 1.75 是为了让**单行胶囊的高度与改前一模一样**（22.1px，实测像素级一致），
        只在真的要折行时才起作用（折了也不挤成两行贴字）。
        ⛔ 别改成 1.5：那会把单行胶囊压矮 2.8px —— 视觉层不动纵向尺寸是本项目的铁律。
   ⚠ 折行只是**兜底**：列宽已经调到装得下现有全部标签（见 .stf-recon-cat），
     它只在窄屏或将来出现更长标签时才起作用 —— 那时宁可两行，也不许溢到隔壁。 */
.stf-recon-k{display:inline-block;padding:1px var(--s-3);border-radius:var(--r-2);font-size:12px;
  white-space:normal;max-width:100%;line-height:1.75;overflow-wrap:anywhere}
/* 分类色阶：只靠颜色区分「差异的严重度」，不靠字号/边框（那会改变行高，见 MEMORY 表格铁律） */
.stf-recon-k.ok{background:rgba(52,168,83,.14);color:#1f7a3d}
.stf-recon-k.warn{background:rgba(255,166,61,.18);color:#a35b00}
.stf-recon-k.bad{background:rgba(211,47,47,.13);color:#b3261e}
.stf-recon-k.info{background:rgba(47,111,237,.13);color:var(--blue-fg)}
.stf-recon-names{margin-top:4px;font-size:12px;color:var(--sub,#5b6b85);line-height:1.8}
/* ⚠ 差异名单里每位占一格、不折行挤压：窄屏下让它自然换行而不是把表头撑高 */
.stf-recon-chip{display:inline-block;margin:0 6px 4px 0;padding:1px var(--s-3);border-radius:var(--r-2);
  background:rgba(0,0,0,.05);white-space:nowrap}
/* ★ D229：「名册有未挂台账」这一类里的每个名字＝**可点的定位按钮**（点一下到外协名册那一条）。
   ⚠ 复用 .stf-recon-chip 的外形（同一族面板、同一套观感），只补三样：
     ① 把 button 的默认字体 / 边框 / 内边距压回去 —— 不压就是一颗系统灰按钮，行高会顶开一格；
     ② 底边改**虚线** + 手型 + 淡蓝描边，让「这里能点」一眼看得出来
        （面板里已经有绿/橙/红/蓝四色语义，所以只借蓝这一色，⛔ 不新增第五种颜色）；
     ③ :hover / :focus-visible 给可见反馈 —— 键盘 Tab 也要能看见焦点在哪。
   ⚠⚠ 边框**必须用明确的蓝色**，⛔ 不能用 .stf-recon-chip 那套 rgba(0,0,0,.10)：
      黑色半透明在**深色主题**上等于「更暗的边」—— 几乎不可见，30 个名字看起来仍是一堆纯文字，
      用户根本不知道能点。这条是第一次截图才发现的（静态断言全绿）。
      浅蓝 #9dc0ee 在深浅两种底上都读得出来。
   ⛔ 不要给它加 position:absolute / transform 位移那类「浮动提示」，chip 是行内流排布，
      一旦脱离文档流就会压住相邻名字（窄屏尤其明显）。 */
.stf-recon-chip.stf-recon-go{font:inherit;color:inherit;text-align:left;cursor:pointer;
  border:1px solid #9dc0ee;border-bottom-style:dashed;background:rgba(63,127,214,.07)}
.stf-recon-chip.stf-recon-go:hover{background:rgba(63,127,214,.18);border-color:#3f7fd6;color:#1f3866}
.stf-recon-chip.stf-recon-go:active{background:rgba(63,127,214,.26)}
.stf-recon-chip.stf-recon-go:focus-visible{outline:2px solid #3f7fd6;outline-offset:1px}
.stf-recon-foot{margin-top:var(--s-4);font-size:12px;color:var(--muted,#8b95a7);line-height:1.7}
/* ===== 「台账⇄花名册」面板的信息外溢修复（2026-10-08 第一百三十二轮 · CSS 末段追加覆盖）=====
   用户报障原话：「这里的部分信息又外溢了，导致部分信息根本看不全」。

   ⚠⚠⚠ 先量后改（三个视口实测，改之前的真实数字）：
     视口 1600 → 面板容器 1322px、内容 1612px（溢出 290）、整页横向溢出 244px
     视口 1100 → 容器 898px、内容 1612px（溢出 714）、整页横向溢出 676px
     视口  390 → 名单格 165px、内容 377px（外溢 212）、整页横向溢出 1250px

   ⇒ 根因不是「某一列太宽」，而是【名单格里的 inline-block 徽标 + table-layout:fixed】：
     每位员工一格、每格含「工号·姓名·部门·入职日期」的长串
     （实测最宽 284px：「黄涛 → SJ04934@调试一部 / SJ00749@调试五部」），
     而 fixed 布局按表头 width 分配列宽，单元格内容可以超出分配到的宽度
     ⇒ 表格按内容 1612px 画 ⇒ 容器装不下 ⇒ 整页出现横向滚动，
     右边的人名【在屏外根本看不到】。

   ⇒ 本轮踩了 6 轮弯路，**逐条留档**（别再重复）：
     ① 徽标改 white-space:normal          ⇒ 徽标最宽 284→142px，但面板仍 1612
     ② 表格改 table-layout:auto            ⇒ 更糟，内容宽 1612→1648（auto 会按内容撑开父容器）⇒ 已回退
     ③ 给 td 加 white-space:normal important ⇒ 那条规则根本没进 styleSheets（见 ⑤）
     ④ 徽标加 important                    ⇒ 规则在 styleSheets 里，但外溢一点没减
     ⑤ ⛔⛔【最隐蔽】把 overflow-x:auto 写在 style 块里，浏览器【当它不存在】：
        文件里语法正确、也在 style 内、动态注入立刻生效（244→0），
        但页面里 getComputedStyle 恒为 visible、styleSheets 里查不到该规则。
        根因：前置注释里的反引号等字符在编辑过程中留下转义残渣，
        把紧邻的声明污染成非法语法 ⇒ 整条被浏览器丢弃。
        ⇒ 功能性保障不能依赖「注释是否干净」。
   ⇒ 正解：overflow-x:auto 写在【元素的 style 属性】上（见该元素的 style=""），
     内联优先级最高，不受 style 块解析 / 特异性博弈 / 注释污染影响。
     这是「让信息可达」而不是「把信息塞进屏幕」—— 名单动辄几十人，
     硬塞进 390px 只会变成谁也认不出的密集文字。

   ⚠ 不动行高（MEMORY 表格铁律：视觉层不碰纵向尺寸）。 */
.stf-recon-tb{table-layout:fixed;width:100%}
.stf-recon,.stf-recon *{box-sizing:border-box}
/* ⚠ 名单格放开换行（第132 轮第二次返工 · 真根因是「禁止换行」而不是「容器装不下」）。
   ⚠⚠ 上一次只写了 overflow-x:auto（横向滚动），结果名单仍是一行挤到底、右边被截断 ——
     滚动只是「让你去拖」，不是「让你看全」。
   实测：名单容器 1134px、内容 1449px、whiteSpace=nowrap；放开后高度 28→55px（多一行）、
   内容宽回到容器宽。
   ⚠ 这条同时用**内联**写在名单容器上（见 staffReconcileRender 的 chips）——
     style 块里的规则会被前置注释的转义残渣污染而不被解析（实测过），两处都写才稳。
     ⚠ 表头 th 仍保持 nowrap（「分类 / 人数 / 名单」三个词很短，拆行只会把表头顶高）。
     ⚠⚠ 但**分类徽章（.stf-recon-k）D231 起改为允许折行** —— 这里原来写的是
       「表头 th 与分类徽章都要保持 nowrap（内容短）」，那句判断**错了**：
       「同名同姓 · 独立的人」9 个字早就超过定宽列，nowrap 的后果不是「难看」而是**溢出格子**。
       ⇒ 徽章：white-space:normal + max-width:100%（兜底），列宽：见 .stf-recon-cat。 */
.stf-recon-tb tbody td{white-space:normal;word-break:break-word}
/* ⚠ 窄屏（≤430px）两处仍会外溢，实测各 17~28px，已一并处理：
     ① `.stf-recon-sum` 是 display:flex 的三段统计，span 之间不换行
        ⇒ 允许换行（它本来就是「三段各自独立的数字」，拆行不影响可读性）
     ② 分类列宽被表头 width:76px 定死，而「有档案未挂台账」这 6 个字要 103px
        ⇒ 窄屏把分类列放宽到 96px（人数列不变）
     ⚠ D231 更正：上面的 76px/96px 都是**当时内联在 th 上的宽度**，改不动、只能写 !important 硬顶；
       现在列宽已挪到 `.stf-recon-cat`（CSS 里），窄屏直接给 96px 即可，不再需要 !important。
   ⚠ 不用 media 里的 !important —— 内联 style 优先级更高，
     而 stf-recon-sum 的 flex 行为只能靠 CSS 改（它没有内联 style）。 */
@media (max-width:430px){
  .stf-recon-sum{flex-wrap:wrap}
  .stf-recon-sum span{white-space:normal}
  /* ★ D231：窄屏把分类列收回 96px，长标签由胶囊自己折行（宁可两行，⛔ 不许溢到「人数」列）。
     ⚠ 这一条能生效的唯一前提：列宽已经不在内联 style 里（见 .stf-recon-cat）。 */
  .stf-recon-tb th.stf-recon-cat{width:96px}
}
/* ===== 筛选条「视觉分组」（2026-10-08 第一百三十二轮 · CSS 末段追加覆盖）=====
   用户报障（深色截图）：筛选框「糊成一片」，看不出哪几个是一组。
   ⚠⚠ 先量后改，量出来的事实（不是感觉）：
     · 14 个相邻空隙**全是 8px**（label→控件、控件→下一组的 label 都是 8）
     ⇒ 「组内」与「组间」在视觉上**没有任何区别**，眼睛无法分组 —— 这才是主因；
     · 下拉框 `padding:5px 26px 5px 6px` ⇒ **左内边距只有 6px**，
       深色主题下 12px 的字贴着左边框，看起来"挤"；
     · 标签高 18px / 控件高 26px，垂直中心差实测 **0px（已对齐）** ⇒ 这项没问题，不动。
   ⇒ 修法：给「控件 → 下一组的标签」加一道竖分隔 + 更大间距，把 7 组明确分开；
     同时把控件左内边距从 6px 提到 10px。
   ⚠ 不改行高/控件高度（MEMORY 表格铁律：视觉层不碰纵向尺寸）。
   ⚠ 标签本身的 margin-right 不加 —— 用 `select+label` 选择器只命中"控件之后的标签"，
     这样「组内标签↔控件」保持紧邻、「组间」才拉开，语义准确。 */
.tb-filters{gap:10px 6px}
.tb-filters label{margin-right:0}
.tb-filters .tb-sep{width:1px;align-self:stretch;margin:2px 4px;background:var(--line);opacity:.9;flex:none}
/* 控件内的字不要贴左边框（实测左内边距只有 6px） */
.tb-filters select,.tb-filters input[type=text]{padding-left:10px}
/* ⚠⚠⚠ 关键：第 1471 行那条 `.tb-filters{flex-wrap:nowrap}` 在**桌面也生效**（不在 media 里），
   ⇒ 筛选条永远一行、宽度不够时**整体撑破页面**。
   实测（1280px 视口）：筛选条 w=705 / 内容 sw=1260 ⇒ **整页横溢 156px**；
   1100px ⇒ 横溢 276px。这个问题是第131 轮加「区域/楼层」把筛选条变长之后才暴露的存量缺陷。
   ⇒ 修法：**允许换行**（flex-wrap:wrap）。横排放不下时自然折到第二行，
     每个控件仍可用，不需要横向滚动。
   ⚠ 换行会改变纵向布局，但**不是"改行高"**（MEMORY 铁律禁的是缩行高/字号，不是允许必要的折行）。 */
.tb-filters{flex-wrap:wrap}
/* ⚠⚠⚠ 关键：**光允许换行是不够的**，标签和它的控件会被拆到两行 ——
   实测（1600px）：筛选条自然宽 1025px、可用 949px，**只需 2 行**，
   但实际占了 5 行，且第二行全是 select/分隔、第三行全是 label ——
   因为 `.toolbar label{...flex:none}` 与 `.toolbar select{...flex:none}`（第 1468/1469 行）
   都是独立的 flex 项，而 **CSS 的 flex-wrap 无法表达「这两个必须同组」**。
   ⇒ 正解：把「标签 + 控件」包进一个 `display:inline-flex` 的容器，
     容器是不可拆的原子 ⇒ 标签与控件**永远同一行**，换行只发生在组与组之间。
     包裹由 groupFilterPairs() 在渲染后做（幂等），不手改 DOM ——
     因为 rebuildDeptFilter / 权限收敛会重建里面的 option。 */
.tb-filters .fb-pair{display:inline-flex;align-items:center;gap:6px;flex:none}
/* ⚠⚠ 别让「搜索」框吃掉**全部剩余宽度**（flex-grow:1）——
   它一变宽就把后面的「共 N 行」挤到下一行，筛选条凭空多占 1~3 行（实测 1600px 下占 5 行）。
   ⇒ flex-grow 收到 0：宽度由 min-width 与 placeholder 长度决定，能放下就同行，放不下才折行。
   ⚠ 用 !important 是因为 1471 行那条 `#fSearch{flex:1 1 150px}` 特异性更高（ID 选择器）。 */
.tb-filters #fSearch{flex:0 1 160px !important;min-width:120px !important}
/* ⚠ 两个无标签的输入框（检索姓名 / 搜索）默认宽偏小，占不满一行右侧的空档。
   量过：1600px 下第二行右侧空余 300+px，而它们各只有 140/160px。
   ⇒ 给到 200/240px，仍留出弹性收缩空间（flex-shrink:1 仍在），
     窄屏时它们会让位而不是撑破页面。 */
.tb-filters #fName{flex:0 1 200px !important;min-width:120px !important}
.tb-filters #fSearch{flex:0 1 240px !important;min-width:120px !important}
/* 「共 N 行」永远贴最右，不参与成组（它是状态不是筛选项） */
.tb-filters #rowCount{margin-left:auto}
/* 窄屏（手机）：原本是 nowrap + 横向滚动（第1471 行设计意图），
   但**它同时是 1280/1100 桌面横溢的元凶** ⇒ 一律改成换行，撤掉横向滚动这条路径。
   手机上筛选条只有 ~330px 宽，换行后每行放 2~3 个控件，可用性反而更好。 */
@media (max-width:1100px){
  .tb-filters .tb-sep{display:none}
  .tb-filters{gap:14px 6px}
}
/* 区域 / 楼层筛选框的宽度收敛（2026-10-08 第一百三十一轮 · CSS 末段追加覆盖）=====
   ⚠ 为什么必须限宽（先量再改，不靠感觉）：
     实测 1600px 视口下筛选条总宽 949px，其中
       #fDept 94px / #fRegion 84px / #fMgr 70px / #fFloor **244px**
     楼层之所以独占 244px，是因为线上有一条脏数据
       「C04-5F/B10-2F/B11-2F/A03-3F/A03-4F」（34 字符）——
     select 的宽度由**最长 option** 决定，于是「一个部门有 26 个楼层」时
     它比部门下拉宽 2.6 倍，把后面的「负责人 / 阶段 / 姓名」全挤到折行。
   ⇒ 做法：给 #fFloor / #fRegion 一个上限，溢出用省略号。
     ⚠ 不动 #fDept / #fMgr —— 它们本来就窄，改了只会让现有布局变样。
     ⚠ 不改行高（MEMORY 表格铁律：视觉层不动纵向尺寸）。 */
#fRegion,#fFloor{max-width:132px;text-overflow:ellipsis}
/* ⛔ 窄屏（手机）：筛选条本身已 nowrap + 横向滚动（见第1471 行），
   这里不能再加限宽，否则四个下拉挤成一小条更难点。 */
@media (max-width:900px){ #fRegion,#fFloor{max-width:104px} }
/* 「超出管辖部门」提示（2026-10-08 第一百三十九轮 · CSS 末段追加覆盖）=====
   用户选择「加提示但不限制」：弹窗里照旧能勾任意部门，只对超出该账号管辖部门的项加醒目提示。
   ⚠ 为什么是这个颜色：本提示表达的是「勾了也不会生效」，属**警示**而非错误（保存不会失败），
     所以用琥珀色而不是错误红 —— 与 .wh-err 的红区分开。
   ⚠ 不改变行高：只加 font-size / padding 与色值，不给父级加纵向尺寸（MEMORY 表格铁律：
     视觉层不动纵向尺寸，否则会连带把行高与首屏行数改掉）。 */
.wh-perm-warn{display:inline-block;margin-left:4px;padding:0 var(--s-2);border-radius:8px;font-size:11px;
  color:#8a5700;background:#FFF3D6;border:1px solid #F0D69A;cursor:help;white-space:nowrap}
/* 全员操作日志弹窗「做大」（2026-10-09 第一百四十三轮 · CSS 末段追加覆盖）=====
   用户报「这个弹窗做大一点」。按纪律先量再改（probe-oplog-size.js，Playwright 四视口）：
     改动前实测 —— 宽度在 1034 / 1366 / 1440 / 1920 四个视口下**恒为 860px**。
     ⚠ 这正说明限制它的是内联的 width:860px，而不是 max-width:94vw
       （94vw 只在视口 <915px 时才起作用）⇒ 光在本层写 width 是**压不住内联**的，
       所以同轮改了 HTML 上那两处内联，别只改这里。
     改动前表体高 = 62vh（852 高视口只有 528px）；外层弹窗 736px，离 90vh 上限还差 31px。
   ⇒ 三件事，只动尺寸，不碰任何颜色与字号：
     ① 宽度上限 860 → min(1400px,94vw)。用 94vw 而不是 98vw 是刻意的：
        窄屏下 .modal 自带 padding:12px，98vw + 24px 会横向溢出被裁；
        1400px 是防超宽屏把「详情」列拉得过长反而不好扫读。
        ⚠ 内联的 max-width:94vw 也必须留着：它一漏，.modalbox 类里的
          max-width:92vw 就会接管，窄屏反而比改前**窄** 2vw（本轮实测踩到过）。
     ② 弹窗改纵向 flex，表体 flex:1 吃掉剩余高度 ⇒ 不再写死 vh，
        换任何视口都自动填满；也就不会出现「表体顶到天花板、外层只好再滚一次」的双滚动条。
     ③ 纵向内边距 22 → 18、横向 24 → 22，把省下的还给内容区。
   ⚠ 只锁 #oplogModal，不做全局 .modalbox 覆写，其余弹窗尺寸维持原样。
   ⚠ 本层不写任何颜色字面量（设计升级层的红线：禁旧灰底色，见第 3129 行说明）。 */
#oplogModal .modalbox{max-height:96vh;padding:18px 22px;display:flex;flex-direction:column}
/* 表体 flex 撑满。min-height 是极窄视口（横屏手机）下的保底 ——
   真放不下时宁可让外层 modalbox 自己滚（它有 overflow-y:auto），
   也不把表体压成一条缝。 */
#oplogModal #oplogBody{flex:1 1 auto;min-height:120px}
/* 窄屏回退：.modal 在手机上是 align-items:flex-start + padding:12px，
   96vh 会正好顶到屏幕上下边 ⇒ 退回与其它弹窗一致的 88vh；
   表体同时收回「按内容高度」，交回外层 modalbox 统一滚动 ——
   即手机端行为与改前完全一致（62vh + 外层滚动）。 */
@media (max-width:760px){
  #oplogModal .modalbox{max-height:88vh}
  #oplogModal #oplogBody{flex:0 0 auto;max-height:62vh}
}
/* 手机上「姓名输入框」收窄（2026-10-09 第一百四十六轮 · CSS 末段追加覆盖）=====
   用户报「手机端的这个姓名显示框长度缩短一点，没必要这么长」。
   按纪律先量再改（probe-nlname-width.js，Playwright 八视口、真实 admin 登录态 + 卡片视图）：
     改动前 —— 手机端姓名框是 flex:1 1 auto，会**吃掉整行剩余宽度**：
       360 视口 行宽 260 → 姓名框 156
       390 视口 行宽 290 → 姓名框 186
       430 视口 行宽 330 → 姓名框 226   ← 用户截图正是这一档
       640 视口 行宽 540 → 姓名框 436
     而 3 个汉字在 16px 字号下只占 48px（4 字 64px）⇒ 430 档里有 170px 纯空。
   ⇒ 收到固定 120px：内容宽 = 120 − 14(padding) − 2(border) = 104px ≈ 6.5 个汉字@16px，
     4~5 字姓名 + 光标完全够用。
   ⚠ 为什么不是照抄桌面的 88px：手机端字号被提到 16px（防 iOS 聚焦缩放，见第 1567 行），
     而桌面那个 88px 是按 12px 字号定的；按容量等比换算 88×16/12 ≈ 117，故取 120。
   ⚠ flex 必须写 0 1 auto，**shrink 要留 1**：外协行（cat=temp）在姓名后还有
     「唯一键」输入框（96px）与「转正」按钮，360 视口下三者相加会超过行宽；
     留 shrink 才能自动让位、不折行（写成 0 0 auto 就会顶穿）。
   ⚠ 只锁 ≤640，与第 1605 行同断点 —— 不改桌面：桌面本来就是固定 88px（实测四个视口恒 88）。
   ⚠ 不改行高、不碰任何颜色（表格铁律：视觉层不动纵向尺寸）。
   ⚠ 为什么不直接改第 1605 行那条老规则：遵守本项目「视觉层一律 CSS 末段追加覆盖」的约定，
     回滚只需删掉本段。 */
@media (max-width:640px){
  .nlname,.nlname.tonly,.nlname.rohalf{flex:0 1 auto;width:120px;min-width:0;max-width:100%}
}
/* 「姓名」多选标签框（2026-10-09 第一百四十八轮 · CSS 末段追加覆盖）=====
   用户问「这里的员工姓名是不是可以一次选择多个」⇒ 姓名从单值输入框改成**标签式多选**；
   数据口径不变（一人一条记录），多选只是录入便利，见 JS 里 XSUP_PICK 那段注释。
   ⚠ 只**新增**类，不改 .injury-grid input 那条既有规则 —— 标签框内部的输入框必须无边框透明，
     否则会在带边框的容器里再套一层边框（看上去像两个框叠在一起）。
   ⚠ 选择器用 .xsup-pick input（0-2-0）压过 .injury-grid input（0-1-1）：同层追加、不动原规则，
     回滚只需删掉本段（本项目视觉层铁律）。
   ⚠ 高度不写死：标签换行时容器跟着长；min-height 只是空态保底，用来对齐旁边 36px 的输入框。
   ⚠ 手机端字号由第 1567 行那条全局 !important 规则提到 16px（防 iOS 聚焦缩放），
     这里**不写死 font-size**，免得跟它打架（写死也会被 !important 盖掉，只会误导后来人）。 */
.xsup-pick{display:flex;flex-wrap:wrap;gap:6px;align-items:center;min-height:36px;
  padding:4px var(--s-3);border:1px solid #cfe0f4;border-radius:8px;background:var(--pure);box-sizing:border-box}
.xsup-pick:focus-within{border-color:#8fb8e8}
/* display:contents 让标签直接参与外层 flex 的换行排布（容器本身不产生盒子）。
   ⚠ 不改成 .xsup-pick-tags{display:flex} —— 那样输入框会被挤到第二行去。 */
.xsup-pick-tags{display:contents}
.xsup-pick input{flex:1 1 130px;min-width:110px;height:auto;min-height:26px;padding:0;
  border:none!important;background:transparent;outline:none;box-shadow:none;font-size:13px}
.xsup-pick-tag{display:inline-flex;align-items:center;gap:var(--s-2);max-width:100%;
  padding:2px 6px 2px 10px;border:1px solid #cfdcf0;border-radius:999px;
  background:#eef3fb;font-size:13px;line-height:1.7;white-space:nowrap}
.xsup-pick-tag b{font-weight:600}
/* 编辑态里「正在编辑的那条记录」用略深的底色标出来（改名 = 改这条，别让人分不清谁是谁） */
.xsup-pick-tag.orig{border-color:#b7d3f5;background:#e4eefc}
.xsup-pick-no{color:#6b7a90;font-size:11px;font-variant-numeric:tabular-nums}
.xsup-pick-no.dup{color:var(--orange-fg)}
.xsup-pick-x{cursor:pointer;border:none;background:none;padding:0 2px;margin-left:1px;
  color:#8fa0b8;font-size:12px;line-height:1}
.xsup-pick-x:hover{color:var(--red-fg)}
.xsup-pick-tip{display:block;font-size:12px;color:#6b7a90}
/* 手机端把「转正 / ⇄ / ✕」收进「⋯」更多菜单（2026-10-09 第一百五十一轮 · CSS 末段追加覆盖）=====
   背景：第一百五十轮曾用「隐藏工号胶囊」给姓名框腾宽度；用户 2026-10-09 决定**回滚那一条**，
        改成统一把这三个**动作**按钮收进一个 ⋯ 触发器后面 —— 工号胶囊恢复常显。
   ⚠ 桌面端一个字节都不改：.nlmore 桌面不显示，.nlacts 用 display:contents 让三个按钮
     **原位内联**（DOM 顺序与改动前完全一致），所以「悬停直出 ⇄/✕」的老习惯原样保留。
   ⚠ 为什么用 display:contents：包裹层本身不产生盒子，子元素直接参与 .nlrow 的 flex 排布
     —— 布局与 DOM 结构解耦，桌面端不必为「包裹」付任何像素代价。（旧安卓不支持它，
       但旧安卓必然是手机宽度，走的是下面那条 display:none 分支，碰不到。）
   ⚠ 手机端展开用**行内换行**，不做 fixed 浮层：.nlboxes 是 max-height + overflow-y:auto
     的滚动容器，弹层方案要么被它裁切、要么得挂到 body 再补一套点击委托
     （本项目已有 fixed-overlay 系列教训，第 112/113 轮都栽在这上面）。
     换行展开零定位、零裁切风险，动作按钮仍留在 .nlrow 里 ⇒ 现成的 tbody 点击委托
     **一行都不用改**（三个按钮的 class 与 data-* 全部照旧）。
   ⚠ 展开时姓名框宽度不变（实测 64px → 64px）：.mopen 下把姓名框的 flex 基数设为 0，
     否则它那条 width:120px 的基数会让整行**提前**折行（实测 120+35+53+66+26+16=316 > 260）。
   ⚠ flex-wrap 只在 .mopen 时开：常开等于把折行判定交给基数，行 1 会当场断开。
   ⚠ 收起态行高仍是 28px（不改纵向尺寸）；只碰这一块的宽度分配，不碰任何颜色 token。 */
.nlmore{display:none}
.nlacts{display:contents}
@media (max-width:640px){
  .nlmore{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
    width:26px;height:26px;padding:0;border:1px solid #cdd6e4;border-radius:6px;
    background:var(--pure);color:#4a5568;font-size:15px;line-height:1;cursor:pointer;outline:none;user-select:none}
  .nlmore:active{background:#e3edfb;border-color:#9dc0ee}
  .nlacts{display:none}
  .nlrow.mopen{flex-wrap:wrap}
  .nlrow.mopen .nlname,
  .nlrow.mopen .nlname.tonly,
  .nlrow.mopen .nlname.rohalf{flex:1 1 0%;width:auto;min-width:0}
  .nlrow.mopen .nlacts{display:flex;flex:1 1 100%;justify-content:flex-end;align-items:center;
    gap:6px;margin-top:var(--s-2);padding-top:var(--s-2);border-top:1px dashed #e3eaf5}
  .nlrow.mopen .nlmore{background:#eaf2fe;border-color:#9dc0ee;color:var(--blue-fg)}
}
/* ══════════════════════════════════════════════════════════════════════════
   首卡（.kpi.pri）副标题两行化 —— 2026-10-09 第一百五十六轮
   ──────────────────────────────────────────────────────────────────────────
   由来：本轮给「在册总人力」副标题加「− 重复 N」后，实测**四个宽度下全被省略号截断**
     （1280 / 1440 / 1680 / 1920：内容 326px，而卡片内容宽只有 237 ~ 304px）。
   ⚠ 但这**不是本轮才坏的**：口径上先后加过「内援」「外援」两项，早就超宽了。
     上面 .kpi:not(.pri) 那条规则里写的「首卡内容宽 277px 本来就放得下」是当时的实测，
     那句话已过期 —— 所以不是改错了规则，是规则的前提变了。
   ⇒ 与次卡同式：允许两行 + overflow-wrap:anywhere，宁可占高也不再从中间断句。
   ⚠ 用**末段追加覆盖**（不改上面那条 :not(.pri) 规则）：同特异度、后写者胜；
     将来要回退只删这一段即可，不必去动历史规则。
   ⚠ 首卡骨架的 .s 必须同时补 .skel 类（见 #kpis 骨架），否则两行化后骨架比真实矮一行。
   ⚠ 首卡占两行**不会撑高卡片**：#kpis 的行高由首卡的大字号 .v 决定（实测 48px），
     两行 11px 文字仍在卡底之内 —— 与次卡两行化那轮的实测结论一致。 */
.kpi.pri .s{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.kpi.pri .s.skel{display:block;min-height:3em}
/* ══════════════════════════════════════════════════════════════════════════
   桌面端次卡副标题放宽到三行 —— 2026-10-09 第一百五十六轮
   ──────────────────────────────────────────────────────────────────────────
   由来：新写的去重回归用例（check-headcount-dedup C15）用 **scrollHeight** 判截断，
   实测发现「正式工」卡在 1280 / 1440 / 768 三档都**放不下**：
     1280 内容 101px 宽、真需 3 行（自然高 50px），而 .s 只有 2 行额度（33px）——
   而 **去掉本轮新增的「− 重复 2」也仍需 3 行** ⇒ 这在 1280 是**既有缺陷**，
   本轮的新文案只是在 1440 那档把它一起暴露了出来。
   ⚠ 上一版探针用 scrollWidth > clientWidth 判截断，那是**假绿**：
     两行盒子（-webkit-box + overflow:hidden）的 scrollWidth 恒等于 clientWidth，
     内容被 clamp 切掉也看不出来 ⇒ 必须用 scrollHeight（见该用例里的注释）。
   ⚠ 为什么只在 ≥641px 生效：375 / 414 手机上卡片更宽（152 ~ 171px），
     这批副标题本来就 1 ~ 2 行收得住，且**手机上次卡余量为 0** ——
     多给一行会当场把卡片撑高、骨架对不上（首屏抖一下）。所以窄屏维持两行不动。
   ⚠ 桌面端撑得住吗：实测 ≥768px 的次卡余量是 17 ~ 23px，而多一行只要 16.5px
     ⇒ 卡片高度**不变**（该行高度由首卡决定，首卡余量 0），
     所以 1280 / 1440 / 1680 / 1920 四档的卡片高度与骨架都不受影响。
   ⚠ 1024px 那档「正式工」真需 5 行（卡片内容宽只有 69px），三行仍不够 ——
     那是该档位本身过窄的既有问题（同档「内援人力」「覆盖部门」「线体 / 设备」也都被切），
     不在本轮范围内；真要用 1024，得先把 #kpis 的列数断点调开。 */
@media (min-width:641px){
  .kpi:not(.pri) .s{-webkit-line-clamp:3}
  /* 骨架同步放宽：真实内容最多三行，骨架必须按三行占位，否则首屏会抖。
     ⚠ 只在 ≥641px 放宽 —— 窄屏仍是两行，骨架也跟着保持 3em。
     ⚠ D294：7 卡 → 5 卡后每张卡更宽，主流档位（≥1280）真实只占 **2 行**，
       骨架若仍按 3 行占位就会比真实高 17px（实测 131 vs 114）⇒ check-headcount-dedup 的
       C17「骨架与真实逐卡等高」当场变红。五卡下按 2 行占位与主流档位吻合；
       1024 那种极窄档真实仍需 3 行以上，那是该档位本身的既有问题（见上方注释）。 */
  .kpi:not(.pri) .s.skel{min-height:3em}
  /* D294：主卡是宽卡（跨 2 列），≥641px 下它的说明只剩 **1 行**（实测 s=17px），
     骨架若按 3em=33px 占位，整行就被它撑高 17px（131 vs 114）。
     ⚠ 只加在 ≥641px：手机主卡是全宽独立一行、说明 2 行 = 33px，正好等于 3em。 */
  .kpi.pri .s.skel{min-height:1.5em}
}

/* ══════════════════════════════════════════════════════════════════════════
   版本胶囊加「改动 v157」副标签 —— 2026-10-09 第一百五十七轮
   ──────────────────────────────────────────────────────────────────────────
   由来：用户看着屏幕问「你这里是 V156，系统是显示是 V190」——
   页面只显示**构建号**（v190：页面文件每存一次 +1），而台账与回退点用的是
   **改动批次号**（v156：一次完整改动算一号）。两个都对，但对不上号。
   现在把批次号也摆上胶囊：桌面显示「版本 v190 改动 v157 2026-10-09 21:20」，
   窄屏（≤640px）仍按既有规则隐藏构建时间，只多出「改动 v157」这几个字。
   ⚠ 宽度是**先量后改**的（_probe-verchip-fit.js，360/375/414/640/768/1024/1440 七档）：
     胶囊 +52px，**任何断点都不横向溢出**；640 / 768 / 1440 三档状态文案 (#impStat)
     宽度**零变化**，其余档只是「本来就被省略号截断的那条状态文案」再窄约 52px。
     这正是本项目既定取舍（见下面 2026-10-05 那段）：宁可让状态文案先被省略，
     也不能让版本号缺字 —— 用户看不到版本号，这次改动就等于白做。
   ⚠ 窄屏三档原本只显示构建号，胶囊 57 → 109px，仍在状态条内，且不挤掉任何**完整**文案。
   ⛔ 别把 .verchip-at 放回窄屏：360 档状态条只剩约 40px 余量（2026-10-05 那段注释）。
   ⛔ 批次号取不到时前端**不渲染这个 span**（绝不猜一个号，见 renderVerChip 注释）。 */
.statusbar .verchip-led{margin-left:6px;font-weight:600;opacity:.88}
body:not(.view-report) .statusbar .verchip-led{color:#AFC6E4;opacity:1}

/* ══════════════════════════════════════════════════════════════════════════
   「OSS 汇报」台账 · **表头四列筛选**（部门 / 项目 / 车间 / 设备）  2026-10-12 · D183
   ──────────────────────────────────────────────────────────────────────────
   用户原话：「在OSS汇报里面这个几个对应的位置后面增加筛选功能」
   （附了表头截图，圈的正是「部门 / 项目 / 车间 / 设备」这四格）。
   ⚠ 两问两答定死后才动手（⛔ 不是猜的）：
     ① 控件形态 = **表头格子里的常显小下拉**（⛔ 不是上方工具条、也不是漏斗弹层）；
     ② 出现范围 = **OSS 汇总视图 ＋ 部门页「OSS 汇报」分区都要**。
   ⚠ 本段**取代**了 D177 的「表格上方 · 部门筛选工具条」（#ossFilter / #ossDeptFilter 已整块撤掉）——
     筛选统一搬进表头，⛔ 同一个状态不摆两个入口。
   ⚠⚠ **一列列宽都没动**（仍是 D182 实测出的 8/6/6/8/5/9/9/7/7/10/10/15）。
     依据是 _probe-ossfilter-183.js 的实测（四档屏宽 × 两档侧内边距 × 五个候选分配，
     都在**最挤**场景 = 部门页「OSS 汇报」分区下量）：
       · 四个下拉本身只需要 **38–50px**，而 th 默认 `padding:9px 10px` 的左右共 20px 白白吃掉了宽度；
       · 把这四格的侧内边距收到 **6px**（白赚 8px 内容宽）后，1536 / 1440 / 1280 / 1180 四档
         下拉**全部完整、无一个被裁**；
       · ⛔ 不改内边距的话，1180px 下「车间」那个下拉会被裁 3.4px（值尾被切）。
     ⚠ 实测里 `OSS 时长` 列在 1180px 同样显示「列 71px < 需 77px」—— 但那在**所有候选
       （含完全不动列宽的现状）**里都一样 ⇒ 是 D182 就存在的既存项，**与本轮无关**，本轮没动它。
   ⚠ 代价：表头因此长高（约 34px → **60px**）—— 标题一行 ＋ 下拉一行，这是「常显下拉」的必然结果
     （已量：thead tr 高 60px，各档一致）。 */
table.oss-table th.oss-hf-th{padding-left:6px;padding-right:6px;vertical-align:bottom;
  white-space:normal;overflow-wrap:anywhere}
table.oss-table th.oss-hf-th .oss-hf-t{display:block}
/* ⚠ 块级 + width:100% 让下拉**恰好占满该列内容宽**（⛔ 不给固定宽度：fixed 布局下那是另一套宽度口径）；
   字号 11px 是把「上下料机 / C11-1F」这类最长取值塞进 6% 窄列的关键（12px 时装不下）。 */
table.oss-table th.oss-hf-th select.oss-hf{display:block;width:100%;max-width:100%;box-sizing:border-box;
  margin-top:3px;font-family:inherit;font-size:11px;line-height:1.5;padding:1px 2px;
  color:#25324a;background:var(--pure);border:1px solid var(--line);border-radius:var(--r-1);outline:none}
table.oss-table th.oss-hf-th select.oss-hf:focus{border-color:#2f54b0;box-shadow:0 0 0 2px rgba(47,84,176,.12)}
/* 候选不足 2 个（典型：部门页里只看得到一个部门）⇒ **只藏控件、保留列名**。
   ⚠ 藏整格会让表头缺一块；藏控件则与「这一列没什么可筛的」语义一致。
   ⚠⚠ 藏了控件的那格必须**改回顶对齐**：本段上面给四格统一设了 vertical-align:bottom
     （为的是让「列名 + 下拉」两行贴着格子底边排），可是只剩列名的那格内容只有一行，
      底对齐会把它压到**下拉那一行**的高度上 —— 实测部门页里「部门」二字比其余三格
      低 **23.5px**，看着就像「部门」孤零零挂在第二行（_shot-ossfilter-183 量出来的）。
      改回 top 后四格列名极差 0px。 */
table.oss-table th.oss-hf-off{vertical-align:top}
table.oss-table th.oss-hf-off select.oss-hf{display:none}

/* ══════════════════════════════════════════════════════════════════════════
   「在职员工信息 / 外协人员信息」· 部门 · 中介 筛选  2026-10-12 · D178
   ──────────────────────────────────────────────────────────────────────────
   用户原话：「在职员工信息里面增加部门筛选功能」「外协人员信息里面增加中介和部门的筛选功能」。
   三处下拉（在职·部门 / 外协·中介 / 外协·部门）都塞在经过改造的 .wh-filter 里，
   与搜索框同一行。
   ⚠ 这里**只补宽度**：背景 / 文字色 / 边框 / 圆角 / 内边距全部沿用样式表前段既有那条
     .wh-filter input,.wh-filter select 规则，⛔ 不重复定义 ——
     同一元素两套声明，必在某个主题或断点下打架（本项目吃过这个亏）。
   ⚠ 只给 min-width，⛔ 不给固定 width：部门名与中介名长度差得很远
     （如「华南售后综合部」对「惠州众鑫」），定宽会在窄屏裁字、在宽屏留一片空白。
     窄屏由 .wh-filter 自己的 flex-wrap 折行，不会再横向溢出。
   ⚠ 本段一律**追加在样式表末段**（本项目对视觉层一贯做法：新规则覆盖旧规则），
     ⛔ 不动上面任何一条老规则 —— 那样才有可能逐字节还原。 */
.wh-filter>select{min-width:132px;max-width:100%;cursor:pointer}
.wh-filter>select:focus{outline:none;border-color:#2f54b0;box-shadow:0 0 0 2px rgba(47,84,176,.12)}
/* ===== D186「OSS 汇报」页内切换：台账 / 规划（只读） =====
   本节一律追加在样式表末段（本项目对视觉层的一贯做法：新规则覆盖旧规则），
   ⛔ 不动上面任何一条老规则 —— 那样才有可能逐字节还原。
   规划页只在「OSS 汇报」独立导航视图里出现；部门视图的 OSS 分区保持原样，
   tab 与规划页在那里一律不显示（下面三条规则都以本视图为前提）。 */
#ossPlanTabs{display:none;gap:8px;margin:18px 0 10px}
body.view-oss #ossPlanTabs{display:flex}
.oss-tab{background:var(--card);color:var(--sub);border:1px solid var(--line);border-radius:var(--r-2);
  padding:var(--s-3) 16px;font-size:13px;font-weight:600;cursor:pointer;
  transition:background .15s ease,border-color .15s ease,color .15s ease}
.oss-tab:hover{background:var(--row-hover)}
.oss-tab.active{background:var(--blue-solid);border-color:var(--blue-solid);color:var(--pure)}
#ossPlanCard{display:none}
body.view-oss.osstab-plan #ossPlanCard{display:block}
body.view-oss.osstab-plan #ossSec>.oss-card:not(.oss-plan-card){display:none}

.oss-plan-kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(178px,1fr));gap:10px;padding:12px 16px}
.oss-plan-kpi{background:var(--card-hd);border-radius:10px;padding:10px 14px;min-width:0}
.oss-plan-kpi .k{font-size:12px;color:var(--sub)}
.oss-plan-kpi .v{font-size:23px;font-weight:700;color:var(--ink);line-height:1.25;margin-top:2px}
.oss-plan-kpi .s{font-size:12px;color:var(--sub);margin-top:2px}

.oss-plan-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:0 16px 10px;font-size:13px;color:var(--sub)}
.oss-plan-bar select{border:1px solid var(--line);border-radius:8px;background:var(--pure);color:var(--ink);
  padding:var(--s-2) 8px;font-size:13px;cursor:pointer;max-width:100%}
#ossPlanHint{font-size:12px;color:var(--blue)}

table.oss-plan-table{width:100%;table-layout:fixed;border-collapse:collapse;font-size:12px}
table.oss-plan-table th,table.oss-plan-table td{padding:var(--s-3) 8px;border-bottom:1px solid var(--line);vertical-align:top}
table.oss-plan-table thead th{background:var(--card-hd);color:var(--sub);font-weight:600;
  text-align:right;white-space:nowrap;word-break:break-word}
table.oss-plan-table thead th.l{text-align:left}
table.oss-plan-table td{text-align:right;color:var(--ink);font-variant-numeric:tabular-nums}
table.oss-plan-table td.l{text-align:left}
table.oss-plan-table tbody tr:hover td{background:var(--row-hover)}
table.oss-plan-table tr.oss-plan-grp td{background:var(--row-alt);color:var(--blue);font-weight:700;
  text-align:left;border-bottom:1px solid var(--line)}
table.oss-plan-table td.plan-machine{white-space:pre-line;color:var(--sub)}
table.oss-plan-table .is-tbd{color:var(--orange);font-weight:600}
table.oss-plan-table .is-null{color:#aab4c2}

.oss-plan-note{padding:12px 16px;border-top:1px solid var(--line);background:var(--card);
  font-size:12px;color:var(--sub);line-height:1.8}
.oss-plan-note b{color:var(--ink)}

/* ── D187 · 规划表表头排版：该居中居中、该分界分界 ──────────────────────────
   用户 2026-10-13 17:43 反馈（附表头截图）「这里显示能做的更合理吗，该居中居中，该分界分界」。
   ⚠ 先量后改（⛔ 不是看着猜的）：1440px 下实测两张跨三列的分段表头（SAT-Peak 段 / MP 段）
     的 text-align 是 right —— 压在各自三列的最右边，读起来像「最后一列的表头」，
     而不是「这三列成一组」；且表头 10 个格的左边框**全是 0px** ⇒ 段与段之间一条竖线都没有。
   ① 居中：表头一律 text-align:center。工站 / 机台两格带 class l，特异性更高、继续左对齐
      （与格内左对齐的站名 / 机台数一致）。⛔ 数值格仍右对齐 —— 数字右对齐才按位可比。
   ② 分界：按列位给「表头两行 ＋ 每条数据行」补竖线，成四段：
      工站 ｜ 机台 ｜ SAT-Peak 三列 ｜ MP 三列。颜色走既有的分隔线变量 hair2
      （深浅两套都定义了它），⛔ 不写死颜色（护眼 / 深色检查会抓硬编码色）。
      分组行是整行合并 8 列，竖线自然断开，不必特殊处理。
   ③ 表头带比数据行高（机台那一格排成两行），表头纵向居中，标题不再贴顶。
   ⚠ 全部 append 在末段覆盖，上面一行不改（视觉层末段覆盖约定）。
   ── D188 追加（2026-10-13 18:5x）：用户看过 D187 的成品后回「你再看看」（附改后截图）。
   ⚠⚠ 先量后改 —— 写 _probe-planhead-188.js 把「表头文字盒」与「数据文字盒」都量出来。
      量到的硬事实：六个数值子表头（客户口径 / 正式工 / 临时工，两段各一组）被 D187 那句
      「一律居中」一起居中了，而它们下面的数字是右对齐的（数字右对齐才按位可比）
      ⇒ 表头文字右缘与同列数字右缘**错开 22–29px**，每列都差小半格，看着就是「表头跟数字没对齐」。
      而 D187 真正要修的是两条**跨三列**的段标题（SAT-Peak 段 / MP 段）—— 它们原本右对齐、
      压在各自三列的最右边，才像「最后一列的表头」。
   ⇒ 改法（用户在三档对照图里选的 B「表头跟数据走」）：
      ① 段标题仍居中：只对**跨列**的表头格居中（这类 th 带 colspan 属性 ⇒ 恰好就是那两条段标题）；
      ② 六个数值子表头**回到右对齐** ⇒ 与同列数字同一条右缘（改后实测 6 列差全是 0）；
      ③ 工站 / 机台两格仍左对齐（带 class l，规则特异性更高），与格内站名 / 机台数同左缘；
      ④ 纵向居中与四段竖线**照旧不动**（D187 那两项是对的）。
   ⚠ 一句话记住：**表头不是一律居中，而是跟它下面的数据同一种对齐** ——
      文本列左对齐、数值列右对齐、跨列的分组标题才居中。 */
table.oss-plan-table thead th{vertical-align:middle}
table.oss-plan-table thead tr:first-child th[colspan]{text-align:center}
table.oss-plan-table thead tr:first-child th:nth-child(2),
table.oss-plan-table thead tr:first-child th:nth-child(3),
table.oss-plan-table thead tr:first-child th:nth-child(4),
table.oss-plan-table thead tr:last-child th:nth-child(1),
table.oss-plan-table thead tr:last-child th:nth-child(4),
table.oss-plan-table tbody tr td:nth-child(2),
table.oss-plan-table tbody tr td:nth-child(3),
table.oss-plan-table tbody tr td:nth-child(6){border-left:1px solid var(--hair2)}

/* ── 抽屉「① 当前归属」的两类身份标签（2026-10-16 第一百九十六轮）──────────────
   ⚠ 只**新增**选择器（.pdp-tag.mgr / .pdp-tag.head），⛔ 上面一行不动 —— 末段覆盖是本项目
     视觉层的一贯做法；特异性 (0,2,0) 高过 .pdp-tag (0,1,0)，无需 !important。
   ⚠ 颜色只是**辅助线索**，语义永远由标签文字承载（写着 总负责人 / 行负责人）⇒
     ⛔ 不承载唯一信息，色觉障碍用户照样读得懂。
   ⚠ 深色那两条**自己带 @media screen 闸门**：外面那一层 @media screen 是给整份深色板用的，
     而本段的位置在那个 media 块**之外** ⇒ 不自己包一层，深色标签会连同底一起印到纸上。
   ⚠ 只用既有令牌（--orange / --purple，深浅两套主题都定义了）与既有浅色底做法，
     ⛔ 不引入新的灰色底。 */
.pdp-tag.mgr{background:#fdf0dd;color:#8a5311}
.pdp-tag.head{background:#efe9fb;color:#5b3a9e}
@media screen{
  body:not(.view-report) .pdp-tag.mgr{background:rgba(var(--orange-rgb),0.15);color:var(--orange)}
  body:not(.view-report) .pdp-tag.head{background:rgba(var(--purple-rgb),0.18);color:var(--purple)}
}

/* ================== 年终绩效考评（D205，2026-10-19）==================
   ⚠⚠ 三条视图闸门规则**一条都不能少**（铁律 3b）：
     ① #perfSec 默认隐藏；② 进入 view-perf 时显示；③ 其它块隐藏（**必须带 !important**，
        因为 JS 常给兄弟卡写内联 display，内联值赢过任何普通选择器）。
   ⚠ 颜色**全部走 CSS 变量**（--card / --ink / --line / --sub / --blue …）：
     本项目的屏幕默认主题是**深色**（见 @media screen 里那份 token 覆盖），
     ⛔ 写死 #fff 会得到一张白底黑字的孤岛，而且打印/报告视图又是一套值。
   ⚠ 段末覆盖是本项目视觉层的一贯做法 —— 本段位置在主样式表末尾，特异性相同即胜出，
     所以不需要也不应该给普通规则加 !important。 */
#perfSec{display:none}
body.view-perf #perfSec{display:block;margin:0}
body.view-perf .wrap > *:not(#perfSec){display:none!important}

.pf-card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:16px 18px 18px;margin-bottom:16px}
.pf-hd{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;flex-wrap:wrap}
.pf-t{font-size:19px;font-weight:800;color:var(--ink);letter-spacing:.3px}
.pf-sub{margin-top:6px;font-size:12.5px;color:var(--sub);line-height:1.75;max-width:920px}
.pf-chips{margin-top:10px;display:flex;flex-wrap:wrap;gap:8px}
.pf-chip{font-size:12px;font-weight:700;padding:3px 10px;border-radius:99px;background:var(--card-hd);
  color:var(--sub);border:1px solid var(--line);white-space:nowrap}
.pf-chip.on{color:var(--green);border-color:rgba(62,207,142,.45)}
.pf-chip.off{color:var(--orange-fg);border-color:rgba(255,165,61,.45)}
.pf-chip.lock{color:var(--red-fg);border-color:rgba(255,61,102,.45)}
.pf-acts{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.pf-btn{padding:6px 14px;font-size:12.5px;white-space:nowrap}
.pf-note{margin-top:12px;padding:10px 14px;border-radius:10px;font-size:12.5px;line-height:1.75;
  background:var(--card-hd);border:1px solid var(--line);color:var(--sub)}
.pf-note.warn{border-color:rgba(255,165,61,.5);color:var(--orange-fg)}
.pf-note.err{border-color:rgba(255,61,102,.5);color:var(--red)}
.pf-note b{color:var(--ink)}
.pf-toolbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:14px 0 10px}
.pf-toolbar input[type=text],.pf-toolbar select{padding:6px 10px;font-size:12.5px;border-radius:8px;
  border:1px solid var(--line);background:var(--card-hd);color:var(--ink);font-family:inherit}
.pf-toolbar input[type=text]{min-width:200px}
.pf-count{margin-left:auto;font-size:12px;color:var(--sub)}
.pf-tblwrap{overflow:auto;max-height:max(420px,calc(100vh - 460px));border:1px solid var(--line);border-radius:10px}
.pf-table{width:100%;border-collapse:collapse;font-size:12.5px;min-width:1040px}
.pf-table th{position:sticky;top:0;z-index:1;background:var(--card-hd);color:var(--sub);
  font-weight:700;font-size:12px;letter-spacing:.3px;padding:8px 10px;text-align:left;
  white-space:nowrap;border-bottom:1px solid var(--line)}
.pf-table td{padding:var(--s-3) 10px;border-bottom:1px solid var(--line);color:var(--ink);vertical-align:middle}
.pf-table tbody tr:hover{background:var(--row-hover)}
.pf-table .pf-num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.pf-table .pf-op{white-space:nowrap}
.pf-table .pf-op button{padding:3px var(--s-4);font-size:12px;margin-right:4px;border-radius:var(--r-2)}
.pf-empty{padding:28px 10px;text-align:center;color:var(--sub);font-size:13px}
/* 考评明细（只读，D232）。
   ⚠ 边框一律走 var(--line)、表头底走 var(--card-hd)：⛔ 不要写 rgba(0,0,0,.1) 这类
     「比背景更暗」的颜色 —— 深色主题下几乎看不见（D229 踩过一次）。
   ⚠ 明细里每张表都可能比弹窗宽（7 列）⇒ 一律套 .pf-dtscroll 自己横滚，整页不横溢。 */
.pf-dtsum{display:flex;flex-wrap:wrap;gap:8px;margin:2px 0 10px}
.pf-dtsum .it{flex:1 1 128px;min-width:0;border:1px solid var(--line);border-radius:10px;
  background:var(--card-hd);padding:8px var(--s-5)}
.pf-dtsum .it .lb{display:block;font-size:11px;color:var(--sub);margin-bottom:3px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.pf-dtsum .it b{font-size:18px;color:var(--ink);font-variant-numeric:tabular-nums}
.pf-dtsum .it b.sm{font-size:13.5px}
.pf-dtsum .it.key{border-color:rgba(76,141,255,.55);background:rgba(76,141,255,.08)}
.pf-dtsum .it.key b{color:var(--blue)}
.pf-dtrule{margin:0 0 12px;font-size:12px;color:var(--sub);line-height:1.8}
.pf-dtrule b{color:var(--ink)}
.pf-dtpart{margin-top:16px}
.pf-dtph{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:2px}
.pf-dtph b{font-size:13.5px;color:var(--ink)}
.pf-dtph .pf-pw{font-size:12px;font-weight:700;color:var(--blue)}
.pf-dtscroll{overflow:auto;border:1px solid var(--line);border-radius:var(--r-2);margin-top:8px}
.pf-dtt{width:100%;border-collapse:collapse;font-size:12.5px;min-width:820px}
.pf-dtt th{background:var(--card-hd);color:var(--sub);font-weight:700;font-size:12px;letter-spacing:.2px;
  padding:var(--s-3) var(--s-4);text-align:left;white-space:nowrap;border-bottom:1px solid var(--line)}
.pf-dtt td{padding:var(--s-3) var(--s-4);border-bottom:1px solid var(--line);color:var(--ink);vertical-align:top;
  white-space:normal;word-break:break-word}
.pf-dtt tbody tr:last-child td{border-bottom:0}
.pf-dtt .pf-num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.pf-dtt .pf-dtd{white-space:pre-wrap;line-height:1.65;color:var(--ink)}
.pf-dtk{display:inline-block;margin-left:6px;font-size:11px;font-weight:700;padding:1px 6px;
  border-radius:99px;white-space:nowrap;color:var(--blue-fg);background:rgba(76,141,255,.12);
  border:1px solid rgba(76,141,255,.35)}
.pf-dtfoot{margin-top:8px;padding:8px var(--s-5);border-radius:var(--r-2);background:var(--card-hd);
  border:1px solid var(--line);font-size:12px;color:var(--sub);line-height:1.8}
.pf-dtfoot b{color:var(--ink)}
/* 汇总表那一层只借 .pf-dtscroll 的「自己滚」，不叠加边框（它自带 1px 网格） */
.pf-dtbare{border:0;border-radius:0;margin-top:12px}
.pf-dtblock{margin-top:8px;border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-4) 12px;background:var(--card-hd)}
.pf-dtblock i{display:block;font-style:normal;font-size:12px;font-weight:700;color:var(--sub);margin-bottom:4px}
.pf-dtblock div{font-size:12.5px;color:var(--ink);line-height:1.8;white-space:pre-wrap;word-break:break-word}
.pf-dtacts{display:inline-flex;gap:8px;flex-wrap:wrap;align-items:center}
/* 窄屏：摘要卡**两列**（一行一个会把 5 张卡叠成半屏，明细正文要往下翻很久才看得到）；
   标签允许折行 —— ⛔ 不用省略号，那样「最终分（以负责人为准）」会变成「最终分（以…」，
   读的人分不清是哪个口径。 */
@media (max-width:560px){
  .pf-dtsum{gap:var(--s-3)}
  .pf-dtsum .it{flex:1 1 calc(50% - 5px);padding:var(--s-3) 10px}
  .pf-dtsum .it b{font-size:16px}
  .pf-dtsum .it .lb{white-space:normal;overflow:visible;line-height:1.35}
  .pf-dtt{min-width:640px}
}
.pf-st{display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:99px;
  white-space:nowrap;background:var(--card-hd);color:var(--sub);border:1px solid var(--line)}
.pf-st.s-self{color:var(--blue);border-color:rgba(76,141,255,.45)}
.pf-st.s-mgr{color:var(--orange);border-color:rgba(255,165,61,.45)}
.pf-st.s-head{color:var(--purple,#a78bfa);border-color:rgba(167,139,250,.45)}
.pf-st.s-done{color:var(--green);border-color:rgba(62,207,142,.45)}
/* 支援标记（D207）：只加一个小小的尾标，⛔ 不新开一列 —— 表已经很宽（窄屏会横向溢出）。
   颜色走 CSS 变量，深浅两套主题自动适配。 */
.pf-sup{display:inline-block;margin-left:var(--s-2);font-size:11px;font-weight:700;padding:1px 6px;
  border-radius:99px;white-space:nowrap;color:var(--blue-fg);background:rgba(76,141,255,.12);
  border:1px solid rgba(76,141,255,.35)}
.pf-grade{font-weight:800;color:var(--ink)}
.pf-selfcard{margin-top:14px;border:1px solid var(--line);border-radius:12px;background:var(--card-hd);padding:14px 16px}
.pf-selftitle{font-size:14px;font-weight:800;color:var(--ink);margin-bottom:10px}
/* D245 草稿条：恢复提示。⛔ 不用 fixed/absolute —— 它要占住版面上的位置，
   否则一出现就压在表格标题上（本项目第 113/114 轮踩过「悬浮元素叠字」）。 */
.pf-draftbar{margin:0 0 12px;padding:var(--s-4) 12px;border-radius:var(--r-2);border:1px solid var(--orange);
  background:color-mix(in srgb,var(--orange) 10%,transparent);color:var(--ink);font-size:13px;line-height:1.7}
.pf-draftbar .btn{margin-left:6px}
.pf-dimrow{display:flex;align-items:center;gap:12px;padding:6px 0;flex-wrap:wrap}
.pf-dimrow .pf-dl{flex:1 1 170px;font-size:13px;color:var(--ink);font-weight:600}
.pf-dimrow .pf-dm{font-size:12px;color:var(--sub);font-weight:400}
.pf-dimrow input{width:92px;padding:6px var(--s-4);font-size:13px;text-align:right;border-radius:8px;
  border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-dimrow input:focus{outline:2px solid rgba(76,141,255,.45);outline-offset:1px}
.pf-row{margin-top:12px}
.pf-row>label{display:block;font-size:12.5px;font-weight:700;color:var(--sub);margin-bottom:6px}
.pf-row textarea{width:100%;padding:var(--s-4) var(--s-5);font-size:13px;border-radius:var(--r-2);border:1px solid var(--line);
  background:var(--card);color:var(--ink);font-family:inherit;resize:vertical}
.pf-right{display:flex;justify-content:flex-end;align-items:center;gap:14px;flex-wrap:wrap}
.pf-total{font-size:14px;font-weight:800;color:var(--ink);font-variant-numeric:tabular-nums}
.pf-total .pf-big{font-size:22px;color:var(--blue);margin:0 3px}
/* 弹窗：⚠ 走 flex 居中 + 面板自身 max-height 滚动，⛔ 不用 transform 居中
   （祖先带 transform 会把 sticky 表头整体平移，本项目记过一次）。 */
.pf-ov{position:fixed;inset:0;background:rgba(8,14,24,.62);display:none;align-items:center;
  justify-content:center;z-index:120;padding:16px;overflow:auto}
.pf-ov.show{display:flex}
.pf-modal{background:var(--card);border:1px solid var(--line);border-radius:14px;
  width:min(640px,100%);max-height:88vh;overflow:auto;padding:18px 20px 16px;
  box-shadow:0 18px 50px rgba(0,0,0,.42)}
/* 宽幅变体：只给「打分」用（D212）。员工自评是页内整幅渲染的，负责人弹窗却挤在 640px 里
   ⇒ 同一张表（8 列）在两边列宽完全不同，负责人打分时看不全「工作内容分解」。
   ⚠ 只加在 id=perfScoreOv 那一处，⛔ 不改 .pf-modal 本体 —— 退回/确认那些小弹窗
     用 640px 正好，一刀切放大会让单行输入框拉成一整屏。 */
.pf-modal.pf-modal-wide{width:min(1180px,100%)}
/* D216：考评设置弹窗从 640 放宽到 820 —— 模板区一行里要放「指标名 + 权重 + 删除」，
   640 那点宽度正是原来每行折成两行的另一半原因。⛔ 不要套用 wide（1180 是给打分表的）。 */
.pf-modal.pf-modal-mid{width:min(820px,100%)}
.pf-mt{font-size:16px;font-weight:800;color:var(--ink);display:flex;justify-content:space-between;
  align-items:center;gap:10px}
.pf-mt .pf-x{cursor:pointer;color:var(--sub);font-weight:700;padding:2px 8px;border-radius:8px}
.pf-mt .pf-x:hover{background:var(--row-hover);color:var(--ink)}
.pf-msub{margin-top:6px;font-size:12.5px;color:var(--sub);line-height:1.75}
.pf-err{min-height:18px;font-size:12.5px;color:var(--red);margin-top:8px}
.pf-mfoot{display:flex;justify-content:flex-end;gap:10px;margin-top:14px;flex-wrap:wrap}
/* D246 危险区：与「保存设置」之间用一条实线分隔，视觉上先划清「这不是设置」 */
/* D249：年资门槛一行（数字 ＋ 单位 ＋ 截至日）。
   ⚠ flex-wrap 必须有：手机窄屏下「98px 输入 + 三行说明 + 日期框」挤不下，不换行会互相压字。 */
.pf-tenrow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.pf-tenrow input[type="number"]{width:88px;padding:var(--s-3) var(--s-5);font-size:13px;border-radius:var(--r-2);border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-tenrow input[type="date"]{padding:6px 10px;font-size:13px;border-radius:var(--r-2);border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-tenlab{font-size:12px;color:var(--sub)}
.pf-tensep{margin-left:10px;font-size:12px;color:var(--sub)}
.pf-tenrow input.bad{border-color:var(--red)}
/* 员工端「不参评」说明块：不是错误，是告知 —— 用中性底色，不用红色 ⚠ */
.pf-tenblock{margin:10px 0 4px;padding:14px 16px;border-radius:12px;border:1px solid var(--line);background:var(--bg);line-height:1.8}
.pf-tenblock b{color:var(--ink)}
.pf-tenblock .pf-tensub{color:var(--sub);font-size:12px;margin-top:6px}
/* 名单里的「新入职·不参评」小标签 */
.pf-tenchip{display:inline-block;margin-left:6px;padding:1px var(--s-3);border-radius:999px;font-size:11px;font-weight:700;background:#fff3e0;color:#b26a00;border:1px solid #ffd9a8;vertical-align:middle}
.pf-clearzone{margin-top:18px;padding-top:14px;border-top:1px dashed var(--line)}
.pf-clearzone-h{font-size:12.5px;font-weight:800;color:var(--red);letter-spacing:.04em;margin-bottom:6px}
/* ================== D221：负责人打分从「弹窗」改成「页」 ==================
   用户原话：「负责人打分这个弹窗能做成页面吗」。
   ⚠ 原来它是 position:fixed 的遮罩 + 1180px/88vh 的盒子内部滚动：五部分很长，
     看第一部分时看不见第五部分的小结，看小结时又看不见上面的分，滚动条还被锁在一个
     比屏幕矮一截的盒子里。现在它就是这个视图里的**一页**：
       · 跟着页面一起滚（⛔ 不再有自己的滚动条）；
       · 占满内容区宽度（不再被 1180px 卡住）；
       · 顶部一条「← 返回名册」，底部操作条**吸在视口底部**（长页面里也不用滚到底找提交）；
       · 名册在打开这一页时整块收起（⛔ 不是叠在下面 —— 两套内容叠着，用户会以为打分没生效）。
   ⚠ 开关仍然是 .show（⛔ 不改成一堆内联 display）：pfOpenScore / 提交 / 取消 / 用例
     全都按它判「开没开」，换开关等于把这些地方同时改一遍。
   ⚠ 内层仍保留 .pf-modal / .pf-modal-wide 两个类：字号、行距、.pf-mt 等样式都挂在这套结构上；
     这里只把它们「弹窗化」的那几条（固定宽、最大高、内滚、阴影、圆角、内边距）收掉。 */
#perfScoreOv{display:none;margin-top:6px;
  /* ⚠⚠ 顶栏是 position:sticky 的（高约 53px）。这一页在文档流里 ⇒ 滚到最上面时，
     它的标题行（含 ✕）正好落在顶栏背后：截图看着正常，但**点不到**（实测
     elementFromPoint 命中的是 HEADER，Playwright 判定「不可见」）。
     scroll-margin-top 让下面的 scrollIntoView 停在顶栏下面，而不是贴着视口最顶。 */
  scroll-margin-top:64px}
#perfScoreOv.show{display:block}
.pf-pagebar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  margin-bottom:12px;padding-bottom:10px;border-bottom:1px solid var(--line)}
.pf-pagebar .pf-dm{flex:1 1 240px;min-width:0}
#perfScoreOv .pf-modal{width:100%;max-height:none;overflow:visible;padding:0;
  border:none;border-radius:0;box-shadow:none;background:transparent}
#perfScoreOv #perfScoreDims{margin-top:10px}
/* ⚠⚠ ✕ 这颗要单独定位。第 749 行有一条历史规则 .pf-x{position:absolute;top:10px;right:12px}，
   它是给另一个弹窗（#pfClose）写的；在旧的打分层里，「最近的定位祖先」是 position:fixed 的遮罩，
   所以 ✕ 落在遮罩右上角。这一页没有遮罩了 ⇒ 它的定位祖先变成**初始包含块**，
   ✕ 会跑到整份文档的右上角（滚动后直接在视口外，实测 y=-176，Playwright 判定不可见、点不到）。
   ⇒ 把标题行设成定位上下文，✕ 就回到标题行右端。 */
#perfScoreOv .pf-mt{position:relative;padding-right:34px}
#perfScoreOv .pf-mt .pf-x{top:0;right:0}
#perfScoreOv .pf-mfoot{position:sticky;bottom:0;z-index:6;margin-top:18px;padding:12px 0 6px;
  background:var(--card);border-top:1px solid var(--line)}
/* 第五部分「必填」标（D221）：两栏都是必填，⛔ 不靠一句「必填」写在说明里 ——
   填的人看到的是框，框旁边没有标就等于没写。 */
.pf-req{font-size:11px;font-weight:800;color:var(--red);border:1px solid rgba(255,61,102,.45);
  border-radius:99px;padding:0 6px;margin-left:6px;vertical-align:1px}
.pf-row textarea.pf-p5miss{border-color:var(--red)}
.pf-admin{margin-top:16px}
.pf-adminbox{border:1px solid var(--line);border-radius:12px;background:var(--card-hd);
  padding:12px 14px;font-size:12.5px;color:var(--sub);line-height:1.85}
.pf-adminbox b{color:var(--ink)}
.pf-adminbox ul{margin:6px 0 0;padding-left:18px}
/* ---- 五部分自评 / 评分表单（D208，对齐《员工年终绩效考核评分表》） ---- */
.pf-part{margin-top:16px;border-top:1px dashed var(--line);padding-top:12px}
.pf-part:first-child{border-top:none;margin-top:0;padding-top:0}
.pf-ph{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-bottom:8px}
.pf-ph b{font-size:13.5px;color:var(--ink)}
.pf-pw{font-size:11px;font-weight:800;padding:1px var(--s-3);border-radius:99px;color:var(--blue);
  background:rgba(76,141,255,.12);border:1px solid rgba(76,141,255,.32);white-space:nowrap}
.pf-p1wrap{overflow-x:auto}
.pf-p1t{width:100%;border-collapse:collapse;min-width:780px}
.pf-p1t th{font-size:11px;color:var(--sub);text-align:left;padding:4px var(--s-2);font-weight:700;
  border-bottom:1px solid var(--line);white-space:nowrap}
.pf-p1t td{padding:4px var(--s-2);vertical-align:top;border-bottom:1px solid var(--line)}
.pf-p1t input,.pf-p1t textarea{width:100%;box-sizing:border-box;padding:var(--s-2) var(--s-3);font-size:12px;
  border-radius:var(--r-2);border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-p1t textarea{resize:vertical;line-height:1.5}
.pf-p1t input[data-p1k="weight"],.pf-p1t input[data-p1k="score"]{text-align:right}
.pf-p1foot{display:flex;align-items:center;gap:12px;margin-top:8px;flex-wrap:wrap}
.pf-p1del{padding:3px 8px;font-size:11px;color:var(--sub)}
.pf-p1ro td{font-size:12px;color:var(--ink);line-height:1.6}
.pf-p1ro input{width:74px}
/* D220：负责人打分界面的第一部分「照填写界面显示」。
   ⚠⚠ 全局 th,td 上的 white-space:nowrap（第 810 行）对纯文字单元格生效 ⇒
      「工作内容分解」那一列被撑到 3203px、整表 3725px，右侧三列全被推出屏幕。
      员工填写界面看不出这个问题，因为那里是 textarea，换行发生在控件内部。
   ⇒ 只对绩效模块这两处文字列放行换行（⛔ 不动全局规则，别的地方还要 nowrap）；
     文字保留员工输入时的换行（pre-wrap），否则他分好的条目会被挤成一段。 */
.pf-p1t.pf-p1ro{table-layout:fixed}
.pf-p1t.pf-p1ro td,.pf-fxt td.pf-desc{white-space:normal;word-break:break-word;overflow-wrap:anywhere}
.pf-rotext{white-space:pre-wrap;font-size:12px;line-height:1.6;color:var(--ink);min-height:20px}
.pf-p1t.pf-p1ro input[data-m1],.pf-p1t.pf-p1ro input[data-mo]{width:100%;text-align:right}
/* D234：负责人打分页里「工作内容分解 / 实际达成情况」两列由**只读**改成**可改**。
   用户原话：「这里选取后的内容不能修改编辑」「实际达成必须填写」。
   ⚠ 这两列是**事实描述**（这项工作具体做什么 / 实际做到什么程度），不是员工定的目标；
     负责人比员工更清楚实际发生了什么，而员工可能已经离职/调动，退回让他重填这条路走不通。
   ⚠ 有框、能拖高、聚焦有高亮 —— 一眼看出这格能写。旧的那套「边框隐形 + transparent」
     （textarea.pf-rotx）随这次改动退役：整份页面已经没有元素用它了，留着只会让下一个人
     以为「只读态还在某处生效」。
   ⚠ 宽度 100% + resize:vertical：横向一拖会把 fixed 表的列宽拖歪。
   ⚠ D221 那条「能拖高」（用户当时问的是这两个框）继续保留：resize 从 .pf-rotx 搬到这里。 */
.pf-p1t textarea.pf-mtx{width:100%;min-height:46px;resize:vertical;line-height:1.55;
  font-size:12px;color:var(--ink);background:var(--card);border:1px solid var(--line);
  border-radius:6px;padding:var(--s-2) var(--s-3);box-sizing:border-box}
.pf-p1t textarea.pf-mtx:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 2px rgba(60,120,255,.12)}
/* 补正过的格子留个淡黄底：负责人回头能一眼看出「这句是我改的」，
   员工申诉时也能对着看（与服务端 out.mgr.fix 的留痕是同一件事的两面）。 */
.pf-p1t textarea.pf-mtx[data-fixed="1"]{background:rgba(240,170,40,.16)}
/* 必填没填（实际达成情况） */
.pf-p1t textarea.pf-miss{border-color:var(--red)}
.pf-p1t textarea.pf-miss:focus{box-shadow:0 0 0 2px rgba(220,60,60,.14)}
/* D235：模板带出的「工作内容分解」锁成**真只读**。
   用户原话：「将这里选取后的内容加限制不能修改编辑，就是不允许编辑」——
   起因是员工会把按指标带出的整段文字改掉（李林林 SJ10285 连「MP量产：」标题行都删了），
   而这段是模板标准，不是个人自由发挥。
   ⚠ 与 D234 方向相反：那轮把两列都开了，这轮只把「分解」关回去；
     「实际达成情况」仍然可编辑 + 必填（那是事实，必须有人填）。
   ⚠ 视觉上必须**明说这是锁住的**（灰底 + 虚线边 + not-allowed 光标 + 无 resize），
     ⛔ 不要做成「看起来是输入框、点进去打不出字」——D214 那次就是栽在「长得像只读却又能改」。 */
.pf-p1t textarea.pf-lock{background:var(--bg);color:var(--sub);
  border-style:dashed;cursor:not-allowed;resize:none}
.pf-p1t textarea.pf-lock:focus{outline:none;border-color:var(--line);box-shadow:none}
.pf-dd{font-size:11px;color:var(--sub);font-weight:400;line-height:1.5;margin-top:2px}
.pf-fixrow{align-items:flex-start}
.pf-fixrow .pf-dl{flex:1 1 320px}
/* D216：弹窗里的每一「行」＝标题一行 + 内容一行。
   ⚠ 第 753 行那条历史规则 .pf-row{display:flex;gap:10px} 是给别的表单「一行并排两个输入框」
     （仓库 / 工伤那几处）用的，选择器没有作用域 ⇒ 落到考评弹窗里就把
     「标签 / 内容 / 说明」并成了三列：卡片被挤到 ~350px，行尾的「%」再被 .pf-iwrow 撑一下
     就整体折行。用户那句「显示不完整，也谈不上美观」的根就在这。
     ⛔ 不去改那条历史规则（别处在用），只在本项目的浮层范围内覆盖。 */
.pf-ov .pf-row,#perfScoreOv .pf-row{display:block}
/* D216：.pf-row input{flex:1}（第 754 行）同样没有作用域 ⇒ 会钻进 .pf-dimrow，
   把「等级线 / 部门负责人」那些本该 80–150px 的框拉成一大条。这里按原位收回。
   ⚠ D221：打分弹窗搬到页面上之后，它不再是 .pf-ov，所以这条要把 #perfScoreOv 一起写上 ——
     否则第四部分那两个扣分/加分框会在页面里被拉成整行宽（弹窗里看着正常、页面上歪掉，
     这类「换个容器就变形」的账本项目记过好几次）。 */
.pf-ov .pf-row .pf-dimrow input,#perfScoreOv .pf-row .pf-dimrow input{flex:0 0 auto}
/* D216：指标名平时按「文字」显示（只有悬停 / 聚焦才出现输入框边框），
   ⛔ 否则 14 个指标就是 14 个空框，看起来像一堆待填项，反而更乱。
   ⚠ 第 4101 行 body:not(.view-report) .pf-row input 会给它刷背景与边框色，所以这里要写死。 */
/* D218：覆盖规则必须连 border-style / border-width 一起写。
   ⚠ 第 4064 行那条 body:not(.view-report) input[type=text] 带 border:1px solid，
     特异性 (0,2,2)，比 .pf-iwrow input.pf-iwl 的 (0,2,1) 高 —— 只覆盖 border-color
     的话样式仍是它给的 solid，虚线底根本不出现（M07 就是这么红的）。
   这里用 (0,4,1) 把整条 border 拿回来。 */
.pf-ov .pf-row .pf-iwrow input.pf-iwl{border:1px dashed transparent;border-bottom-color:var(--line);background:transparent}
.pf-ov .pf-row .pf-iwrow input.pf-iwl:hover{border:1px solid var(--line);background:var(--card)}
.pf-ov .pf-row .pf-iwrow input.pf-iwl:focus{border:1px solid var(--line);background:var(--card)}
/* D216：「考评模板」区。⛔ 不再用一排 span + input 靠 flex 撑 ——
   原来 .pf-iwrow .pf-dm 的 flex:1 1 160px 把行尾那个「%」也当成标题、要了 160px 的底宽，
   ⇒ 每行都被挤成两行（用户截图：数字框一行、% 单独掉到下一行，9 项全是这样）。
   现在：名字吃满剩余宽度、分值固定宽、% 紧跟分值后面。 */
.pf-cfgpart{border:1px solid var(--line);border-radius:var(--r-3);background:var(--card-hd);
  padding:10px 12px;margin-bottom:10px}
.pf-cfghead{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.pf-cfghead input.pf-cfgname{flex:1 1 170px;min-width:0;padding:6px 10px;font-size:13px;font-weight:800;
  text-align:left;border-radius:8px;border:1px solid var(--line);background:var(--card);
  color:var(--ink);font-family:inherit}
.pf-cfgpw{display:flex;align-items:center;gap:var(--s-2);font-size:12px;color:var(--sub);white-space:nowrap}
.pf-cfgpw input{width:62px;padding:6px 8px;font-size:13px;text-align:right;border-radius:8px;
  border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-cfgmeta{font-size:12px;color:var(--sub);margin-top:4px;line-height:1.6}
.pf-cfgiw{margin-top:8px;border-top:1px dashed var(--line);padding-top:6px}
.pf-iwrow{display:flex;align-items:center;gap:var(--s-3);padding:2px 0}
.pf-iwrow input.pf-iwl{flex:1 1 auto;min-width:0;padding:var(--s-2) var(--s-3);font-size:12.5px;text-align:left;
  border-radius:var(--r-2);border:1px dashed transparent;border-bottom-color:var(--line);background:transparent;color:var(--ink);
  font-family:inherit;cursor:text}
.pf-iwrow input.pf-iwl:hover{border-color:var(--line);background:var(--card)}
.pf-iwrow input.pf-iwl:focus{outline:2px solid rgba(76,141,255,.45);outline-offset:1px;background:var(--card)}
.pf-iwrow .pf-iwv{flex:0 0 auto;display:flex;align-items:center;gap:3px}
.pf-iwrow .pf-iwv input{width:60px;padding:var(--s-2) var(--s-3);font-size:12.5px;text-align:right;border-radius:var(--r-2);
  border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-iwu{font-style:normal;font-size:11px;color:var(--sub)}
.pf-iwdel{flex:0 0 auto;border:0;background:transparent;color:var(--sub);cursor:pointer;
  font-size:12px;padding:2px 6px;border-radius:6px;line-height:1.2}
.pf-iwdel:hover{color:var(--red);background:rgba(240,90,90,.12)}
.pf-cfgsum{font-size:12px;color:var(--sub);margin-top:var(--s-2)}
.pf-cfgsum.bad{color:var(--red);font-weight:800}
.pf-cfgadd{margin-top:6px;font-size:12px;padding:3px 10px}
/* 第一部分候选指标（模板）：员工端「考核指标」那一格就是从这里取的 */
.pf-oprow{display:flex;align-items:center;gap:var(--s-3);padding:3px 0}
.pf-oprow input.pf-opn{flex:1 1 auto;min-width:0;padding:var(--s-2) 8px;font-size:12.5px;border-radius:var(--r-2);
  border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-oprow input.pf-opw{flex:0 0 auto;width:56px;padding:var(--s-2) var(--s-3);font-size:12.5px;text-align:right;
  border-radius:var(--r-2);border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-opd{flex:0 0 auto;border:1px solid var(--line);background:transparent;color:var(--sub);
  cursor:pointer;font-size:11px;padding:3px var(--s-3);border-radius:99px;white-space:nowrap}
.pf-opd:hover{color:var(--blue);border-color:rgba(76,141,255,.5)}
.pf-opdesc{margin:2px 0 8px 0}
.pf-opdesc textarea{width:100%;box-sizing:border-box;min-height:70px;padding:6px 8px;font-size:12px;
  line-height:1.55;border-radius:8px;border:1px solid var(--line);background:var(--card);
  color:var(--ink);font-family:inherit;resize:vertical}
/* ---- D218：模板全字段可改（用户原话「改模板我是需要所有内容都可以修改」）----
   ⚠ 上一版指标名是 input，但边框与底色全是透明的 —— 看上去就是一行普通文字，
     用户因此问「这里的内容我如何修改」。现在统一改成「虚线底」，hover 才亮出整框：
       · 看起来仍然像文字（不是一堆待填的空框），但一眼能看出是可点的输入位；
       · 与「权重」那种常显框区分开 —— 常显的是数字，虚线的是文字。 */
.pf-tplbar{font-size:12px;line-height:1.85;color:var(--ink);background:rgba(76,141,255,.07);
  border:1px solid rgba(76,141,255,.28);border-radius:var(--r-2);padding:8px var(--s-5);margin:0 0 10px}
.pf-tplbar b{color:var(--blue)}
.pf-cfgtag{flex:0 0 auto;font-size:11px;font-weight:800;color:var(--blue);
  background:rgba(76,141,255,.12);border-radius:99px;padding:2px 8px;white-space:nowrap}
.pf-cfgparams{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:var(--s-3);padding:var(--s-3) var(--s-4);
  border:1px dashed var(--line);border-radius:8px}
.pf-cfgparams label{display:flex;align-items:center;gap:4px;font-size:11px;color:var(--sub);white-space:nowrap}
.pf-cfgparams input{width:56px;padding:3px 6px;font-size:12px;text-align:right;border-radius:6px;
  border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-cfgnote{margin-top:var(--s-3)}
.pf-cfgnote input{width:100%;box-sizing:border-box;padding:var(--s-2) 8px;font-size:12px;border-radius:var(--r-2);
  border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-iwd{flex:0 0 auto;border:1px solid var(--line);background:transparent;color:var(--sub);
  cursor:pointer;font-size:11px;padding:2px var(--s-3);border-radius:99px;white-space:nowrap}
.pf-iwd:hover{color:var(--blue);border-color:rgba(76,141,255,.5)}
.pf-iwd.on{color:var(--blue);border-color:rgba(76,141,255,.5);background:rgba(76,141,255,.1)}
.pf-idesc{margin:0 0 var(--s-3) 2px}
.pf-idesc textarea{width:100%;box-sizing:border-box;min-height:64px;padding:6px var(--s-4);font-size:12px;
  line-height:1.6;border-radius:8px;border:1px solid var(--line);background:var(--card);
  color:var(--ink);font-family:inherit;resize:vertical}
/* 部分的「补充说明」（模板 part.note）：管理员填了才出现，留空时 pfPartNote 返回空串。 */
.pf-pnote{font-size:12px;color:var(--sub);line-height:1.7;margin:-2px 0 8px;padding-left:2px}
.pf-tplnote{font-size:12px;color:var(--sub);line-height:1.75;margin:0 0 6px}
/* ---- D210：按用户表补齐的版面 ---- */
.pf-p1sub{font-size:12px;font-weight:700;color:var(--ink);margin:12px 0 6px;
  padding-left:8px;border-left:3px solid var(--blue)}
.pf-fxt{width:100%;border-collapse:collapse;min-width:720px}
.pf-fxt th{font-size:11px;color:var(--sub);text-align:left;padding:var(--s-2);font-weight:700;
  border-bottom:1px solid var(--line);vertical-align:bottom}
.pf-fxt td{padding:var(--s-2);vertical-align:top;border-bottom:1px solid var(--line);font-size:12px}
.pf-fxt input{width:100%;box-sizing:border-box;padding:var(--s-2) var(--s-3);font-size:12px;text-align:right;
  border-radius:var(--r-2);border:1px solid var(--line);background:var(--card);color:var(--ink);font-family:inherit}
.pf-fxt tfoot td{border-top:2px solid var(--line);border-bottom:0;padding-top:var(--s-3)}
.pf-fxt .pf-desc{color:var(--sub);font-size:12px;line-height:1.6}
.pf-flv{color:var(--blue);font-weight:700;font-variant-numeric:tabular-nums;margin-left:8px}
/* 工作饱满程度：表里是三列（评分标准 / 自评 / 上级评价），D213 起照表还原 */
.pf-flowt td.pf-flb{color:var(--ink)}
.pf-flowt td.pf-fc{text-align:center}
.pf-flowt input[type=radio]{margin:0;cursor:pointer}
.pf-flowchk{color:var(--blue);font-weight:800;font-variant-numeric:tabular-nums;white-space:nowrap}
.pf-p1total{margin-top:10px;padding:8px 10px;border-radius:8px;background:rgba(76,141,255,.08);
  border:1px solid rgba(76,141,255,.25);font-size:12px;color:var(--ink)}
.pf-p1total b{font-variant-numeric:tabular-nums}
.pf-p1t td[data-p1gain],.pf-p1t td[data-ogain],.pf-p1t td[data-m1gain],.pf-p1t td[data-mogain]{
  text-align:right;font-variant-numeric:tabular-nums;color:var(--sub)}
/* D211：选中考核指标后自动带出的「工作内容分解」——左边一道细线，表示这段是表里给的、
   可以改（不是只读灰字）。员工一改写就还是同一个框，不引入额外状态。 */
textarea.pf-autofill{border-left:3px solid rgba(76,141,255,.7);background:rgba(76,141,255,.05)}
.pf-muted{color:var(--sub)}
/* D214：分数/权重超出范围时当场标红。值会被钳回边界，这道红只是为了让人看见「刚才被拦了」，
   ⛔ 不是「红着但不处理」—— 那样等于告诉他可以带着 1100 走下去。 */
.pf-p1t input.pf-bad,.pf-p1t select.pf-bad,.pf-dimrow input.pf-bad{outline:2px solid var(--red);outline-offset:1px}
/* D214：日常工作补充的权重照模板做成下拉（0/5/10/15/20），⛔ 不再是自由数字 */
.pf-p1t select.pf-outw{width:100%;box-sizing:border-box;padding:var(--s-2) 6px;font-size:12px;
  border-radius:6px;border:1px solid var(--line);background:var(--card);color:var(--ink)}
/* D214：只读块（第四部分加减分 / 上级评价回显）——虚线框，形态上就不是输入框 */
.pf-ro{padding:10px 12px;border-radius:var(--r-2);border:1px dashed var(--line);background:var(--card-hd);
  font-size:12.5px;color:var(--sub);line-height:1.8}
.pf-ro b{color:var(--ink)}
/* D214：汇总表 —— 照用户表底部那张（考核部分 / 考核项目 / 考核项得分 / 权重 / 个人考核分数 / 等级） */
.pf-sum{margin-top:14px}
.pf-sum table{width:100%;border-collapse:collapse;font-size:12.5px;min-width:660px}
.pf-sum th,.pf-sum td{border:1px solid var(--line);padding:var(--s-3) 10px;text-align:center;color:var(--ink)}
.pf-sum th{background:var(--card-hd);color:var(--sub);font-weight:700;font-size:12px}
.pf-sum td.pf-sumlab{text-align:left;background:var(--card-hd);color:var(--sub);font-weight:700;
  width:112px;white-space:nowrap}
.pf-sum .pf-sumbig{font-size:15px;font-weight:800;color:var(--blue);font-variant-numeric:tabular-nums}
.pf-sum .pf-sumnote{text-align:left;color:var(--ink);line-height:1.7;font-size:12.5px}
.pf-sum .pf-tag{font-size:11px;font-weight:700;padding:1px 6px;border-radius:99px;
  color:var(--blue);background:rgba(76,141,255,.12);margin-left:6px}
/* D217：第五部分底部的「评分说明」—— 模板最后那段口径话。
   ⚠ 这段话里的权重与等级线由 pfP5RuleHtml 从 config 现算，⛔ 不是写死在 CSS 里的死字；
     这里只管版式：与上面的汇总表拉开 12px、整块淡底、行距放宽到 1.85（三行字挤在一起没人读）。 */
.pf-p5rule{margin-top:12px;padding:10px 12px;border:1px solid var(--line);border-radius:10px;
  background:var(--card-hd);font-size:12px;line-height:1.85;color:var(--sub)}
.pf-p5rule>b{display:block;color:var(--ink);margin-bottom:3px}
.pf-p5 .pf-sum{margin-top:12px}
/* D217：自评页里第五部分两栏是「左标签 + 右大框」（照模板）。
   但第 753 行那条没有作用域的 .pf-row{display:flex} 会把只有 4 个字的标签压成
   「个人优 / 势」两行 —— 这里把标签宽度钉住并禁止折行。
   ⚠ 弹窗里 .pf-ov .pf-row 已经是 display:block（上下布局），此时宽度自适应，不受影响。 */
.pf-p5 .pf-row>label{flex:0 0 88px;white-space:nowrap}
/* ── D257 · 系统状态胶囊与面板（仅管理员可见） ──────────────────────────
   放右下角：避开左侧导航（fixed left）与仓库页右侧滚动条（right:5px 中部）。
   ⚠ 用 class 控制显隐而不是 [hidden]：面板要 display:flex，hidden 会被 display 覆盖，
     症状是「隐藏了但还在」——这是本项目已经踩过一次的坑（容器只隐藏=坏着）。 */
.sys-pill{display:none;position:fixed;right:14px;bottom:14px;z-index:95;align-items:center;gap:var(--s-3);
  padding:6px 12px;border-radius:999px;background:var(--pure);border:1px solid #d7dde8;
  box-shadow:0 4px 14px rgba(15,35,70,.13);font-size:12px;color:#475569;cursor:pointer;
  user-select:none;line-height:1.5;max-width:calc(100vw - 28px)}
.sys-pill.show{display:flex}
.sys-pill:hover{border-color:#b9c6d9;box-shadow:0 6px 18px rgba(15,35,70,.18)}
.sys-dot{width:8px;height:8px;border-radius:50%;background:#16a34a;flex:none;box-shadow:0 0 0 3px rgba(22,163,74,.15)}
.sys-pill.warn .sys-dot{background:#f59e0b;box-shadow:0 0 0 3px rgba(245,158,11,.18)}
.sys-pill.bad .sys-dot{background:#dc2626;box-shadow:0 0 0 3px rgba(220,38,38,.18)}
.sys-pill span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sys-panel{display:none;position:fixed;right:14px;bottom:54px;z-index:96;
  width:min(430px,calc(100vw - 28px));max-height:min(72vh,580px);overflow:auto;
  background:var(--pure);border:1px solid #d7dde8;border-radius:14px;
  box-shadow:0 18px 44px rgba(15,35,70,.20);padding:14px var(--s-8) 12px;font-size:12px;color:#334155}
.sys-panel.show{display:block}
.sys-panel h4{margin:0 0 2px;font-size:13px;color:#1e293b;display:flex;align-items:center;justify-content:space-between;gap:8px}
.sys-panel .sys-sub{color:var(--muted);font-size:12px;margin-bottom:10px}
.sys-close{border:0;background:transparent;font-size:16px;line-height:1;color:#94a3b8;cursor:pointer;padding:0 2px}
.sys-close:hover{color:#475569}
.sys-sec{border-top:1px dashed #e3e8f0;padding-top:8px;margin-top:8px}
.sys-sec:first-of-type{border-top:0;padding-top:0;margin-top:0}
.sys-sec-t{font-size:12px;color:var(--muted);margin-bottom:var(--s-2);letter-spacing:.3px}
.sys-kv{display:grid;grid-template-columns:repeat(auto-fit,minmax(96px,1fr));gap:6px 10px}
.sys-kv > div{background:#f8fafc;border:1px solid #eef2f7;border-radius:8px;padding:var(--s-2) 8px;min-width:0}
.sys-kv b{display:block;font-size:14px;color:#1e293b;font-variant-numeric:tabular-nums;line-height:1.35}
.sys-kv i{display:block;font-style:normal;font-size:11px;color:var(--muted);margin-top:1px}
.sys-kv b.bad{color:#dc2626}
.sys-kv b.warn{color:#d97706}
.sys-list{margin:0;padding:0;list-style:none}
.sys-list li{display:flex;gap:8px;align-items:baseline;padding:3px 0;border-bottom:1px solid #f4f7fb;font-size:12px}
.sys-list li:last-child{border-bottom:0}
.sys-list .p{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#475569;font-family:ui-monospace,Menlo,Consolas,monospace}
.sys-list .v{flex:none;color:#334155;font-variant-numeric:tabular-nums}
.sys-list .v.bad{color:#dc2626;font-weight:600}
.sys-note{font-size:12px;color:var(--muted);margin-top:8px;line-height:1.55}
.sys-tip{background:#fff7ed;border:1px solid #fed7aa;color:#9a3412;border-radius:8px;padding:6px var(--s-4);font-size:12px;margin-bottom:8px;line-height:1.5}
@media (max-width:520px){
  .sys-pill{right:10px;bottom:10px;padding:var(--s-2) 10px;font-size:12px}
  .sys-panel{right:10px;left:10px;width:auto;bottom:48px;max-height:70vh}
}

/* 深色主题适配：本页默认深色底，白面板会「刺眼且不统一」。
   ⚠ 不写死成深色：页面若切浅色，深面板同样难看。由 JS 读实际背景色决定挂不挂 .sys-dark。 */
.sys-pill.sys-dark{background:#1e293b;border-color:#334155;color:#cbd5e1;box-shadow:0 4px 14px rgba(0,0,0,.35)}
.sys-pill.sys-dark:hover{border-color:#475569;box-shadow:0 6px 18px rgba(0,0,0,.45)}
.sys-panel.sys-dark{background:#111c2e;border-color:#334155;color:#cbd5e1;box-shadow:0 18px 44px rgba(0,0,0,.55)}
.sys-panel.sys-dark h4{color:#e2e8f0}
.sys-panel.sys-dark .sys-sub{color:#7f8da3}
.sys-panel.sys-dark .sys-close{color:#64748b}
.sys-panel.sys-dark .sys-close:hover{color:#cbd5e1}
.sys-panel.sys-dark .sys-sec{border-top-color:#22314a}
.sys-panel.sys-dark .sys-sec-t{color:#7f8da3}
.sys-panel.sys-dark .sys-kv > div{background:#182437;border-color:#22314a}
.sys-panel.sys-dark .sys-kv b{color:#e2e8f0}
.sys-panel.sys-dark .sys-kv i{color:#7f8da3}
.sys-panel.sys-dark .sys-kv b.bad{color:#f87171}
.sys-panel.sys-dark .sys-kv b.warn{color:#fbbf24}
.sys-panel.sys-dark .sys-list li{border-bottom-color:#1b2942}
.sys-panel.sys-dark .sys-list .p{color:#94a3b8}
.sys-panel.sys-dark .sys-list .v{color:#cbd5e1}
.sys-panel.sys-dark .sys-list .v.bad{color:#f87171}
.sys-panel.sys-dark .sys-note{color:#7f8da3}
.sys-panel.sys-dark .sys-tip{background:#3b1d0a;border-color:#7c3a12;color:#fdba74}

/* ── D292 · 「已手动添加 N 条记录」提示的文字色 ─────────────────────────────────
   原先写死内联 「color:#d9483b」：深色主题下它压在 #24201C 的提示条上只有 **3.81:1**
   （AA 要 4.5），而在浅色底上又必须用深红才够 —— 一个值不可能两头都对，所以按主题分开。
   基础层＝浅色 / 打印（深红，浅底 4.90:1）；深色层覆盖成亮红（深底 6.37:1）。
   ⚠ D368 修（check-staffoutsfilter-178 A6）：① A6 原本防「本段 CSS 里混进反引号」——样式表内出现反引号
     会让整段 CSS 在 HTML 里变成非法字符（本项目铁律，check-ledger-ver.js 1.12 同款判据）；
     ② 此处 6281 行原本用两个反引号包住 color:#d9483b，属于纯注释里的引用；
     ③ 判据一字未改，仍量「本段反引号数量为 0」，只是把引用符号换成中文引号「」。 */
.imp-manual-note{color:var(--red-fg)}
@media screen{ body:not(.view-report) .imp-manual-note{color:#FF7A6E} }

/* ══ D291 · 高对比档（只提前景，不动底色） ═════════════════════════════════════
   触发：① body 加 .hc（留给将来的手动开关）；② 系统「提高对比度」自动生效。
   ⛔ 不换底色令牌：深色层 244 处 background 里 124 处是组件硬编码（不走 --card/--bg），
   换底色只会得到「页面变亮、卡片没变」的斑驳 ⇒ 这一层只动文字与描边。
   对比度（以 #0C1118 为底）：说明文字 8.98 → 12.99:1；次要文字 7.02 → 10.82:1。 */
@media screen{
  body:not(.view-report).hc{
    --ink:#F5F9FE; --ink-2:#DEE7F3; --sub:#CBD7E7;
    --muted:#B7C5D8; --disabled:#74869C;
    --line:#3E5166; --stroke:#56708C; --stroke-strong:#7A93B0;
    /* D301：-fg 也是「前景」，高对比档必须跟着提亮，否则批二把文字挂到 -fg 以后
       高对比用户拿到的只是普通深色值 —— 这一档就白设了。
       判据：对 --card #131A24 ≥ 11:1（原值 8.12~10.95:1），色相不动、只向白提亮。 */
    --ink-head:var(--ink); --pure:#FFFFFF; --rpt-muted:#8E9FB6;
    --blue-fg:#B4CFFF; --green-fg:#7FE1B2; --orange-fg:#FFC27B;
    --red-fg:#FFBCCB;  --purple-fg:#D2C7FD;
  }
  /* 「系统状态」面板自成一套硬编码深色（.sys-dark），令牌管不到 ⇒ 单独补两行 */
  body:not(.view-report).hc .sys-panel.sys-dark .sys-note{color:var(--muted)}
  body:not(.view-report).hc .sys-panel.sys-dark .sys-kv i{color:var(--muted)}
}
@media screen and (prefers-contrast: more){
  body:not(.view-report){
    --ink:#F5F9FE; --ink-2:#DEE7F3; --sub:#CBD7E7;
    --muted:#B7C5D8; --disabled:#74869C;
    --line:#3E5166; --stroke:#56708C; --stroke-strong:#7A93B0;
    /* D301：与 .hc 同一套提亮值（两条触发路径必须给同一个结果，否则
       「手动开高对比」和「系统开高对比」会显示成两种颜色）。 */
    --ink-head:var(--ink); --pure:#FFFFFF; --rpt-muted:#8E9FB6;
    --blue-fg:#B4CFFF; --green-fg:#7FE1B2; --orange-fg:#FFC27B;
    --red-fg:#FFBCCB;  --purple-fg:#D2C7FD;
  }
  body:not(.view-report) .sys-panel.sys-dark .sys-note{color:var(--muted)}
  body:not(.view-report) .sys-panel.sys-dark .sys-kv i{color:var(--muted)}
}

/* ══ D298 · 交互反馈与动效（UI/UX 审计落地，5 条） ═══════════════════════════
   审计读数（改前）：:hover 174 处 / :active 仅 5 处；手机 40 个可点目标里 33 个小于 44×44；
   34 个 transition 全挂在按钮/下拉/卡片上，**页级切换零过渡**；
   KPI 副卡在窄屏可点展开但没有任何视觉提示（功能存在、没人知道）。
   这四条是「用户能感觉到」的部分，所以先做。 */

/* ① 按压态。全站只有 5 处 :active，点下去没有任何「按到了」的即时反馈。
   ⚠ 只动 transform / filter / box-shadow —— 不碰 width/height/margin，不触发重排，
     CLS 不受影响（本页 CLS 有硬阈值，见 check-perf-mobile）。
   ⚠ 用 :active 而不是 JS：键盘 Space 激活按钮时会先经过 :active，天然覆盖键盘用户。 */
.btn:active,.viewall-btn:active{transform:translateY(1px);box-shadow:none;filter:brightness(.94)}
.sb-item:active{transform:translateY(1px)}
.pjt-ft:active{transform:translateY(1px);background:rgba(128,140,160,.14)}
.expbtn:active{transform:translateY(1px);filter:brightness(1.15)}
.btn:active:disabled,.btn:disabled:active{transform:none;filter:none}

/* ② 触控热区。⚠ 这里刻意**不改视觉尺寸**：改 min-height 会把首屏再推长（D293 刚省下 200px），
   所以用伪元素把「可点区域」撑到 44px，画的还是原来那么大。
   ⚠ 只在小屏生效 —— 桌面上鼠标精度高，热区大会抢相邻元素的点击（尤其图表右上角与标题挨着）。 */
@media(max-width:640px){
  .expbtn::after{content:'';position:absolute;left:-10px;right:-10px;top:50%;
    transform:translateY(-50%);height:44px;min-height:100%}
  .pjt-ft::after{content:'';position:absolute;left:0;right:0;top:50%;
    transform:translateY(-50%);height:40px;min-height:100%}
  .pjt-ft{position:relative}
  /* select 是替换元素，伪元素不渲染 ⇒ 只能抬高度。26 → 34，筛选行整体 +8px，
     比「6 个筛选器每个都点不准」划算。 */
  .tb-filter select,.wkt-ctl select,#toolbar select,.oss-ctl select{min-height:34px}
}

/* ③ 页级淡入 —— **已存在，别再加一份**。
   本项目在 switchView 尾部已有 .wrap.view-enter + @keyframes viewFade（.2s，opacity .45→1，
   见样式表 3540 行附近），并且严格遵守「只做 opacity、不动 transform」——
   .wrap 里有仓库表 sticky 表头，祖先一 transform 就会把它一起推下去（2026-09-18 实测少一整行）。
   ⚠ 审计第一版我按 transition 数量判断「页级切换无过渡」，漏看了 animation —— 结论是错的。
     判断动效要同时数 transition 和 @keyframes，只看前者会把已有实现当成缺失。 */

/* ④ KPI 副卡「可展开」的视觉提示。D293 把窄屏副标题压成单行省略省了 45px，
   同时做了点击展开（document 上的事件委托），但**没有任何提示** ——
   用户只看到省略号，不会想到去点。
   .s-clip 由 markKpiClip() 按「真的被截断了」判（scrollWidth > clientWidth），不是无脑加；
   展开后角标翻成 ⌃，再点收起。 */
@media(max-width:640px){
  .kpi.s-clip:not(.pri){position:relative}
  .kpi.s-clip:not(.pri)::after{content:'⋯';position:absolute;right:7px;bottom:5px;
    font-size:12px;line-height:1;color:var(--sub);opacity:.75;pointer-events:none}
  /* ⚠ 特异性必须 ≥ 上面那条：.kpi.s-clip:not(.pri)::after 是 (0,3,1)，
     写成 .kpi.s-open::after (0,2,1) 会被压住 —— 实测点击后 class 加了、文字也放开了，
     但角标还是 ⋯（check-uiux-298 的 2.5 抓到的就是这个）。 */
  .kpi.s-clip.s-open:not(.pri)::after{content:'⌃';opacity:.6}
}

/* ================= D377 · 手机 APP 化 A 层（纯新增，不改任何既有行） =================
   ① 底部 Tab 用独立 .hn-tab，⛔ 不复用 .sb-item：13 个守卫用
      .sb-item[data-view=...] / .sb-item[data-act="manual"] 做严格定位器，
      复制一份会让 Playwright strict mode 撞成两个匹配（假红）。
   ② 显形写死 body.authed：boot-gate / gate-locked / guest-mode / 登录页
      全都没有 .authed，一条规则全覆盖，不必逐个加隐藏。
   ③ 颜色走 var()/rgba，间距走 var() 与 calc()，动效只用标尺内 150ms + ease
      ⇒ 棘轮门禁（866/171/30/10）零违规增量。
   ④ 本批只做骨架；弹窗底部抽屉会改到 ~75 个弹窗的 align-items，留给 B 层单独验。 */
.hn-tabbar{display:none}
@media(max-width:640px){
  body.authed .hn-tabbar{
    position:fixed;left:0;right:0;bottom:0;z-index:8888;
    display:flex;align-items:stretch;justify-content:space-around;
    width:100%;max-width:100%;box-sizing:border-box;
    background:var(--card);border-top:1px solid var(--line);
    padding-bottom:env(safe-area-inset-bottom);
    box-shadow:0 -2px 12px rgba(15,30,60,.10)}
  .hn-tab{
    flex:1 1 0;min-width:0;min-height:56px;
    display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:var(--s-1);
    border:0;background:transparent;color:var(--sub);
    font:inherit;font-size:11px;line-height:1.2;text-align:center;
    -webkit-tap-highlight-color:transparent;cursor:pointer;
    transition:opacity 150ms ease}
  .hn-tab .hn-ico{font-size:19px;line-height:1}
  .hn-tab .hn-lbl{white-space:nowrap}
  .hn-tab:active{opacity:.55}
  .hn-tab.on{color:var(--blue);font-weight:700}
  /* 顶部横向胶囊：触控目标补到 44px（原来约 32px），并给按压反馈 */
  .sidebar .sb-item{min-height:44px;-webkit-tap-highlight-color:transparent;transition:opacity 150ms ease}
  .sidebar .sb-item:active{opacity:.6}
  .viewall-btn,#tableEditBtn{min-height:44px}
  /* 内容让位：底部 Tab 56px + 安全区 + 余量 */
  body.authed .wrap{padding-bottom:calc(72px + env(safe-area-inset-bottom))}
  /* 既有底部悬浮条让出 Tab 高度，避免叠在一起 */
  #unsavedBar,#empBatchBar{bottom:calc(64px + env(safe-area-inset-bottom))}
  .hn-toast-host{bottom:calc(76px + env(safe-area-inset-bottom))}
}

/* D378 / B 层 A 档：手机端常规表单弹窗改底部抽屉。纯追加，不改写任何既有规则。
   颜色走 var 与 rgba，间距走 var 与 calc，圆角走令牌；只用标尺内时长。
   手册浮层、确认框、登录浮层保持原样，不参与抽屉化。 */
@media(max-width:640px){
  .modal:not(#loginModal),.modal-overlay,.pwchange-overlay,.pf-ov{
    align-items:flex-end;padding:0;z-index:9500
  }
  .modal:not(#loginModal) .modalbox,
  .modal-overlay .modalbox,
  .modal-overlay .modal-box,
  .pwchange-overlay .pwchange-box,
  .pf-ov .pf-modal{
    width:100%;max-width:100%;
    max-height:calc(100dvh - var(--s-4));
    border-radius:var(--r-4) var(--r-4) 0 0;
    padding-bottom:calc(var(--s-4) + env(safe-area-inset-bottom));
    box-shadow:0 -2px 12px rgba(15,30,60,.18)
  }
}
