/* 希捷 ERP —— 样式全部走变量 (§8)
   字号 5 级 / 圆角 4 级 / 断点 1024 920 720
   金额显示: ¥ + 千分位 + 2 位小数
*/
:root{
  /* ★ 2026-09-16 换肤：对齐百草商贸通（色值从本机百草 swf + 官网截图提取）
     ★★ 2026-09-26（张总「界面美化对标百草」P0）：中性色换成**百草真机取样**的暖色系 ——
        内容底 #F3F1E9（暖米）、分组灰字 #999999、顶栏 #373737。
        ★ 只动"底/描边/文字"这些中性色：**主色与顶栏仍由官网皮肤注入**
          （张总拍板：ERP 继续跟随官网皮肤，这一条不学百草）。 */
  --bg:#F3F1E9;              /* 主背景 暖米（百草真机取样） */
  --panel:#ffffff;           /* 卡片 / 面板 */
  --panel-2:#FAF7F0;         /* 表头 / 悬停底（暖白） */
  --line:#E6E0D2;            /* 描边 暖灰（跟着暖底走，冷蓝灰会显脏） */
  /* ★★ 2026-10-17（界面美化 2.0 · P2-a）：**补一档更深的线**。
     起因是一次真 bug：我给看板图表写了 `background:var(--line-strong)`，
     而本文件**从来没定义过这个令牌** ⇒ 声明在计算值阶段失效、回落到初始值
     （`transparent`）—— 0 值灰条与日均虚线**整条看不见**，页面照样 200、判据全绿。
     ⇒ 补上定义（值取暖灰更深一档，与 `--line` 同色系），并作为"图表/分隔"用的线。 */
  --line-strong:#CFC8B4;     /* 图表基线 / 0 值柱 / 更明确的分隔 */
  --ink:#373737;             /* 主文字（百草真机取样） */
  --ink-2:#5c6670;           /* 次要文字 */
  --ink-3:#95a0ab;           /* 弱文字 */
  --brand:#3da7ff;           /* 品牌 / 选中态 亮天蓝（被官网皮肤覆盖，见上） */
  --brand-dk:#2d8fe0;        /* 按下 / 悬停 深蓝 */
  --brand-ink:#ffffff;       /* 品牌上的文字 */
  --brand-soft:#e6f4ff;      /* 选中浅底 */
  --ok:#5cb85c;              /* 成功 / 库存充足 */
  --warn:#f5a623;            /* 预警 / 低于安全库存 */
  --danger:#ff4d4f;          /* 逾期应收 / 盘亏 / 负毛利 */
  --topbar:#2f2f2f;          /* 顶部栏深灰（百草同款；实际值由皮肤注入） */
  --topbar-ink:#e8edf3;      /* 顶部栏文字 */
  --topbar-ink-2:#9aa5b1;    /* 顶部栏弱文字 */
  --fs-1:20px; --fs-2:16px; --fs-3:14px; --fs-4:13px; --fs-5:12px;
  /* ★★ 2026-10-17（张总「界面美化 2.0 · 超越百草」拍板 A 案「温润纸感」，方案
     `02_方案…\20261017_方案_界面美化2.0_超越百草.md` §8.1）：
     圆角与阴影**各放大/调柔一档**，是**刻意离开百草**的第一步（百草更"方"、阴影更硬）。
       · 卡片 `--r-3` 6→**8**：原来那句"百草的卡片比我们方一点"是上一轮**对齐百草**的取舍，
         本轮张总改口径为"可以超过它" ⇒ 这一档**主动学回现代的圆**；
       · 弹窗 `--r-4` 12→**14**；按钮/输入 `--r-2` 6px 不动（控件要"利落"，圆角越大越糊）；
       · 阴影换成**更淡、更散**的一档（暖色调 `rgba(60,50,30,…)`，不换色系）。
     ★ 只动令牌值，**不动任何选择器、不动布局** ⇒ 结构零变化（改前/改后逐页真浏览器比对）。
     ★ 判据 `tests/test_ui_baicao_p0.py` 那条「圆角收到 6px」已同步改成 8px 并写明理由。 */
  --r-1:4px; --r-2:6px; --r-3:8px; --r-4:14px;
  --sp-1:var(--uik-sp-1, 4px); --sp-2:var(--uik-sp-2, 8px); --sp-3:var(--uik-sp-3, 12px); --sp-4:var(--uik-sp-4, 16px); --sp-5:var(--uik-sp-5, 24px); --sp-6:var(--uik-sp-6, 32px);
  /* ★★ 2026-09-29（张总「全部 字段拥挤   对不齐的都优化掉」）：
     一行控件的**统一口径**。为什么必须有一个 token，而不是各处调 padding ——
     本文件原来**没有任何一处**定义"一个控件有多高"：高度是 padding 加上各自
     的行高碰出来的（`input/select` 走浏览器默认的 line-height:normal，
     `button` / `a.btnlike` 继承 body 的 1.6）。于是同一行里实测出五种高度：
       input 27.0 · select 29.0 · button 31.0 · a.btnlike 34.8 · 快捷药丸 27.2
     （1440/1280/1024 三个宽度上真浏览器量出来的实数，不是估的）。
     现在所有控件都吃 --ctl-h，所有相邻间距都吃 --ctl-gap，一处改全站改。 */
  --ctl-h:32px; --ctl-gap:8px;
  /* ★★ 2026-10-17（界面美化 2.0 · P0-a）：阴影分三档、全部换暖色且**更淡更散**，
     外加**动效令牌**（本仓原来没有任何时长/缓动令牌：全站 23 处 transition 各写各的
     `.15s ease`、`.2s`、`.25s`，同一个"移上去"在不同页手感不同）。
       --sh-1 卡片常态 · --sh-2 悬停/下拉 · --sh-3 弹窗
       --dur-1 按压/悬停 · --dur-2 展开/切页 · --dur-3 弹窗
     ★ 值与既有那两个**同一色系**（rgba(60,50,30,…)），只是变淡变散 ⇒ 不换观感基调。 */
  --sh-1:0 1px 2px rgba(60,50,30,.05);
  --sh-2:0 6px 18px rgba(60,50,30,.09);
  --sh-3:0 16px 40px rgba(60,50,30,.14);
  --dur-1:var(--uik-dur-1, 120ms); --dur-2:var(--uik-dur-2, 180ms); --dur-3:var(--uik-dur-3, 260ms);
  --ease:var(--uik-ease, cubic-bezier(.2,.7,.3,1));
}
*{box-sizing:border-box}
/* ★ [hidden] 必须真的隐藏。
   作者样式里的 display:flex/block 会盖掉浏览器默认样式表的 [hidden]{display:none}
   （.gate 就踩过这个坑：开单页上空的「门禁条」变成一根细黄条）。
   这一条是全局兜底，别再逐类写 .xxx[hidden]。 2026-09-18 */
[hidden]{display:none!important}
body{margin:0;background:var(--bg);color:var(--ink);
  font:var(--fs-3)/1.6 "Microsoft YaHei","Segoe UI",system-ui,sans-serif}
a{color:var(--brand);text-decoration:none}
a:hover{text-decoration:underline}
/* ★★ 2026-10-17（界面美化 2.0 · P0-a）：**全站统一焦点环**。
   改前：`:focus-visible` 全站只有 4 处 ⇒ 键盘 Tab 走一遍，多数控件"没有焦点提示"
   （而本仓 `static/js/keynav.js` 是支持 ↑↓ 选行、回车打开的 ⇒ 确实有人这么用）。
   改后：一条全局兜底；`focus-visible` **只对键盘导航生效**，鼠标点击不出现
   ⇒ 鼠标用户的观感零变化（不影响任何"点一下会渲染东西"的判据）。 */
:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

.top{height:52px;background:var(--topbar);color:var(--topbar-ink);
  border-bottom:none;box-shadow:var(--sh-1);
  display:flex;align-items:center;gap:var(--sp-4);padding:0 var(--sp-5);position:sticky;top:0;z-index:9}
/* ★★ 2026-10-18（第 1588 批）：品牌名**不许折行** —— 原来它可折，于是顶栏一挤就把
   「优秀者工作台」排成 6 行竖排（实测 721–1179 都这样，logo 高 154px、`display:flex`
   让 24px 的 logo 图跟着拉高）。产品名被拆成竖排字是**最难看的一种坏**，而且它不报错、
   HTML 也没坏 —— 只有真浏览器量高度才看得见。
   ★ 代价（如实记）：它从"可折到 24px"变成"最少 110px"，顶栏最小宽因此 +86px。
     所以同一批在 ≤1179 让搜索框能缩到 0（见下面「顶栏的中间空档」那段），两处一起来。
   判据：`tests/test_viewport_bands.py` 判据 [2]（logo 高 ≤ 同一页 1440 基线 ×1.5）。 */
.top .logo{font-size:var(--fs-2);font-weight:600;letter-spacing:.5px;color:#fff;
  white-space:nowrap}
.top .logo small{font-size:var(--fs-5);color:var(--topbar-ink-2);font-weight:400;margin-left:var(--sp-2)}
.top .spacer{flex:1}
.top .who{font-size:var(--fs-4);color:var(--topbar-ink-2)}
.top .who b{color:#fff}
.top .role{display:inline-block;padding:2px var(--sp-2);border-radius:var(--r-1);
  background:rgba(255,255,255,.16);color:#fff;font-size:var(--fs-5);margin-left:var(--sp-2)}
.top a{color:var(--topbar-ink-2)}
/* ★★ 2026-10-16（第 1285 批 · 桌面端按族收口 · **一处顶 54 页**）：
   顶栏那颗「退出」原来没有类、热区只有 **28×22**；而它长在**每一页**上
   （扫描器：`parent=top` **55 处 / 54 页**，桌面端最大的「可真改」族）。
   ⇒ 撑到 **≥24×24**（★ 桌面用**指针**口径 WCAG 2.2 AA「Target Size (Minimum)」；
     不是触屏的 44 —— 那个口径套到桌面会假红一片，理由见台账 §1874）。 */
/* ★★ 2026-10-18（第 1511 批 · 375 宽真截图抓到）：这颗「退出」在 375 宽下
   **逐字换行**（「退」上「出」下）—— inline-flex 容器被挤小时文字会换行。
   ⇒ 加 white-space:nowrap（两个字不该被拆开）；热区与高度一个字不动。 */
.top a.quit{display:inline-flex;align-items:center;min-height:24px;padding:0 var(--sp-2);white-space:nowrap}
.top a:hover{color:#fff}
.top .topnav{padding:4px var(--sp-3);border-radius:var(--r-1);font-size:var(--fs-4);
  text-decoration:none;margin-left:var(--sp-3)}
.top .topnav:hover{background:rgba(255,255,255,.12)}
.top .topnav.on{background:rgba(255,255,255,.2);color:#fff;font-weight:600}
/* ★★★ 2026-10-13（第 1039 批 · 张总「后台活摆到设置齿轮图标按钮里面 不要影响大局
   可以参考同行业美观度的放置方法」）：顶栏右上那颗**设置齿轮** ＋ 它的小面板。
   · 零 JS：`<details>/<summary>` 原生开关（同行业后台那种"点齿轮弹一小片设置入口"）；
   · 面板**绝对定位、右边贴齐**、盖在内容之上 ⇒ 顶栏高度与排布一个像素都不动（"不影响大局"）；
   · 条目真源是 `settings_entries(role)`（`erp/app.py`，桌面与手机共用一份），
     面板里每一项都过权限、没落点的渲染成灰字（`.gear-it.off`，**不是链接**）；
   · **刻意不带 `.topnav` 类**：那一套在 ≤720px 会被媒体查询收起，而齿轮手机上也要用
     （手机顶栏另有一颗同源的，见 `m/base.html` 的 `.mgear`）。 */
.top .gear{position:relative;margin-left:var(--sp-3)}
.top .gear>summary{list-style:none;cursor:pointer;display:flex;align-items:center;
  padding:4px 6px;border-radius:var(--r-1);color:var(--topbar-ink-2)}
.top .gear>summary::-webkit-details-marker{display:none}
.top .gear>summary:hover,.top .gear[open]>summary{background:rgba(255,255,255,.12);color:#fff}
.top .gear>summary .ic{width:18px;height:18px}
.top .gear-pop{position:absolute;top:calc(100% + 8px);right:0;z-index:20;width:290px;
  background:var(--panel);color:var(--ink);border:1px solid var(--line);
  border-radius:var(--r-3);box-shadow:var(--sh-2);padding:var(--sp-2) 0;text-align:left}
.top .gear-hd{font-size:var(--fs-5);color:var(--ink-3);padding:4px var(--sp-4) 6px;
  border-bottom:1px solid var(--line);margin-bottom:4px}
.top .gear-it{display:block;padding:8px var(--sp-4);text-decoration:none;color:var(--ink)}
/* ★ 这一行的选择器必须比 `.top a:hover{color:#fff}` 更具体 ——
   否则鼠标一停面板里的字变成白色（浅底白字＝看不见），而**不会报错**。 */
.top .gear-pop a.gear-it:hover{background:var(--bg);color:var(--ink);
  text-decoration:none}
.top .gear-it b{display:block;font-size:var(--fs-4);font-weight:600}
.top .gear-it small{display:block;font-size:var(--fs-5);color:var(--ink-3);margin-top:2px}
.top .gear-it.off{color:var(--ink-3);cursor:default}
.top .gear-it.off b{font-weight:400}
/* ★★ 2026-10-10 第 588 批②（张总「一起干掉」）：**一级模块标签条**（顶栏下沿一条，
   只有「所有」页会渲染 —— `layouts/base.html` 里由 `module_tabs` 变量把着）。
   · 底色与顶栏同一档（观感上属于"chrome"，不是页内容）；自己可横向滚动，
     模块再多也不挤顶栏已有一排入口（塞进 `.top` 试过：会把「求助/快捷键」挤成两行）。
   · 当前那一格与 `.topnav.on` 同一档高亮；手机端在 ≤720px 段里 display:none。 */
.mtabs{display:flex;align-items:center;gap:2px;overflow-x:auto;scrollbar-width:none;
  background:var(--topbar);color:var(--topbar-ink);
  padding:4px max(var(--sp-3), calc((100vw - 1400px) / 2 + var(--sp-3))) 6px}
.mtabs::-webkit-scrollbar{display:none}
.mtab{padding:3px var(--sp-2);border-radius:var(--r-1);font-size:var(--fs-5);
  color:var(--topbar-ink-2);white-space:nowrap;text-decoration:none;flex:0 0 auto}
.mtab:hover{background:rgba(255,255,255,.12);color:#fff}
.mtab.on{background:rgba(255,255,255,.2);color:#fff;font-weight:600}
/* ★ 2026-10-16（第 1296 批 · 桌面按族收口；规格 §4.4：指针目标 24×24）：
   「← 返回」是 base.html 里那**唯一一个** `.backlink` —— **独立一行的入口**，
   不是句内链接（句内那种按 WCAG 2.2 AA 明文的「句内目标」豁免，见豁免登记表）
   ⇒ 按 Target Size 真改：它以前只有 43×21，矮 3px。
   ★ 一处顶 **49 页**，是桌面剩下各族里杠杆最大的一条。
   `min-height` 只加高盒、不动字号与行高 ⇒ 文字位置不变（量具：
   `tools/_m_tap_floor_sweep.py --scope desktop --floor 24`）。 */
.backlink{display:inline-block;min-height:24px;margin-bottom:var(--sp-4);font-size:var(--fs-4);
  color:var(--ink-2);text-decoration:none}
.backlink:hover{color:var(--brand)}

.wrap{display:flex;min-height:calc(100vh - 52px)}
/* ★★★ 2026-10-20（张总「①A」）：左侧竖栏 → **顶栏下沿的入口条**。
   为什么是横条：那 4 格是「待你处理的事」（带角标），不能删；但它们**不该占一列**——
   1440 下 132px 就是 9.2% 的内容区，而且窄屏时竖栏还要自己再变一次横排。
   做成横条后：任何页都在、窄屏可横滑、角标照旧，内容区吃满宽度。 */
.entriesbar{display:flex;align-items:center;gap:6px;height:44px;padding:0 var(--sp-4);
  background:var(--panel);border-bottom:1px solid var(--line);
  overflow-x:auto;overflow-y:hidden;white-space:nowrap;
  scrollbar-width:thin}
/* ★ 角色分支里既有 `<h4>常用</h4>` 这类分段标题 —— 结构一个字没改，
   但横条里它不该占位（竖着排时的"分组"在横排里没有意义）⇒ 只在视觉上收掉。 */
.entriesbar h4{display:none}
/* ★★★ 2026-10-20（收红 · 桌面 44px 下限）：原来这条是 `padding:6px 10px;min-height:32px`
   —— 4 颗入口在**每一页**都算「小可点」（`test_desktop_tap_floor` 实测 108 页 × 约 4 个
   ＝ 430，门禁上限 2 ⇒ 当场红；全量 08:48 那次起红，正好对上批 A 落库的 08:33）。
   ⇒ 入口颗本身给到 **44**（条高同步 40→44，那条线自己的 `test_viewport_bands` 允许 36~60）。
   ★ 为什么不是"只登记不修"：这 4 颗是**每页都在**的主入口，比任何一处更该够手点；
     登记表是给"改不动的历史遗留"用的，不是给"刚搬上来的新部件"用的。 */
.entriesbar a{display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;
  padding:0 10px;min-height:44px;border-radius:var(--r-2);
  font-size:var(--fs-4);color:var(--ink-2);
  transition:background .15s ease,color .15s ease}
.entriesbar a:hover{background:var(--panel-2);text-decoration:none;color:var(--ink)}
.entriesbar a.on{background:var(--brand-soft);color:var(--brand);font-weight:600;
  box-shadow:inset 0 -3px 0 var(--brand)}
.main{flex:1;padding:var(--sp-5);min-width:0}

h1{font-size:var(--fs-1);margin:0 0 var(--sp-4);font-weight:600}
h2{font-size:var(--fs-2);margin:var(--sp-5) 0 var(--sp-3);font-weight:600}

.cards{display:grid;gap:var(--sp-3);grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-3);
  padding:var(--sp-4);box-shadow:var(--sh-1);transition:box-shadow .2s ease,transform .2s ease}
.card .k{font-size:var(--fs-5);color:var(--ink-3)}
.card .v{font-size:var(--fs-1);font-weight:600;margin-top:var(--sp-2)}
.card .v.warn{color:var(--danger)}

.panel{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-3);
  overflow:hidden;box-shadow:var(--sh-1)}
.panel .hd{padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap}
.panel .hd .t{font-size:var(--fs-2);font-weight:600}
.panel .bd{padding:0}

table{width:100%;border-collapse:collapse;font-size:13.5px;line-height:1.55}
/* ★★ 2026-10-10 第 588 批④（张总「一起干掉」）：**放宽表格密度**。
   改前：`th,td` 上下内边距 7px、字号 `var(--fs-4)`=13px、行高 42px、1540×960 一屏约 18 行。
   改后：上下 10px ＋ `line-height:1.55` ＋ 表格字号 **13.5px**（真浏览器实测行高约 47px、一屏约 16 行）。
   ★ 字号**只在 `table` 这一条里写死 13.5px**，不动全站令牌 `--fs-4` ——
     令牌一动会连侧栏/按钮/表单一起动（方案 §2 第 6 条的取舍）；卡片区留白也一个字没动。
   ★ 代价如实记：一屏少约 2 行（老板一屏看单量）；前后对照数据见第 588 批台账。
   ★★ 横向内边距由 `--sp-3`(12px) 收到 `--sp-2`(8px)：字号放大后列会变宽，
     真浏览器实测销售单的横向溢出从 559px 涨到 **589px**（**这是本批造成的退化**）
     ⇒ 收窄横向内边距把它抵回来（见台账的前后对照表，改完不许比 559 更差）。 */
th,td{padding:10px var(--sp-2);text-align:left;border-bottom:1px solid var(--line);
  white-space:nowrap;max-width:320px;overflow:hidden;text-overflow:ellipsis}
th{background:var(--panel-2);color:var(--ink-2);font-weight:500;font-size:var(--fs-5)}
/* ★ 允许换行的格子（A1-96，2026-09-29）：给「说明 / 备注 / 原因」这类长句列用。
   上面那条 `th,td{…white-space:nowrap;max-width:320px;overflow:hidden;text-overflow:ellipsis}`
   是列表页"一行一格、紧凑不换行"的口径（长列表靠它才不串行），**保留不动**；
   但长句会被它**吃掉**：真浏览器实测 /payroll 的提成明细「说明」列
   （`#cm_rows`）被压到 clientWidth=321px、文字 scrollWidth=1057px ⇒ 截掉 736px
   —— 张总截图里"说明列的长句被截断"就是这一类（单元格上写 `overflow-wrap:anywhere`
   是**没用的**：`white-space:nowrap` 在，就根本没有换行机会）。
   ⇒ 给一个**显式开关** `<td class="cellwrap">`：只对点了名的格子放开换行，
     不动列表页的紧凑口径，也不靠"逐表打补丁"碰运气。
   ★★ 名字**必须**是 `cellwrap` 这类新词 —— 第一版我叫它 `wrap`，**当场撞车**：
     main.css 第 75 行早有 `.wrap{display:flex;min-height:calc(100vh - 52px)}`
     （那是全站**页面外壳**的容器类），于是 `td.wrap` 也吃到 `min-height:948px`
     ⇒ 每一行数据都被撑成"一屏高"（实测 /payroll `#cm_rows` 48 行 = 46453px）。
     真浏览器量到的证据：`.wrap { display:flex; min-height: calc(-52px + 100vh); }`。
   ★ `min-width:220px` 是**必需的第二半**（第一版漏了它）：
     表格是 auto 布局，`overflow-wrap:anywhere` 会把这一列的 min-content 压到 1 个字，
     于是"要收缩就只收缩这一列" —— 给一个下限，收缩就不会全压到它身上。
     实测：`#cm_rows` 说明列稳定落在 325px（两行文字），无横向滚动、行高 36→(换行后)≈36px。 */
td.cellwrap,th.cellwrap{white-space:normal;overflow-wrap:anywhere;max-width:none;min-width:220px}
/* ★★ 2026-10-17（界面美化 2.0 · P1①）：**数字列右对齐 ＋ 等宽数字**。
   改前（真浏览器量 /v/wholesale_grade）：12 列里 4 个纯数字列（折扣%／年采购额门槛／
   能看价／能下单）**全是 text-align:left**，只有「操作」列是右对齐 —— 于是
   `0 / 10000 / 50000` 左对齐时个位、十位、百位各错一格，对账要一行一行数位。
   改后：渲染时按**值**判出数字格加 `.num`，表头落地后回填（见 `pages/list.html`），
   这里只给 `.num` 两条规矩：右对齐 ＋ `tabular-nums`（数字等宽 ⇒ 小数点成一条竖线）。
   ★ 为什么不是"给 table 全表右对齐"：名字/货号/备注必须左对齐 —— 一律右对齐同样难读。
   ★ 为什么不是"按列名写死名单"：列名会变、视图有 40+ 个，写死就是第二份真源。 */
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums;letter-spacing:.2px}
tbody tr:hover{background:var(--panel-2)}
/* ★ #24 列表整行双击进编辑（老板 4 次截图 = 商品/客户/仓库同一个根因）。
   ★ 为什么只给 .rowlink 加手型、不给所有 tr：只读视图 / 无详情页的行
     双击没去处，光标变手型就是**骗人**（点了没反应比不点更糟）。
   ★ 数量列逐格标单位那条也一并强化：hover 底色已是既有行为，这里只补
     "这行能点"的视觉暗示（左侧 2px 品牌色条）+ 手型。 */
tbody tr.rowlink{cursor:pointer}
tbody tr.rowlink:hover{background:var(--panel-2);box-shadow:inset 2px 0 0 var(--brand)}
/* 行尾「编辑」入口。默认低调（它是"显式入口"，不是每行都要抢眼），
   hover 到这一行时才变明显 —— 免得整张表一片蓝按钮。 */
th.editcol,td.editcol{width:1%;text-align:right;white-space:nowrap}
a.rowedit{color:var(--ink-2);font-size:var(--fs-5);text-decoration:none;
  padding:2px 8px;border:1px solid var(--line);border-radius:var(--r-2)}
tbody tr.rowlink:hover a.rowedit{color:var(--brand);border-color:var(--brand)}
a.rowedit:hover{background:var(--brand);color:var(--brand-ink);border-color:var(--brand)}
/* ★ #24 键盘流的“当前行”高亮：↑/↓ 移动到哪一行，回车打开它。
   ★ 为什么必须比 hover 更重：悬停的鼠标会离开，而“当前行”的高亮
     要在手离开之后照旧在 —— 否则老板按一下↓就不知道自己选了哪一行。
   ★ 左侧色条用 3px（比 .rowlink:hover 的 2px 粗一点点）
     —— 两者可能同时在（鼠标正停在这一行上），差一个像素才看得出“选中”更重。 */
tbody tr.sel{background:var(--panel-2);box-shadow:inset 3px 0 0 var(--brand)}
tbody tr.sel a.rowedit{color:var(--brand);border-color:var(--brand)}
td.num{text-align:right;font-variant-numeric:tabular-nums}
td.money{text-align:right;font-variant-numeric:tabular-nums;color:var(--ink)}
/* ★ 数字单元格的告警态。原来只写了 .tag.warn / .sub.warn，模板里
   class="num warn" 是**没有任何样式**的 —— 锁定面板「可用库存不够」
   的红字高亮一直不显示（2026-09-18 实机验收抓到）。 */
td.num.warn{color:var(--warn);font-weight:600}

/* ★ M4 / A1-14：表单控件（下拉 / 输入框）的告警态。
   与上面那条同一个毛病 —— 光加 class 不加规则等于没加：
   过账面板「这一行还没选批次」要把那一格框成红的，员工才知道改哪里。
   ★ 用 border + 阴影两条：只改 border 在浅色边框的表格里很不显眼。 */
select.warn,input.warn{border-color:var(--danger) !important;
  box-shadow:0 0 0 2px rgba(198,40,40,.14)}

.empty{padding:var(--sp-6);text-align:center;color:var(--ink-3);font-size:var(--fs-4)}

.tag{display:inline-block;padding:1px var(--sp-2);border-radius:var(--r-1);
  font-size:var(--fs-5);border:1px solid var(--line);color:var(--ink-2)}
.tag.ok{color:var(--ok);border-color:var(--ok)}
.tag.warn{color:var(--warn);border-color:var(--warn)}
.tag.danger{color:var(--danger);border-color:var(--danger)}
/* ★ A组 #37/#38（2026-09-22）：补两个色档。
   原来只有 ok/warn/danger 三档，「中性」是靠"不加类"实现的 ——
   但那样一来 .tag 和 .tag.mute 没法在模板里用同一个函数切出来。
   补上 mute（不用管了）与 brand（进行中，用品牌蓝）。
   一句话记法：橙＝要你做 · 蓝＝在做 · 绿＝做完了 · 灰＝不用做。 */
.tag.brand{color:var(--brand);border-color:var(--brand)}
.tag.mute{color:var(--ink-3);border-color:var(--line)}
/* ★ 2026-09-28（多租户平台控制台）：套餐里"基础必含"的功能点要一眼分得出来
   —— 它们是**恒开**的（`is_core`），跟"可以买的增值项"不是一回事。
   做成唯一一个**实心底**的档：其余四档都是描边彩字（同一张页面上第一眼就能扫到）。
   颜色只用现成 token（brand-soft 底 + brand-dk 字），不引入新色 —— 与图标那套同一条纪律。 */
.tag.core{color:var(--brand-dk);border-color:var(--brand-soft);background:var(--brand-soft)}

/* ★ 同上：状态按钮的四档色。用在不同状态的动作按钮上 ——
   「当前所处的那个状态」实心打勾禁用，其余素色描边、hover 才预告颜色。
   为什么不用现成的 .ok/.warn：按钮要的是**实心底 + 白字**，
   跟标签的"描边 + 彩字"不是一回事，硬套会让按钮在白底上看不清。 */
.stbtn{transition:background .12s,color .12s,border-color .12s}
.stbtn.is-warn{background:var(--warn);border-color:var(--warn);color:#fff}
.stbtn.is-brand{background:var(--brand);border-color:var(--brand);color:#fff}
.stbtn.is-ok{background:var(--ok);border-color:var(--ok);color:#fff}
.stbtn.is-mute{background:var(--ink-3);border-color:var(--ink-3);color:#fff}
.stbtn[disabled]{opacity:1;cursor:default}
/* 素色按钮 hover 时预告自己的颜色（"标记才显示颜色"：没选中的不抢戏） */
.stbtn:not([disabled]):hover[data-hint="warn"]{border-color:var(--warn);color:var(--warn)}
.stbtn:not([disabled]):hover[data-hint="brand"]{border-color:var(--brand);color:var(--brand)}
.stbtn:not([disabled]):hover[data-hint="ok"]{border-color:var(--ok);color:var(--ok)}
.stbtn:not([disabled]):hover[data-hint="mute"]{border-color:var(--ink-3);color:var(--ink-3)}

/* ★ 工具栏一行内的**统一间距**。原来写死 var(--sp-2)，值一样（8px），
   但"这一行有多松"没有名字 —— 于是 .quicktime 自己又加了个 4px 的
   margin-inline-end，实测出现 ['12.0','8.0','8.0'…]（张总说的"间距忽宽忽窄"）。
   现在这一行只有一个间距变量。 */
.toolbar{display:flex;gap:var(--ctl-gap);align-items:center;flex-wrap:wrap}
/* ★★ 工具条里的**裸 <form>**（网站线索的筛选表单就是这种写法：
   `<form style="display:inline"><select><input><button></form>`）。
   行内元素之间只靠一个空格分开 ⇒ 控件之间是 **4.2px**，而工具条自己的
   gap 是 8px，实测 ['4.2','4.2','8.0'] —— 一笔"忽宽忽窄"就是这么来的
   （张总截图那一页正是它）。让它也按工具条的间距排，空格不再参与。 */
.toolbar > form{display:inline-flex;align-items:center;flex-wrap:wrap;
  gap:var(--ctl-gap);margin:0}

/* ---------------------------------------------------------------- #42 列表页业务日期区间
   起止两个 <input type=date>。★ 为什么要写这一条：
   `input[type=date]` **不在**上面 `input[type=text]...` 那条选择器里，
   不补的话两个日期框会是浏览器默认的窄小样式，跟旁边灰底下拉**明显不是一套** ——
   老板会以为「这俩框是坏的/没做完」。这类「加了 class 没加规则」的坑本仓踩过多次。
   ★ 与下拉的 min-width:180px 对齐（两个日期框并排已是 360px 级），不再撑宽。 */
.daterange{display:inline-flex;align-items:center;gap:var(--ctl-gap)}
.daterange input[type=date]{
  padding:0 var(--sp-2);border:1px solid var(--line);border-radius:var(--r-2);
  font-size:var(--fs-4);background:var(--panel);color:var(--ink);min-width:0}
.daterange .dash{color:var(--ink-3);padding:0 2px}
/* ★★ 2026-09-29（张总「全部 字段拥挤   对不齐的都优化掉」）：
   这一条原来只列了 text/password/search/select —— **date / number / file
   完全没有规则**，于是它们退回浏览器默认样式。真浏览器量到的实数：
   销售报表「自选日期」21.3px、商品表单的数字框 21.0px、商品表单视频那一行的
   文件框 23.0px，而同一行的文本框是 27.0px。
   本仓其实栽过一次同类（见上面那段注释：「`input[type=date]` 不在上面
   `input[type=text]...` 那条选择器里」）—— 但当时**只补了 .daterange 里那两个**，
   报表页 / 表单页 / 对账单上那些裸的没补。这次一次列全，不再按"用在哪"零敲碎打。
   ★ 高度改由 --ctl-h 一处定死：一行里 input / select / 日期框 / 按钮**一样高**。
   ★ line-height 显式写 normal：input/select 本来就是这个值，写出来是为了
     不让 body 的 1.6 从别处漏进来 —— 那正是"按钮比输入框高 4px"的根因。 */
input:not([type]),
input[type=text],input[type=password],input[type=search],input[type=email],
input[type=tel],input[type=url],input[type=number],input[type=date],
input[type=month],input[type=week],input[type=time],input[type=datetime-local],
input[type=file],select{
  height:var(--ctl-h);padding:0 var(--sp-3);border:1px solid var(--line);
  border-radius:var(--r-2);font-size:var(--fs-4);line-height:normal;
  /* ★ vertical-align:middle：非 flex 的行（如对账单的
     `<label>起 <input type=date></label>`、报表页的 `<label>自选 …</label>`）
     里控件是**行内**排的，默认按基线对齐 —— 不同控件的基线偏移不一样，
     等高的两个框也会差几像素。按中线对齐，等高就真的齐。 */
  vertical-align:middle;
  background:var(--panel);color:var(--ink);min-width:180px}
/* ★★ 2026-10-16（第 1299 批 · 桌面按族收口；规格 §4.4 指针目标 24×24）：
   上面那串选择器**一次列全**了，却仍漏了 HTML 最常见的简写：**不写 `type` 属性的 input**
   —— `input[type=text]` 这类的**属性选择器要求属性真的写在标签上**，而 `<input name=x>` 默认
   就是 text 却一条都不命中 ⇒ 它回落到浏览器默认 ~21px。
   真现场（`tools/_b1299_small_ctl_census.py` 普查，桌面 1440）：**61 处 / 10 页**，
   样例 `<input id="emp_new_user" placeholder="与 ERP 账号一致" style="width:150px">`
   —— 结果是**同一个表单里 32px 与 21px 混排**（这正是上面那段注释要治的病，治漏了第二例）。
   ⇒ 补 `input:not([type])` 进同一处真源，**不改任何按"用在哪"零敲碎打的老路**。
   ★ 紧跟的那条 `min-width:0` 是**故意**的：不写 type 的框多半带模板作者 inline 的 `width`
     （他们按 21px 的小框排的版），这一批只统一**竖向尺寸**，宽度还给作者。 */
input:not([type]){min-width:0}
/* ★ 按钮高度必须与输入框**同源**：原来靠 `padding:6px` 撑（6+6+边框+
   继承来的行高 1.6 ⇒ 31px），跟输入框的 27px 天然差 4px，谁也没错、就是不等。 */
button{height:var(--ctl-h);padding:0 var(--sp-4);border:1px solid var(--brand);
  border-radius:var(--r-2);line-height:normal;vertical-align:middle;
  background:var(--brand);color:var(--brand-ink);font-size:var(--fs-4);cursor:pointer;
  transition:background .12s ease,border-color .12s ease,transform .12s ease}
button:hover:not(:disabled){background:var(--brand-dk);border-color:var(--brand-dk)}
button:active:not(:disabled){transform:scale(.98)}
button.ghost{background:var(--panel);color:var(--ink-2);border-color:var(--line)}
button.ghost:hover:not(:disabled){background:var(--brand-soft);color:var(--brand);
  border-color:var(--brand)}
button:disabled{opacity:.5;cursor:not-allowed}

/* ★ 报表页的「快捷区间 / 导出」是 <a>，不是 <button>（它们要能直接进 URL、
   能被收藏、能在新标签页打开），所以 button 那套样式吃不到 —— 这里补一份
   同形外观，让它看起来就是个按钮而不是链接。
   ★ 本仓教训（见上面 .warn 那两条注释）：光在模板里写 class、CSS 里没有对应
   规则 = 等于没加，而且不报错。所以这个类必须在 CSS 里真的有。 2026-09-22 */
a.crumb-btn{display:inline-flex;align-items:center;height:var(--ctl-h);
  padding:0 var(--sp-4);line-height:normal;
  border:1px solid var(--line);border-radius:var(--r-2);background:var(--panel);
  color:var(--ink-2);font-size:var(--fs-4);cursor:pointer;text-decoration:none;
  transition:background .12s ease,border-color .12s ease,color .12s ease}
a.crumb-btn:hover{background:var(--brand-soft);border-color:var(--brand);
  color:var(--brand);text-decoration:none}
a.crumb-btn[aria-current="true"]{background:var(--brand);border-color:var(--brand);
  color:var(--brand-ink);font-weight:600}
a.crumb-btn[aria-current="true"]:hover{background:var(--brand-dk);
  border-color:var(--brand-dk);color:var(--brand-ink)}

/* ★ 与区间按钮同形、但语义不是"一个区间"的按钮（如「导出 CSV」）。
   2026-09-22 加：导出原本也用 crumb-btn，真浏览器 E2E 里被"快捷区间"的
   选择器一并选中（它的 href 本来就带 start=/end=）⇒ 断言误判。
   给它一个独立类，既好选也好读。
   ★ 注意：这里只是**轻微弱化**（别抢区间的视觉焦点），
   仍要是可点的按钮，别做成禁用态。
   ★★ 返工记录（同日，看截图后改）：第一版写成 `color:var(--ink-3)`
   （#95a0ab，白底上对比度只有约 2.6:1）—— 那不是"弱化"，是**看不清**，
   老板第一眼会以为这个按钮是禁用的。改成 ink-2(#5c6670，约 6.7:1)，
   仍然比区间按钮的 ink-2 弱一档靠**透明底 + 常规字重**来体现，
   而不是靠把字调灰。结论：弱化手段要用"底色/字重"，不要用"降低对比度"。 */
a.crumb-btn-aux{background:transparent;color:var(--ink-2);font-weight:400}
a.crumb-btn-aux:hover{background:var(--brand-soft);border-color:var(--brand);
  color:var(--brand)}

/* ---- 图标（内联 SVG sprite，见 _icons.html）---- */
.ic{width:16px;height:16px;flex:none;vertical-align:-3px;stroke-width:2}
/* ★ 2026-10-20：竖栏换成入口条后，图标与角标跟着换容器名（类名一一对应，样式不变）。 */
.entriesbar .ic{width:15px;height:15px;color:var(--ink-3);margin-right:2px}
.entriesbar a:hover .ic{color:var(--ink-2)}
.entriesbar a.on .ic{color:var(--brand)}
/* ★ 入口条里角标**不再**用 `margin-inline-start:auto`（横排里那会把它推到条的尽头）；
   改成紧跟文字、间距 4px。0 条时后端根本不渲染这个元素（宏里判过）。 */
.entriesbar .navnum{margin-inline-start:4px;flex:0 0 auto;min-width:18px;padding:0 5px;
  border-radius:999px;background:var(--brand);color:#fff;font-size:var(--fs-5);
  font-weight:600;line-height:18px;text-align:center;font-variant-numeric:tabular-nums}

/* ---- 顶栏全局搜索 ---- */
.gsearch{display:flex;align-items:center;gap:var(--sp-2);
  background:rgba(255,255,255,.13);border:1px solid rgba(255,255,255,.18);
  border-radius:18px;padding:3px var(--sp-3);min-width:230px;
  transition:background .15s ease,border-color .15s ease}
.gsearch:focus-within{background:rgba(255,255,255,.2);border-color:rgba(255,255,255,.35)}
.gsearch .ic{width:15px;height:15px;color:var(--topbar-ink-2)}
.gsearch input[type=search]{background:transparent;border:none;outline:none;
  color:#fff;font-size:var(--fs-4);width:100%;min-width:0;padding:2px 0;
  /* ★ 顶栏这颗搜索框是**胶囊里的内嵌件**，不是工具条里的一行控件 ——
     显式退回"按内容定高"，免得吃全站统一高度后把顶栏胶囊撑变形。 */
  height:auto;line-height:inherit}
.gsearch input[type=search]::placeholder{color:var(--topbar-ink-2)}
.gsearch.big{min-width:380px;max-width:520px;background:var(--panel);
  border-color:var(--line);border-radius:var(--r-3);padding:var(--sp-2) var(--sp-3);
  margin-bottom:var(--sp-4)}
.gsearch.big .ic{color:var(--ink-3);width:18px;height:18px}
.gsearch.big input[type=search]{color:var(--ink);font-size:var(--fs-3)}

.kv{display:grid;grid-template-columns:140px 1fr;gap:1px 0;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-3);overflow:hidden;margin-bottom:var(--sp-4)}
.kv .k{background:var(--panel-2);padding:7px var(--sp-4);font-size:var(--fs-4);color:var(--ink-2);
  border-bottom:1px solid var(--line)}
.kv .v{padding:7px var(--sp-4);font-size:var(--fs-4);border-bottom:1px solid var(--line);
  word-break:break-all;white-space:normal}

.login{max-width:320px;margin:14vh auto;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-4);padding:var(--sp-6)}
.login h1{text-align:center;font-size:var(--fs-2)}
.login input{width:100%;margin-bottom:var(--sp-3)}
.login button{width:100%}
.err{color:var(--danger);font-size:var(--fs-4);margin-bottom:var(--sp-3);text-align:center}

.hint{color:var(--ink-3);font-size:var(--fs-5);padding:var(--sp-3) var(--sp-4)}

@media (max-width:1024px){
  .main{padding:var(--sp-4)}
  /* ★ 2026-10-20：入口条在窄档不再有「宽度」可变（它本来就是整条）——
     只把间距收紧，让 4 颗在 1024 上仍能一屏放下。 */
  .entriesbar{gap:4px;padding:0 var(--sp-3)}
}
@media (max-width:920px){
  .wrap{flex-direction:column}
  /* ★ 2026-10-20：竖栏以前在这一档自己变横排（占满一行）；
     现在是入口条，本来就是横的 ⇒ 这里只钉「**可横滑、不折行、不隐藏**」，
     免得以后有人给它加 overflow:hidden 把第 4 颗藏掉（判据 test_viewport_bands 组 5）。 */
  .entriesbar{height:44px;padding:0 var(--sp-2);overflow-x:auto;flex-wrap:nowrap}
  /* ★ 2026-10-20：窄档原来写 `min-height:36px` —— 36 也低于 44 那条下限
     （`test_desktop_tap_floor` 只扫桌面路由，但 44 是**全端**的行业下限），一并对齐。 */
  .entriesbar a{min-height:44px}
}
@media (max-width:720px){
  :root{--fs-1:18px}
  .kv{grid-template-columns:110px 1fr}
  th,td{max-width:180px}
}
/* ═══════════ #11 审核状态条 + 标题两层 + 手机端单据页 ═══════════
   ★ 为什么这些规则必须**有**（不是样式偏好）：
     老板 2026-09-20 在手机上打开 192.168.0.115:8002 的一张销售单，反馈两件：
       ① 「审核的要在明显的地方看」  ② 「界面不友好，挤在一堆」
     实测根因：本文件有 11 个 @media 断点，**没有一条碰过**
     .top / .who / h1 / .tag / .bar —— 顶栏和标题在手机上
     "不是适配得不好，是**根本没做**"。所以下面这段是新写的，不是微调。 */

/* 审核大状态条：位置在标题之前（见 order.html 顶部），这里只管"更醒目"。
   ★ 配色**不在这里定义** —— 直接用 .alert 的 err/ok/warn 三档
     （同一个意思只留一处定义，避免"改了一处忘一处"）。 */
.alert.verifybar{border-left-width:6px}
.verifybar .vb-h{font-size:var(--fs-2);font-weight:700;line-height:1.35}
.verifybar .vb-why{font-size:var(--fs-4);font-weight:600;margin-top:var(--sp-1)}
.verifybar .vb-by,.verifybar .vb-tip{font-size:var(--fs-5);margin-top:4px}

/* 次要状态一层（款 / 关 / 票 / 锁 / 业务已完成）—— 从 h1 里降下来。
   负 margin-top 是把 h1 的底距收回来一点，免得两行之间空得发虚。 */
.tags2{display:flex;flex-wrap:wrap;gap:var(--sp-2);
  margin:calc(-1 * var(--sp-3)) 0 var(--sp-4);font-size:var(--fs-5)}

@media (max-width:720px){
  /* ① 顶栏溢出：老板截图里"标 所 有""老板"重复三次就是这么来的 ——
        口号 / 角色标签 /「所有」入口 全挤在一行。
        手机上：口号不显示、角色标签不显示、「所有」收起来（从侧栏进），
        搜索框吃掉剩下的宽度。
     ★ 只改**显示与宽度**，不动 DOM、不动任何链接 —— 桌面端逐字不变。 */
  .top{gap:var(--sp-2);padding-left:var(--sp-3);padding-right:var(--sp-3)}
  .top .logo{flex:0 0 auto;min-width:0}
  .top .logo small{display:none}
  .top .topnav{display:none}
  /* ★ 第 588 批②：一级模块标签条在手机端也收起（与「所有」入口同一套做法）。 */
  .mtabs{display:none}
  .top .spacer{display:none}
  .top .gsearch{flex:1 1 auto;min-width:0}
  .top .gsearch input{width:100%;min-width:0}
  .top .who{flex:0 0 auto;font-size:var(--fs-5)}
  .top .who .role{display:none}

  /* ② 单据页操作条钉在屏幕底部 ——「打开就能审」，不用往下滚两三屏找按钮。
     ★ 只钉 #actions（单据页那一条），**不钉所有 .toolbar**：
       列表页的工具栏（搜索 / 筛选 / 批量审核）钉在底部会一直盖住表格。
     ★ 44px 是"手指点得准"的下限，不是审美选择。 */
  .bar#actions{position:fixed;left:0;right:0;bottom:0;z-index:20;
    margin:0;padding:var(--sp-2) var(--sp-3);
    background:var(--panel);border-top:1px solid var(--line);
    box-shadow:0 -2px 10px rgba(28,36,48,.10);
    /* ★ 不换行、可横滑。这一条是**真浏览器量出来**才改的：
       换行版在 375px 下条高 **273px**，吃掉屏幕三分之一，
       而且审核按钮被挤到第三行 —— 正好违反"打开就能审"。
       改成一整行 + 横滑后条高约 62px。 */
    flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden}
  .bar#actions button,.bar#actions .btnlink,.bar#actions .btnlike{
    flex:0 0 auto;min-height:44px;white-space:nowrap;margin-left:0}
  /* ★★ 这一条是**真浏览器量出来**才补的（第一次改完条高只从 273 → 238）。
     工具栏的直接子项**不只有 <button>** —— 还有两个裸 <span> 包装器
     （#customsBtns / #fapiaoBtns，一个套报关按钮、一个套发票按钮）。
     它们没被上面那条规则覆盖 ⇒ 作为 flex 项被挤压到 99px 宽，
     里面的文字竖着排，span 自己长到 132px / 221px，
     而**条高由最高的子项决定** ⇒ 整条又被撑回 238px。
     ★ 判据：只要“有一个子项能缩”，条高就不可控 ⇒ 统一不缩。 */
  .bar#actions > *{flex:0 0 auto}
  /* 包装器内部自己排成一行（不再换行），否则里面的按钮照样竖排。 */
  .bar#actions > span{display:flex;align-items:center;flex-wrap:nowrap;
    gap:var(--sp-2)}

  /* ★ 只改**视觉顺序**（order），不动 DOM —— Tab 顺序与桌面端一字不变。
     老板要"打开就能审" ⇒ 审核那一组排最左，「返回列表」排最右。 */
  .bar#actions .verify-act{order:-3}
  .bar#actions .btnlink{order:9}
  /* 固定条会盖住页面最后一段内容 —— 给它留出高度 */
  .main{padding-bottom:88px}
}

/* ================= 开单页 / 单据页 (§8 样式全部走变量) ================= */
.alert{padding:var(--sp-3) var(--sp-4);border-radius:var(--r-2);font-size:var(--fs-4);
  margin-bottom:var(--sp-4);border:1px solid}
.alert.err{background:#fff0f0;border-color:#ffd6d6;color:#c62828}
.alert.ok{background:#eaf7ee;border-color:#bfe6c8;color:#1a7f42}
/* ★ A 组 #62（2026-09-22）：补 .alert.warn。
   原来只有 err / ok 两档，而 customer_pool.html 写的是 class="alert warn"
   —— 那条「还剩 N 天掉公海，快去跟进」的提醒**一直是没有底色的**
   （跟普通 div 长得一样，飘在页面上没人注意）。
   ★ 这次全仓普查还发现：order.html / refund_detail.html 的「审核不通过」
   横幅写的是 class="alert bad"，而 .alert 只有 .err —— 已一并统一成 .err
   （同一个意思只留一个名字，别让 CSS 里存在两个别名）。 */
.alert.warn{background:#fff8e6;border-color:#ffe3a3;color:#a86a00}

.fgrid{display:grid;gap:var(--sp-3);
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.fitem{display:flex;flex-direction:column;gap:var(--sp-1);min-width:0}
.fitem label{font-size:var(--fs-5);color:var(--ink-3);line-height:22px}
.fitem input,.fitem select{width:100%;min-width:0}
.fwide{grid-column:span 2}
@media (max-width:720px){.fwide{grid-column:span 1}}

/* ══════════════ ★★★ 顶栏的「中间空档」（第 1588 批 · 张总截图「这里版式错位」→ 根查）══════════════
   病（真浏览器量的）：顶栏原来**只有两档** —— >980 正常、≤720 收起，**721–979 一条规则都没有**；
   而顶栏的最小内容宽实测 = **980px**（`form.gsearch` 的 `min-width:230px`
   ＋ 其余 14 项 `flex:0 1 auto` 的 min-content 之和，谁都没写着"页面最小宽 980"）。
   ⇒ 那一整段**每一页都整页横滚**：横滚 = 980 − 视口宽，721/780/860/916/940/979 六档逐个对得上；
      同时 `.logo` 被挤成 **6 行竖排**（实测高 154px —— 一张「优秀者工作台」竖着排成
      「者/工/作/台」），右侧「快捷键/齿轮/铃铛/老板/退出」被整排推出视口。
   ★ 三条反证（都做过，见根查报告 §1.2）：① 注入单列版式后横滚**一个都不变** ⇒ 不是分栏造成的；
     ② 逐个隐藏顶栏 15 个子元素 → 隐藏 `form.gsearch` 时横滚 **87 → 0**，其余任一项只降 1–35px；
     ③ `<div class="top">` 的 `scrollWidth` 恒为 980（与视口无关）⇒ 它才是"页面最小宽"。
   ★ 为什么**不把 980 写死成断点**：980 是**今天**的实测值，加一个顶栏入口就往上推
     （本仓真踩：`⌨快捷键`/齿轮/铃铛就是后加的）。所以这里只做「**让它能缩**」，
     判据钉的是「横滚 = 0」（`tests/test_viewport_bands.py` 判据 [1][2]），不是 980。
   ★ 证据全文 `02_方案…\20261018_根查_版式错位与顶栏980横滚.md`；基因：双端规格 §4.5。 */

/* 第一档 ≤1179：三处「不许缩」换成「能缩」—— 只改伸缩与换行，颜色/顺序/DOM 一个不动。 */
@media (max-width:1179px){
  /* ① logo 不折行 —— **已上移到基础规则 `.top .logo`**（这里不再重复；
        门槛按实测取：1180 也不许折，写成 max-width:1179 会恰好漏掉 1180 那一档） */
  /* ② 搜索框从「定宽 230」改成「能缩到 0」—— 它是 980 那个下限的主体 */
  .top .gsearch{flex:1 1 auto;min-width:0;max-width:230px}
  /* ③ 用户名/角色可缩（原来 `flex:0 0 auto` 不许缩 ⇒ 整页被顶出去） */
  .top .who{flex:0 1 auto;min-width:0;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
  .top .who .role{display:none}
}

/* 第二档 ≤979：四个**板块入口**收起，从侧栏进（与 ≤720 同一口径）。
   ★ 只收 `[data-top]`（定目标/管钱货/做推广/所有 这四个锚点各自带 data-top）——
     「求助」「⌨快捷键」「通知铃铛」是**功能入口**，不许连坐。
     （≤720 那条是 `.top .topnav{display:none}`，把这三颗也收了 —— 那是手机端既有行为，不动。） */
@media (max-width:979px){
  .top .topnav[data-top]{display:none}
  .top .gsearch{max-width:none}
}

.pick{position:relative}
.pick input{width:100%}
/* 商品格: 输入框 + 「⋯」按钮并排, 按钮是"不记货号也能选"的入口 */
.pickRow{display:flex;gap:var(--sp-1);align-items:stretch;min-width:0}
.pickRow input{flex:1;min-width:0}
/* ★ 这三族是**刻意的非标准高度**，显式退出全站统一高度：
   · .pickRow button.pickBtn —— 挤在输入框右端的一颗极小「⋯」
   · .skuBook .hd input —— 选商品面板的搜索框（字号 16px，自己更大）
   · .lgrid input/select —— 表格里就地编辑用的密排格（行高本来就矮）
   不写这三条的话，它们会被 --ctl-h 拉齐，格子和面板反而被撑开。 */
.pickRow button.pickBtn{flex:none;padding:1px 6px;font-size:var(--fs-3);
  line-height:1.3;height:auto}

/* 「选择商品」面板 —— 给记不住货号的人用的大列表 */
.skuBook{width:min(920px,94vw);max-height:86vh;display:flex;flex-direction:column;
  padding:0;overflow:hidden}
.skuBook .hd{padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--line);
  display:flex;gap:var(--sp-3);align-items:center}
.skuBook .hd h3{margin:0;font-size:var(--fs-2);flex:none}
.skuBook .hd input{flex:1;min-width:0;padding:6px var(--sp-3);
  font-size:var(--fs-2);height:auto;line-height:inherit}
.skuBook .cols{display:grid;grid-template-columns:120px 1fr 150px 130px 88px 88px;
  gap:var(--sp-2);padding:6px var(--sp-5);background:var(--panel-2);
  color:var(--ink-2);font-size:var(--fs-5);border-bottom:1px solid var(--line)}
.skuBook .cols .num{text-align:right}
.skuBook .list{flex:1;overflow-y:auto;min-height:200px}
.skuBook .it{display:grid;grid-template-columns:120px 1fr 150px 130px 88px 88px;
  gap:var(--sp-2);padding:9px var(--sp-5);border-bottom:1px solid var(--line);
  cursor:pointer;font-size:var(--fs-3);align-items:baseline}
.skuBook .it:hover{background:var(--panel-2)}
.skuBook .it.on{background:var(--brand-soft);box-shadow:inset 3px 0 0 var(--brand)}
.skuBook .it .code{color:var(--ink-2);font-variant-numeric:tabular-nums}
.skuBook .it .nm{font-weight:500}
.skuBook .it .sp{color:var(--ink-3);font-size:var(--fs-5)}
.skuBook .it .num{text-align:right;font-variant-numeric:tabular-nums}
.skuBook .it .zero{color:var(--ink-3)}
.skuBook .it .neg{color:var(--danger)}
.skuBook .ft{padding:var(--sp-3) var(--sp-5);border-top:1px solid var(--line);
  display:flex;gap:var(--sp-3);align-items:center;color:var(--ink-2);font-size:var(--fs-5)}
.skuBook .ft .spacer{flex:1}
.skuBook .empty{padding:var(--sp-6);text-align:center;color:var(--ink-3)}
.pickMenu{position:absolute;z-index:20;left:0;right:0;top:100%;margin-top:2px;
  max-height:250px;overflow-y:auto;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--r-2);font-size:var(--fs-4)}
.pickMenu .mi{padding:6px var(--sp-3);cursor:pointer;display:flex;gap:var(--sp-2);
  align-items:baseline;flex-wrap:wrap;border-bottom:1px solid var(--line)}
.pickMenu .mi:last-child{border-bottom:0}
.pickMenu .mi:hover{background:var(--brand-soft)}
.pickMenu .mi b{font-weight:500}
.pickMenu .mi em{font-style:normal;color:var(--ink-3);font-size:var(--fs-5);
  margin-left:auto}
.pickMenu .mi em.danger{color:var(--danger)}
.muted{color:var(--ink-3);font-weight:400}

.lgrid{display:grid;gap:var(--sp-2);align-items:center;padding:7px var(--sp-4)}
.lgrid.lhead{background:var(--panel-2);color:var(--ink-2);font-size:var(--fs-5);
  border-bottom:1px solid var(--line)}
.lgrid.lrow{border-bottom:1px solid var(--line);font-size:var(--fs-4)}
.lgrid.lrow:hover{background:var(--panel-2)}
.lgrid.lrow.bad{background:#fdf6f6}
.lgrid input,.lgrid select{width:100%;min-width:0;padding:4px var(--sp-2);
  font-size:var(--fs-4);height:auto;line-height:inherit}
.cellstack{display:flex;flex-direction:column;gap:1px;min-width:0}
.cellops{text-align:right}
.ta-r{text-align:right}
.strong{font-variant-numeric:tabular-nums}
.sub{font-size:var(--fs-5);color:var(--ink-3);font-variant-numeric:tabular-nums}
.sub.warn{color:var(--warn)}
.sub.danger{color:var(--danger)}
/* ★★ 2026-10-16（第 1286 批 · 桌面端按族收口② · **一处顶 118 处**）：
   `.chklab` 本来就是 `<label>`（把勾和词一起包住）—— 问题是**高度随内容**（13~16），
   于是"有效热区＝元素或包住它的 label"取大的那个仍然是 13~16。
   ⇒ 给它 `min-height:24px`（桌面**指针**口径 WCAG 2.2 AA；不是触屏 44，理由见台账 §1874）。
   ★ 受益面：不止 `/admin`（扫描器量 `parent=chklab` **118 处 / 1 页**），
     全站凡用 `.chklab` 的页（`form.html`／`pay_list`／`import`／`_mail_reply_card` …）一起变大。 */
.chklab{display:flex;align-items:center;gap:4px;font-size:var(--fs-5);
  min-height:24px;color:var(--ink-3)}
.chk{width:auto!important;min-width:0!important}
/* ★★ 2026-10-16（第 1289 批 · 桌面端按族收口③ · **不依赖类名**）：
   「**有 `<label>` 包住、但那个 label 自己太矮**」的那几族 —— 第 1288 批量到的四数是
   `w=13 h=13 lw=78 lh=21`（`/v/customer/new`「继续创建」那种）⇒ **宽够、高差 3px**。
   ⇒ 用 `:has()` 选中"包着勾/单选的 label"给 24 的最小高度（桌面**指针**口径 WCAG 2.2 AA）；
     ★ 用**后代**形式（不是 `>`）：有些页把 `input` 套在 `<span>` 里，只用子选择器会漏。
     ★ 支持性已核：本机 **Edge 152.0.4191.66** ✓（`:has()` 自 Chromium 105 起）。
     ★ 取舍（看得见的变化）：这些 label 会**高 3px 左右**（21→24）—— 换取指针可点性达标；
       不这么做就得逐页加类名（几十处），那是把同一件事抄几十遍。 */
label:has(input[type=checkbox]), label:has(input[type=radio]){min-height:24px}
button.tiny{padding:1px var(--sp-2);font-size:var(--fs-5);line-height:1.4;
  height:auto}
button.danger{border-color:var(--danger);color:var(--danger)}

.bar{margin-top:var(--sp-4);align-items:center}
.btnlink{font-size:var(--fs-4);margin-left:auto}
.gate{margin-top:var(--sp-3);padding:var(--sp-3) var(--sp-4);
  border-radius:var(--r-2);background:#fdf3e6;border:1px solid #f0d5a8;color:#854f0b;
  font-size:var(--fs-4);display:flex;flex-direction:column;gap:var(--sp-2)}
.gate .chklab{color:#854f0b}
.card .v.ok{color:var(--ok)}

.modalBack{position:fixed;inset:0;background:rgba(31,35,41,.35);display:flex;
  align-items:center;justify-content:center;z-index:50}
.modal{background:var(--panel);border-radius:var(--r-4);padding:var(--sp-5);
  width:340px;max-width:92vw;
  /* ★ 2026-09-24 全站弹窗统一限高（老板实测「弹出的是卡死的」后定的通用法则）：
     内容比屏幕高的弹窗（49 个勾选框那种）以前会顶出屏幕外 ——
     保存/取消看不见 + 遮罩锁整页 = 用户看是"死机"。
     矮弹窗不受影响（max-height 只封顶）；超高的内部滚动。
     overscroll-behavior 防止滚到头把后面整页带着滚。 */
  max-height:86vh;overflow:auto;overscroll-behavior:contain}
.modal h3{margin:0 0 var(--sp-4);font-size:var(--fs-2);font-weight:600}
.modal .flab{display:flex;flex-direction:column;gap:var(--sp-1);
  margin-bottom:var(--sp-3)}
.modal .flab span{font-size:var(--fs-5);color:var(--ink-3)}
.modal .flab input{width:100%}

@media (max-width:920px){
  .lgrid{min-width:760px}
}

/* ===== 求助名片弹窗（093，张总 2026-09-28「求助打开这样」）+ 顶栏入口 =====
   为什么单独一段而不是复用 .modal 就够了：
     · .modal 固定 340px 且只管"表单弹窗"的字号，名片是**展示型**卡片，
       要更宽（380px）且行距不同 ⇒ 只在这里覆盖宽度，不动 .modal 本体
       （动它会波及全站 49 处弹窗）。
     · 头像/二维码用 object-fit:contain —— 传上来的图比例千奇百怪，
       cover 会把二维码**裁掉一个角**（客户扫不出来，而页面上看着"有图"）。
     ★ 顶栏入口 .navhelp 只借 .topnav 的输赢态样式，位置由它自己决定
       （顶栏右侧一串动作的**最前面**，与「⌨ 快捷键」同侧、同一档）——
       刻意**不**去动左边那四个「定目标 / 管钱货 / 做推广 / 所有」的结构。 */
.helpcard{width:380px}
/* ★ max-height 比通用 .modal（86vh）再收一点：名片是"展示型"卡片，
   内容比屏幕高的后果是**底部的「知道了」要滚动才看得见**（真浏览器截图看到过）。
   收到 82vh + 内部滚动，短名片不受影响。 */
.helpcard{max-height:82vh}
.helpcard .hc-top{display:flex;gap:var(--sp-4);align-items:center}
.helpcard .hc-ava{width:72px;height:72px;border-radius:50%;object-fit:cover;
  border:1px solid var(--line);background:var(--panel-2);flex:0 0 auto}
.helpcard .hc-ava.ph{display:flex;align-items:center;justify-content:center;
  font-size:26px;color:var(--ink-3)}
.helpcard .hc-name{font-size:var(--fs-2);font-weight:600}
.helpcard .hc-title{font-size:var(--fs-5);color:var(--ink-3);margin-top:2px}
.helpcard .hc-intro{margin:var(--sp-3) 0 0;font-size:var(--fs-4);line-height:1.7;
  white-space:pre-wrap}
/* ★ 二维码 180px 太高会把自我介绍顶出可视区（实测），收到 140px：
   二维码扫得出来靠的是"图清楚 + 屏幕亮"，不靠像素大。 */
.helpcard .hc-qr{margin:var(--sp-3) auto 0;width:140px;max-width:60%;
  border:1px solid var(--line);border-radius:var(--r-2);padding:6px;background:#fff}
.helpcard .hc-qr img{width:100%;display:block;object-fit:contain}
.helpcard .hc-qr .cap{font-size:var(--fs-5);color:var(--ink-3);text-align:center;
  margin-top:4px}
.helpcard .hc-row{display:flex;align-items:center;gap:var(--sp-2);
  margin-top:var(--sp-3);font-size:var(--fs-4)}
.helpcard .hc-row .k{color:var(--ink-3);flex:0 0 52px}
.helpcard .hc-row .v{font-weight:600;word-break:break-all}
.helpcard .hc-guide{font-size:var(--fs-4);line-height:1.7}
.helpcard .hc-guide ol{margin:var(--sp-2) 0 0;padding-left:1.3em}
.helpcard .hc-guide li{margin:4px 0}

/* ===== [UI] 经营看板 /dash + 快捷入口 =====
   这一块是验证方（界面侧）加的，看到 [UI] 标记请整块保留。
   变量全部走 :root，不写死颜色。
*/
.cards .card.kpi{border-left:3px solid var(--line)}
.cards .card.kpi.brand{border-left-color:var(--brand)}
.cards .card.kpi.red{border-left-color:var(--danger)}
.cards .card.kpi.orange{border-left-color:var(--warn)}
.cards .card.kpi.ok{border-left-color:var(--ok)}
.cards .card.kpi .sub{font-size:var(--fs-5);color:var(--ink-3);
  margin-top:var(--sp-1);line-height:1.4}
.kpi-link{display:block;color:inherit;text-decoration:none}
.kpi-link:hover{text-decoration:none}
.cards .card.kpi:hover{box-shadow:var(--sh-2)}

.quick{display:flex;flex-wrap:wrap;gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-4)}
.qbtn{display:inline-flex;align-items:center;gap:var(--sp-2);
  padding:10px var(--sp-4);border:1px solid var(--line);
  border-radius:var(--r-3);color:var(--ink);background:var(--panel)}
.qbtn:hover{border-color:var(--brand);color:var(--brand);text-decoration:none}
.qbtn .ic{width:16px;height:16px;color:var(--brand)}

/* ===== [UI] 丝滑动效（2026-09-16 统一补）=====
   全部走 CSS transition，不引动画库、不拖慢渲染。
   时长分级：交互反馈 120ms / 卡片 200ms / 侧栏 150ms / 页面 200ms。
   遵循 prefers-reduced-motion，用户系统开了「减少动态」就全部关闭。
*/
/* 侧栏：激活项左侧滑入 + 图标微动 */
.side a{transition:background .15s ease,color .15s ease,padding-inline-start .18s ease}
.side a:hover{padding-inline-start:calc(var(--sp-4) + 2px)}
.side a.on{padding-inline-start:calc(var(--sp-4) + 2px)}
.side a .ic{transition:transform .2s ease,color .15s ease}
.side a:hover .ic{transform:translateX(2px)}

/* 卡片：hover 上浮（KPI 卡已有一份，这里覆盖通用 .card） */
.card{transition:box-shadow .2s ease,transform .2s ease,border-color .2s ease}
.card:hover{box-shadow:var(--sh-2);transform:translateY(-2px)}

/* 快捷入口按钮 */
.qbtn{transition:border-color .15s ease,color .15s ease,background .15s ease,transform .12s ease}
.qbtn:hover{transform:translateY(-1px)}
.qbtn:active{transform:scale(.98)}

/* 表格行 hover 微亮 */
table tr{transition:background .12s ease}

/* 消息提示：滑入淡入 */
.alert{animation:xj-slide-in .24s ease}
@keyframes xj-slide-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}

/* 弹窗：淡入上浮 */
.modalBack{animation:xj-fade .18s ease}
.modal{animation:xj-pop .22s ease}
@keyframes xj-fade{from{opacity:0}to{opacity:1}}
@keyframes xj-pop{from{opacity:0;transform:translateY(10px) scale(.98)}to{opacity:1;transform:none}}

/* 主内容区：页面切换淡入 */
.main{animation:xj-page-in .2s ease}
@keyframes xj-page-in{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}

/* 顶栏下拉菜单（若有）展开 */
.top select,.sel{transition:border-color .15s ease,box-shadow .15s ease}

/* 尊重系统「减少动态」设置 */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
}

/* ===== [UI] 网站管理中枢（2026-09-16）===== */
.tabs{display:flex;flex-wrap:wrap;gap:var(--sp-2);
  border-bottom:1px solid var(--line);margin-bottom:var(--sp-4)}
.tabs a{display:inline-flex;align-items:center;gap:6px;
  padding:9px var(--sp-4);font-size:var(--fs-4);color:var(--ink-2);
  border-bottom:2px solid transparent;cursor:pointer;
  transition:color .15s ease,border-color .15s ease,background .15s ease;
  text-decoration:none;border-radius:var(--r-2) var(--r-2) 0 0}
.tabs a:hover{color:var(--brand);background:var(--brand-soft)}
.tabs a.on{color:var(--brand);border-bottom-color:var(--brand);font-weight:600}
.tabs a .ic{width:16px;height:16px}

.tab-panel{display:none}
.tab-panel.on{display:block;animation:xj-page-in .2s ease}

/* ★★★ 2026-10-17（界面美化 2.0 · P4）：**网站后台「15 个横排页签」→ 左栏分组导航**。
   只作用在 `.sadm` 里（`pages/siteadmin.html` 自己包的容器）——
   因为 `.tabs` / `.tab-panel` 还有 `customer_pool` / `workbench` / `lend_list` /
   `refund_new` / `_agent_panel` 四个模板在用，写成全局规则就是"改一个类砸五页"。
   布局手法：nav `float:left` 成左栏（`position:sticky` 跟随滚动），
   右侧 `.sadm-body` 给同宽左外边距 ⇒ 两块并排，**14 块 pane 一行都不用搬**。
   ≤1024 宽整条退回横排可滑动（下面那条媒体查询）。 */
.sadm .tabs{float:left;width:206px;flex-direction:column;flex-wrap:nowrap;gap:0;
  margin:0 var(--sp-4) var(--sp-4) 0;padding:var(--sp-2);
  border:1px solid var(--line);border-radius:var(--r-3);background:var(--panel);
  box-shadow:var(--sh-1);position:sticky;top:60px;
  max-height:calc(100vh - 78px);overflow:auto}
.sadm .tabs .tg{display:flex;flex-direction:column;gap:1px;margin-bottom:var(--sp-2)}
.sadm .tabs .tg:last-child{margin-bottom:0}
.sadm .tabs .tg-hd{display:block;font-size:var(--fs-5);color:var(--ink-3);
  letter-spacing:.4px;padding:6px 10px 3px}
.sadm .tabs a{border:0;border-radius:var(--r-2);padding:7px 10px;
  font-size:var(--fs-4);color:var(--ink-2)}
.sadm .tabs a:hover{background:var(--panel-2);color:var(--ink)}
.sadm .tabs a.on{background:var(--brand-soft);color:var(--brand);font-weight:600}
.sadm .sadm-body{margin-left:222px}
@media (max-width:1024px){
  .sadm .tabs{float:none;width:auto;flex-direction:row;flex-wrap:wrap;
    gap:var(--sp-2);position:static;max-height:none;padding:0;
    border:0;box-shadow:none;border-bottom:1px solid var(--line);
    margin:0 0 var(--sp-4)}
  .sadm .tabs .tg{flex-direction:row;flex-wrap:wrap;gap:var(--sp-2);margin:0}
  .sadm .tabs .tg-hd{display:none}
  .sadm .sadm-body{margin-left:0}
}

.skin-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));
  gap:var(--sp-3)}
.skin-card{display:flex;flex-direction:column;gap:var(--sp-3);align-items:flex-start;
  padding:var(--sp-4);border:2px solid var(--line);border-radius:var(--r-3);
  background:var(--panel);color:var(--ink);cursor:pointer;
  transition:border-color .15s ease,box-shadow .15s ease,transform .12s ease}
.skin-card:hover{border-color:var(--brand);box-shadow:var(--sh-2);transform:translateY(-2px)}
.skin-card.sel{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.skin-card .sw{display:flex;gap:4px}
.skin-card .sw i{width:22px;height:22px;border-radius:6px;border:1px solid var(--line)}
.skin-card .lbl{font-size:var(--fs-4);font-weight:600}

.form-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));
  gap:var(--sp-3)}
.field{display:flex;flex-direction:column;gap:var(--sp-1)}
.field label{font-size:var(--fs-4);font-weight:600}
.field label .muted{font-weight:400}
.field input{padding:0 var(--sp-3);border:1px solid var(--line);border-radius:var(--r-2);
  font:inherit;line-height:normal;background:var(--panel)}
.field input:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 2px var(--brand-soft)}

/* ★ 2026-10-16（第 1296 批 · 桌面按族收口；规格 §4.4：指针目标 24×24）：
   `.btnlink` 是「下一页 ›」「末页 »」「逐日明细 →」这类**独立行入口**（不是句内链接）
   ⇒ 真改：以前 26×20，矮 4px。★ 工具条里那份另有 `height`（见下面那条「两条打架」注释），
   所以这条 min-height 不会把工具条里的链接顶高。 */
.btnlink{display:inline-flex;align-items:center;min-height:24px;gap:6px;margin:var(--sp-3) 0 0;
  color:var(--brand);font-weight:500}

/* ★★ 2026-09-29（张总「全部 字段拥挤   对不齐的都优化掉」）：
   工具条 / 按钮行里的 `.btnlink` 必须**没有段间距**。
   为什么会有这一条 —— 本文件里 `.btnlink` 有**两条互相打架**的规则：
     · 452 行那条给了 `margin-left:auto`          ← "推到行尾"
     · 624 行那条给了 `margin:var(--sp-3) 0 0`    ← "链接型入口"的段间距
   两者选择器权重相同（都是单类），源顺序在后的赢 ⇒ 工具条里那个 <a> 被顶上
   12px，盒心比邻居低 **6.0px**。真浏览器量到中心 196.3 vs 202.3 ——
   **张总截图里「导出 CSV 比旁边矮半截」就是这个**（网站线索的 /leads 页，
   它把「导出 CSV」的 button 套在一个 btnlink 的 <a> 里，正是本文件下面
   那条注释里点名禁止的写法）。
   ★ 这里只把"工具条上下文里的段间距"归零，不动那条给正文用的规则
     （商品详情之类页面上的「查看全部 →」仍要保持段间距）。
   ★ 注释里**一个花括号都不要写**：本仓多个门禁（test_button_contrast /
     test_hidden_attr）用「最朴素的正则按花括号切规则」读这个文件，注释里
     出现花括号会把后面的规则切错 —— 我第一版就在这一条里抄了一整条规则
     （选择器带大括号那种），当场把 test_button_contrast 的反证弄红。
     写"某个选择器 + 某个属性"就够了，别把整条规则抄进注释。 */
.toolbar .btnlink,.bar .btnlink,.sec-tools .btnlink,.sec-head .btnlink,
.panel .hd .btnlink{margin:0;height:var(--ctl-h);line-height:normal}
.btnlink:hover{text-decoration:none;color:var(--brand-dk)}

/* ══════════════ 待审数字块（2026-10-18 第 1582 批 · 张总「干」）══════════════
   首页（`/`）与「管钱货」（`/money`）共用那一段（`pages/_home_blocks.html`）：
   原来「等着你审的单」是**四句文字链**（待审销售单 25 张 → 待审采购单 8 张 → …），
   扫一眼要读字、还容易看漏。改成**大数字 ＋ 小标签**的块，整块可点。
   ★ 只动"怎么摆"，不动"取什么数"：数字仍是服务端现算的 `pending.scrap/sample/docs`，
     去处仍是原来那几个 `url_for(...)`，一个字都没换（判据见下）。
   ★ 为什么不做成 `.card.kpi` 那一套：那一套是"看板数字卡"（有单位/口径小字），
     这里只有"几张单"，用更轻的一档（暖白底 + 细描边）才不抢上面 6 个数字。
   ★ 判据：`tests/test_m4_scrapui.py`（老板首页有「等着你审的单」＋「待审报损」，
     运营看不到）与 `tests/test_money_hub.py`（三块在两页都在）照旧绿；
     真浏览器另量 375 宽不横滚（`tools/_ui_audit_pass.py` 与 `test_m_double_floor.py`）。 */
.pend-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
  gap:var(--sp-2);margin:var(--sp-1) 0 var(--sp-2)}
.pend-item{display:flex;align-items:center;gap:var(--sp-2);
  padding:9px var(--sp-3);background:var(--panel-2);border:1px solid var(--line);
  border-radius:var(--r-2);color:var(--ink);text-decoration:none;
  transition:border-color .18s ease,box-shadow .18s ease}
.pend-item:hover{border-color:var(--brand);box-shadow:var(--sh-1);text-decoration:none}
.pend-n{flex:0 0 auto;font-size:var(--fs-1);font-weight:600;line-height:1.1;
  font-variant-numeric:tabular-nums;color:var(--ink)}
.pend-t{font-size:var(--fs-5);line-height:1.3;color:var(--ink-3)}
@media (max-width:720px){.pend-grid{grid-template-columns:1fr 1fr}
  /* ★ 375 宽下整块给到 44 高（《双端技术规格》§4.4 的触屏下限）——
     实测不加这条时是 40 高；虽然首页是桌面页、不在手机巡检表里，
     但张总在手机上直接看这个地址时也要好点。 */
  .pend-item{min-height:44px;padding:12px var(--sp-3)}}

/* 导航型按钮（长得像 button，其实是个链接）。
   ★ 为什么要这个类：`<a class="btnlink"><button>取消</button></a>` 是**非法
     HTML** —— `<button>` 不能嵌在 `<a>` 里。浏览器解析时会把内层 button
     提出来，于是页面上出现一个「空心胶囊 + 一个按钮」的怪东西。
     实测 2026-09-18：通用表单页的保存/取消区就是这个样子。
     凡是"点了要跳页"的按钮一律用 a.btnlike，别再套 button。 */
a.btnlike{display:inline-flex;align-items:center;gap:6px;
  height:var(--ctl-h);padding:0 var(--sp-4);line-height:normal;
  border:1px solid var(--line);
  border-radius:var(--r-2);background:var(--panel);color:var(--ink-2);
  font-size:var(--fs-4);text-decoration:none;cursor:pointer}
a.btnlike:hover{background:var(--brand-soft);color:var(--brand);
  border-color:var(--brand);text-decoration:none}
/* ★★ 2026-10-17（界面美化 2.0 · P1②）：**工具条里只有一颗"主操作"**。
   改前（真浏览器量 /v/wholesale_grade）：工具条里「＋ 新建批档位」「搜索」「清空」
   与两个下拉**同款同重**（都是 `.btnlike` / 白底描边），老板得读字才知道该按哪个。
   改后：`#btnNew`（列表页那颗「＋ 新建××」，模板里本来就有这个 id，见
   `pages/list.html` 的「A1-61（C09）」那条注释）**唯一实心**；其余保持空心。
   ★ 为什么用 `#btnNew` 而不是给按钮加新类：那个 id 早就存在、且**只给新建**用，
     改样式不必碰 40+ 个视图的模板（"能调用绝不新建"）。
   ★ 为什么不是"所有主按钮都变实心"：实心一多就等于没有主次（这是本条的初衷）。
   ★ 对比度：与全站 `button{}` 同一套（品牌底 ＋ `--brand-ink` 字），不新造搭配。 */
.toolbar #btnNew{background:var(--brand);border-color:var(--brand);
  color:var(--brand-ink);font-weight:600}
.toolbar #btnNew:hover{background:var(--brand-dk);border-color:var(--brand-dk);
  color:var(--brand-ink);text-decoration:none}
.toolbar #btnNew .ic{color:currentColor}

/* ══════════ 内容编辑 · 板块可视化 ══════════ */
.section-list{display:flex;flex-direction:column;gap:var(--sp-3)}
.sec-item{border:1px solid var(--line);border-radius:var(--r-2);background:var(--panel);
  overflow:hidden}
.sec-item.is-off{opacity:.55}
.sec-head{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-3)}
.sec-head b{font-size:var(--fs-3);font-family:ui-monospace,monospace}
.sec-actions{margin-inline-start:auto;display:flex;gap:var(--sp-2)}
/* ★★ .sec-btn / .skin-card 必须显式写 color（2026-09-18 实测抓到）
   基础的 `button{background:var(--brand); color:var(--brand-ink)}`（brand-ink 就是 #fff）
   是**白字**。
   这两个类只把底色改成了 var(--panel)（也是 #fff），**却没改文字色** ——
   于是白底白字，按钮上的字完全看不见（框还在、点得动、就是不显示文字）。
   受影响的都是"次要按钮"：客户公海弹窗的「取消 / × 关闭」、网站管理的
  「编辑 / 删除 / 上架 / 翻译成 9 语种 / ＋ 加 FAQ」、主题卡片的名字、工作台。
   `.sec-btn.primary` 因为显式写了 `color:#fff`（brand 底）才一直正常 ——
   正因为"主要的那个看得见"，这个缺陷才这么久没被发现。
   守卫：tests/test_button_contrast.py（含自测 + 反证）。
   ★★ A1-96（2026-09-29）补：上面第 2 行原来**照抄了一句行内注释**
     （`color:var(--brand-ink)` 后面跟着的那个 #fff 注释）—— 那句里的
     "注释开始两字符 + 注释结束两字符" 会让**本段注释提前闭合**，
     剩下的汉字于是被当成"下一条规则的选择器"（里头的花括号就是规则开始）
     ⇒ 紧跟其后的那条 `.sec-btn` 规则（padding / border / radius…）**整条被丢弃**
     （真浏览器 CSSOM 实测：数不到 `.sec-btn`，只有 a.sec-btn 与 .sec-btn.primary）——
     与下面 `.btnico` 那次是**同一个事故类型**。
   ★ 纪律：**CSS 注释里永远不要写出注释标记本身**（要指代就写"开始/结束两字符"）；
     也**不要在注释里写"类名紧跟花括号"那种字面量** —— 本仓多个门禁用朴素正则
     读花括号（`tests/test_button_contrast.py` 就按 `.sec-btn` + 花括号 去找规则），
     注释里写一次就会把它们的解析带偏。 */
.sec-btn{padding:5px 12px;border:1px solid var(--line);border-radius:var(--r-2);
  /* ★ 刻意保留自己高度：它是网站后台内容编辑区的小按钮，不要吃 --ctl-h
     （吃了会被撑成 32px、与它旁边的小字不搭）。
     ★ 这一条**写在 color 那几句之前**是有意的：tests/test_button_contrast.py
     的反证补丁按字面量（background / color / cursor / font-size 连着的那一段
     直到右花括号）去替换 CSS。把这几个声明拆开、或插在它们中间，那个补丁就
     匹配不上、反证不红 —— 别人的门禁锚点不许被我顺手弄断。
     ★ 这段注释里**一个花括号都没有**，原因同上一条：朴素正则读不得花括号。 */
  height:auto;line-height:inherit;
  background:var(--panel);color:var(--ink);cursor:pointer;font-size:var(--fs-4)}
.sec-btn:hover{border-color:var(--brand)}
/* ★ .sec-btn 原本只给 button 用。表格里的「选它」是**跳转**，必须是 <a>
   （<a> 里再套 <button> 会渲染成空心胶囊，2026-09-18 已在 list.html 踩过）。
   但 <a> 默认 display:inline，纵向 padding 不撑行高，看着是扁的；
   而且链接色/下划线要压掉。这三行就是给 a.sec-btn 补上的。 */
a.sec-btn{display:inline-block;text-decoration:none;color:var(--ink)}
.sec-btn.primary{background:var(--brand);color:#fff;border-color:var(--brand)}
.sec-btn.primary:hover{background:var(--brand-dk)}
.sec-body{padding:var(--sp-3);border-top:1px solid var(--line);display:flex;
  flex-direction:column;gap:var(--sp-3)}
.sec-body .field{display:flex;flex-direction:column;gap:var(--sp-1)}
.sec-body .field label{font-size:var(--fs-4);font-weight:600}
.sec-body input[type="text"],.sec-body input:not([type]),.sec-body .sec-url{
  padding:0 var(--sp-3);border:1px solid var(--line);border-radius:var(--r-2);
  font:inherit;line-height:normal;background:var(--panel)}
.sec-body textarea{padding:8px var(--sp-3);border:1px solid var(--line);
  border-radius:var(--r-2);font:inherit;background:var(--panel);resize:vertical}
.sec-imgs{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-bottom:var(--sp-2)}
.sec-img{position:relative;width:90px;height:90px;border:1px solid var(--line);
  border-radius:var(--r-2);overflow:hidden}
.sec-img img{width:100%;height:100%;object-fit:cover;display:block}
.sec-img i{position:absolute;top:2px;right:2px;width:20px;height:20px;background:rgba(0,0,0,.6);
  color:#fff;border-radius:50%;display:grid;place-items:center;cursor:pointer;font-style:normal;
  font-size:13px;line-height:1}
.sec-addimg{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap}
.sec-addimg .sec-url{flex:1;min-width:180px}

/* ══════════ 版块行 · 员工友好版（2026-09-23 张总要求）
   原话：「中文部分要写清楚 是网页的哪个部分 点击进去不知道 有个截图图标
           让员工一眼就知道 以后维护靠他们 他们文化不高」
   设计：每行左侧一张"这一块在官网长什么样"的缩略图 + 详细中文位置说明，
         右侧「🔍 看官网效果」按钮直接跳到官网那一块。
   ★ 全部用 light 主题变量，不写死颜色。 */
.sec-row{display:flex;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-3)}
.sec-thumb{flex:0 0 168px;width:168px;height:104px;border:1px solid var(--line);
  border-radius:var(--r-2);overflow:hidden;background:var(--panel-2,#f3f5f7);
  position:relative;cursor:zoom-in}
.sec-thumb img{width:100%;height:100%;object-fit:cover;object-position:top center;display:block}
.sec-thumb .no-pic{display:grid;place-items:center;height:100%;font-size:var(--fs-4);
  color:var(--muted);text-align:center;padding:var(--sp-2);line-height:1.5}
.sec-thumb .shot-at{position:absolute;left:0;right:0;bottom:0;background:rgba(0,0,0,.55);
  color:#fff;font-size:11px;padding:2px 6px;text-align:right;letter-spacing:.2px}
.sec-thumb.stale{border-color:#e0a800;box-shadow:0 0 0 2px rgba(224,168,0,.18)}
.sec-thumb .stale-badge{position:absolute;left:0;top:0;background:#e0a800;color:#3a2c00;
  font-size:11px;padding:2px 6px;border-bottom-right-radius:var(--r-2);font-weight:600}
.sec-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px}
.sec-where{font-size:var(--fs-3);font-weight:600;line-height:1.5;color:var(--ink)}
.sec-where .pg{display:inline-block;background:var(--brand);color:#fff;border-radius:var(--r-2);
  padding:1px 8px;font-size:var(--fs-4);margin-inline-end:6px;white-space:nowrap}
.sec-where .none{color:var(--muted);font-weight:400}
.sec-key{font-family:ui-monospace,monospace;font-size:var(--fs-4);color:var(--muted)}
.sec-tools{display:flex;gap:var(--sp-2);flex-wrap:wrap;align-items:center;margin-top:2px}
.sec-tools .sec-btn{white-space:nowrap}
.sec-tools .spacer{flex:1}
/* ★ 保存反馈就地显示在按钮上（2026-09-23 张总"点保存没提示"）：
   顶部 #alert 在页面最顶上，滚到下面编辑根本看不见 ⇒ 按钮自己变色变字。 */
.sec-btn.saved-ok{background:#1e9e5a;color:#fff;border-color:#1e9e5a}
.sec-btn.saved-err{background:#c0392b;color:#fff;border-color:#c0392b}
.sec-btn:disabled{opacity:.7;cursor:wait}
/* 保存失败的具体原因，就地显示在保存按钮左边 */
.save-err{margin-inline-end:auto;align-self:center;font-size:var(--fs-4);
  color:#c0392b;background:#fdecea;border:1px solid #f5c6c0;
  border-radius:var(--r-2);padding:3px 10px;max-width:60%;line-height:1.5}
/* 顶部的体检/重抓工具条 */
.thumb-bar{display:flex;gap:var(--sp-2);align-items:center;flex-wrap:wrap;
  padding:var(--sp-3);border:1px solid var(--line);border-radius:var(--r-2);
  background:var(--panel);margin-bottom:var(--sp-3)}
.thumb-bar .tip{font-size:var(--fs-4);color:var(--muted);line-height:1.6;flex:1;min-width:220px}
.thumb-bar .tip b{color:#c0392b}
/* 图片放大预览（点缩略图看大图） */
.thumb-zoom{position:fixed;inset:0;background:rgba(0,0,0,.72);display:none;
  align-items:center;justify-content:center;z-index:9999;cursor:zoom-out;padding:24px}
.thumb-zoom.on{display:flex}
.thumb-zoom img{max-width:92vw;max-height:92vh;border-radius:var(--r-2);
  box-shadow:0 12px 48px rgba(0,0,0,.5);background:#fff}
.sec-foot{display:flex;justify-content:flex-end}

/* ══════════ 博客编辑 + 客户授权 ══════════ */
.sec-editor{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:60;
  display:flex;align-items:center;justify-content:center;padding:var(--sp-4)}
.sec-editor[hidden]{display:none}
.sec-editor>.hd{background:var(--panel);border-bottom:1px solid var(--line);
  padding:var(--sp-3);border-radius:var(--r-2) var(--r-2) 0 0;display:flex;
  align-items:center;gap:var(--sp-2);font-weight:600}
.sec-editor>.bd{background:var(--panel);border-radius:0 0 var(--r-2) var(--r-2);
  padding:var(--sp-4);max-width:920px;width:100%;max-height:86vh;overflow:auto}
.blog-langs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:var(--sp-3);margin-top:var(--sp-3)}
.blog-lang{border:1px solid var(--line);border-radius:var(--r-2);padding:var(--sp-3);
  display:flex;flex-direction:column;gap:var(--sp-2)}
.blog-lang b{font-size:var(--fs-3)}
.blog-lang label{display:flex;flex-direction:column;gap:var(--sp-1);font-size:var(--fs-4)}
.blog-lang input,.blog-lang textarea{border:1px solid var(--line);
  border-radius:var(--r-2);font:inherit;background:var(--panel)}
.blog-lang input{padding:0 var(--sp-3);line-height:normal}
.blog-lang textarea{padding:8px var(--sp-3);resize:vertical}
.frm{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-2) var(--sp-3)}
.frm label{display:flex;flex-direction:column;gap:var(--sp-1);font-size:var(--fs-4)}
.frm input,.frm select,.frm .inp{padding:0 var(--sp-3);border:1px solid var(--line);
  border-radius:var(--r-2);font:inherit;line-height:normal;background:var(--panel)}
.cust-item{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-3);
  border:1px solid var(--line);border-radius:var(--r-2);background:var(--panel);flex-wrap:wrap}
.cust-item .ci-main{display:flex;flex-direction:column;gap:2px;min-width:200px}
.cust-item .ci-main b{font-size:var(--fs-3)}
.cust-item .ci-tags{display:flex;gap:var(--sp-2);flex-wrap:wrap;margin-inline-start:auto;align-items:center}
.cust-item .ci-actions{display:flex;gap:var(--sp-2)}
.badge{display:inline-block;padding:2px 8px;border-radius:999px;font-size:var(--fs-4);border:1px solid var(--line)}
.badge.t1{background:#fff3e0;color:#b26a00;border-color:#ffd8a8}
.badge.t2{background:#e3f2fd;color:#1565c0;border-color:#b3e5fc}
.badge.t3{background:#ede7f6;color:#5e35b1;border-color:#d1c4e9}
.badge.off{background:#fbe9e7;color:#c62828;border-color:#ffccbc}
.blog-meta{display:flex;gap:var(--sp-2);flex-wrap:wrap;align-items:center}
.blog-meta .muted{font-size:var(--fs-4)}
@media (max-width:760px){.blog-langs{grid-template-columns:1fr}.frm{grid-template-columns:1fr}}

/* ══════════ 监控统计 ══════════ */
.stats-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3);margin-top:var(--sp-3)}
.stats-grid>.panel{margin:0}
.stats-grid>.panel>.hd{font-size:var(--fs-4);font-weight:600}
.bars{display:flex;flex-direction:column;gap:6px}
.bar-row{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-4)}
.bar-label{width:120px;flex-shrink:0;color:var(--ink-2);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis;font-variant-numeric:tabular-nums}
.bar-track{flex:1;height:14px;background:var(--panel-2);border-radius:7px;overflow:hidden}
.bar-fill{display:block;height:100%;background:var(--brand);border-radius:7px;
  transition:width .4s ease}
.bar-num{width:80px;text-align:right;color:var(--ink-3);font-variant-numeric:tabular-nums}
@media (max-width:760px){.stats-grid{grid-template-columns:1fr}.bar-label{width:80px}}



/* ══════════ 知识库投喂 ══════════ */
.kb-top{display:flex;flex-direction:column;gap:var(--sp-3)}
.kb-actions{display:flex;gap:var(--sp-3);align-items:center;flex-wrap:wrap}
.kb-upload{display:inline-flex;align-items:center;gap:8px;padding:8px 16px;
  border:1px dashed var(--brand);color:var(--brand);border-radius:var(--r-2);
  cursor:pointer;font-size:var(--fs-4)}
.kb-upload:hover{background:var(--brand-soft,rgba(26,127,90,.06))}

/* ══════════ 希捷工作台（并入 ERP 的管理台账） ══════════ */
.ws-kpi{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-3);margin:var(--sp-3) 0}
.ws-kpi-box{border:1px solid var(--line);border-radius:var(--r-2);background:var(--panel);
  padding:var(--sp-4);display:flex;flex-direction:column;gap:var(--sp-1)}
.ws-kpi-box .n{font-size:28px;font-weight:700;color:var(--brand);font-variant-numeric:tabular-nums}
.ws-kpi-box .l{font-size:var(--fs-4);color:var(--ink-2)}
.badge.warn{background:#fdecea;color:#c62828;border-color:#ffcdd2}
/* ★★ 2026-09-26（参考百草·一期 1-5）：列表页**状态色块**要用到的三档。
   原来 .badge 只有 t1/t2/t3/off/warn 五个实心档（都偏"标签"用途）——
   缺「做完了(绿)」「作废/停用(灰)」「卡住了(红)」这三档，而那正是状态列的语义。
   ★ 颜色与 .tag.ok / .tag.mute / .tag.danger 用同一套 CSS 变量，
     免得同一个"已审核"在详情页是绿字标签、在列表页是另一个绿。
   ★ 为什么必须加规则：本仓反复踩过「加了 class 没加规则」——
     那样子元素会退回 .badge 的灰底，老板看不出哪个做完了（等于没上色）。 */
.badge.ok{background:#e8f5e9;color:var(--ok);border-color:#c8e6c9}
.badge.mute{background:#f4f5f6;color:var(--ink-3);border-color:var(--line)}
.badge.danger{background:#fdecea;color:var(--danger);border-color:#ffcdd2}
/* ★ 时间筛选快捷档（一期 1-2）：一排药丸，选中那个实心。
   ★★ 2026-09-29：药丸**也吃 --ctl-h**（原来 3px 上下内边距 ⇒ 实测 27.2px，
   与旁边 31/32px 的按钮挤在一行里"高矮不一"，张总说的就是这一排）。
   同时干掉 `margin-inline-end:var(--sp-1)` —— 那一笔 4px 让药丸与后一个
   控件之间变成 12px，而工具条别的间距都是 8px（实测 ['12.0','8.0','8.0'…]）
   ⇒ 这就是"间距忽宽忽窄"的另一半。药丸仍是胶囊形（999px 圆角），
   只统一**高度与间距**，不动颜色/字号。 */
.quicktime{display:inline-flex;flex-wrap:wrap;gap:var(--ctl-gap)}
.quicktime .qchip{display:inline-flex;align-items:center;height:var(--ctl-h);
  padding:0 10px;border:1px solid var(--line);border-radius:999px;
  font-size:var(--fs-5);color:var(--ink-2);text-decoration:none;background:var(--panel);
  white-space:nowrap}
.quicktime .qchip:hover{border-color:var(--brand);color:var(--brand)}
.quicktime .qchip.on{background:var(--brand);border-color:var(--brand);color:var(--brand-ink)}
/* ★ 列选择器面板（一期 1-1）：钉在工具条下方的浮层 */
.colspanel{position:absolute;z-index:40;margin-top:6px;width:min(760px,92vw);
  max-height:60vh;display:flex;flex-direction:column;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-2);
  box-shadow:0 8px 24px rgba(0,0,0,.14)}
.colspanel .cp-hd{padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line);
  display:flex;gap:var(--sp-3);align-items:baseline;font-size:var(--fs-4)}
.colspanel .cp-bd{padding:var(--sp-3) var(--sp-4);overflow:auto;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:2px 12px}
.colspanel .colitem{display:flex;align-items:center;gap:6px;font-size:var(--fs-4);
  padding:2px 0;cursor:pointer}
.colspanel .colitem .muted{font-size:var(--fs-5)}
.colspanel .cp-ft{padding:var(--sp-3) var(--sp-4);border-top:1px solid var(--line);
  display:flex;gap:var(--sp-2);justify-content:flex-end}
@media (max-width:760px){.ws-kpi{grid-template-columns:1fr}}

/* ══════════ OKR 对齐树 ══════════ */
.okr-legend{display:flex;align-items:center;gap:16px;font-size:var(--fs-4);color:var(--ink-2);margin-bottom:var(--sp-3)}
.okr-legend .dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-inline-end:5px;vertical-align:middle}
.okr-legend .dot.g{background:#639922}.okr-legend .dot.y{background:#EF9F27}.okr-legend .dot.r{background:#E24B4A}
.okr-tree{min-height:200px;overflow-x:auto;padding:var(--sp-3) 0}
.okr-tree .okr-row{display:flex;align-items:center;gap:12px;padding:8px 4px;border-radius:var(--r-2);cursor:pointer;transition:background .15s}
.okr-tree .okr-row:hover{background:var(--bg-2)}
.okr-tree .okr-row .nd{flex:0 0 auto;padding:8px 14px;border-radius:999px;border:1px solid var(--line);
  background:var(--panel);font-size:var(--fs-4);color:var(--ink);white-space:nowrap;position:relative}
.okr-tree .okr-row .nd .pct{font-weight:700;margin-inline-start:6px;font-variant-numeric:tabular-nums}
.okr-tree .okr-row .nd.g{border-color:#97C459;background:#EAF3DE}
.okr-tree .okr-row .nd.y{border-color:#FAC775;background:#FAEEDA}
.okr-tree .okr-row .nd.r{border-color:#F09595;background:#FCEBEB}
.okr-tree .okr-row .nd.g .pct{color:#3B6D11}
.okr-tree .okr-row .nd.y .pct{color:#854F0B}
.okr-tree .okr-row .nd.r .pct{color:#A32D2D}
.okr-tree .okr-row .own{font-size:var(--fs-4);color:var(--ink-2);white-space:nowrap}
.okr-tree .okr-children{margin-inline-start:32px;padding-inline-start:16px;border-inline-start:2px dashed var(--line)}
.okr-tree .okr-krs{font-size:var(--fs-4);color:var(--ink-2);margin-inline-start:8px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}
.okr-tree .okr-acts{margin-inline-start:auto;display:flex;gap:6px;opacity:0;transition:opacity .15s}
.okr-tree .okr-row:hover .okr-acts{opacity:1}

/* ══════════ 首页三大板块入口 ══════════ */
.home-hero h1{font-size:24px;font-weight:500;margin-bottom:4px}
.home-hero .sub{color:var(--ink-2);font-size:14px;margin-bottom:var(--sp-5)}
.home-boards{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4)}
.home-boards .board{display:flex;gap:var(--sp-3);align-items:flex-start;padding:var(--sp-5);
  border:1px solid var(--line);border-radius:var(--r-2);background:var(--panel);
  text-decoration:none;transition:transform .12s,box-shadow .12s}
.home-boards .board:hover{transform:translateY(-2px)}
.home-boards .board .ic{width:44px;height:44px;border-radius:12px;display:flex;
  align-items:center;justify-content:center;flex:0 0 44px}
.home-boards .board .ic .ic{width:22px;height:22px;color:#fff}
.home-boards .board .name{font-size:17px;font-weight:500;color:var(--ink);margin-bottom:2px}
.home-boards .board .desc{font-size:13px;color:var(--ink-2);margin-bottom:8px}
.home-boards .board .items{font-size:12px;color:var(--ink-3)}
.home-boards .b-goal .ic{background:#534AB7}
.home-boards .b-money .ic{background:#0F6E56}
.home-boards .b-grow .ic{background:#185FA5}
@media (max-width:760px){.home-boards{grid-template-columns:1fr}}

/* ══════════ 经营看板 全屏驾驶舱（老板专属） ══════════ */
.dash-head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4);flex-wrap:wrap}
.dash-okr{display:flex;align-items:center;gap:var(--sp-3);background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-3);padding:var(--sp-3) var(--sp-4)}
.dash-okr-label{font-size:var(--fs-5);color:var(--ink-3)}
.dash-okr-pct{font-size:22px;font-weight:600;color:var(--brand)}
.dash-okr-dots{display:flex;align-items:center;gap:var(--sp-1);font-size:var(--fs-5);color:var(--ink-2)}
.dash-okr-dots .d{width:9px;height:9px;border-radius:50%;display:inline-block}
.dash-okr-dots .d.green{background:var(--ok)}
.dash-okr-dots .d.yellow{background:var(--warn)}
.dash-okr-dots .d.red{background:var(--danger)}

.dash-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:var(--sp-4);margin-bottom:var(--sp-4)}
.dash-todos h2,.dash-right h2{font-size:var(--fs-2);margin:0;padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--line)}
.dash-todo-desc{font-size:var(--fs-5);color:var(--ink-3);margin-top:2px}
.dash-right{display:flex;flex-direction:column;gap:var(--sp-4)}

/* ★★★ 2026-10-17（界面美化 2.0 · P2-a）：「近 7 日销售额」重画的样式。
   三件事（对应模板里那段注释）：柱加宽；柱顶标值；日均虚线 ＋ 0 值灰条。
   ★ 图例（`.trend-note`）是**一句话**，不引入图表库、不做交互 —— 看板是"扫一眼"的页。 */
.dash-trend{display:flex;align-items:flex-end;gap:var(--sp-2);height:138px;
  padding:20px var(--sp-4) 0;position:relative}
.dash-trend .bar-col{flex:1;position:relative;height:100%;min-width:0}
/* ★★ 柱顶数值**绝对定位**（不占列内高度）。
   为什么必须这样：第一版把 `<span>` 和柱当两个 flex 子项、柱写 `height:X%`，
   实测（`tools/_p2_measure_trend.py`）最大值那根只有 **86px**、而列高 118px ——
   %高度是相对**列高**算的，算完再被 flex 收缩（兄弟 span 占了 13+3px）⇒
   所有柱**按同一比例被压扁**，柱顶与数值之间还固定多出 19px 空隙。
   ⇒ 数值走绝对定位（`bottom:calc(柱高 + 3px)`，柱高由内联 `--h` 给），柱高就回到真比例。 */
.dash-trend .bar-val{position:absolute;left:0;right:0;
  bottom:calc(var(--h, 0%) + 3px);text-align:center;
  font-size:10px;line-height:1.2;color:var(--ink-3);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.dash-trend .bar{position:absolute;left:50%;transform:translateX(-50%);bottom:0;z-index:1;
  width:64%;max-width:40px;height:var(--h, 0%);background:var(--brand);
  border-radius:var(--r-1) var(--r-1) 0 0;min-height:2px;
  transition:height var(--dur-2) var(--ease)}
/* 0 值那天：**画一根灰条**（不是不画）—— 空柱与"图表坏了"在观感上必须能分开 */
.dash-trend .bar.zero{background:var(--line-strong);border-radius:2px}
/* 日均虚线：**压在柱子下面**（z-index:0 —— 柱子 1）。第一版画在柱子上层，
   实测截图上虚线会从绿柱身上穿过去（`_dash_crop2.png`），像划了一道疤。 */
.trend-avg{position:absolute;left:var(--sp-4);right:var(--sp-4);z-index:0;
  border-top:1px dashed var(--line-strong);pointer-events:none}
.trend-avg span{position:absolute;right:0;top:-15px;font-size:10px;color:var(--ink-3);
  background:var(--panel);padding:0 4px;font-variant-numeric:tabular-nums}
.bar-axis{display:flex;gap:var(--sp-2);padding:5px var(--sp-4) 0}
.bar-axis span{flex:1;text-align:center;font-size:10px;color:var(--ink-3);
  font-variant-numeric:tabular-nums;min-width:0;overflow:hidden;text-overflow:ellipsis}
.trend-note{font-size:11px;color:var(--ink-3);margin:8px var(--sp-4) 12px}

.dash-okr-list{list-style:none;margin:0;padding:var(--sp-2) var(--sp-4) var(--sp-4)}
.dash-okr-list li{display:flex;align-items:center;gap:var(--sp-2);padding:5px 0;font-size:var(--fs-4)}
.dash-okr-list .okr-name{flex:0 0 auto;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink-2)}
.dash-okr-list .okr-owner{flex:0 0 auto;font-size:var(--fs-5);color:var(--ink-3)}
.dash-okr-list .okr-bar{flex:1;height:6px;background:var(--panel-2);border-radius:3px;overflow:hidden}
.dash-okr-list .okr-bar .fill{display:block;height:100%;border-radius:3px}
.dash-okr-list .okr-bar .fill.green{background:var(--ok)}
.dash-okr-list .okr-bar .fill.yellow{background:var(--warn)}
.dash-okr-list .okr-bar .fill.red{background:var(--danger)}
.dash-okr-list .okr-pct{flex:0 0 auto;width:38px;text-align:right;font-size:var(--fs-5);color:var(--ink-2)}

@media (max-width:920px){.dash-grid{grid-template-columns:1fr}}

/* ══════════ 宫格卡片（`.acc-grid` / `.acc-item`）══════════
   ★ 名字里的 `acc` 是历史（当年「管钱货」是个 `<details>` 手风琴），**手风琴那半套已经删了**
     （见下面 2026-10-06 第 126 批那条）。现在这两个类由 `pages/hr.html`（人事四板块卡，
     配 `.acc-grid.hr-cards` 的宽列）与 `pages/workbench.html` 在用 ——
     ★ **改名的代价大于收益**（两个模板 + 人事那套列宽 + 判据都要跟着动），所以名字留着，
       但"到底谁在用"由门禁**现算**（`tests/test_ui_baicao_p1.py` 第 2 节），不靠注释。 */
.acc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));
  gap:var(--sp-2);padding:0 var(--sp-4) var(--sp-4)}
.acc-item{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-3);
  border:1px solid var(--line);border-radius:var(--r-2);background:var(--panel);
  color:var(--ink);font-size:var(--fs-3);text-decoration:none;transition:border-color .12s}
.acc-item:hover{border-color:var(--brand);text-decoration:none}
.acc-item .tag{flex:0 0 auto}
/* ★★ 2026-10-06（第 126 批·**删掉死样式**）：这里原来还有手风琴那一套
   （`.acc{…}` / `.acc summary…` / `.acc-ic…` / `.acc-t…` / `.acc-arrow{…}` / `.acc[open]…`，
   共 11 条规则）—— 它们唯一的用户是 `pages/money.html`，而那个模板在第 125 批**已删**
   （它是孤儿：第 124 批删掉它那条影子 `/money` 路由之后再没有任何 `render_template` 指向它）。
   ⇒ 现算"CSS 里有定义、但没有任何模板在用"的类 = `.acc` / `.acc-ic` / `.acc-t` / `.acc-arrow`
   （探针 `_baicao_study/tools/_q_b125_tpl.py`）⇒ 本批把这 11 条**整段删掉**。
   ★ 判据跟着升级（`tests/test_ui_baicao_p1.py`）：第 125 批那条"只打印不判红"的留痕
     变成**有牙的判据** —— 「main.css 里不许再出现**没有任何模板在用**的 `acc*` / `home-hero` 类」
     （反证锚：把任意一条加回去 ⇒ 当场红，`tests/_falsify_b126_dead_css.py`）。
   ★ 剩下的 `.acc-grid` / `.acc-item` **不能删**：hr.html 与 workbench.html 在用（现算）。 */


/* ══════════════ 客户档案「账务」面板（2026-09-26 对标百草 · 台账第 1 项）══════════════
   ★ 为什么单独一套 grid 而不用现成的 .cards：`.cards` 是 `auto-fill(minmax(150px,1fr))`，
     auto-fill 会**留着空轨道** —— 4 张卡被塞进 8 条 150px 的轨道里，每张只剩 ~165px，
     于是「¥2,780,651.10」这种七位数就被挤出框了（真浏览器探针量到的，不是猜的）。
     `auto-fit` 会把空轨道收掉、卡片撑满整行 ⇒ 4 张卡每张 ~330px，七位数也放得下。 */
.acct-cards{display:grid;gap:var(--sp-3);
  grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.acct-cards .card .v{font-size:19px;white-space:nowrap}

/* ══════════════ P3（2026-09-26 对标百草）：商品**卡片视图**（带图）══════════════
   百草的「商品」是带图的卡片墙（张总截图：「销售点击进来要是这样的」）。
   我们原来是 12 列密表 —— 认货靠看图的活（选品、报价、给客户发图）干不了。
   ★ 只对有图的视图开放（服务端 CARD_VIEWS 判），表格一键切回。
   ★ 图是**懒加载**的：一屏 50 张，不 lazy 会在打开瞬间发 50 个请求。 */
.skucards{display:grid;grid-template-columns:repeat(auto-fill,minmax(178px,1fr));
  gap:var(--sp-3);padding:var(--sp-4)}
.skucard{display:flex;flex-direction:column;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-2);overflow:hidden;
  text-decoration:none;color:var(--ink);box-shadow:var(--sh-1);
  transition:box-shadow .18s ease,transform .18s ease,border-color .18s ease}
.skucard:hover{box-shadow:var(--sh-2);transform:translateY(-2px);
  border-color:var(--brand);text-decoration:none}
.skucard.nolink{cursor:default}
.skucard .sc-img{display:block;aspect-ratio:1/1;background:#F7F4EC;overflow:hidden;
  position:relative}
.skucard .sc-img img{width:100%;height:100%;object-fit:cover;display:block}
/* ★ 没有图的商品：**明说「无图」**（灰底 + 一行小字）——
   不拿别的图凑、也不留一个让人以为是"图没加载出来"的空白格。 */
.skucard.noimg .sc-img::after{content:'无图';position:absolute;right:6px;bottom:6px;
  font-size:var(--fs-5);color:var(--ink-3);background:rgba(255,255,255,.85);
  border:1px solid var(--line);border-radius:var(--r-1);padding:0 4px}
.skucard .sc-t{padding:6px 8px 0;display:flex;flex-direction:column;gap:1px;min-width:0}
.skucard .sc-t b{font-size:var(--fs-4);font-weight:600;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.skucard .sc-t small{font-size:var(--fs-5);color:var(--ink-3)}
.skucard .sc-p{padding:2px 8px 8px;font-size:var(--fs-4);font-weight:600;color:var(--brand)}
.skucard .sc-p i{font-style:normal;font-weight:400;color:var(--ink-3);
  font-size:var(--fs-5)}
/* 卡片/表格 切换键的"当前态" */
#btnCard.on{background:var(--brand-soft);color:var(--brand);border-color:var(--brand)}
/* ★★ 2026-10-07（第 237 批 · 照百草「客户列表＝卡片墙」）：**往来方头像卡片**。
   与商品卡片共用 `.skucards/.skucard/.sc-t/.sc-p` 那一套（不新造一套卡片样式），
   只把"图"那一格换成**圆形头像**：有图走 `<img>`、没图走首字占位 `.sc-ph`。 */
.skucard .sc-img.av{display:flex;align-items:center;justify-content:center;
  background:#F3F5F8}
.skucard .sc-img.av img{width:64%;height:64%;border-radius:50%;object-fit:cover}
.skucard .sc-ph{width:64%;height:64%;border-radius:50%;background:#DDE3EA;color:#7C8794;
  display:flex;align-items:center;justify-content:center;font-size:20px;font-weight:600}
.skucard.avcard .sc-p{font-weight:400;color:var(--ink-3);font-size:var(--fs-5)}
/* ★★ 2026-10-21（张总「往来 360 页面在哪里 我后台找不到」· 拍板 A）：
   卡片视图原来**没有** 360 入口（只有表格视图行尾那一颗），而卡片是默认视图
   ⇒ 入口等于不存在。这里给每张往来方卡片补一颗「360 看全」。
   ★ 外层 `div.avcardwrap` 是因为卡片本身是 `<a>`，`<a>` 不能套 `<a>`
     （非法 HTML，浏览器会拆开）⇒ 360 只能做**兄弟节点**。
   ★ `min-height:44px`：`tests/test_desktop_tap_floor` 是**只减不增**的棘轮
     （375 宽下全站可点件 <44 的合计 ≤ 2），新加的可点件必须自己给到 44。 */
.avcardwrap{display:flex;flex-direction:column;gap:6px;min-width:0}
.avcardwrap > .skucard{flex:1 1 auto}
.avcard360{display:flex;align-items:center;justify-content:center;
  min-height:44px;padding:0 8px;border:1px solid var(--line);border-radius:var(--r-2);
  background:var(--panel);color:var(--brand);font-size:var(--fs-5);font-weight:600;
  text-decoration:none;white-space:nowrap}
.avcard360:hover{border-color:var(--brand);background:var(--brand-soft);
  text-decoration:none;color:var(--brand)}
@media (max-width:920px){.skucards{grid-template-columns:repeat(auto-fill,minmax(132px,1fr))}}

/* ★ P2（2026-09-26）列表页三件小事：
   ① 工具条按钮里加小图标（百草是「图标+文字」）—— 只加壳，不动任何 JS/参数；
   ② 表格**行悬停**：暖底（--panel-2）—— 一屏 20+ 行时，眼睛不容易串行；
   ③ 表头**吸顶**：长列表往下滚时列名不跑掉（`.list-panel` 见下面的 overflow 说明）。
   ★★ A1-96（2026-09-29）补：上面这段注释的**开始两字符**曾被人删掉（只剩结尾两字符），
      于是 CSS 解析器把整段汉字当成"`.btnico` 那条规则的前半截选择器"
      ⇒ `.btnico{display:inline-flex;…}` **整条被丢弃**。真浏览器 CSSOM 实测：
      main.css 解析出 552 条规则，找不到 `.btnico`，只有 `.btnico .ic`（图标+文字之间
      那 6px 间距与对齐全站失效）。补回开始两字符即修好。
   ★ 纪律（写这一段时**自己又踩了一次**，被 tests/test_a196_table_head.py 当场判红）：
     **注释里绝对不要写出注释标记本身**（我第一版在这里引用了"结束两字符"，
     注释当场提前闭合，`.btnico` 又被吃掉）。要指代就写"开始/结束两字符"。 */
.btnico{display:inline-flex;align-items:center;gap:6px}
.btnico .ic{width:15px;height:15px;flex:0 0 15px;color:currentColor;opacity:.9}
tbody tr:hover>td{background:var(--panel-2)}

/* ★★ 表头吸顶：**默认不吸顶**，只给"长列表页"那一种情形开（A1-96 修根因，2026-09-29）
   ─────────────────────────────────────────────────────────────────────────
   原来是一条**全局**规则：选择器就是裸的 `thead th`（不带任何容器限定），
   声明是 `position:sticky` + `top:52px` + `z-index:2`。
   它只对"表格自己待在文档流里、靠**窗口**滚动"的超长列表页是对的
   （销售单/采购单/库存/供应商列表：真浏览器实测滚 1500px 后表头正好停在 52px）。
   但 sticky 的偏移量是相对**最近的滚动容器**算的，于是另外两种情形全坏：
     ① 表格待在自己带 `overflow:auto` 的盒子里
        （`<div class="bd" style="overflow:auto">`，payroll 页 8 处）：
        那个 52px 变成"相对这个小盒子" ⇒ 表头被整排**推下来 52px，压在第一条数据行上**；
     ② 表格待在 `overflow:hidden` 的 `.panel` 里（.panel 就是 overflow:hidden）：
        同上（实测 /hr 两张考勤表分别压 19.9px / 17.5px）。
   历史上是**逐表打补丁**（payroll.html 6 张表、payroll_run.html 3 张表、
   main.css 里 `.tabpane .bd` 一条）—— 打一张漏一张：2026-09-29 全站实测
   仍有 **8 张表**压行（/payroll 6 张 + /hr 2 张），而逐表补丁只能盖住有人想起来的那几张。
   ⇒ 反过来写：**默认表头跟着表格走**；只给真正需要吸顶的那一种容器开。
   `.panel.list-panel` 就是列表页（pages/list.html）那张面板 —— 它是全站**唯一**
   显式把 overflow 放开（`.panel.list-panel{overflow:visible}`）好让窗口滚动接管的长列表，
   所以"需要吸顶"这件事用容器 class 就能判准，不必逐表点名。 */
thead th{position:static}
.panel.list-panel thead th{position:sticky;top:52px;z-index:2}
/* ★ .panel 原本是 overflow:hidden（为圆角裁切）—— 那会让里头的 sticky 表头失效
   （sticky 只能相对"可滚动的祖先"生效）。列表页这张面板显式放开，
   代价只是表格四角不再被圆角裁切（视觉上几乎看不出）。 */
.panel.list-panel{overflow:visible}
/* 卡片入场：轻微上浮淡入（百草那种"一片片铺开"的感觉）。
   ★ 尊重 prefers-reduced-motion：系统里关了动画的人不该被这个晃到。 */
@keyframes xjCardIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.card-grid .gcard{animation:xjCardIn .18s ease both}
.cards .card{animation:xjCardIn .18s ease both}
.card-grid .gcard:nth-child(4n+2),.cards .card:nth-child(2){animation-delay:.02s}
.card-grid .gcard:nth-child(4n+3),.cards .card:nth-child(3){animation-delay:.04s}
.card-grid .gcard:nth-child(4n+4),.cards .card:nth-child(4){animation-delay:.06s}
@media (prefers-reduced-motion:reduce){
  .card-grid .gcard,.cards .card{animation:none}
  .gcard:hover{transform:none}
}

/* ══════════════ P1（2026-09-26 对标百草）：卡片宫格 + 彩色扁平图标 ══════════════
   为什么**新加**一套而不是改 .acc：`.acc` 是「所有」与「管钱货」两页共用的手风琴
   （money.html 还在用），改它会连带动那两页；这里只给「所有」页换成百草那种
   **一眼看全、点卡直达**的宫格（对照图 对比1_首页宫格.png）。
   ★ 图标：卡片用彩色扁平 `#if-*`（40px），拿不到扁平图的分组回落线条 `#i-*`。 */
/* ★★★ 2026-10-18（第 1577 批 · 张总「你检查一遍」→「全部做好」）：**分组标题搬到左边一列**。
   病（量出来的，不是感觉）：`/all` 页高 **2989px**、16 组每组一行、5 列；其中
   **1 张卡的组 4 个、2 张卡的组 2 个** ⇒ 右半屏大片空白，"一眼看全"被拉长成滚动。
   改法：`.grid-sec` 自己变成「左标题 ＋ 右卡片」两列（标题列 190px），卡片列自动铺。
   实测（同机同宽 1440，注入 CSS 量的）：页高 **2989 → 2367（−21%）**；1440 不横滚。
   ★ 为什么不做 B 案（更密、−36%）：B 案要把卡片从 228px 缩到 170px 并收内边距，
     卡上的字会变小 —— 张总的原话是「美观、好用」，不是"塞得多"，所以取 A 案。
   ★ 为什么用 CSS 而不是改模板：`/all` 与 `/money` 共用 `_grid_cards.html`
     （第 1038 批定的真源一处），改 CSS 两页一起对；改模板等于又动那份共用片段。
   ★ 手机（≤720px）**必须回到上下堆叠** —— 手机上左标题占掉 190px 会把卡片列压成一条缝
     （本仓对 375 宽有硬指标：不横滚、可点区 ≥44）。

   ★★★ 2026-10-18（第 1588 批 · 张总截图「**这里版式错位**」→ 根查，**修正上一条的适用区间**）：
   病（真浏览器量的，1440/916/721 三档）：上面那批只验了「1440 不横滚 ＋ 手机 ≤720 堆叠」，
   **中间那一段没验**。实测两处坏：
     ① 916 宽：190px 标题列把内容区吃掉 26%，卡片只剩 3 列 ⇒ 左边一大块只有一行灰字
        （张总红框那一处）；721 宽时「仓库」组左列下方空出 **972px**；
     ② **721–979 整页横滚**（横滚 = 980 − 视口宽，一档不差）—— 那是**顶栏**的最小内容宽，
        与本节无关（同批另修，见下面顶栏那两档）。
   ⇒ 本节只治 ①：**两列只在内容区真的够宽时才启用**，窄了回到上下堆叠。
   阈值为什么是 `min-width:1180px`（视口）＝内容区 ≈1000px：1180 时卡片区 794px，四列
   （每列 ≥196）排得下，标题列只占 19%；再窄一格（1179）内容区就掉到 1000 以下、
   标题列占比跳上去 —— 实测就是从这个宽度开始出现"左边一列只有一行字"的观感。
   ★ 为什么不按「卡片张数」判（`:has()` 数卡）：**留白的大小与卡数正相关、与"好不好看"反相关**
     —— 1 张卡的组（`/all` 的「序列号」）左留白只有 6px、本来就不难看；23 张卡的「仓库」组
     才空出 370px。按卡数判会**恰好判反**。
   ★ 为什么不干脆全回退单列（B 案）：1577 批压掉的 21% 页高是真收益，1440 下两列确实更好看
     —— 所以是**收回适用区间**，不是推翻它。
   判据：`tests/test_viewport_bands.py` 判据 [3][4]（893 必须单列／1180·1440 仍两列），
   反证锚：把断点改成 720 ⇒ 721 档当场红。 */
.grid-sec{display:block;margin-bottom:var(--sp-4)}
.grid-sec-t{font-size:var(--fs-5);font-weight:400;color:var(--ink-2);
  margin:0 0 var(--sp-2);padding-top:0}
.grid-sec-t .muted{color:var(--ink-3)}
/* ★★★ 2026-10-18 二次订正（第 1589 批 · 张总「我看的还是老样子 刷新过了」）：
   **两列彻底取消** —— 分组标题永远在卡片**上方**，不再有「左边一列」。
   ─────────────────────────────────────────────────────────────────────────
   我上一版（第 1588 批）判断错了，如实记：我以为张总红框那处是"**窄屏才难看**"，
   于是把两列收窄到 `min-width:1180px` 以上才启用、宽屏继续保留 —— 结果他刷新后
   "还是老样子"：他的窗口 ≥1180，看到的正是原样。
   证据（他红框那一处与宽度无关）：**1440 下「仓库」组左侧也空着 370px**
   （卡片 5 行高 390px，标题只有一行 20px）；916 时 457px；721 时 972px。
   ⇒ 「左列只有一行字、下面一大片空白」**本身就是错位**，不是"窄屏才有的副作用"。
   ★ 代价（如实记）：`/all` 页高 2920 → **3505**（第 1577 批省下的那 17% 还回去）。
     这是张总亲自定的取舍：**宁可页面长一点，也不要左边那块空白**。
   ★ 为什么不选「标题列垂直居中」：留白还在，只是标题挪到中间 —— 治标不治本，
     而且"标题与卡片第一行不对齐"会变成新问题。
   判据：`tests/test_viewport_bands.py` 判据 [3][4] —— **所有六档都必须单列**。
   反证锚：把两列注回（`display:grid;grid-template-columns:190px …`）⇒ 六档全红。 */
.card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(196px,1fr));
  gap:var(--sp-3)}
.gcard{display:flex;align-items:center;gap:var(--sp-3);padding:14px 16px;
  background:var(--panel);border:1px solid var(--line);border-radius:var(--r-3);
  box-shadow:var(--sh-1);color:var(--ink);text-decoration:none;
  transition:box-shadow .18s ease,transform .18s ease,border-color .18s ease}
.gcard:hover{box-shadow:var(--sh-2);transform:translateY(-2px);
  border-color:var(--brand);text-decoration:none}
.gcard-ic{flex:0 0 44px;width:44px;height:44px;display:flex;align-items:center;
  justify-content:center}
.gcard-ic .iflat{width:40px;height:40px}
.gcard-ic .ic{width:20px;height:20px;color:var(--brand)}

/* ★★ 2026-09-27 P4.3 模块彩色扁平图标（`#if-*`，自绘，见 templates/_icons.html）：
   新接线处（人事页四板块卡 / 预警中心卡片 / 报表中心分组标题）的尺寸。
   ★ 为什么必须有 `.iflat` 这条兜底：`<svg class="iflat">` 不带 width/height 时，
     浏览器按 SVG 的默认尺寸（约 300×150）铺开 ⇒ 整块版面被撑破，
     而且**页面不报错**、HTML 也没坏，只有真浏览器截图才看得出来。
   ★ 更具体的两条（.gcard-ic .iflat 40px / .card.kpi .kpi-ic .iflat 30px）
     优先级更高，宫格与看板KPI 的尺寸一个字没动。 */
.iflat{display:inline-block;vertical-align:middle;flex:0 0 auto;
  width:32px;height:32px}
.acc-item .iflat{width:26px;height:26px;flex:0 0 26px}   /* 人事四板块卡 */
/* ★ 人事四板块卡要更宽的列：`.acc-grid` 的 160px 列宽是给「管钱货」那种
   **短条目密集排列**用的（那里的卡片 49px 高）；人事这四张卡每张是一整句中文
   说明，160px 里塞进"图标 + 板块标签 + 整句"之后整句被挤成竖排 ——
   真浏览器实测：卡片高度从 138px 涨到 250px，一屏全是竖排字。
   ⇒ 这一页单独放宽容度（自动填充：1600 屏宽下 4 张卡各 ~346px，文字 2 行内）。 */
.acc-grid.hr-cards{grid-template-columns:repeat(auto-fill,minmax(340px,1fr))}
.wcard-ic{flex:0 0 26px;display:flex;align-items:center}  /* 预警中心卡片 */
.wcard-ic .iflat{width:26px;height:26px}
.rpt-grp-ic{flex:0 0 26px;display:flex;align-items:center} /* 报表中心分组标题 */
.rpt-grp-ic .iflat{width:26px;height:26px}
.gcard-t{min-width:0;display:flex;flex-direction:column;gap:1px}
.gcard-t b{font-size:var(--fs-3);font-weight:600;color:var(--ink);
  /* ★ 2026-09-26 P1：**不省略号**。原来写 nowrap+ellipsis，真浏览器量到
     「预警中心（要动手的清单）」被截成「预警中心（要动手…」—— 名字被吃掉一半。
     宫格卡允许换行（卡片高度自己撑），宁可高一行，也不把入口名字藏起来。 */
  white-space:normal;overflow-wrap:anywhere}
.gcard-t small{font-size:var(--fs-5);color:var(--ink-3)}
@media (max-width:920px){.card-grid{grid-template-columns:repeat(auto-fill,minmax(160px,1fr))}}
/* ★★★ 2026-10-18（第 1588 批）：**删掉 `@media (max-width:720px){.grid-sec{display:block}…}`**。
   为什么删而不是留着：它已经是**死键** —— `.grid-sec` 的**基础值**（上面那段）现在就是
   `display:block`，两列改由 `@media (min-width:1180px)` 开 ⇒ 这条 ≤720 的规则
   **永远不可能改变任何东西**（§16.2-6「死键自检：命不中＝永久静默」）。
   留着的代价是真：下一个人会以为"手机端是这条在兜"，改坏基础值也不会有人发现。 */

/* 空状态：百草那种「居中插画 + 一句说明 + 一个出口」（原来是干巴巴一行灰字）。
   ★ 插画是**自绘**的内联 SVG（见 pages/all.html 的 .empty-ill），不是图片素材。 */
.empty-ill{width:132px;height:92px;display:block;margin:0 auto var(--sp-3)}
.empty-cta{margin-top:var(--sp-4)}

/* 看板 KPI 卡：百草风（彩色图标 + 大数字 + 副说明）—— 数字一个都没改口径
   ★ 图标放在**标签那一行**（不是单独一列）：这样金额能占满整张卡的宽度。
     第一版把图标做成左侧独立一列，真浏览器量到「¥29,600.00」被挤到出框
     （探针 _shot_p1_home.py 的 clipped 检查抓的）。 */
.card.kpi .kpi-head{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:2px}
.card.kpi .kpi-ic{flex:0 0 32px;width:32px;height:32px;display:flex;align-items:center;
  justify-content:center}
.card.kpi .kpi-ic .iflat{width:30px;height:30px}
.card.kpi .kpi-ic .ic{width:18px;height:18px;color:var(--brand)}
/* ★★ 2026-10-10 第 588 批⑤（张总「一起干掉」）：KPI 卡右侧那颗**小环**。
   · 与「目标健康度」那颗大环同一个套路（纯 SVG、`stroke-dasharray` 画弧），只是小一号；
   · `margin-left:auto` 把它推到标签行右端 —— 金额那行**一个字都没动**（不会被挤）；
   · 环里写百分比整数；`pct` 超 100 时弧画满、数字照实（见 core/dashstat.py 的说明）。 */
.card.kpi .kpi-ring{position:relative;flex:0 0 44px;width:44px;height:44px;
  margin-left:auto}
.card.kpi .kpi-ring svg{width:44px;height:44px;transform:rotate(-90deg)}
.card.kpi .kpi-ring circle{fill:none;stroke-width:3.6}
.card.kpi .kpi-ring .track{stroke:var(--line)}
.card.kpi .kpi-ring .val{stroke:var(--brand);stroke-linecap:round}
.card.kpi .kpi-ring b{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;font-size:11px;font-weight:600;color:var(--ink-2)}
.card.kpi .v{font-size:19px;white-space:nowrap}
/* ★★ 2026-10-15（第 1072 批 · 双端同源）：百草看板那几格的**单位（「万」）比数字小一号** ——
   照百草手机看板**真机**（`_baicao_study/shots/_emu/_b1070_kpi_zoom.png`，2 倍放大：
   数字高约 30px、「万」约 22px ⇒ 比 ≈ 0.73）。同一条规则手机端在
   `erp/templates/m/_board.html`（`.mb-kpi .v small`），两处都写 .72em。
   ★ 只动**单位那几个字**的字号：`.v` 自己的字号/字重**一个字不动**。 */
.card.kpi .v small{font-size:.72em}

/* 目标健康度环图（★ 只有**真的是百分比**的指标才画环 —— 见 _dash_body.html 的说明） */
.ring{position:relative;width:92px;height:92px;flex:0 0 92px}
.ring svg{width:92px;height:92px;transform:rotate(-90deg)}
.ring .track{fill:none;stroke:var(--panel-2);stroke-width:9}
.ring .val{fill:none;stroke:var(--ok);stroke-width:9;stroke-linecap:round;
  transition:stroke-dasharray .4s ease}
.ring .ring-txt{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;line-height:1.1}
.ring .ring-txt b{font-size:20px;font-weight:600;color:var(--ink)}
.ring .ring-txt small{font-size:10px;color:var(--ink-3)}
.ring-card{display:flex;align-items:center;gap:var(--sp-4);background:var(--panel);
  border:1px solid var(--line);border-radius:var(--r-3);padding:var(--sp-3) var(--sp-4)}

/* ===== A组: 录入键盘流焦点高亮 (#23, 2026-09-22) =====
   为什么要有这块：键盘流靠"回车跳下一格"，如果焦点看不见，
   员工敲两下就不知道光标跑到哪去了 —— 那还不如用鼠标点。
   所以焦点必须**一眼可见**，且不能只靠浏览器默认的细虚线（投影仪/老显示器上看不清）。

   ★ 用 :focus-visible 而不是 :focus：
     鼠标点进来不该亮（会闪得很吵），只有键盘走格才亮 —— 这正是我们要区分的。
     浏览器不支持 :focus-visible 时，后面那条 :focus 兜底（老 Edge/旧内核）。

   ★ 只加"描边 + 浅底"，不改尺寸/边距 —— 免得一聚焦整页就跳一下。 */
.fitem input:focus-visible,
.fitem select:focus-visible,
.fitem textarea:focus-visible,
.fitem input:focus,
.fitem select:focus,
.fitem textarea:focus{
  outline:none;
  border-color:var(--brand);
  box-shadow:0 0 0 3px var(--brand-soft);
  background:var(--panel);
  position:relative;
  z-index:2;
}
/* 键盘走格时，整格轻微提亮；鼠标悬停不亮，避免"到处都亮" */
.fitem:focus-within > label{color:var(--brand);font-weight:600}
.fitem input.chk:focus-visible,
.fitem input.chk:focus{
  box-shadow:0 0 0 3px var(--brand-soft);
}

/* ══════════════════════════════════════════════════════════════════
   [账号与权限] 双击详情抽屉 + 权限矩阵标签（2026-09-24）
   ------------------------------------------------------------------
   ★ 显隐一律由 JS 设 style.display，**不靠 [hidden]**：
     本块自己写了 display:flex/none，作者样式会压掉 [hidden] 的 UA 样式 ——
     这个坑在 [hidden] 被作者样式顶掉那一次已经交过学费。
   ★ 颜色全部走 :root 变量，不写死色值（换肤才不会漏掉这一块）。
   ══════════════════════════════════════════════════════════════════ */
.drBack{position:fixed;inset:0;background:rgba(31,35,41,.35);z-index:60}
.dr{position:absolute;right:0;top:0;bottom:0;width:560px;max-width:94vw;
  background:var(--panel);box-shadow:-6px 0 24px rgba(16,24,40,.16);
  display:flex;flex-direction:column;animation:xj-dr .2s ease}
@keyframes xj-dr{from{transform:translateX(28px);opacity:.5}to{transform:none;opacity:1}}
.dr-hd{display:flex;align-items:center;gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--line)}
.dr-hd .t{flex:1;font-size:var(--fs-2);font-weight:600}
.dr-bd{flex:1;overflow:auto;padding:var(--sp-5)}
.dr-kv{display:grid;grid-template-columns:88px 1fr;gap:6px var(--sp-3);
  font-size:var(--fs-4)}
.dr-kv b{color:var(--ink-3);font-weight:500}
.mxhd{margin:var(--sp-5) 0 var(--sp-2);font-size:var(--fs-3);font-weight:600}
.mxgrp{margin-bottom:var(--sp-3)}
.mxgrp .gh{font-size:var(--fs-5);color:var(--ink-3);margin-bottom:6px}
.dr-line{font-size:var(--fs-4);padding:3px 0}
.mxitem{display:inline-block;margin:0 6px 6px 0;padding:1px 8px;
  border-radius:var(--r-1);font-size:var(--fs-5);border:1px solid var(--line);
  color:var(--ink-2);background:var(--panel)}
/* 三档一眼分清：能改（品牌浅底）/ 只能看（灰底）/ 看不见（淡到几乎融进背景） */
.mxitem.rw{background:var(--brand-soft);border-color:var(--brand);
  color:var(--brand-dk)}
.mxitem.ro{background:var(--panel-2);border-color:var(--line);color:var(--ink-2)}
.mxitem.no{border-style:dashed;color:var(--ink-3);opacity:.6}
.dr-act{display:flex;gap:8px;align-items:center;flex-wrap:wrap;
  margin-top:var(--sp-5);padding-top:var(--sp-4);border-top:1px solid var(--line)}

/* ══════════════════════════════════════════════════════════════════════
   条码标签打印（2026-09-27 / erp/templates/pages/barcode_labels.html）
   ──────────────────────────────────────────────────────────────────────
   用途：一张 A4 排多联标签，剪开贴在货上，手机/扫码枪扫条码查商品。
   为什么样式写在这里而不是模板里：全仓规矩 —— 颜色只走 :root 变量，
   模板里写死色值一换肤就漏一块（本仓踩过）。
   ★ 尺寸全部按**毫米**给：打印是物理尺寸，px 在不同 DPI 的打印机上不一样，
     而 1mm 永远是 1mm。
   ★ 条码四周的空白（静区）算在 SVG 里（core/barcode.py 的 QUIET_LEFT/RIGHT），
     这里只给标签留内边距 —— 两处都留，剪标签时才有安全的留白。
   ══════════════════════════════════════════════════════════════════════ */
.barcode-labels{display:grid;grid-template-columns:repeat(4,50mm);
  gap:3mm;justify-content:flex-start;align-items:start;
  background:var(--panel);padding:var(--sp-4);border:1px solid var(--line);
  border-radius:var(--r-2)}
.bc-label{border:1px dashed var(--line);border-radius:var(--r-1);
  padding:1.2mm 2mm;box-sizing:border-box;width:50mm;height:25mm;
  overflow:hidden;background:var(--panel)}
.bc-name{font-size:var(--fs-5);font-weight:600;color:var(--ink);
  line-height:1.1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.bc-code{font-size:var(--fs-6);color:var(--ink-3);line-height:1.1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* ★ 条码图撑满可用宽度但**不拉伸**（height:auto 保持 SVG 自己的长宽比）：
   拉伸会把 1 模块宽的细条拉粗/拉细，扫码枪就读不出宽度比了。
   ★ SVG 的 module_px/height 由 erp/barcode_bp.py 按**联宽**算（1.5px/模块、
     46px 高）——这里只负责把它摆好，不再二次缩放。 */
.bc-bar{margin:.6mm 0}
.bc-bar svg{display:block;width:100%;height:auto;max-height:9.5mm}
.bc-meta{font-size:var(--fs-6);color:var(--ink-2);line-height:1.1;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* ── 打印：只在本页生效 ───────────────────────────────────────────────
   ★ 为什么用 `:has()` 而不是给 <body> 加类：base.html 是**两个会话共用**的文件，
     能不碰就不碰（同 erp/app.py 的理由）。`:has()` 现代浏览器都支持
     （Edge/Chrome 105+，本仓验收用的就是系统 Edge）；
     万一某个老浏览器不认，这一整块**整体不生效** —— 结果是"打印时多出页头
     和侧栏"，不是排版错乱，属于可接受的降级。 */
@media print{
  @page{size:A4 portrait;margin:8mm}
  /* ★★ 这一串是"标签纸之外的一切"，实测是逐次补出来的（每一条都是真浏览器
     截图上抓到的，不是拍脑袋列的）：
       .top/.side     —— 页头与侧栏；
       .backlink      —— base.html 里那个「← 返回」（在 .main 里，页头藏了它还在，
                         打印预览左上角就挂着两个字）；
       .noprint       —— 本页的标题/按钮/说明（模板里显式标了）。 */
  body:has(.barcode-labels) .top,
  body:has(.barcode-labels) .side,
  body:has(.barcode-labels) .backlink,
  body:has(.barcode-labels) .noprint{display:none !important}
  body:has(.barcode-labels){background:#fff}
  /* 去掉侧栏那 168px 的缩进，让 4 联刚好占满 A4 的可用宽度（194mm） */
  body:has(.barcode-labels) .wrap{margin-left:0;padding:0;display:block}
  body:has(.barcode-labels) .barcode-labels{border:none;padding:0;gap:2mm;
    grid-template-columns:repeat(4,47mm)}
  body:has(.barcode-labels) .bc-label{width:47mm;height:26mm;
    page-break-inside:avoid;break-inside:avoid;border-color:#ccc}
}

/* ══════════════════════════════════════════════════════════════════
   P4.2 单据详情页的页签（对标百草：它的销售单详情有 26 个页签）
   ------------------------------------------------------------------
   ★ 为什么要有：改造前详情页是一条长条
     （单据链 → 收付款记录 → 商品行 → 汇总 → 发货面板 → 锁定），
     跟单在手机上要翻好几屏才找得到"这单挂的物流单是哪张"。
   ★ 颜色**全部走 :root 变量**，不写死色值（全仓规矩：换肤不能漏一块）。
   ★ 默认显隐：`.tabpane{display:none}` + `.tabpane.on{display:block}`，
     **不用 `!important`、也不在模板里写行内 display** —— 两者都要不得：
       · 模板里的行内 `display:none` 优先级高于作者样式，只能配 `!important` 打开，
         而 `!important` 又会把 JS 后面写的 `style.display='none'` 一起压掉
         ⇒ 表现就是"点了页签，新内容出来了、旧内容不下去"（真浏览器验收当场抓到）；
       · 纯 class 方案没有这个陷阱，且**JS 挂了也照样看得见第一个页签**
         （服务端已经给它 .on）—— 换肤/降级都不会漏。
   ★ JS 只负责**改 class**，绝不写 style.display（同一个坑的另一半）。
   ★ postPanel / lockPanel / draftBar **不在页签里**（张总拍板）——
     那是主流程动作，藏起来会让人找不到。这里只管页签自己的样式。
   ══════════════════════════════════════════════════════════════════ */
.dtabs{margin-top:var(--sp-3)}
.dtabbar{display:flex;flex-wrap:wrap;gap:var(--sp-1);
  border-bottom:1px solid var(--line);margin-bottom:var(--sp-1)}
.dtab{background:none;border:1px solid transparent;border-bottom:none;
  border-radius:var(--r-2) var(--r-2) 0 0;padding:var(--sp-2) var(--sp-3);
  font-size:var(--fs-4);color:var(--ink-2);cursor:pointer;line-height:1.4}
.dtab:hover{background:var(--panel-2);color:var(--ink)}
.dtab.on{background:var(--panel);border-color:var(--line);
  color:var(--brand);font-weight:600;
  margin-bottom:-1px;padding-bottom:calc(var(--sp-2) + 1px)}
.tabpane{display:none}
.tabpane.on{display:block}
/* ★★ 页签里的表头**不吸顶** —— 这条是**真浏览器验收当场抓到的**：
   main.css 全局曾有一条 `thead th{position:sticky;top:52px}`（为长列表页写的），
   而详情页这些新表在页面很下面（top ≈ 650px，早已滚过 52px），
   于是表头一进视口就粘在 52px 处、**盖住了下面唯一那一行数据**
   （门禁全绿、innerText 也查得到，就是屏幕上什么都看不见 ——
   元素命中测试命中的是 `TH|行`，不是 TD）。
   ★ 为什么用 `!important`：要压掉的是**别处**（原来的全局段）那条规则，
     不是本段自己的开关 —— 与上面 `.tabpane` 的纯 class 方案不冲突。
   ★ 单行数据的详情页页签不需要吸顶；表头跟着表格走才是对的。
   ★★ A1-96（2026-09-29）：那条全局规则已改成"**默认 static**，只给
      `.panel.list-panel` 开吸顶"（见上面「表头吸顶」那一段），所以本条现在是
      **同向的第二道保险**（万一将来又有人给某处开 sticky，页签里仍然不吸顶）。 */
.tabpane .bd thead th{position:static !important}
/* ★ 这里**故意不写** `.tabpane > .panel{margin-top:...}`：
   商品行/汇总那些面板的上边距是它们自己行内就有的（原来怎么排就还怎么排）——
   在页签里再叠一条 margin 规则，等于把"搬家"变成了"顺带改了排版"。 */
/* ══ P4.2-页签样式段-结束（★ 判据锚点，别删别改名）══
   `tests/test_p4_detail_tabs.py` 那条「页签样式没有写死的颜色」原来是从上面那段
   **一直扫到文件末尾** —— 而这个文件此后又被好几支会话往下追加（回信屏两栏化、
   第 1555 批横滚、左栏覆盖段、空态组件…），那些段里只要出现一个十六进制色值，
   这条就**假红**（2026-10-18 实测：`var(--green-100,#eef6f0)` / `#f5c6c0` 都算）。
   ⇒ 改判**块内归属**：扫到这条结束标记为止（见那套件里的 `_seg_tabs()`）。
   ★ 加标记是"判据跟着实现走"的最小代价；这条注释本身不含十六进制色值，不会自伤。 */

/* ══════════════════════════════════════════════════════════════════════
   ★★ 2026-10-17（第 3 批·下，张总「继续」）：**回信屏两栏化**
   左＝原信（只读）／右＝编辑区。为什么要做：他写回信时要一边看客户原话、
   一边改，原来只能靠"记住上面那段"或来回滚（一封长信就翻丢了）。
   · ≥1200px 并排；左栏 `position:sticky`（滚正文时原信不跑掉）；
   · <1200px 叠成一列（手机上就是原来那样），左栏限高可滚，不把正文顶下去；
   · 左栏**只读**（里面没有 textarea / contenteditable）—— 判据钉着。
   ══════════════════════════════════════════════════════════════════════ */
.mrcard-grid{display:grid; grid-template-columns:1fr; gap:12px; align-items:start}
.mrcard-orig{background:var(--green-100,#eef6f0); border:1px solid var(--line,#e5e7eb);
  border-radius:10px; padding:10px 12px; min-width:0}
.mrcard-orig .orig-body{margin:6px 0 0; white-space:pre-wrap; word-break:break-word;
  font-size:var(--fs-4); line-height:1.75; max-height:9.5em; overflow:auto}
@media (min-width:1200px){
  .mrcard-grid{grid-template-columns:minmax(300px, 36%) 1fr; gap:16px}
  .mrcard-orig{position:sticky; top:8px}
  .mrcard-orig .orig-body{max-height:64vh}
}

/* ════════════════════════════════════════════════════════
   第 1536 批：窄屏下「标签 ｜ 控件」两列表格不许把控件挤出去
   来由：375 宽截图人眼发现（第 1535 批）—— /pay/transfer 的
   两个账户下拉被卡片右边界裁断，用户看不全账户名与币种。
   实测根因：模板里是 <th style="width:130px"> + <select style="min-width:320px">
   ⇒ 130 + 320 = 450 > 375，而且 table 自带的 	able-layout 不会自动降级。
   ★ 按族改（不只救这一页），但**只命中含控件的行** —— 数据表的行一字不动。
   ════════════════════════════════════════════════════════ */
@media (max-width:720px){
  /* ① 表格里**要占满一行**的控件：不许用 min-width 撑破卡片。
     ★★ 第 1554 批：**单选 / 多选 / 隐藏域不算** ——
        实测 `table input{width:100%!important}` 把 checkbox／radio 拉成了整行宽
        （`/stock` 9 颗、`/hr/rule` 2 颗、`/pay/batch` 1 颗），
        「奖励 ○／惩罚 ○」变成上下两行各一颗巨大的圆点。
        它们本来就是"一行里并排几颗"，占满一行反而是坏的。 */
  table select, table textarea,
  table input:not([type=radio]):not([type=checkbox]):not([type=hidden]){
    min-width:0!important; width:100%!important; max-width:100%; box-sizing:border-box}
  /* ② 含控件的行：两列降级为上下堆叠（只命中这种行，数据行不动）*/
  /* ★★ 第 1552 批：**tr 也必须变 block** ——
     实测（getComputedStyle）：只给 th/td 设 block 时，
     	tr 仍是 	able-row ⇒ 浏览器会把块子包进匿名单元格，**还是两列**。
     ⇒ 把 tr 一起改成 block，两列才真正降级为上下堆叠。 */
  /* ★★ 第 1554 批：判「这一行有没有控件」时必须**排掉隐藏域** ——
     实测 `/hr/rule`／`/hr/punish`／`/hr/keyevent` 三页共 **11 行纯数据行**，
     只因行里挂着一个 `<input type=hidden>`（删除表单带的 id）就被判成"表单行"，
     八列数据被上下堆叠成一长条（375 截图里看得清清楚楚）。
     ⇒ 与门禁 [7] 同一条口径：`input:not([type=hidden])`。 */
  /* ★★★ 第 1557 批（375 人眼复核抓到的真缺陷）：降级**只许命中"表单/参数行"**。
     来由：/stock 在 375 下每一行（12 格）被拆成 12 行竖排、/task（7 格）23 行、
     /mail（7 格）6 行 —— 它们只是"行里有一个勾选框/一个就地编辑控件"，
     却被 `tr:has(控件)` 判成表单行，整张数据表被竖着摊开（用户要滚十几屏看一条货）。
     ★ 判据（真机逐页量出来的两种形状）：
       · **表单/参数行**：行内有标签格 `<th>`（/pay/transfer 2 格、/hr/ 开头的那些页 4 格），
         或整行只有 ≤5 格（/payroll 的参数行 5 格）⇒ 该堆叠；
       · **数据/录入网格**：≥6 格且没有 th（/stock 12、/task 7、/mail 7）⇒ **不堆叠**，
         它们交给第 1555 批那条「卡片内容横滚」。
     实测（改前→改后要降级的行）：/stock 8→0、/task 23→0、/mail 6→0；
     /pay/transfer 4→4、/bookkeep/new 6→6、/hr/ 开头的那些页 11→11、/payroll 28→18（10 行是 6 格以上的宽行）。 */
  table > tbody > tr:has(select, textarea, input:not([type=hidden])):is(:has(> th), :not(:has(> :nth-child(6)))){display:block}
  table > tbody > tr:has(select, textarea, input:not([type=hidden])):is(:has(> th), :not(:has(> :nth-child(6)))) > th,
  table > tbody > tr:has(select, textarea, input:not([type=hidden])):is(:has(> th), :not(:has(> :nth-child(6)))) > td{
    display:block; width:auto!important; padding-left:0; padding-right:0}
  table > tbody > tr:has(select, textarea, input:not([type=hidden])):is(:has(> th), :not(:has(> :nth-child(6)))) > th{padding-bottom:2px}
  /* ★★ 第 1554 批：降级后的行还要**解除 180px 上限**。
     来由：交接文章 §8 第 ② 条留着一句「没查成」——「下拉实测 180px，
     `width:100%` 为什么没生效」。今晚量出来不是宽的问题，是**包含块只有 180px**：
     ≤720px 段上面还有一条 `th,td{max-width:180px}`（给数据表防列撑宽的），
     它把降级后的整行一起卡住 ⇒ 控件只有半宽，且行里的**说明文字被裁**
     （实测 td clientWidth=180 而 scrollWidth=292~648；375 截图里
       「转出币种：跟着所选账户（目前…」就是在卡片右边界断掉的）。
     ★ 只给「含控件的行」解上限 —— 数据表那 180px 一字不动（门禁 [5] 钉着）。
     ★ 量到的面（2026-10-18 真机逐页）：9 页 / 139 格没撑满、13 格文字被裁
       （/pay/transfer、/bookkeep/new、/payroll、/hr/rule、/hr/dept、/hr/punish、
        /hr/keyevent、/mfg/bom/new、/assembly/new）—— 所以这条必须按族写在 CSS 里，
        不许逐页打补丁。 */
  table > tbody > tr:has(select, textarea, input:not([type=hidden])):is(:has(> th), :not(:has(> :nth-child(6)))) > th,
  table > tbody > tr:has(select, textarea, input:not([type=hidden])):is(:has(> th), :not(:has(> :nth-child(6)))) > td{max-width:none}
  /* ★★ 第 1554 批（同一批的第二个原因）：**行里要能换行**。
     全局数据表那条 `th,td{white-space:nowrap;max-width:320px;overflow:hidden}`
     的 nowrap 会**被继承**：表单格里的 `<div class="hint">转出币种：跟着所选账户…`
     拿到的是 nowrap ⇒ 无论卡片多宽，说明文字**永远不会折行**、右半句被裁
     （实测 /pay/transfer td scrollWidth 425 / clientWidth 344；
      /bookkeep/new 787 / 494）。只给「含控件的行」放开换行，数据表仍 nowrap。 */
  table > tbody > tr:has(select, textarea, input:not([type=hidden])):is(:has(> th), :not(:has(> :nth-child(6)))) > th,
  table > tbody > tr:has(select, textarea, input:not([type=hidden])):is(:has(> th), :not(:has(> :nth-child(6)))) > td{white-space:normal}
  /* ★★ 第 1555 批：**数据表比卡片宽时，让卡片内容自己横滚**，别把右边列裁掉。
     来由（375 真机量）：`.panel{overflow:hidden}`（本来是给圆角用的）＋
     `.bd{overflow:visible}` ⇒ 10 列的 /ar 表（775px）、/pay/transfer「最近转账」（859px）、
     /refund/new 原单表（546px）都被卡片**裁在右边界**，而且**没有横滚容器** ⇒ 右侧列
     用户根本看不到（文档宽仍是 375，连"整页横滚"这条退路也没有）。
     ★ 2026-10-18（第 1562 批）**把 `:has(> table)` 放宽成 `:has(table)`**：
       `/payroll` 有一张 6 列的提成明细表（539px），它的 `.bd` 里隔着一层无 class 的 `div`
       ⇒ 直接子元素不是 table ⇒ 没拿到横滚容器 ⇒ 右边两列**被卡片裁掉、够不着**（实测 docW 仍是 375）。
       放宽后它也有横滚了。
     ★ 仍然收窄在「`.panel` 的内容块里**有**表格」这种情况：表单面板（`.bd` 里没有表格）不受影响，
       所以弹层/日期选择器不会被一起裁掉。
     ★ `.list-panel` 不碰：那是长列表页，靠 window 滚动 + 表头吸顶（main.css:1362）。 */
  .panel:not(.list-panel) > .bd:has(table){
    overflow-x:auto; -webkit-overflow-scrolling:touch}
  /* ★★ 第 1568 批：**表格直接挂在 `.panel` 下**的那种，也得能横滚。
     来由（375 真机量）：`/customers/pool` 那张 8 列表（469px）的父级是
     `div.panel`（`overflow:hidden`），中间**没有 `.bd`** ⇒ 上面那条不命中
     ⇒ 右边两列（停用/删除）**够不着**（docW 仍是 375，连整页横滚都没有）。
     ★ 收窄到「`.panel` 的**直接子元素**里有表格」：不然会把整块面板的弹层都带上滚动条。 */
  .panel:has(> table){overflow-x:auto; -webkit-overflow-scrolling:touch}
}

/* ══════════════════════════════════════════════════════════════════════════
   ★★★ 2026-10-18 · 左栏重做（张总选 A 案 ＋ B 宽度 132px）—— **覆盖段**
   方案：`02_方案…\20261018_方案_桌面左栏重做_必要功能随手好用.md`

   · 为什么是"覆盖段"而不是就地改：本文件今晚被另一条会话持续编辑，
     就地改会跟别人抢同一行；追加段**一行既有代码都不动**，回退只需删这一段。
   · 覆盖了谁（逐条点名）：
       `.side{width:168px}`            → 132px（左栏只剩 5 格，不需要那么宽）
       `.side a{padding:7px 16px}`     → 6px 8px（收紧，132 宽才放得下 4 个汉字 + 角标）
       `.side .ic{15px}`               → 15px 保持，去掉 `margin-right`（改了 gap 排版）
       `.gear-pop{width:290px}`        → 348px（中文说明原来折行）
   ══════════════════════════════════════════════════════════════════════════ */
.side{width:132px}
/* ★★ 2026-10-18 真浏览器截图（1440 宽）当场抓到的两处，逐条记：
   ① 「邮箱管理」**折成了两行**（"邮箱管" + "理"）。算式：132 减去 `margin-right` 8px、
      左右内边距各 10px ⇒ 只剩 104px；里面要放 15(图) + 6(间距) + 52(4 个汉字 ×13px)
      + 6 + 24(两位角标) = 103px —— **刚好差一像素**，于是折行。
      ⇒ 内边距收到 8px、角标收小（见下面 `.navnum` 那条），并显式 `white-space:nowrap`。
        **宁可挤、也不折行**：五格里最高的那一格会把整列的节奏打乱（截图里一眼能看出来）。
   ② 悬停/选中那 2px 的"推挤"是照**旧内边距 16px** 算的（`calc(var(--sp-4) + 2px)`）
      ⇒ 内边距改小以后，鼠标一过整行就**从 10px 跳到 18px**（肉眼看到的是一抖）。
      `:hover / .on` 两条都按新内边距重算，与 `.side a` 同权重、写在后面才压得住。 */
.side a{padding:6px 8px;font-size:var(--fs-4);gap:6px;white-space:nowrap}
.side a:hover,.side a.on{padding-inline-start:calc(8px + 2px)}
.side .ic{width:15px;height:15px;flex:0 0 15px;margin-right:0}
/* 角标收一档（132px 才放得下两位数字）：min-width 18→16、内边距 5→4、字号 12→10.5 */
.side .navnum{min-width:16px;padding:0 4px;font-size:10.5px;line-height:16px}
.side h4{margin:var(--sp-3) 8px var(--sp-1)}
/* ★★★ 2026-10-18（第 1588 批 · 同一条根查）：**把 459 行那条窄屏规则救回来** ——
   上面 `.side{width:132px}` 是「左栏重做」的覆盖段（2026-10-18 张总选 A 案 ＋ B 宽度），
   它把第 459 行 `@media (max-width:920px){ .side{width:100%;display:flex;overflow-x:auto} }`
   里的 **`width:100%` 整条盖死**了（同一选择器、同一权重，这条晚 1300+ 行）⇒ 那条规则
   **从写下的那天起就没赢过**：实测 916 宽时侧栏仍是 132px，而它内部内容宽 462px
   ⇒ 被自己的 `overflow-x:auto` 裁掉，屏幕上只剩「工作台」和半个「网」。
   这就是「**覆盖段要点名全部受害者**」（双端规格 §4.5-4）的欠账：上一段注释只点了
   `.side{width:168px}` 一条，没点媒体查询里那条。
   ⇒ 补一条**写在覆盖段之后**的同档规则把它救回来（只放宽度与方向，颜色/字号/角标一个不动）。
   判据：`tests/test_viewport_bands.py` 判据 [5]（721/893 下 computed 宽 ≥ 视口×0.6）；
   反证锚：把侧栏写死 40px ⇒ 当场红。 */
@media (max-width:920px){
  .side{width:auto;flex:0 0 auto}
}
/* 角标分色：brand＝要跟进的业务数 · warn＝要回的信 · danger＝要命的告警。
   ★ 只加档位类，不动 `.navnum` 本体。 */
.side .navnum.warn{background:var(--warn)}
.side .navnum.danger{background:var(--danger)}
/* 顶栏那颗「铃铛」＝通知中心（从左栏挪上来的运维告警入口）
   ★ 角标要**正圆**：`--navnum` 本体只有 `line-height:18px`，靠行高撑高度 ⇒
     在这条 16px 的行高里会变成"20×22 的圆角方块"（3 倍放大截图里一眼看出来）。
     这里显式给 `height` + `box-sizing:border-box` ⇒ 一位数是 16×16 正圆，
     两位数自动长成胶囊（和左栏、别处的角标同一个规矩）。 */
.top .bell{position:relative;display:inline-flex;align-items:center;gap:2px}
.top .bell .ic{width:16px;height:16px}
.top .bell .navnum{margin-inline-start:0;box-sizing:border-box;height:16px;
  min-width:16px;padding:0 4px;line-height:16px;font-size:11px;
  background:var(--danger);border-radius:999px}
/* 齿轮面板：三组 ＋ 图标位（8 条平铺 → 三组，条数不变、只是有了视觉锚点） */
.top .gear-pop{width:348px;padding:4px 0}
.top .gear-hd{font-size:var(--fs-5);color:var(--ink-3);padding:4px 14px 6px;
  border-bottom:1px solid var(--line);margin-bottom:4px}
.top .gear-grp{font-size:11px;color:var(--ink-3);letter-spacing:.4px;
  padding:8px 14px 4px}
.top .gear-grp:not(:first-child){border-top:1px solid var(--line);
  margin-top:4px;padding-top:8px}
.top .gear-it{display:flex;gap:9px;align-items:flex-start;padding:7px 14px}
/* ★ 图标位原来是「#e6f4ff 浅蓝底」，在浅色纸上**几乎与白底同色**（截图里像没画），
   补一圈极浅的描边把方块兜住；图标 13→14px、颜色用深一档的 brand。 */
.top .gear-g{flex:0 0 auto;width:22px;height:22px;border-radius:6px;
  background:var(--brand-soft);color:var(--brand);display:flex;
  box-shadow:inset 0 0 0 1px rgba(100,116,139,.16);
  align-items:center;justify-content:center;margin-top:1px}
.top .gear-g .ic{width:14px;height:14px}
.top .gear-tx b{display:block;font-size:var(--fs-4);font-weight:600;line-height:1.4}
.top .gear-tx small{display:block;font-size:11px;color:var(--ink-3);line-height:1.35}

/* ══════════════════════════════════════════════════════════════════════════
   ★★ 2026-10-18 · 界面美化 2.0 · P2-c：**空态组件**（`.empty` 家族）
   真源：模板 `erp/templates/_empty.html`（`empty_state()` 宏，插画 SVG 只在那一处）；
        样式就是下面这几条。判据 `tests/test_empty_state.py`。

   · 为什么只加不删：`.empty` 原来那条（居中灰字）**保留**——全仓 82 处里有一半是
     "表格某一格没值"的格级占位，它们只该淡、不该有插画/按钮（塞进去会把一行数据
     撑成一屏）。组件只管**页级/面板级**空态。
   · `.empty-t` / `.empty-hint` / `.empty-ic` / `.empty-sm` / `.empty-cta .btn-pri`
     —— 都是新类名，不覆盖任何既有类（既有 `.empty` / `.empty-ill` / `.empty-cta` 照旧）。
   ══════════════════════════════════════════════════════════════════════════ */
.empty .empty-t{display:block;font-size:var(--fs-3);color:var(--ink-2);
  font-weight:600;line-height:1.6}
/* 说明句比标题淡一档、行高放宽 —— 空态本来就是"读一句话"的地方，不要挤成一片 */
.empty .empty-hint{display:block;margin-top:var(--sp-1);font-size:var(--fs-4);
  color:var(--ink-3);line-height:1.7;max-width:520px;margin-inline:auto}
/* 小号：面板/页签里的空（28px 图标，不占地方） */
.empty.empty-sm{padding:var(--sp-5) var(--sp-4)}
.empty-sm .empty-ic{width:28px;height:28px;display:block;margin:0 auto var(--sp-2);
  color:var(--ink-3)}
.empty-sm .empty-t{font-size:var(--fs-4)}
.empty-sm .empty-ill{width:88px;height:62px;margin-bottom:var(--sp-2)}
/* 主按钮：唯一一颗（组件里最多渲染一个），走品牌底 —— 与 `button` 同一套观感 */
.empty-cta .btn-pri{display:inline-flex;align-items:center;justify-content:center;
  height:var(--ctl-h);padding:0 var(--sp-4);border-radius:var(--r-2);
  background:var(--brand);border:1px solid var(--brand);color:var(--brand-ink);
  font-size:var(--fs-4);font-weight:600;text-decoration:none;
  transition:background var(--dur-1) var(--ease),border-color var(--dur-1) var(--ease)}
.empty-cta .btn-pri:hover{background:var(--brand-dk);border-color:var(--brand-dk);
  color:var(--brand-ink);text-decoration:none}
.empty-cta .btn-pri:active{transform:scale(.98)}
/* 通用列表（`list.html`，40 多个 `/v/<key>` 共用）的空态是**JS 拼的**：
   插画由服务端渲染成 JS 字符串再拼进去（插画仍然只有一处真源），
   这里的 `.empty-js` 只把 JS 那段 `<span class="sub">` 的观感与组件对齐。 */
.empty.empty-js .sub{display:block;margin-top:var(--sp-2);font-size:var(--fs-4);
  color:var(--ink-3);line-height:1.7;text-align:left;max-width:560px;margin-inline:auto}
.empty.empty-js .empty-ill{width:88px;height:62px;margin-bottom:var(--sp-2)}
/* 错误态（`.empty-err`）：**不是空**，是"读不出来"。必须一眼分开 ——
   空态是灰的（"还没有数据，去建第一张"），错误是红的（"这不是没有，是坏了"）。
   改之前 `leads.html` 把整库不可用也渲染成灰灰的 `.empty`，看着像"你还没录数据"。
   ★ 只用令牌，不写死色值（换肤不能漏一块 —— 本仓老规矩）。 */
.empty.empty-err{color:var(--danger);background:var(--panel-2);
  border:1px solid var(--danger);border-radius:var(--r-2);text-align:center}

/* ══════════════════════════════════════════
   ★★★ 2026-10-18 · 第 1573 批：顶栏品牌徽标（张总「可以 继续」）
   · 徽标 24px（样张里比过 28/32：28 会把右边那排入口往右挤），
     与文字**同基线居中**，间距 6px；
   · 字号/letter-spacing一个字没动 ⇒ 顶栏高度仍 52px（真浏览器量过）。
   ══════════════════════════════════════════ */
.top .logo{display:flex;align-items:center;gap:6px}
.top .logo .top-logo{width:24px;height:24px;flex:0 0 24px;display:block;
  border-radius:50%;background:#fff}

/* ══════════════════════════════════════════
   ★★★ 2026-10-18 · 第 1572 批：375 下**整页不许横滚**（真浏览器量出来的两处）
   来由：`/task` 在 375 下 `documentElement.scrollWidth = 662`（视口 375）——
   客户得**左右拖**才能看全；而"宽表够不着"那条门禁把"整页能横滚"当成"可达"，
   所以它一直没红。量了 73 个有表格的页，超宽的只有两类形状：

   ① **表格直接挂在 `main.main` 下**（没有 `.panel` / `.bd` 包着）：
      /task、/task/mine、/hr/punish、/hr/rule、/hr/dept、/hr/keyevent、
      /customers/follows、/customers/dealt —— 前几批补的四条选择器一条都命中不了它。
      ⇒ 让**内容列自己横滚**（不是让整页横滚）：`main.main:has(> table)`。
      ★ 为什么不是给 `table` 加 `display:block`：那会改掉表格布局（列宽重新分配），
        老列表页会跟着变形；只把"谁当滚动容器"换掉，视觉最小。
   ② **顶栏 12px 溢出**（`a.quit` 右边界 387）：`.top .who` 不许缩 ⇒ 整页被顶出 12px。
      ⇒ 允许它缩，并省略号收尾。
   ══════════════════════════════════════════ */
@media (max-width:720px){
  main.main:has(> table){overflow-x:auto;-webkit-overflow-scrolling:touch}
  .top .who{flex:0 1 auto;min-width:0;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
}
/* ★ 第 1572 批补：量完第一轮后剩下 3 页，父级是 `.card` / `.paper`（同样"表直接挂"）：
   `/`、`/money`（仪表盘卡片里的表 367px）、`/slow_moving/print`（打印纸 651px）。
   同一套做法：只换"谁当滚动容器"，页面本身不许横滚。 */
@media (max-width:720px){
  .card:has(> table), .paper:has(> table){overflow-x:auto;-webkit-overflow-scrolling:touch}
}
/* ★★★ 2026-10-18 · 第 1573 批：`/task` 的「回复」框在 375 下只有 48px（量出来的）
   来由：`task_list.html` 那条回复表单是 `display:flex;gap:4px` 一行三件
   （文本框 size=26 ＋ 选图 ＋ 回复按钮）⇒ 窄屏上被挤成 **48×32**，
   连占位符「回复…」都显示不全，根本没法打字（实测 61 个 <100px 控件就是它们）。
   改法：窄屏下**换行**，让文本框独占一行（`flex:1 1 100%`），选图与按钮排第二行。
   ★ 用 `[action*="/task/reply/"]` 命中：模板里那三条回复表单（含修改/删除回复）都吃这一条，
     不必去改模板的 inline style（那里面写不了 media query）。 */
@media (max-width:720px){
  form[action*="/task/reply/"]{flex-wrap:wrap}
  form[action*="/task/reply/"] input[name="content"]{flex:1 1 100%;min-width:0}
  form[action*="/task/reply/"] input[type="file"]{flex:0 0 auto}
}
/* ★★★ 2026-10-18 · 第 1574 批：**可编辑宽表**里的控件被压成 26px（375）
   来由：全站扫 206 条路由，12 页共 **218 个 <100px 的输入控件**；最窄的 26px 集中在
   `/fee/type`(39)、`/package/type`(22)、`/wholesale`(13)、`/stock`(8)、
   `/mfg/bom/new`(4)、`/assembly/new`(7)、`/pay`(28)、`/payroll`(11)。
   根因不是"它们本该这么窄"：`th,td{max-width:180px}` ＋ 表格 `width:100%`
   ⇒ 表格被**压进 375**，单元格跟着缩 ⇒ 输入框 26px，字都打不进去。
   ★ 为什么不把这类行"降级堆叠"：第 1557 批已经定过 —— ≥6 格的宽行属于**数据/录入网格**，
     降级会把 12 列拆成 12 行（/stock 踩过）。这里要的是**表格保持原宽、让它自己横滑**。
   改法：只给"表内确实有控件"的表加 `min-width:max-content`
   ⇒ 表格按内容取宽，外层容器（.bd/.panel/main.main，前几批已改成可横滚）负责横滑。
   实测（零改源码的 what-if，注入同一条规则）：最窄控件
     /fee/type 26→97、/package/type 26→97、/wholesale 26→98、/pay 48→164、
     /mfg/bom/new 26→80、/payroll 61→80；`docW` 全部仍是 375（页面不横滚、容器横滚）。 */
@media (max-width:720px){
  table:has(input:not([type="hidden"]), select, textarea){min-width:max-content}
}
/* ★★★ 2026-10-18 · 第 1575 批：375 下**整页横滚**最后两页（全量复查 207 条路由找出来的）
   第 1572 批把"表格直挂"那一族修完了（73 → 0），但那条判据只覆盖"渲染出 `<table>` 的页"。
   这一批按**全部无参 GET 路由**量 `documentElement.scrollWidth > 375`，207 条里剩 **2 页**：

   ① `/search` `docW=396`：`form.gsearch.big` 有 `min-width:380px`（第 433 行）⇒ 比视口还宽 21px。
      ⇒ 窄屏下把 `min-width` 让出来（`max-width:100%`），搜索框跟着容器走。
   ② `/statement` `docW=443`：那条筛选表单是**行内 style** 写的
      `display:flex;gap:6px;align-items:center;margin-left:8px`，5 个子项一行排不下 ⇒ 溢出 68px。
      ⇒ 行内 style 只能用 `!important` 压（这是唯一"必须 !important"的场合：覆盖 template 的 inline）。
   两页都改完再复量：目标 `docW <= 376`。 */
@media (max-width:720px){
  .gsearch.big{min-width:0;max-width:100%}
  form[action="/statement"]{flex-wrap:wrap !important;margin-left:0 !important;max-width:100% !important}
}
/* ★★★ 2026-10-18 · 第 1577 批：**桌面页缩到 375 时的 44×44 可点下限**（基因「双端下限」）
   来由：全站量 121 条桌面路由（已剔除 `/m/` 开头的手机页）—— **104 页、3298 个可点元素 <44**。
   分布极集中（前几族就占了绝大多数）：
     `nav.side a`（左侧栏链接）111×33 → 196 个 / 99 页；`summary` 30×26 → 99 个 / 99 页；
     `a.topnav.bell` 58×24 → 99 个 / 99 页；`button[submit].ghost` 60×32 → 163 个 / 6 页；
     `a`（表格里的 SKU 链接）111×18 → 103 个；`span.t a`（全部/只看收款…）64×21。
   处方分两类（与 /m 那条同尺子，只是尺子这次架到桌面页上）：
     ① **块状控件**（按钮 / summary / 侧栏与顶栏链接）⇒ `min-height:44px` ＋ 垂直居中；
     ② **句内行内链**（表格里的 SKU、筛选链接）⇒ 只加 `padding`：
        行内元素的 padding **不进 line-height**（第 1250 批实测），行高与排版不动，但**热区到 44**。
        这条正是总纲 §19.7 推翻「③ 只能登记豁免」的那个做法。 */
@media (max-width:720px){
  nav.side a, .top a, .btn, button, summary{
    min-height:44px; display:inline-flex; align-items:center}
  nav.side a{min-height:44px}
  /* ★ 第 1578 批：13px 只到 **43**（差 1px 也是不达标 —— 实测 /slow_moving 106 个、/price_tier 52 个
     全是 43 高）⇒ 提到 15px。行内 padding 仍不进 line-height（第 1250 批实测），排版不动。 */
  td a, span.t a, a.backlink{padding:15px 10px}   /* 横向也要够：/pay 的「明细」39 宽 ⇒ 47 */
  /* ★★ 2026-10-20（第 1571 批 · 375 宽真浏览器抓的**真缺陷**）：
     上面那些 `padding:15px 10px` 打在**行内元素**上，padding **不进 line-height**（第 1250 批已记过）；
     单行时看不出问题，但窄屏**换行**之后，第二行的链接会**压在第一行上面**
     （实测 `/leads`：第一行 top=224 高 51、第二行 top=237 ⇒ 重叠约 38px，
     上排「全部 7」的几何中心落在下排的 `a` 上；`/hr/partner` 两个伙伴链接同理）
     ⇒ `accept_unclickable` 当场报「真点不到」（/leads 2、/quote-apply 2、/hr/partner 1）。
     处方：`display:inline-block` —— padding 计入行盒、行高跟着长，换行不再相撞，
     热区仍有 44 高；★ 不用 `inline-flex`：那会让链接内部的文字不再折行，
     长链接在窄屏会顶出横向滚动（宁可保守）。 */
  td a, span.t a, a.backlink, p a, div.muted a, span.sub a, span.hint a,
  span.tag a{display:inline-block;vertical-align:middle}
  /* ★★ 2026-10-20（张总拍板「换行」）：`/dash` 的「要办的事」那张表在窄屏把**事项**列
     截成省略号 —— 实测内容 270/260/356px、可见只有 180px；桌面靠 `xj_ellipsis.js`
     补 `title` 还能悬停看，**手机上根本没有悬停** ⇒ 那一格等于看不到。
     ★ 第一版只写了 `white-space:normal` ⇒ 事项列被两边写死的 64/96/110px 挤到 ~40px，
       中文变成**一列竖排单字**（技术上"换行了"，可读性反而更差，当场看截图发现）。
     ⇒ 现在：放开换行**并给它最小宽度**，同时把两个数字列在窄屏收窄（th 上的 width 是
       内联样式，不用 !important 压不住）。判据：被截断格子 3 → 0，事项列 ≥150px，
       `docW` 仍 375（没把缺陷挪到页面层）。 */
  .dash-todos table th:nth-child(2), .dash-todos table td:nth-child(2){
    white-space:normal; overflow-wrap:anywhere; max-width:none; overflow:visible;
    min-width:130px}
  .dash-todos table th:nth-child(3), .dash-todos table td:nth-child(3),
  .dash-todos table th:nth-child(4), .dash-todos table td:nth-child(4){
    width:70px !important}
  /* ★ 第 1578 批补：剩下几族都是"矮/窄"，但都不是普通按钮 —— 各自点名，避免一刀切伤到正文里的行内链 */
  .menu a{min-height:44px;display:flex;align-items:center}
  a.tag, a.mmeta{min-height:44px;display:flex;align-items:center}
  span.tag[role="button"]{min-height:44px;min-width:44px;display:inline-flex;
    align-items:center;justify-content:center}
}
/* ★ 第 1577 批补（第一轮复量后剩的族）：高度到 44 了、**宽度还不够**的，以及被更高优先级压住的
   · `summary` 30×44 → 图标类，补 `min-width:44px`；
   · `button` 40×44 → 同上；
   · `a.quit` 44×24 → `.top a.quit{min-height:24px}`（main.css 第 103 行）特定性比 `.top a` 高，
     所以我那一条压不住它 ⇒ 单独点名。 */
@media (max-width:720px){
  nav.side a, .top a, .btn, button, summary{min-width:44px}
  .top a.quit{min-height:44px}
}
/* ★ 第 1577 批补（第二轮复量后）：**下拉框**是剩下最大的一族（27–32 高，约 268 个 / 30+ 页）；
   另有面包屑按钮 `a.crumb-btn` 60×32。两者都只是"矮"，加 `min-height:44px` 即可。 */
@media (max-width:720px){
  select, a.crumb-btn{min-height:44px}
}
/* ★ 第 1579 批：剩 162 个里最后几大族（复量聚合出来的，都只是"矮"或"行内"）
   · `a.btnlink` / `a.btnlike` / `a.sec-btn` / `a.sm-tab` / `a.tab` / `a.kpi-link`：32–41 高 ⇒ 44；
   · `button[type=submit]`（登录页那颗 317×40）⇒ 44；
   · `a.muted`（48×16，例「＋加视频」49 个）、`.hint a`（80×16，例「‹ 回到客户公海」）：
     都是**行内** ⇒ 只加 padding（行内 padding 不进 line-height，排版不动、热区到 44）。 */
@media (max-width:720px){
  a.btnlink, a.btnlike, a.sec-btn, a.sm-tab, a.tab, a.kpi-link,
  button[type="submit"]{min-height:44px}
  a.muted, .hint a, span.hint a, p.hint a{padding:14px 6px}
}
/* ★ 第 1580 批：最后 45 个里的"矮"族（逐族点名；共用规则已经收不动了）
   `a.ghost`（工具栏 22 高）、`a.mbtn`（/hr/care 30）、`a.pt-tab`（/price_tier 34）、
   `button.go` / `button.cl`（/slow_moving/print 37）；行内链接（`span.tag a` / `p a` /
   `div.muted a` / `span.sub a`）只加 padding —— 行内 padding 不进 line-height。 */
@media (max-width:720px){
  a.ghost, a.mbtn, a.pt-tab, button.go, button.cl{min-height:44px}
  /* ★ 第 1580 批补：13px 只到 42/43（又差 1–2px）⇒ 15px；横向再给到 10px（窄的 `a.btnlink` 26 宽） */
  span.tag a, p a, div.muted a, span.sub a, span.hint a{padding:15px 10px}
  a.ghost, a.btnlink, a.mbtn, a.pt-tab{min-width:44px}
}
/* ★ 第 1580 批补（最后 12 个里的形状）：筛选条 / 分页 / 打印选项里的"矮"块与行内链
   · `div.tabs a`（/refund/new 84×41）、`div.pager a`（/admin/log 51×22）、`div.sm-foot a`（36×19）⇒ 44；
   · `span.sm-opt a`（24×16 行内）⇒ 只加 padding；
   · `div.bd a`（/customers/follows 筛选 36×44）⇒ 横向 padding 补宽。 */
@media (max-width:720px){
  div.tabs a, div.pager a, div.sm-foot a{min-height:44px;min-width:44px}
  span.sm-opt a, div.bd a.on, div.bd a{padding:15px 10px}
  /* ★ 第 1581 批：最后一个自有项 —— `/package/new` 的 `span.sub a` 是 34×84（横向窄）⇒ 横向再放宽 */
  span.sub a{padding-left:14px;padding-right:14px}
}
/* ★★★ 2026-10-18 · 第 1583 批：**360 宽**（安卓最常见）比 375 更窄 —— 实测 99 页整页横滚，
   其中 98 页是**同一个 +3px**：`a.quit`（退出）右边界 363（视口 360）。
   ⇒ 顶栏在 ≤400px 再收一档（gap / padding），并给 `who` 一个上限，把 3px 让出来。 */
@media (max-width:400px){
  .top{gap:4px;padding-left:8px;padding-right:8px}
  .top .who{max-width:84px}
}
/* ★ 第 1583 批补：360 下剩下 4 页里三族「容器自己比视口宽」
   · `a.acc-item`（/hr、/hr/approve 340+左距 ⇒ right 372）
   · `.lbl-col`（/label/design 354+padding ⇒ right 370）
   · `.bd`（/assembly/new 358+左距 ⇒ right 375）
   三者都是「宽度按内容/固定值 + 外边距」，窄到 360 就顶出去 ⇒ 允许它们收回 100%。 */
@media (max-width:400px){
  a.acc-item, .lbl-col, .bd{max-width:100%}
  /* ★ 第 1584 批修正：这里的 `main.main/.wrap{min-width:0}` 已删 ——
     实测它让 `/assembly/new` 在 **375** 下也变成 376（本来 375 是干净的）。
     "让容器能缩"这种改动会连带改掉子元素的宽度计算，**不能顺手加**。 */
}
/* ★ 第 1584 批：360 下最后 4 页，逐页按**真因**收（都只在 ≤400px 一档）
   · `/hr`、`/hr/approve`：`.acc-grid` 是 `minmax(340px,1fr)` 网格 ⇒ 列比容器宽（340 > 328）；
   · `/label/design`：`.lbl-col` 固定 354px，容器 328；
   · `/assembly/new`：`.panel > .bd` 被算成 358（容器 360，左偏 17 ⇒ 右界 375）；
     里面那张表已经能横滚（`min-width:max-content` + `.bd{overflow-x:auto}`），只需把 `.bd` 收回收；
   ★ 三条都用 `!important`：它们各自有更高优先级的固定宽度/网格模板，这是在**覆盖版式口径**。 */
/* ★ 2026-10-18（第 1584 批修正）：这一档必须写 **≤370px**，不能写 ≤400 —— 实测 375 也会命中，
   而 `.panel > .bd{width:auto}` 在 375 下反而把 `/assembly/new` 顶成 376（我上一版就这么错的）。 */
@media (max-width:370px){
  .acc-grid{grid-template-columns:1fr !important}
  .lbl-wrap{display:block !important}
  .lbl-col{width:auto !important;max-width:100% !important}
  .panel > .bd{width:auto !important;max-width:100% !important}
}
/* ══════════════════════════════════════════════════════════════════════
   ★★★ 2026-10-19（批 1 · 张总「按钮太多　看得乱」）：邮箱页「一条工具条」＋
   回信卡「辅助件在同一个位置换内容」。
   ──────────────────────────────────────────────────────────────────────
   为什么追加在文件末尾：main.css 是**全站共用**文件、也是别的会话常改的文件，
   追加段一行既有规则都不动，回退只需删掉这一段（与 10-18 左栏那批同一做法）。
   只影响两处、且都带自己的类名：`.mail-*`（邮箱页）／`.mr-*`（回信卡），
   没有一个选择器命中别的页面。
   ══════════════════════════════════════════════════════════════════════ */

/* ── 邮箱页：顶部一条工具条（原来两块：概览 + 收件箱/已发送） ────────── */
.mail-bar > .hd{gap:10px;flex-wrap:wrap;align-items:center}
.mail-bar > .hd .tag{text-decoration:none}
.mail-bar .mail-more{position:relative}
.mail-bar .mail-more > summary{list-style:none;cursor:pointer;user-select:none}
.mail-bar .mail-more > summary::-webkit-details-marker{display:none}
.mail-bar .mail-more[open] > summary{background:var(--panel-2)}
.mail-bar .mail-more-pop{position:absolute;right:0;top:calc(100% + 6px);z-index:40;
  width:min(430px,92vw);background:#fff;border:1px solid var(--line);
  border-radius:var(--r-3,8px);box-shadow:0 12px 32px rgba(60,50,30,.12);padding:10px 12px;
  text-align:left}
.mail-bar .mail-more-pop .tag{margin:2px 4px 2px 0}

/* ── 回信卡：正文上方那一行 4 颗 + 同位置展开的那一块 ───────────────── */
.mr-tools{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:8px 0 0}
.mr-tools .mrt{border:1px solid var(--line);background:#fff;border-radius:var(--r-2,6px);
  padding:5px 10px;font-size:var(--fs-4);color:var(--ink-2);cursor:pointer;
  display:inline-flex;gap:5px;align-items:center;line-height:1.4}
.mr-tools .mrt:hover{background:var(--panel-2);border-color:var(--line-strong,#CFC8B4)}
.mr-tools .mrt.on{background:var(--brand-soft,#e6f4ff);border-color:var(--brand);
  color:var(--ink);font-weight:600}
.mr-pane{margin:8px 0 0;padding:10px 12px;background:#fff;border:1px solid var(--line);
  border-radius:var(--r-3,8px);box-shadow:inset 0 1px 0 rgba(60,50,30,.03)}

/* 工具栏「⋯」：缩进/清格式/撤销/重做 收在这里（按钮本身没动，只是不占一行） */
.mr-toolmore{position:relative;display:inline-block}
.mr-toolmore > summary{list-style:none;cursor:pointer;user-select:none}
.mr-toolmore > summary::-webkit-details-marker{display:none}
.mr-toolmore[open] > summary{background:var(--panel-2)}
.mr-toolmore-pop{position:absolute;left:0;top:calc(100% + 4px);z-index:30;display:flex;
  gap:4px;flex-wrap:wrap;width:max-content;max-width:min(320px,80vw);background:#fff;
  border:1px solid var(--line);border-radius:var(--r-3,8px);padding:8px;
  box-shadow:0 8px 24px rgba(60,50,30,.12)}

/* 窄屏（≤720）：4 颗辅助按钮允许横滑，不折成两行 */
@media (max-width:720px){
  .mr-tools{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;
    padding-bottom:2px}
  .mr-tools .mrt{white-space:nowrap;flex:none;min-height:36px}
}
/* 分类树两层（2026-10-19 批 2 下）：第一层大类、第二层细分；一屏不许铺满 */
.ins-l1,.ins-l2{display:flex;flex-wrap:wrap;gap:4px 6px}
.ins-l2{margin-top:4px;padding-top:4px;border-top:1px dashed var(--line)}
.ins-l1 .ghost,.ins-l2 .ghost{margin:0}
/* ═══════════ 2026-10-20 · 「所有」页：找功能（张总「②A」）═══════════
   ★ 为什么放在 hero 那一行：这一页是"功能都在这"，最缺的是
     「我知道有这功能，在哪一格」—— 顶栏那颗全局搜索搜的是**数据**（单号/客户/货号），
     不能拿来当功能搜索（会把数据搜索弄丢，见方案 ② 被否掉的那条路）。
   ★ 这一颗**只筛不造**：条目全部来自本页宫格（`_grid_cards.html`），不打接口、没有第二份清单。 */
.home-hero{display:flex;justify-content:space-between;align-items:flex-start;
  gap:var(--sp-4);flex-wrap:wrap}
.find-fn{display:flex;flex-direction:column;gap:4px;min-width:280px;max-width:420px;flex:1 1 auto}
.find-fn input{height:36px;padding:0 12px;border:1px solid var(--line);border-radius:var(--r-2);
  font-size:var(--fs-4);background:var(--panel);color:var(--ink)}
.find-fn input:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-soft)}
.find-fn .muted{font-size:var(--fs-5);min-height:16px}
@media (max-width:720px){
  .find-fn{min-width:0;max-width:none;width:100%}
}

/* ===== 推广赚佣金：单据页页脚那一条（2026-10-22 张总「在订单和所有互动的单据页面加入 推广赚佣金」）
   为什么做成"页脚一条"而不是浮球/弹窗：不挡审核/打印/发送那些干活按钮；每条单据都在。
   颜色一律用变量，别写死（全站换肤只改一处）。 ===== */
.promoBar{margin:var(--sp-2) 0 var(--sp-3)}
.promoBar .promoIn{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:8px 12px;border:1px dashed var(--line);border-radius:var(--r-2);
  background:var(--panel-2);color:var(--ink);text-decoration:none;font-size:var(--fs-5);
  min-height:44px}
.promoBar .promoIn:hover{border-color:var(--brand);color:var(--brand)}
.promoBar .promoIn .pi{font-size:15px}
.promoBar .promoIn b{font-size:var(--fs-4)}
.promoBar .promoIn .pt{color:var(--ink-2)}
.promoBar .promoIn .pg{margin-left:auto;color:var(--brand);font-weight:600}
@media (max-width:720px){ .promoBar .promoIn{font-size:var(--fs-6)} .promoBar .promoIn .pt{display:none} }
