/* ==========================================================================
   cb-theme.css —— CityB2B 工业控制台主题层
   建立 2026-09-23。基底 Bootstrap 5.3.3（MIT）。本文件只做四件事：
     ① 定义**语义 token**，白天/夜间各一套取值
     ② 把 Bootstrap 的 --bs-* 映射到这些语义 token
     ③ 补 Bootstrap 没有的：后台三段骨架（顶栏/侧栏/主区）
     ④ 补数据表的两条关键能力：数字列等宽右对齐 · 表头 sticky
   --------------------------------------------------------------------------
   为什么不自己写组件库：
     弹窗的焦点陷阱 · Esc 关闭 · 点遮罩关闭 · 背景滚动锁定 · ARIA ·
     下拉的键盘导航 · 标签页的 roving tabindex —— Bootstrap 做了二十年，
     自己重写一遍只会把它踩过的坑重踩一遍。

   --------------------------------------------------------------------------
   【主题怎么切】
     <html class="cb" data-bs-theme="light">   白天
     <html class="cb" data-bs-theme="dark">    夜间
   §2 里**每一个 token 两套里都有**，所以组件层（§4 起）只认语义名，
   一行都不用为夜间另写。

   ⚠ 不挂 prefers-color-scheme：ERP 要投屏、要打印、两个人共用一台机器，
     由系统偏好替人做决定会出乱子。切换是显式的，记在 localStorage 里。
   ⚠ 打印一律强制白天（§11）—— 夜间主题打出来是一张黑纸。

   --------------------------------------------------------------------------
   三条硬规矩（由 scripts/cb_css_lint.php 钉死）：
     ① 每条选择器要么以 .cb 开头，要么是 :root。**没有裸元素选择器。**
        老页面 DOM 上永远不会出现 class="cb"，Bootstrap 与本文件对它们都不可达。
     ② `!important` 只许出现在 §11 打印段 —— 那里必须压过夜间主题的取值。
        其它任何地方要用它，都说明那一页/那条规则设计错了，应该报红。
     ③ §2 之外不许出现 hex/rgb 字面量，全走 var()。

   ⚠ Bootstrap 5.3 的 .btn-primary 在编译产物里是写死的 --bs-btn-bg:#0d6efd，
     **改 --bs-primary 不会改到按钮**。配色要按组件覆盖 --bs-btn-* / --bs-table-*，
     见 §3。这是无构建工具改主题时最容易踩空的一条。

   ⚠ <body> 必须带 data-bs-no-jquery：页面上还有 jQuery 1.8.2，
     Bootstrap 5 检测到 window.jQuery 会自动把插件注册上去，挂到那么老的 jQuery
     上是自找麻烦。开关是官方的。
   ========================================================================== */

/* ==========================================================================
   §1 尺寸与几何 —— 与主题无关，两套共用
   ========================================================================== */
:root {
  /* ⚠ 2026-09-23 整体上调一档（用户：「样稿的感觉字体大一些」）。
     基准字 13→14px，控件 30→32、行 32→34 —— 三个数一起动，
     只改字号不改几何的话，字会顶到控件边框上。 */
  --cb-ctl-h:     32px;
  --cb-ctl-h-sm:  26px;
  --cb-row-h:     34px;
  --cb-side-w:    200px;  /* 14px 字，「当日订单生产管理」这种七字菜单不折行 */
  --cb-side-w-rail: 52px; /* 收起态：只留图标的那条轨 */
  --cb-topbar-h:  46px;
  /* 层级：跟 Bootstrap 的层对齐（固定条 1030 · 浮层 1050 · 弹窗 1055 · 弹窗之上 1060 · toast 1090）。
     页面自己的浮层 / 遮罩一律用这几个名字，不许再写数字 —— 数字一多，谁压谁就没人说得清 */
  --cb-z-bar:     1030;
  --cb-z-pop:     1050;
  --cb-z-modal:   1055;
  --cb-z-over:    1060;
  --cb-z-toast:   1090;
}

/* ==========================================================================
   §2 语义 TOKEN —— 整份文件唯一允许出现色值字面量的地方
   每个名字在「白天」和「夜间」两块里都要有，缺一个那里就会露出 Bootstrap 默认色
   ========================================================================== */

/* ---------- 白天 ---------- */
.cb,
.cb[data-bs-theme="light"] {
  /* 强调色 #00A1E9 是「骨架已经同意的那个色」：factory/side.htm 与
     company/side.htm 的 <style> 里一级菜单图标色和 hover 色各 4 处都是它
     （590 个页面共用那块侧栏），prod 模块也是它。
     ⚠ 白字压 #00A1E9 只有 2.89:1，不达标 —— 所以填充按钮用 -strong(4.94:1)，
       正文链接用 -text(6.58:1)。identity 色只用在边框、图标、选中指示条。 */
  --cb-accent:        #00A1E9;
  --cb-accent-strong: #0077b0;
  --cb-accent-hover:  #005f8d;
  --cb-accent-text:   #00629b;
  --cb-accent-soft:   #e8f4fd;

  /* 面 —— 对齐 prod 现值，保证 prod 改吃 token 时零位移 */
  --cb-bg:          #f4f6f8;   /* 页面底 */
  --cb-surface:     #ffffff;   /* 卡片 · 表体 */
  --cb-surface-2:   #fafbfc;   /* 行 hover */
  --cb-surface-3:   #eceff2;   /* 禁用 · 分隔带 */
  --cb-thead-bg:    #f4f6f8;   /* 表头 */
  --cb-line:        #e3e6eb;
  --cb-line-strong: #cdd2d9;

  /* 字 */
  --cb-text:    #3d444d;
  --cb-text-2:  #6b7480;       /* 白底 4.72:1 ✓ */
  --cb-text-3:  #9aa2ac;       /* 只许用在 ≥14px 或非文本上 */
  --cb-heading: #1f242b;
  --cb-on-fill: #ffffff;

  /* 状态。⚠ 徽章一律「浅底+边框+深字」不做「实底+白字」——
     #34C079 配白字只有 2.34:1，那种徽章全站都不达标 */
  --cb-ok-fill: #34C079; --cb-ok-text: #1c7c3f; --cb-ok-soft: #e6f7ec; --cb-ok-line: #b6e4c6;
  --cb-wn-fill: #FD6921; --cb-wn-text: #8a5a00; --cb-wn-soft: #fff6e5; --cb-wn-line: #ffd99b;
  --cb-bad-fill:#e8503a; --cb-bad-text:#b3382a; --cb-bad-soft:#fdecea; --cb-bad-line:#f5c6c0;
  /* 列表行的状态底色（下单页选产品：常购 = 柠檬黄，已加进订单 = 绿）。
     ⚠ 常购不借 --cb-wn-*：那一套偏橙（#fff6e5 是桃色），和迁移前的柠檬黄不是一个颜色，也会和折扣徽章撞色。
     -soft 是平时，-soft-2 是这一行被键盘/鼠标指着时 —— 指着的那一行不能反倒变淡 */
  --cb-star: #f5a623; --cb-on-star: #3d2800;
  --cb-star-soft: #fffbe0; --cb-star-soft-2: #fff3b8; --cb-star-line: #ffc107; --cb-star-text: #856404;
  --cb-ok-soft-2: #cfeedd;

  /* 骨架 —— 顶栏在白天也是深色（工业控制台的常规做法：把导航压下去，让数据浮上来） */
  --cb-topbar-bg:  #1f242b;
  --cb-topbar-fg:  #ffffff;
  --cb-topbar-dim: #cdd2d9;
  --cb-topbar-hover: rgba(255,255,255,.14);
  --cb-side-bg:    #ffffff;

  --cb-focus-ring: rgba(0, 161, 233, .32);
  --cb-shadow-1: 0 1px 2px rgba(16,24,40,.06);
  --cb-shadow-3: 0 12px 32px rgba(16,24,40,.18);
  --cb-scrim:    rgba(16,24,40,.32);
}

/* ---------- 夜间 ---------- */
.cb[data-bs-theme="dark"] {
  /* 夜间提亮 identity 色（#00A1E9 在深底上偏闷），但填充按钮仍用同一支 ——
     #0077b0 配白字 4.94:1，深底浅底都达标，两套主题按钮观感一致 */
  --cb-accent:        #38b6f0;
  --cb-accent-strong: #0077b0;
  --cb-accent-hover:  #0a8fd0;
  --cb-accent-text:   #5cc2f5;   /* 深底上 8.3:1 ✓ */
  --cb-accent-soft:   #10303f;

  /* 面 —— 不用纯黑。纯黑配白字在长时间盯数字时反而更累 */
  --cb-bg:          #12161b;
  --cb-surface:     #1a1f26;
  --cb-surface-2:   #222831;
  --cb-surface-3:   #2b323c;
  --cb-thead-bg:    #222831;
  --cb-line:        #2e3742;
  --cb-line-strong: #3d4854;

  --cb-text:    #d6dbe1;
  --cb-text-2:  #98a2ae;
  --cb-text-3:  #6e7885;
  --cb-heading: #f2f5f8;
  --cb-on-fill: #ffffff;

  /* 状态：深底上把 soft 压暗、text 提亮，语义位置不变 */
  --cb-ok-fill: #34C079; --cb-ok-text: #6ee7a0; --cb-ok-soft: #10291b; --cb-ok-line: #1f5a37;
  --cb-wn-fill: #FD6921; --cb-wn-text: #f0b45c; --cb-wn-soft: #2e2213; --cb-wn-line: #6b4a1a;
  --cb-bad-fill:#e8503a; --cb-bad-text:#f08c80; --cb-bad-soft:#2e1917; --cb-bad-line:#6b2f28;
  --cb-star: #f5b642; --cb-on-star: #2a1c00;
  --cb-star-soft: #2a2710; --cb-star-soft-2: #3d3714; --cb-star-line: #7a6412; --cb-star-text: #f2d25c;
  --cb-ok-soft-2: #183d26;

  --cb-topbar-bg:  #0d1116;
  --cb-topbar-fg:  #e6eaee;
  --cb-topbar-dim: #98a2ae;
  --cb-topbar-hover: rgba(255,255,255,.10);
  --cb-side-bg:    #1a1f26;

  --cb-focus-ring: rgba(56, 182, 240, .40);
  --cb-shadow-1: 0 1px 2px rgba(0,0,0,.40);
  --cb-shadow-3: 0 12px 32px rgba(0,0,0,.55);
  --cb-scrim:    rgba(0,0,0,.55);
}

/* ==========================================================================
   §3 把 Bootstrap 的 --bs-* 映射到语义 token
   这一段两套主题共用 —— 换主题只换 §2 的取值，这里一个字都不用动。
   ========================================================================== */
.cb {
  --bs-body-font-family: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
  /* ⚠ 字号一律 **px，不许用 rem**（2026-09-24 实测改过来的）。
     `rem` 相对浏览器根字号，而这台机器的根字号是 **14px** 不是默认的 16px ——
     `.875rem` 渲染出来是 **12.25px**，比设计值小 12.5%，用户看到的就是「字太小」。
     更要命的是**几何全是 px**（控件 32 / 行 34 / 侧栏 200）：字用 rem 就会和它们脱钩，
     根字号一变，字缩了而控件纹丝不动，比例失衡。
     项目其余 2300 个模板也全是 px —— 同一个系统不许两种行为。 */
  --bs-body-font-size: 14px;
  --bs-body-line-height: 1.45;
  --bs-body-color: var(--cb-text);
  --bs-body-bg: var(--cb-bg);
  --bs-emphasis-color: var(--cb-heading);
  --bs-secondary-color: var(--cb-text-2);
  --bs-tertiary-color: var(--cb-text-3);
  --bs-border-color: var(--cb-line);
  --bs-border-color-translucent: var(--cb-line);
  --bs-secondary-bg: var(--cb-surface-3);
  --bs-tertiary-bg: var(--cb-surface-2);

  /* 圆角 3 档（现在全站 15 种）。Bootstrap 默认 6px 太圆，密集台面显松散。
     ⚠ **用 px 不用 rem** —— 见下面字号那条：根字号不是 16px 时 rem 会整体缩水。 */
  --bs-border-radius-sm: 2px;
  --bs-border-radius:    3px;      /* 全站 321 次那一套胜出 */
  --bs-border-radius-lg: 6px;
  --bs-border-radius-xl: 10px;     /* 10px 徽章 */

  --bs-link-color: var(--cb-accent-text);
  --bs-link-hover-color: var(--cb-accent-hover);
  --bs-primary: var(--cb-accent-strong);
  --bs-focus-ring-color: var(--cb-focus-ring);
  --bs-focus-ring-width: 2px;

  /* 这几样 Bootstrap 默认跟着 --bs-body-bg 走，会让卡片和页面同色，必须显式给 */
  --bs-card-bg: var(--cb-surface);
  --bs-card-cap-bg: var(--cb-surface-2);
  --bs-card-border-color: var(--cb-line);
  --bs-modal-bg: var(--cb-surface);
  --bs-modal-header-border-color: var(--cb-line);
  --bs-modal-footer-border-color: var(--cb-line);
  --bs-dropdown-bg: var(--cb-surface);
  --bs-dropdown-border-color: var(--cb-line);
  --bs-dropdown-link-color: var(--cb-text);
  --bs-dropdown-link-hover-bg: var(--cb-surface-2);
  --bs-toast-bg: var(--cb-surface);
  --bs-toast-border-color: var(--cb-line);
  --bs-form-control-bg: var(--cb-surface);
  --bs-form-control-color: var(--cb-text);

  font-family: var(--bs-body-font-family);
  font-size: var(--bs-body-font-size);
  line-height: var(--bs-body-line-height);
  color: var(--bs-body-color);
  background: var(--bs-body-bg);
}

/* 表单控件：Bootstrap 5.3 的 .form-control 背景写死在 --bs-body-bg 上，
   夜间会和页面同色、看不出边界，所以显式给面色 */
.cb .form-control, .cb .form-select, .cb textarea.form-control {
  background-color: var(--cb-surface);
  color: var(--cb-text);
  border-color: var(--cb-line-strong);
}
.cb .form-control::placeholder { color: var(--cb-text-3); }
.cb .form-control:disabled, .cb .form-control[readonly], .cb .form-select:disabled {
  background-color: var(--cb-surface-3); color: var(--cb-text-3);
}
.cb .input-group-text { background: var(--cb-surface-2); border-color: var(--cb-line-strong); color: var(--cb-text-2); }
/* 夜间把关闭按钮的黑色叉翻白 —— Bootstrap 的 .btn-close 是一张深色 SVG */
.cb[data-bs-theme="dark"] .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }

/* ==========================================================================
   §4 按 Bootstrap 组件覆盖配色
   ⚠ 改 --bs-primary 改不到按钮 —— 编译产物里 .btn-primary 是写死的 #0d6efd。
   ========================================================================== */
.cb .btn-primary {
  --bs-btn-bg: var(--cb-accent-strong);
  --bs-btn-border-color: var(--cb-accent-strong);
  --bs-btn-color: var(--cb-on-fill);
  --bs-btn-hover-bg: var(--cb-accent-hover);
  --bs-btn-hover-border-color: var(--cb-accent-hover);
  --bs-btn-hover-color: var(--cb-on-fill);
  --bs-btn-active-bg: var(--cb-accent-hover);
  --bs-btn-active-border-color: var(--cb-accent-hover);
  --bs-btn-disabled-bg: var(--cb-surface-3);
  --bs-btn-disabled-border-color: var(--cb-line-strong);
  --bs-btn-disabled-color: var(--cb-text-3);
}
/* ⚠ **`.btn-outline-primary` 一开始漏了**，于是底部那三颗批量按钮印的是
   Bootstrap 写死的 #0d6efd，而顶部的主按钮是 #0077b0 —— **同一页两种蓝**。
   「改 --bs-primary 改不到按钮」这条已经写在文件抬头，覆盖时**每个变体都要列**，
   少列一个就是静默地漏出原厂色（2026-09-23 在浏览器里量出来的）。 */
.cb .btn-outline-primary {
  --bs-btn-color: var(--cb-accent-text);
  --bs-btn-bg: transparent;
  --bs-btn-border-color: var(--cb-accent-strong);
  --bs-btn-hover-bg: var(--cb-accent-strong);
  --bs-btn-hover-color: var(--cb-on-fill);
  --bs-btn-hover-border-color: var(--cb-accent-strong);
  --bs-btn-active-bg: var(--cb-accent-hover);
  --bs-btn-active-color: var(--cb-on-fill);
  --bs-btn-active-border-color: var(--cb-accent-hover);
  --bs-btn-disabled-color: var(--cb-text-3);
  --bs-btn-disabled-border-color: var(--cb-line);
}
.cb .btn-outline-secondary {
  --bs-btn-color: var(--cb-text);
  --bs-btn-bg: var(--cb-surface);
  --bs-btn-border-color: var(--cb-line-strong);
  --bs-btn-hover-bg: var(--cb-surface-2);
  --bs-btn-hover-color: var(--cb-heading);
  --bs-btn-hover-border-color: var(--cb-text-3);
  --bs-btn-active-bg: var(--cb-surface-3);
  --bs-btn-active-color: var(--cb-heading);
  --bs-btn-active-border-color: var(--cb-text-3);
  --bs-btn-disabled-color: var(--cb-text-3);
  --bs-btn-disabled-border-color: var(--cb-line);
}
.cb .btn-outline-danger {
  --bs-btn-color: var(--cb-bad-text);
  --bs-btn-bg: var(--cb-bad-soft);
  --bs-btn-border-color: var(--cb-bad-line);
  --bs-btn-hover-bg: var(--cb-bad-line);
  --bs-btn-hover-color: var(--cb-bad-text);
  --bs-btn-hover-border-color: var(--cb-bad-line);
}
.cb .btn-danger {   /* 确认框里「删除 / 作废 / 撤销」的实心红键（cbConfirm danger:true 用的就是它） */
  --bs-btn-color: var(--cb-on-fill);
  --bs-btn-bg: var(--cb-bad-fill);
  --bs-btn-border-color: var(--cb-bad-fill);
  --bs-btn-hover-color: var(--cb-on-fill);
  --bs-btn-hover-bg: var(--cb-bad-text);
  --bs-btn-hover-border-color: var(--cb-bad-text);
  --bs-btn-disabled-bg: var(--cb-bad-line);
  --bs-btn-disabled-border-color: var(--cb-bad-line);
}
/* 「成功 / 完成」类动作（智能排单等老页面里原来就是绿键）。
   不做实心：白字压在 --cb-ok-fill 上只有 2.4:1，夜间的 --cb-ok-text 又是浅绿 —— 用浅底深字，两套主题都读得清 */
.cb .btn-success {
  --bs-btn-color: var(--cb-ok-text);
  --bs-btn-bg: var(--cb-ok-soft);
  --bs-btn-border-color: var(--cb-ok-line);
  --bs-btn-hover-color: var(--cb-ok-text);
  --bs-btn-hover-bg: var(--cb-ok-line);
  --bs-btn-hover-border-color: var(--cb-ok-line);
  --bs-btn-active-color: var(--cb-ok-text);
  --bs-btn-active-bg: var(--cb-ok-line);
  --bs-btn-active-border-color: var(--cb-ok-line);
  --bs-btn-disabled-color: var(--cb-text-3);
  --bs-btn-disabled-bg: var(--cb-ok-soft);
  --bs-btn-disabled-border-color: var(--cb-ok-line);
}
.cb .btn-link { --bs-btn-color: var(--cb-accent-text); --bs-btn-hover-color: var(--cb-accent-hover); }
.cb .table {
  --bs-table-bg: var(--cb-surface);
  --bs-table-color: var(--cb-text);
  --bs-table-border-color: var(--cb-line);
  --bs-table-hover-bg: var(--cb-surface-2);
  --bs-table-hover-color: var(--cb-text);
  --bs-table-active-bg: var(--cb-accent-soft);
  --bs-table-active-color: var(--cb-text);
}
/* 列表组（下单页选客户 / 选产品）：选中项原来是 Bootstrap 写死的 #0d6efd 实心蓝，
   行里的编号(.mono)、电话/地址(.cb-dim) 是灰字，压在饱和蓝上看不清。
   改成与表格选中行同一套：浅强调底 + 正文色 + 强调色描边，灰字照样读得出来。 */
.cb .list-group {
  --bs-list-group-color: var(--cb-text);
  --bs-list-group-bg: var(--cb-surface);
  --bs-list-group-border-color: var(--cb-line);
  --bs-list-group-action-color: var(--cb-text);
  --bs-list-group-action-hover-color: var(--cb-text);
  --bs-list-group-action-hover-bg: var(--cb-surface-2);
  --bs-list-group-action-active-color: var(--cb-text);
  --bs-list-group-action-active-bg: var(--cb-accent-soft);
  --bs-list-group-active-color: var(--cb-text);
  --bs-list-group-active-bg: var(--cb-accent-soft);
  --bs-list-group-active-border-color: var(--cb-accent);
}
/* 列表行的状态（下单页选产品，2026-09-29 恢复迁移前的样子）：
   常购 = 整行柠檬黄底，已加进这张订单 = 整行绿底 + 左边一道绿条。一眼就要分得出来。
   改的是 Bootstrap 自己的变量，所以 hover / 键盘指着（.active）时底色只加深、不会被刷回白的。
   两个都占时绿的赢 ——「已经在单子上了」是此刻更要紧的那件事，常购还有星标和徽章说着 */
.cb .list-group-item.cb-row-star {
  --bs-list-group-bg: var(--cb-star-soft);
  --bs-list-group-action-hover-bg: var(--cb-star-soft-2);
  --bs-list-group-action-active-bg: var(--cb-star-soft-2);
  --bs-list-group-active-bg: var(--cb-star-soft-2);
}
.cb .list-group-item.cb-row-ok {
  --bs-list-group-bg: var(--cb-ok-soft);
  --bs-list-group-action-hover-bg: var(--cb-ok-soft-2);
  --bs-list-group-action-active-bg: var(--cb-ok-soft-2);
  --bs-list-group-active-bg: var(--cb-ok-soft-2);
  box-shadow: inset 4px 0 0 var(--cb-ok-fill);
}
/* 分组抬头：「常购产品 (4)」那一条 */
.cb .list-group-item.cb-row-head-star { background: var(--cb-star-soft); color: var(--cb-star-text); border-color: var(--cb-star-line); }
/* 「常购」徽章：黄底黄边，和橙色的折扣徽章（.badge.wn）分得开 */
.cb .badge.star { background: var(--cb-star-soft); border-color: var(--cb-star-line); color: var(--cb-star-text); }
.cb .list-group-item.cb-row-head { background: var(--cb-surface-3); color: var(--cb-text-2); }
.cb .cb-star-t { color: var(--cb-star); }
/* 「只看常购」开关：平时黄色描边，打开时黄色实底深字（白字压在琥珀上只有 2:1） */
.cb .btn-star {
  --bs-btn-color: var(--cb-star-text);
  --bs-btn-bg: var(--cb-surface);
  --bs-btn-border-color: var(--cb-star-line);
  --bs-btn-hover-color: var(--cb-star-text);
  --bs-btn-hover-bg: var(--cb-star-soft);
  --bs-btn-hover-border-color: var(--cb-star);
  --bs-btn-active-color: var(--cb-on-star);
  --bs-btn-active-bg: var(--cb-star);
  --bs-btn-active-border-color: var(--cb-star);
}
/* 胶囊标签（下单页的小类）：Bootstrap 编译产物里选中底是写死的 #0d6efd，和全站的强调蓝不是一个蓝 */
.cb .nav-pills {
  --bs-nav-pills-link-active-color: var(--cb-on-fill);
  --bs-nav-pills-link-active-bg: var(--cb-accent-strong);
}
.cb .btn-star i { color: var(--cb-star); }
.cb .btn-star.active i { color: inherit; }
.cb .nav-tabs {
  --bs-nav-tabs-border-color: var(--cb-line);
  --bs-nav-tabs-link-active-color: var(--cb-accent-text);
  --bs-nav-tabs-link-active-bg: transparent;
  --bs-nav-tabs-link-active-border-color: transparent transparent var(--cb-accent);
  --bs-nav-link-color: var(--cb-text-2);
  --bs-nav-link-hover-color: var(--cb-accent-text);
}
.cb .pagination {
  --bs-pagination-bg: var(--cb-surface);
  --bs-pagination-color: var(--cb-text);
  --bs-pagination-border-color: var(--cb-line);
  --bs-pagination-hover-color: var(--cb-accent-text);
  --bs-pagination-hover-bg: var(--cb-surface-2);
  --bs-pagination-hover-border-color: var(--cb-line);
  --bs-pagination-active-bg: var(--cb-accent-strong);
  --bs-pagination-active-border-color: var(--cb-accent-strong);
  --bs-pagination-active-color: var(--cb-on-fill);
  --bs-pagination-disabled-color: var(--cb-text-3);
  --bs-pagination-disabled-bg: var(--cb-surface-3);
  --bs-pagination-disabled-border-color: var(--cb-line);
}
.cb .modal-footer { background: var(--cb-surface-2); }
/* 夜间 Bootstrap 默认的阴影几乎看不见，用自己的那一档 */
.cb .modal-content { box-shadow: var(--cb-shadow-3); }
.cb .progress { background: var(--cb-surface-3); }

/* ==========================================================================
   §5 密度 —— Bootstrap 默认给触屏留的余量，桌面 ERP 不需要
   控件 38px → 30px，行高压到 32px。这一节就是「密集型控制台」的全部。
   ========================================================================== */
.cb .form-control, .cb .form-select, .cb .btn, .cb .input-group-text {
  height: var(--cb-ctl-h); padding-top: 0; padding-bottom: 0;
  line-height: calc(var(--cb-ctl-h) - 2px); font-size: var(--bs-body-font-size);
}
.cb textarea.form-control { height: auto; line-height: var(--bs-body-line-height); padding: 6px 8px; }
/* 多选 / 列表框（select[multiple] 或带 size）：不许被压成一行高。
   实测新增调度的「配送区（可多选）」只露出一行半，人根本没法按 Ctrl 多选（用户 2026-09-24 截图） */
.cb select.form-select[multiple], .cb select.form-select[size]:not([size="1"]) {
  height: auto; min-height: 120px; line-height: var(--bs-body-line-height); padding: 4px 8px; background-image: none; }
.cb .btn { display: inline-flex; align-items: center; justify-content: center; gap: 4px; }
/* 多行按钮（下单页选规格：规格名 + 库存 +「上次购买」+ 最少订货量）：高度随内容长，不许被压成一行。
   实测两行字被 30px 定高压在一起、溢出按钮（用户 2026-10-05 截图） */
.cb .btn.cb-btn-multi { height: auto; min-height: var(--cb-ctl-h); padding-top: 4px; padding-bottom: 4px;
  line-height: var(--bs-body-line-height); }
.cb .btn-sm, .cb .form-control-sm, .cb .form-select-sm {
  height: var(--cb-ctl-h-sm); line-height: calc(var(--cb-ctl-h-sm) - 2px); font-size: 13px;
}
/* 一排同级动作里，两个字的按钮只有 38px、邻居 149px，差 4 倍。给个下限 */
.cb .btn-sm {
  min-width: 64px;
}
.cb .form-label { font-size: 12px; color: var(--cb-text-2); margin-bottom: 2px; }
.cb .form-text { color: var(--cb-text-3); }
.cb .table > :not(caption) > * > * { padding: 6px 8px; }
.cb .table > tbody > tr { height: var(--cb-row-h); }
.cb .card-body { padding: 12px; }
.cb .modal-header, .cb .modal-footer { padding: 12px 16px; }
.cb .modal-body { padding: 16px; }
.cb .nav-tabs .nav-link { padding: 8px 12px; }

/* ==========================================================================
   §6 数据表的两条关键能力 —— Bootstrap 没有，而这是 ERP 的命根子
   ========================================================================== */

/* 数字列：右对齐 + 等宽数字。
   没有 tabular-nums 时 1 比 7 窄，小数点会飘、右边缘参差，一列数字没法上下比。
   现在全站主表是 .main-list table th,td{text-align:center} —— 金额也居中。 */
.cb .table th.num, .cb .table td.num, .cb .num {
  text-align: right; font-variant-numeric: tabular-nums;
}
.cb .table td.mono, .cb .mono {
  font-family: Consolas, "Courier New", monospace; font-size: 13px; color: var(--cb-text-2);
}

/* 表头 sticky —— 2137 行的订单列表，这是单项收益最大的一处改动 */
.cb .table-sticky { max-height: 100%; overflow: auto; }
/* 占满剩余高度的那张表卡（.flex-grow-1）：可以收缩，但不许缩到看不见几行。
   屏幕矮、筛选栏高的时候，卡片守住 320px，多出来的由 .cb-main 整体滚 —— 原来模板里内联的
   min-height:0 会把它压到 135px（手机实测）。 */
.cb .table-sticky.flex-grow-1 { min-height: 320px; }
.cb .table-sticky > .table > thead > tr > th {
  position: sticky; top: 0; z-index: 2;
  background: var(--cb-thead-bg); border-bottom: 1px solid var(--cb-line-strong);
}
.cb .table > thead > tr > th {
  background: var(--cb-thead-bg);
  font-weight: 600; color: var(--cb-text-2); font-size: 13px; white-space: nowrap;
}
.cb .table > tbody > tr > td { white-space: nowrap; vertical-align: middle; }
.cb .table td.wrap { white-space: normal; }

/* ==========================================================================
   §7 后台三段骨架 —— Bootstrap 是通用框架，不含管理后台的壳，这一段必须自己写
   ========================================================================== */
/* 应用壳：只让 .cb-main 滚，侧栏自己滚自己的。
   ⚠ .cb-app 的 height:100% 要靠 html/body 也有高度才成立。原来没钉，html/body 被内容撑到 2155px，
   整个文档一起滚 —— 右边一滚，左边菜单跟着走（用户 2026-09-24 报的就是这个）。
   `.cb` 就是 <html class="cb">。overscroll-behavior 让滚到头时不把滚动链给外层。 */
.cb, .cb > body { height: 100%; }
.cb-app { display: flex; flex-direction: column; height: 100%; min-width: 1280px; }
.cb-topbar { height: var(--cb-topbar-h); flex: 0 0 auto; display: flex; align-items: center;
             gap: 16px; padding: 0 16px; background: var(--cb-topbar-bg); color: var(--cb-topbar-fg); }
.cb-topbar .brand { font-size: 14px; font-weight: 600; letter-spacing: .3px; }
.cb-topbar .crumb, .cb-topbar .meta { font-size: 12px; color: var(--cb-topbar-dim); }
.cb-topbar .meta { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.cb-topbar a { color: var(--cb-topbar-dim); }
.cb-topbar a:hover { color: var(--cb-topbar-fg); }

/* 主题开关：顶栏右侧一颗。白天显示月亮、夜间显示太阳 —— 图标表示「点了会变成什么」 */
.cb-themetoggle { display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border: 1px solid var(--cb-topbar-dim); border-radius: var(--bs-border-radius);
  background: none; color: var(--cb-topbar-fg); cursor: pointer; padding: 0; line-height: 1; }
.cb-themetoggle:hover { background: var(--cb-topbar-hover); }

/* 顶栏里的下拉触发钮（站点 / 账号 / 语言）。用 Bootstrap 的 .dropdown 骨架，
   这里只给触发钮一个贴合深色顶栏的皮 —— 老 header.htm 那三个手写 mouseenter
   下拉不能用键盘、没有 ARIA、点外面也不关。 */
.cb-topbar-btn { display: inline-flex; align-items: center; gap: 6px; height: 26px;
  padding: 0 8px; border: 1px solid transparent; border-radius: var(--bs-border-radius);
  background: none; color: var(--cb-topbar-fg); font-size: 12px; cursor: pointer; }
.cb-topbar-btn:hover, .cb-topbar-btn[aria-expanded="true"] {
  background: var(--cb-topbar-hover); border-color: var(--cb-topbar-dim); }
.cb-topbar .dropdown-menu { font-size: var(--bs-body-font-size); }
.cb .dropdown-item.active, .cb .dropdown-item:active {
  background: var(--cb-accent-soft); color: var(--cb-accent-text); }
.cb .dropdown-item-text { font-size: 12px; }
/* _cb_modules.htm 是从 header.htm 原样剪来的 <li> 列表，没有 .dropdown-item。
   在这里给它上皮，共用的那份 markup 就不用为新旧任何一边改。 */
.cb-topbar .dropdown-menu li { list-style: none; }
.cb-topbar .dropdown-menu li > a {
  display: block; width: 100%; padding: 5px 16px; clear: both; font-weight: 400;
  color: var(--bs-dropdown-link-color); text-align: inherit; text-decoration: none;
  white-space: nowrap; background: none; border: 0; }
.cb-topbar .dropdown-menu li > a:hover { background: var(--bs-dropdown-link-hover-bg); color: var(--cb-accent-text); }
.cb-topbar .dropdown-menu { max-height: 70vh; overflow-y: auto; }
.cb-dim { color: var(--cb-text-3); }

/* 窄屏把商家名/用户名让位给操作按钮 */
@media (max-width: 767.98px) { .cb-hide-sm { display: none; } }

.cb-body { flex: 1 1 auto; display: flex; min-height: 0; }
/* ⚠ 底色**不能写在 .cb-side 上**：Bootstrap 在 @media (min-width:992px) 里给
   `.offcanvas-lg` 写了 `background-color: transparent !important` —— 拼特异度没用，
   加 !important 是这一层的禁忌。2026-09-23 实测 `getComputedStyle(.cb-side).backgroundColor`
   是 `rgba(0,0,0,0)`，**白天/夜间两套都没上过色**，侧栏一直在借页面底色。
   底色挪到里面那层（.box 是菜单本体的唯一根，991 行 markup 一个字不动）。 */
.cb-side { flex: 0 0 var(--cb-side-w); width: var(--cb-side-w);
           border-right: 1px solid var(--cb-line); overflow-y: auto; }
.cb .cb-side > .box { background: var(--cb-side-bg); min-height: 100%; }
.cb-side .grp { padding: 6px 0; border-bottom: 1px solid var(--cb-line); }
.cb-side a { display: block; padding: 7px 12px 7px 16px; color: var(--cb-text);
             border-left: 2px solid transparent; text-decoration: none; }
.cb-side a:hover { background: var(--cb-surface-2); color: var(--cb-accent-text); }
.cb-side a.on { background: var(--cb-accent-soft); color: var(--cb-accent-text);
                border-left-color: var(--cb-accent); font-weight: 600; }

.cb-main { flex: 1 1 auto; min-width: 0; overflow: auto; overscroll-behavior: contain; }
.cb-side { overscroll-behavior: contain; scrollbar-width: thin; }
.cb-page { padding: 16px; display: flex; flex-direction: column; gap: 12px; height: 100%; }
.cb-pagehead { display: flex; align-items: center; gap: 12px; }
.cb-pagehead h1 { font-size: 16px; font-weight: 600; color: var(--cb-heading); margin: 0; line-height: 1.2; }
.cb-pagehead .sub { font-size: 12px; color: var(--cb-text-2); }

/* ==========================================================================
   §8 徽章 · 状态点 · 提示条 —— 用 Bootstrap 的 .badge / .alert 骨架，只换配色
   ========================================================================== */
.cb .badge { font-weight: 500; border: 1px solid transparent; border-radius: var(--bs-border-radius-xl); }
.cb .badge.ok   { background: var(--cb-ok-soft);  border-color: var(--cb-ok-line);  color: var(--cb-ok-text); }
.cb .badge.wn   { background: var(--cb-wn-soft);  border-color: var(--cb-wn-line);  color: var(--cb-wn-text); }
.cb .badge.bad  { background: var(--cb-bad-soft); border-color: var(--cb-bad-line); color: var(--cb-bad-text); }
.cb .badge.info { background: var(--cb-accent-soft); border-color: var(--cb-accent); color: var(--cb-accent-text); }
.cb .badge.mut  { background: var(--cb-surface-3); border-color: var(--cb-line); color: var(--cb-text-2); }

.cb .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 4px; }
.cb .dot.ok  { background: var(--cb-ok-fill); }
.cb .dot.wn  { background: var(--cb-wn-fill); }
.cb .dot.bad { background: var(--cb-bad-fill); }
.cb .dot.mut { background: var(--cb-line-strong); }

.cb .alert { padding: 8px 12px; margin: 0; border-radius: var(--bs-border-radius); display: flex; gap: 8px; }
.cb .alert.ok   { background: var(--cb-ok-soft);  border-color: var(--cb-ok-line);  color: var(--cb-ok-text); }
.cb .alert.wn   { background: var(--cb-wn-soft);  border-color: var(--cb-wn-line);  color: var(--cb-wn-text); }
.cb .alert.bad  { background: var(--cb-bad-soft); border-color: var(--cb-bad-line); color: var(--cb-bad-text); }
.cb .alert.info { background: var(--cb-accent-soft); border-color: var(--cb-accent); color: var(--cb-accent-text); }
.cb .alert a { color: inherit; text-decoration: underline; }

/* 语义文字色 —— 给图标和状态字用。迁移把散落的 color:#58964E / #F30 / #999
   收敛到这四个，它们跟着白天/夜间翻。 */
.cb .cb-ok-t   { color: var(--cb-ok-text); }
.cb .cb-wn-t   { color: var(--cb-wn-text); }
.cb .cb-bad-t  { color: var(--cb-bad-text); }
.cb .cb-link-t { color: var(--cb-accent-text); }
.cb .cb-fieldlabel { display: block; font-weight: 600; margin-bottom: 4px; font-size: 12px; color: var(--cb-text-2); }

/* 作废 / 已取消的行：整行变灰。Bootstrap 的表格给每一格写了 color，挂在 <tr> 上的颜色类压不过，要落到格子上 */
.cb .table > tbody > tr.cb-row-mut > td { color: var(--cb-text-3); }
.cb .table > tbody > tr.cb-row-mut > td a { color: inherit; }

/* 算不出来的数：印这个，不许印 0 —— 一个算不出来的数不该长得像一个算出来的 0 */
.cb .na { color: var(--cb-text-3); font-variant-numeric: tabular-nums; }

/* ==========================================================================
   §9 Toast —— 位置只有一处。
   实测现在：prod 的在屏幕顶部是绿的，dispatch_assist 的在屏幕底部是深蓝灰的。
   ========================================================================== */
.cb-toaster { position: fixed; right: 16px; top: calc(var(--cb-topbar-h) + 12px);
              z-index: var(--cb-z-toast); display: flex; flex-direction: column; gap: 8px; }
.cb .toast { font-size: var(--bs-body-font-size); box-shadow: var(--cb-shadow-1); }
.cb .toast [data-cb-n] { white-space: nowrap; }   /* 同一句重复时的「×N」，不许折成两行 */

/* ==========================================================================
   §10 应用层小件 —— Bootstrap 确实没有的那几样，只有这三个
   筛选栏 / 表单行 / 分页 全部用 Bootstrap 原件 + 工具类，不另写。
   ========================================================================== */
.cb .cb-empty { padding: 32px 16px; text-align: center; }
.cb .cb-empty .t { font-size: 14px; color: var(--cb-text-2); margin-bottom: 8px; }
.cb .cb-empty .m { font-size: 12px; color: var(--cb-text-3); margin-bottom: 12px; }

.cb .cb-kv { display: grid; grid-template-columns: 88px 1fr; gap: 8px 12px; margin: 0; }
.cb .cb-kv dt { color: var(--cb-text-2); text-align: right; font-weight: 400; }
.cb .cb-kv dd { margin: 0; color: var(--cb-text); }

.cb .cb-chip { background: var(--cb-accent-soft); border: 1px solid var(--cb-accent);
               color: var(--cb-accent-text); font-weight: 400; }
.cb .cb-chip button { border: 0; background: none; padding: 0 0 0 4px; line-height: 1;
                      color: inherit; cursor: pointer; }

/* Vue 编译完之前藏住模板原文（{{ … }}）。老页面各自在页内 <style> 里写这一条，
   迁移后页面不许有 <style>，所以收在这里（S3 请求 3，2026-09-25） */
.cb [v-cloak] { display: none; }

/* 嵌入模式（_cb_page_open 的 cb_embed=1）：页面装在别的页的 iframe 弹窗里 ——
   不出顶栏、不出侧栏，主区只留一圈窄边（S2 请求 3，2026-09-25） */
.cb .cb-app[data-embed="1"] .cb-page { padding: 8px; }

/* ==========================================================================
   §11 打印 —— 天天打单。
   ⚠ 强制回到白天：夜间主题打出来是一张黑纸，而且会把碳粉吃干净。
     这是全文件唯一允许用 !important 的地方 —— 它必须压过 §2 的夜间取值。
   ========================================================================== */
@media print {
  .cb[data-bs-theme="dark"] {
    --cb-bg: #ffffff !important;      --cb-surface: #ffffff !important;
    --cb-surface-2: #ffffff !important; --cb-surface-3: #f2f2f2 !important;
    --cb-thead-bg: #f2f2f2 !important;
    --cb-line: #cccccc !important;    --cb-line-strong: #999999 !important;
    --cb-text: #000000 !important;    --cb-text-2: #333333 !important;
    --cb-text-3: #666666 !important;  --cb-heading: #000000 !important;
    --cb-ok-soft: #ffffff !important; --cb-wn-soft: #ffffff !important; --cb-bad-soft: #ffffff !important;
    --cb-ok-text: #000000 !important; --cb-wn-text: #000000 !important; --cb-bad-text: #000000 !important;
  }
  .cb .cb-topbar, .cb .cb-side, .cb .cb-toaster, .cb .cb-noprint { display: none; }
  .cb-page { padding: 0; }
  /* 屏幕上是「只让 .cb-main 滚」的壳；打印时要放开，不然只印出第一屏 */
  .cb, .cb > body, .cb .cb-app { height: auto; }
  .cb .cb-main { overflow: visible; }
  .cb .table-sticky { max-height: none; overflow: visible; }
  .cb .table-sticky > .table > thead > tr > th { position: static; }
}

/* ==========================================================================
   §13 响应式 —— 手机 / 平板 / 桌面一套页面
   --------------------------------------------------------------------------
   为什么必须做，而且必须现在做：
     `getUserDevice()` 把 iPad 与安卓平板判成 'tablet'，而 `display_pc_mobile()`
     里 tablet != desktop —— **平板用户现在拿到的是为 5 寸屏做的手机模板**。
     微信内置浏览器（'wechat'）同理。

   老手机版怎么退场（开关只有一处，不许散着改）：
     每迁完一页，把那一页的 `display_pc_mobile($pc, $mob)` 改成 `display($pc)`。
     改一处退一页，**不动其余 175 个调用点**，也不删任何模板 ——
     模板等全部调用点退完之后单独一轮清理（`docs/ui-dead-templates.md`）。
     ⚠ 反过来做（先删模板）会当场 500：`display_pc_mobile` 没有回落分支。

   断点用 Bootstrap 官方的，不另立一套：
     <576 手机竖屏 · 576–767 手机横屏 · 768–991 平板竖屏 ·
     992–1199 平板横屏/小笔记本 · ≥1200 桌面
   ========================================================================== */

/* 桌面才锁最小宽度。锁在 .cb-app 上会让手机横向滚动，那是老页面 body{min-width:1150px} 的老毛病 */
.cb-app { min-width: 0; }
@media (min-width: 1200px) { .cb-app { min-width: 1280px; } }

/* 窄屏不做「表卡里面再滚」：手机上筛选栏就占去大半屏，嵌套的纵向滚动既挤又难用。
   <992px 让整页（.cb-main）一起滚，表卡只保留横向滚动。 */
@media (max-width: 991.98px) {
  .cb .cb-page { height: auto; }
  .cb .table-sticky, .cb .table-sticky.flex-grow-1 { max-height: none; min-height: 0; }
}

/* --- 侧栏：<992px 收成 Bootstrap 的 offcanvas ---------------------------
   markup 用 `<nav class="cb-side offcanvas-lg offcanvas-start" id="cbSide">`，
   顶栏的汉堡按钮用 `data-bs-toggle="offcanvas" data-bs-target="#cbSide"`。
   遮罩 · Esc 关闭 · 焦点陷阱 · 滚动锁定全是 Bootstrap 自带，**零自写 JS**。 */
/* ⚠ 抽屉宽度要改 Bootstrap 的变量，不要去拼特异度：
   它的 `.offcanvas-lg.offcanvas-start`（0-2-0）压得过 `.cb-side`（0-1-0），
   直接写 width 会被忽略 —— 实测抽屉出来是 400px，占了 778px 平板的一半还多。 */
.cb .cb-side { --bs-offcanvas-width: 260px; --bs-offcanvas-bg: var(--cb-side-bg); }
@media (max-width: 991.98px) {
  .cb .cb-side { flex: 0 0 auto; border-right: 0; }
}
/* ⚠ 特异度要压得过 `.cb .btn { display:inline-flex }`（0-2-0）——
   写成 `.cb-burger{display:none}`（0-1-0）的话汉堡在桌面宽度下也会显示出来，
   而那时侧栏本来就在。2026-09-23 在浏览器里实测到的，四层脚本一条都没报。 */
.cb .cb-burger { display: none; }
@media (max-width: 991.98px) { .cb .cb-burger { display: inline-flex; } }

/* --- 页面骨架 ---------------------------------------------------------- */
@media (max-width: 991.98px) {
  .cb-page { padding: 12px; gap: 10px; }
  .cb-topbar { gap: 10px; padding: 0 10px; }
  .cb-topbar .crumb { display: none; }          /* 面包屑在窄屏挤掉标题，标题更重要 */
}
@media (max-width: 767.98px) {
  .cb-page { padding: 10px; }
  .cb-pagehead { flex-wrap: wrap; }
  .cb-pagehead > .cb-actions { margin-left: 0; width: 100%; }
  .cb-topbar .meta > span { display: none; }    /* 商家名/用户名让位，退出与主题开关留着 */
  .cb-topbar .meta > a, .cb-topbar .meta > button { display: inline-flex; }
}

/* --- 表格：窄屏横向滚，并让第一列吸住 ----------------------------------
   ERP 表格 13 列，手机上无论怎么排都放不下。横向滚 + 首列吸附是唯一诚实的做法：
   人滑到第 9 列时还知道这一行是哪一单。不做「一行摊成一张卡」——
   那会把 17 行变成 17 屏，密集台面的价值当场归零。 */
.cb-tablewrap, .cb .table-sticky { -webkit-overflow-scrolling: touch; }
@media (max-width: 991.98px) {
  .cb .table-stickcol > .table > thead > tr > th:first-child,
  .cb .table-stickcol > .table > tbody > tr > td:first-child {
    position: sticky; left: 0; z-index: 1; background: var(--cb-surface);
  }
  .cb .table-stickcol > .table > thead > tr > th:first-child { z-index: 3; background: var(--cb-thead-bg); }
  .cb .table-stickcol > .table > tbody > tr:hover > td:first-child { background: var(--cb-surface-2); }
}

/* --- 筛选栏 ------------------------------------------------------------
   ⚠ **这一栏是内联流，不是 flex。** 2026-09-23 从 flex 改回来的：
     老筛选栏用 `<br>` 分行（标签、控件、`<br>` 全是兄弟，290 行 markup 就长这样），
     而 **Chrome 在 flex 容器里根本不给 `<br>` 分配宽度** —— 量出来 `width = 0`，
     写 `flex: 0 0 100%` 也一样。于是那一行根本不换行：`搜索:` 被挤到上一行的
     尾巴上、和它的输入框分了家（用户：「这些文字的位置都不对」）。
     内联流里 `<br>` 是它本来的意思，作者安排的行分组原样成立，markup 一个字不动。

   ⚠ `line-height: 1` 是必须的：默认 1.5 时每一行会多出 ~20px 的行距，
     三行筛选栏就白占大半屏。控件靠 `vertical-align: middle` 对齐，不靠行高。

   ⚠ `br + br { display: none }`：老 markup 里到处是 `<br><br>`，两次换行之间
     会多出一整个空行。折掉第二个，三行的行距就一样了（实测 40px / 40px / 40px）。
     这是**纯呈现**的折叠 —— 没有任何数据或 name 被碰到。

   宽度收敛成 5 个尺寸类，模板里不许写内联 width（窄屏要压它就只能上 !important）。
   `cb_design_lint` 断言：迁移页的 .cb-filters 里不许出现 style="width。 */
.cb .cb-filters { display: block; line-height: 1; }
.cb .cb-filters > * { vertical-align: middle; margin: 0 8px 6px 0; }
.cb .cb-filters > br { display: block; height: 0; margin: 0; }
.cb .cb-filters > br + br { display: none; }

/* 直接摆在筛选栏里的输入控件：**统一一个宽度**，不许按内容各长各的。
   原来是 min 140 / max 240 的自适应，于是同一行上五个下拉五种宽度、右边缘参差 ——
   那正是「不像产品」的样子。要更宽更窄的用下面五个 .cb-w-* 显式标出来。 */
.cb .cb-filters > .form-control, .cb .cb-filters > .form-select,
.cb .cb-filters > input[type=text], .cb .cb-filters > select {
  display: inline-block; width: 150px;
}

/* ⚠ 裸控件也要长成一样。实测这一页的筛选栏里
     `chooselogistic_schedule_id` / `sk` / startTime / endTime / 两个 button
     **当初没给 Bootstrap 类**，高度是 20 / 22 / 17，摆在 32px 的下拉旁边参差不齐。
   按元素兜底而不是逐个补 class —— 后面还有 ~60 个列表页要迁，
   逐页补 class 的话必然漏，而漏了是「静默地矮一截」，没人会报错。 */
.cb .cb-filters > input[type=text], .cb .cb-filters > select {
  height: var(--cb-ctl-h); padding: 0 8px;
  border: 1px solid var(--bs-border-color); border-radius: var(--bs-border-radius);
  background-color: var(--bs-body-bg); color: var(--bs-body-color);
  font-size: var(--bs-body-font-size); font-family: inherit;
}
.cb .cb-filters > input[type=text]:focus, .cb .cb-filters > select:focus {
  outline: 0; border-color: var(--cb-accent); box-shadow: var(--cb-focus);
}
/* ⚠ 按钮要分主次，不能一刀切成同一个样子（用户 2026-09-23：
     「这个主搜索按钮，都看不清？你要设计 ui 一定要先分析元素，哪些重要，
     哪些不重要，按钮用什么颜色」）。这一栏里只有一个主动作 —— 提交筛选。

   判据用**形状**不用 class：`type=submit` 是主，其余是次。
   逐页去标 class 的话，后面 60 个列表页必然漏，而漏了是「主按钮变成灰的」，
   页面照样能用，没人会报错。 */
/* ⚠ `:not(.btn)` 不能省：这条兜底是 0-3-0，会把显式标了 `.btn-outline-primary`
     的按钮（0-1-0）整个顶掉 —— 实测顶部那颗「打印」标了类却还是灰的。
     兜底只该管**没有标过任何按钮类**的裸 button。 */
.cb .cb-filters > button:not(.btn) {
  height: var(--cb-ctl-h); min-width: 64px; padding: 0 14px;
  border: 1px solid var(--bs-border-color); border-radius: var(--bs-border-radius);
  background-color: var(--cb-surface-2); color: var(--bs-body-color);
  font-size: var(--bs-body-font-size); font-family: inherit; cursor: pointer;
}
.cb .cb-filters > button:not(.btn):hover { border-color: var(--cb-accent); color: var(--cb-accent-text); }
/* 主动作：实心强调色。--cb-accent-strong 配白字 4.94:1，达标 */
.cb .cb-filters > button:not(.btn)[type="submit"], .cb .cb-filters > .btn-primary {
  background-color: var(--cb-accent-strong); border-color: var(--cb-accent-strong);
  color: var(--cb-on-fill); font-weight: 600;
}
.cb .cb-filters > button:not(.btn)[type="submit"]:hover, .cb .cb-filters > .btn-primary:hover {
  background-color: var(--cb-accent); border-color: var(--cb-accent); color: var(--cb-on-fill);
}
.cb .cb-filters > button:not(.btn):focus-visible {
  outline: 0; box-shadow: var(--cb-focus);
}
.cb .cb-filters > .btn { min-width: 64px; }
.cb .cb-filters > label { margin: 0 8px 6px 0; }
/* 只管老的原生勾选框。Bootstrap 的 .form-check-input 是 appearance:none 画出来的，width:auto 会把它压成 2px（看不见）——
   产品列表迁移时实测踩到，那四个「显示中文名 / 英文名 / 特价 / 多级价」的勾全没了 */
.cb .cb-filters > input[type=checkbox]:not(.form-check-input), .cb .cb-filters > input[type=radio]:not(.form-check-input) { width: auto; }

.cb .cb-filters .cb-fg { display: inline-block; }
/* 筛选栏折叠（_cb_js 的 cbFilterFold）：第二行以后收进「更多筛选」。只是不显示，照样随表单提交 */
.cb .cb-filters > .cb-flt-hide { display: none; }
.cb .cb-filters > .cb-flt-toggle { text-decoration: none; padding-left: 4px; padding-right: 4px; }
.cb .cb-filters .cb-fg > .form-control, .cb .cb-filters .cb-fg > .form-select { width: 100%; }
.cb-w-1 { width: 96px;  min-width: 96px; }
.cb-w-2 { width: 120px; min-width: 120px; }
.cb-w-3 { width: 160px; min-width: 160px; }
.cb-w-4 { width: 190px; min-width: 190px; }
.cb-w-5 { width: 100%; }

@media (max-width: 991.98px) {
  /* 窄屏不去动宽度分档 —— 内联流本来就会自己折行，控件保持自然宽度即可。
     只有手机上才铺满一行（那时一行本来也只放得下一个）。 */
  .cb .cb-w-4 { width: 150px; min-width: 150px; }
}
@media (max-width: 575.98px) {
  .cb .cb-filters > .form-control, .cb .cb-filters > .form-select,
  .cb .cb-filters > input[type=text], .cb .cb-filters > select,
  .cb .cb-w-1, .cb .cb-w-2, .cb .cb-w-3, .cb .cb-w-4 { width: 100%; min-width: 0; }
  .cb .cb-filters > .cb-fg { display: block; }
}
/* --- 触屏：把控件放大到手指够得着 --------------------------------------
   ⚠ 判据用 `pointer: coarse` 不用屏幕宽度 —— 触屏笔记本、带触屏的收银机都是宽屏，
     按屏宽判会漏掉它们。 */
@media (pointer: coarse) {
  .cb { --cb-ctl-h: 40px; --cb-ctl-h-sm: 34px; --cb-row-h: 44px; }
  .cb .btn { padding-left: 14px; padding-right: 14px; }
  .cb .form-check-input { width: 1.15em; height: 1.15em; }
  .cb-side a { padding-top: 11px; padding-bottom: 11px; }
  .cb .table > :not(caption) > * > * { padding: 10px 8px; }
}

/* --- 弹窗：手机上贴底铺满，比居中小卡片好按 ---------------------------- */
@media (max-width: 575.98px) {
  .cb .modal-dialog { margin: 0; max-width: none; width: 100%; position: absolute;
                      bottom: 0; left: 0; right: 0; }
  .cb .modal-content { border-radius: var(--bs-border-radius-lg) var(--bs-border-radius-lg) 0 0; }
  .cb .modal-footer .btn { flex: 1 1 0; }
  .cb-toaster { left: 10px; right: 10px; top: auto; bottom: 10px; }
}

/* --- 指标卡：桌面 4 列 → 平板 2 列 → 手机 1 列 ------------------------- */
@media (max-width: 991.98px) { .cb-stats > * { flex: 1 1 calc(50% - 6px); } }
@media (max-width: 575.98px) { .cb-stats > * { flex: 1 1 100%; } }

/* ==========================================================================
   §14 侧栏菜单 —— 适配现成的 side_factory.htm（991 行菜单本体，纯 markup）
   --------------------------------------------------------------------------
   为什么要有这一节：
     菜单本体自己没有任何样式，样式一直写在 factory/side.htm 与 company/side.htm
     那对孪生文件的 <style> 里 —— 而新壳不加载它们（那段脚本依赖 header.htm 的
     changeSideMainWidth() 和 js/b2b.js 的 getCookie()，新壳两个都没有）。
     所以这里把那套样式**用 token 重写一遍**，顺带让它跟着白天/夜间翻。
     菜单本体 991 行一个字都不动 —— 它是 19 个一级 / 94 个二级的唯一真相。

   ⚠ 2026-09-23 重做（用户：「更工整一些」「列表项太多」）。两处变化：
     ① **去掉逐项的横分隔线。** 19 条横线把菜单切成 19 个抽屉，看着像表格不像导航；
        样稿上一条都没有，靠 hover / current 的底色块区分。现在只在**一级项之间**
        留 2px 的呼吸，选中项整块上底色 + 左侧一条强调色。
     ② **收起态（rail）**：只留图标、宽 52px，二级菜单 hover 时**浮出**在右边。
        状态挂在 .cb-app[data-side] 上，由 _cb_js.htm 记进 localStorage。
        ⚠ 浮出那一版只在 ≥992px 生效 —— 更窄时侧栏本来就是 offcanvas 抽屉，
          两套状态叠在一起会互相打架。
   ========================================================================== */
.cb .cb-side .box { padding: 6px 0; }
.cb .cb-side .mutil-level-menu { list-style: none; margin: 0; padding: 0; }
.cb .cb-side .mutil-level-menu > li { position: relative; margin-bottom: 2px; }

.cb .cb-side .top-level {
  display: flex; align-items: center; gap: 8px;
  min-height: 36px; padding: 4px 12px; line-height: 1.35; cursor: pointer;
  border-left: 3px solid transparent;
  font-size: var(--bs-body-font-size); color: var(--cb-text);
}
.cb .cb-side .top-level:hover { background: var(--cb-surface-2); }
.cb .cb-side .top-level > i { color: var(--cb-accent); flex: 0 0 auto; width: 18px; text-align: center; }
.cb .cb-side .top-level p { margin: 0; min-width: 0; overflow-wrap: break-word; }
.cb .cb-side .top-level a { color: inherit; text-decoration: none; }
.cb .cb-side .top-level a.current { color: var(--cb-accent-text); font-weight: 600; }
/* 选中的那一组整块高亮 —— rail 态下文字不在了，只剩这块底色能说明「你在这里」。
   .is-current 由 _cb_js.htm 挂上去（不用 :has()，那要 Chrome 105+）。 */
.cb .cb-side .top-level.is-current {
  background: var(--cb-accent-soft); border-left-color: var(--cb-accent);
}

.cb .cb-side .second-level { list-style: none; margin: 0; padding: 2px 0 8px 39px; display: none; }
.cb .cb-side .second-level a {
  display: block; padding: 6px 10px; font-size: 13px;
  color: var(--cb-text-2); text-decoration: none;
  border-radius: var(--bs-border-radius);
  overflow-wrap: break-word;
}
.cb .cb-side .second-level a:hover { color: var(--cb-accent-text); background: var(--cb-surface-2); }
.cb .cb-side .second-level a.current {
  color: var(--cb-accent-text); font-weight: 600; background: var(--cb-accent-soft);
}

/* 那两个折叠图标是老宽度算术留下的，新布局用 offcanvas，不需要它们 */
.cb .cb-side .svg-icon { display: none; }

/* --- 收起态：只留图标 ---------------------------------------------------
   ⚠ 只在 ≥992px 生效。更窄时侧栏是 offcanvas 抽屉，rail 没有意义，
     而且两套状态会争同一个 width。 */
@media (min-width: 992px) {
  /* ⚠ 改的是【变量】不是 width —— .cb-side 是 flex item，§7 给了它
     `flex: 0 0 var(--cb-side-w)`，而 **flex-basis 压得过 width**。
     实测直接写 width 时 data-side 翻成 rail、文字也藏了，侧栏却纹丝不动停在 200px。
     改 token 只要这一处重定义，flex-basis 和别处用到它的地方一起跟着变。 */
  .cb-app[data-side="rail"] { --cb-side-w: var(--cb-side-w-rail); }
  .cb-app[data-side="rail"] .cb-side .top-level { justify-content: center; padding: 6px 0; gap: 0; }
  .cb-app[data-side="rail"] .cb-side .top-level > p,
  .cb-app[data-side="rail"] .cb-side .top-level > a { display: none; }
  .cb-app[data-side="rail"] .cb-side .top-level > i { width: 20px; font-size: 16px; }

  /* 二级菜单浮出在轨的右边。
     ⚠ 切换到 rail 时 _cb_js.htm 会把 slideToggle 留下的 inline display 清掉 ——
       不清的话，之前展开过的那一组会在轨上一直挂着，而这里又没法用 !important。 */
  .cb-app[data-side="rail"] .cb-side .second-level { padding: 6px; }
  .cb-app[data-side="rail"] .cb-side .mutil-level-menu > li:hover > .second-level,
  .cb-app[data-side="rail"] .cb-side .mutil-level-menu > li:focus-within > .second-level {
    display: block; position: absolute; left: 100%; top: 0;
    min-width: 190px; z-index: 1035;
    background: var(--cb-surface); border: 1px solid var(--cb-line);
    border-radius: var(--bs-border-radius); box-shadow: var(--cb-shadow-3);
  }
}

/* 折叠按钮只在桌面出现；<992px 那里摆的是抽屉开关（.cb-burger），两颗各管各的 */
.cb .cb-railtoggle { display: none; }
@media (min-width: 992px) { .cb .cb-railtoggle { display: inline-flex; } }
/* ==========================================================================
   §15 开关（.onoffswitch）—— 从 customer_list.htm 的页内 <style> 搬过来
   --------------------------------------------------------------------------
   为什么原样保留 class 名而不换成 Bootstrap 的 .form-switch：
     换 markup 就要动那几段 toggle 的 JS，而这一轮要证明的是「只换外观、
     数据一个字没变」。几何一模一样、只把写死的色值换成 token —— 这样它
     跟着白天/夜间翻，而 DOM 与脚本零改动。
   ========================================================================== */
/* ⚠ 2026-09-23 从 70×34 缩到 52×26（用户：「更工整一些」）。
   原尺寸比行高还大，「send to Fs」那一列被撑成三行，整张表的行高就跟着它走。
   几何比例原样保留：轨 = 2×文字宽、把手是正方形。 */
.cb .onoffswitch { position: relative; width: 52px; margin: auto; user-select: none; }
.cb .onoffswitch-checkbox { display: none; }
.cb .onoffswitch-label { display: block; overflow: hidden; cursor: pointer;
  border: 2px solid var(--cb-line-strong); border-radius: 22px; }
.cb .onoffswitch-inner { display: block; width: 200%; margin-left: -100%;
  transition: margin .3s ease-in 0s; }
.cb .onoffswitch-inner:before, .cb .onoffswitch-inner:after {
  display: block; float: left; width: 50%; height: 22px; padding: 0;
  line-height: 22px; font-size: 10px; font-weight: 600; box-sizing: border-box; }
.cb .onoffswitch-inner:before { content: "ON"; padding-left: 6px;
  background: var(--cb-accent-strong); color: var(--cb-on-fill); }
.cb .onoffswitch-inner:after { content: "OFF"; padding-right: 7px;
  background: var(--cb-surface-3); color: var(--cb-text-3); text-align: right; }
.cb .onoffswitch-switch { display: block; width: 8px; margin: 7px;
  background: var(--cb-surface); position: absolute; top: 0; bottom: 0; right: 24px;
  border: 2px solid var(--cb-line-strong); border-radius: 22px; transition: all .3s ease-in 0s; }
.cb .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner { margin-left: 0; }
.cb .onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch { right: 0; }
.cb .onoffswitch-checkbox:disabled + .onoffswitch-label { cursor: not-allowed; opacity: .5; }   /* 禁用的开关不许长得像能点 */

/* 服务端生成的分页 HTML（<{$pager}>）带的是 .pager，PHP 那边改不动，
   在这里给它一身和 Bootstrap .pagination 同款的皮 */
.cb .pager { display: flex; flex-wrap: wrap; gap: 4px; align-items: center;
  font-size: 13px; padding: 4px 0; }
.cb .pager a, .cb .pager em, .cb .pager span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--cb-ctl-h-sm); height: var(--cb-ctl-h-sm); padding: 0 8px;
  border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius);
  background: var(--cb-surface); color: var(--cb-text); font-style: normal;
  text-decoration: none; font-variant-numeric: tabular-nums; }
.cb .pager a:hover { border-color: var(--cb-accent); color: var(--cb-accent-text); }
.cb .pager a.current, .cb .pager em { background: var(--cb-accent-strong);
  border-color: var(--cb-accent-strong); color: var(--cb-on-fill); font-weight: 600; }
/* 上一页 / 下一页的禁用态（第一页的「上一页」、最后一页的「下一页」）不许长得像当前页 */
.cb .pager em.prev, .cb .pager em.next { background: var(--cb-surface-3); border-color: var(--cb-line);
  color: var(--cb-text-3); font-weight: 400; cursor: default; }
/* 页码太多时由 _cb_js 的 cbPagerFold 折叠：藏起来的链接仍在 DOM 里 */
.cb .pager .cb-pg-hide { display: none; }
.cb .pager .cb-pg-gap { border-color: transparent; background: transparent; color: var(--cb-text-3); min-width: 16px; padding: 0 2px; }

/* select2（只在 cb_extra_js='select2' 的页上有）：和别的控件一个高度、一种边框、跟着白天/夜间翻。
   它的下拉层挂在 <body> 下面，所以 .select2-dropdown 那几条也写在 .cb 下（<html class="cb">） */
.cb .select2-container { vertical-align: middle; }
.cb .select2-container--default .select2-selection--single {
  height: var(--cb-ctl-h); border: 1px solid var(--cb-line-strong); border-radius: var(--bs-border-radius);
  background: var(--cb-surface); }
.cb .select2-container--default .select2-selection--single .select2-selection__rendered {
  line-height: calc(var(--cb-ctl-h) - 2px); color: var(--cb-text); padding-left: 10px; }
.cb .select2-container--default .select2-selection--single .select2-selection__arrow { height: calc(var(--cb-ctl-h) - 2px); }
.cb .select2-container--default.select2-container--focus .select2-selection--single,
.cb .select2-container--default.select2-container--open .select2-selection--single { border-color: var(--cb-accent); }
.cb .select2-dropdown { background: var(--cb-surface); border-color: var(--cb-line-strong); color: var(--cb-text); }
.cb .select2-container--default .select2-search--dropdown .select2-search__field {
  background: var(--cb-surface); color: var(--cb-text); border-color: var(--cb-line-strong); }
/* 选项列表：下拉层至少 440px 宽（产品名是「编号 - 品名 - 规格 - 单位」一长串，跟着 190px 的框走会折成四行），
   列表高到 60vh（原来写死 200px，一屏只见四五个产品）。窄屏不超出视口 */
.cb .select2-dropdown { min-width: min(440px, calc(100vw - 32px)); }
.cb .select2-container--default .select2-results > .select2-results__options { max-height: min(460px, 60vh); }
.cb .select2-results__option { padding: 6px 10px; line-height: 1.4; }
/* 「当前已选中」和「鼠标所在」两种态必须各自成对定底色 + 字色。
   原来选中态只写了灰底、又排在悬停态后面，同特异度后者赢 —— 鼠标移到已选中那一项时成了「灰底 + 白字」，看不清。
   现在：选中 = 浅强调底 + 强调字；悬停（含悬停在已选中项上）= 实心强调底 + 白字，写在后面且多一级，稳压选中态 */
.cb .select2-container--default .select2-results__option[aria-selected=true],
.cb .select2-container--default .select2-results__option--selected {
  background: var(--cb-accent-soft); color: var(--cb-accent-text); }
.cb .select2-container--default .select2-results__option.select2-results__option--highlighted[aria-selected],
.cb .select2-container--default .select2-results__option.select2-results__option--highlighted.select2-results__option--selectable {
  background: var(--cb-accent-strong); color: var(--cb-on-fill); }

/* 库房 / 库区拖动排序（js/store_house_sort.js）：它自带的 store_house_sort.css 是写死的颜色、老页面（库区列表）还在用，
   不动它；迁过的页面在这里换成 token，夜间才看得清 */
.cb .sh-handle, .cb .sh-sort-cell, .cb .sh-sort-help { color: var(--cb-text-3); }
.cb .sh-sort-hint { color: var(--cb-accent-text); }
.cb .sh-drag-over-top { box-shadow: inset 0 3px 0 0 var(--cb-accent); }
.cb .sh-drag-over-bot { box-shadow: inset 0 -3px 0 0 var(--cb-accent); }

/* ==========================================================================
   §16 页内 tab 条（.colCurTit）—— 全站 63 个 *_menu_top.htm 都是这个形状
   --------------------------------------------------------------------------
   它们的样式原来在 web_global.css 里，而新壳不加载那份 —— 不给它们上皮，
   迁移过的页面上那一排会退化成项目符号列表（实测第一次打开就是这样）。
   在这里写一次，63 个 tab 条全都对，模板一个字不用改。
   ========================================================================== */
.cb .colCurTit { margin: 0 0 2px; }
.cb .colCurTit > ul { display: flex; flex-wrap: wrap; gap: 2px; list-style: none;
  margin: 0; padding: 0; border-bottom: 1px solid var(--cb-line); }
.cb .colCurTit li { list-style: none; }
.cb .colCurTit li > span { display: block; }
.cb .colCurTit a { display: block; padding: 8px 12px; font-size: var(--bs-body-font-size);
  color: var(--cb-text-2); text-decoration: none; border-bottom: 2px solid transparent;
  margin-bottom: -1px; white-space: nowrap; }
.cb .colCurTit a:hover { color: var(--cb-accent-text); }
.cb .colCurTit a.current { color: var(--cb-accent-text); font-weight: 600;
  border-bottom-color: var(--cb-accent); }
/* 老结构里 .colCurTit 兼作标题容器，里面可能直接躺着文字与 .links */
.cb .colCurTit > .links { display: inline-flex; gap: 8px; margin-left: auto; }

/* ==========================================================================
   §17 帮助气泡（.help-box）—— 全站唯一一个跨模块一致的老组件，样式原在 web_global.css
   --------------------------------------------------------------------------
   不给它上皮的话，`data-content` 那段说明会**整段漏在表头里**（实测「简码」那一列
   就是这样）。js/help_box.js 生成 .help-icon + .help-info，这里只换皮不改 JS。
   ========================================================================== */
.cb .help-box { position: relative; display: inline-block; height: 16px; padding-left: 4px;
  vertical-align: middle; }
.cb .help-icon { display: inline-block; width: 16px; height: 16px; line-height: 16px;
  text-align: center; border-radius: 50%; font-size: 10px;
  background: var(--cb-surface-3); color: var(--cb-text-2); cursor: help; }
.cb .help-icon:hover { background: var(--cb-accent); color: var(--cb-on-fill); }
.cb .help-info { display: none; position: absolute; left: 24px; top: -8px; z-index: 1080;
  width: 320px; padding: 10px 12px; border-radius: var(--bs-border-radius);
  background: var(--cb-heading); color: var(--cb-surface);
  font-size: 12px; line-height: 1.6; font-weight: 400; white-space: normal;
  box-shadow: var(--cb-shadow-3); }
.cb .help-info i { position: absolute; top: 9px; left: -9px; color: var(--cb-heading); }

/* ==========================================================================
   §18 反馈八种形态 —— docs/ui-standard.md §S6，实现在 _cb_js.htm
   放在文件最后：F1 的红框要压过 §3 给表单控件定的边框色、以及筛选栏里
   按元素兜底的那条（同特异度，靠后者赢）。
   ========================================================================== */
/* F1 字段错误：红框 + 紧跟在框后面的一行字（内联，不打乱筛选栏的内联流） */
.cb .is-invalid,
.cb .form-control.is-invalid, .cb .form-select.is-invalid,
.cb .cb-filters > input.is-invalid, .cb .cb-filters > select.is-invalid {
  border-color: var(--cb-bad-fill);
  box-shadow: 0 0 0 2px var(--cb-bad-soft);
}
.cb .cb-fe { display: inline-block; margin-left: 6px; font-size: 12px; line-height: 1.4;
             color: var(--cb-bad-text); vertical-align: middle; }
.cb .form-control + .cb-fe, .cb .form-select + .cb-fe,
.cb textarea + .cb-fe { display: block; margin: 4px 0 0; }
/* 输入组里的框（「+61 手机号 [获取验证码]」这种）：字段错误那一行排到整组下面，不许把组撑开 */
.cb .input-group > .cb-fe { display: block; flex: 0 0 100%; order: 99; margin: 4px 0 0; }

/* F2 表单错误条 */
.cb .cb-formerr { margin-bottom: 12px; }

/* F4/F5 确认框的几何 —— 原来写在 JS 拼的 inline style 里 */
.cb .cb-dlg   { max-width: 420px; margin-top: 72px; }
.cb .cb-dlg-w { max-width: 480px; }
.cb .modal-title { font-size: 14px; font-weight: 600; color: var(--cb-heading); }
.cb .cb-note  { font-size: 12px; }
/* 确认框正文保留换行（toast 见 §F3 那条）：老 confirm()/alert() 的文案常用换行符分行列要点，
   textContent 写进去之后默认会压成一行，「· 订单状态变为已完成 · 同时标记为已送达」挤在一起读不了 */
.cb .cb-dlg-text { white-space: pre-line; }

/* F7 长任务：整页罩 + 逐步进度 */
.cb .cb-busy { position: fixed; inset: 0; z-index: 1100; background: var(--cb-scrim);
               display: flex; align-items: flex-start; justify-content: center; padding-top: 120px; }
.cb .cb-busy-card { background: var(--cb-surface); color: var(--cb-text); min-width: 320px; max-width: 480px;
                    padding: 16px; border-radius: var(--bs-border-radius-lg); box-shadow: var(--cb-shadow-3); }
.cb .cb-busy-title { font-weight: 600; margin-bottom: 8px; display: flex; align-items: center; gap: 8px; }
.cb .cb-busy-steps { list-style: none; margin: 0; padding: 0; font-size: 13px; }
.cb .cb-busy-steps li { padding: 3px 0; color: var(--cb-text-3); }
.cb .cb-busy-steps li .i { display: inline-block; width: 18px; }
.cb .cb-busy-steps li[data-s="run"] { color: var(--cb-text); font-weight: 600; }
.cb .cb-busy-steps li[data-s="ok"]  { color: var(--cb-ok-text); }
.cb .cb-busy-steps li[data-s="bad"] { color: var(--cb-bad-text); }

/* F3 toast：叉号靠右，长消息能折行 */
.cb .toast { max-width: 380px; }
.cb .toast .toast-body { word-break: break-word; white-space: pre-line; }   /* 汇总类消息用 \n 分行 */

/* ==========================================================================
   §19 登录前的页面（_cb_auth_open.htm）：登录 · 二次验证 · 忘记密码 · 注册
   同一条深色顶栏，下面是一张居中的卡片。窄屏卡片贴边铺满。
   ========================================================================== */
.cb-auth { display: flex; flex-direction: column; height: 100%; }
.cb-auth-main { flex: 1 1 auto; overflow: auto; overscroll-behavior: contain;
                display: flex; align-items: flex-start; justify-content: center; padding: 48px 16px; }
.cb-auth-card { width: 100%; max-width: 400px; background: var(--cb-surface); color: var(--cb-text);
                border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius-lg);
                box-shadow: var(--cb-shadow-1); padding: 24px; }
.cb-auth-card.cb-auth-wide { max-width: 560px; }
.cb .cb-auth-title { font-size: 18px; font-weight: 600; color: var(--cb-heading); margin: 0 0 16px; }
.cb .cb-auth-card .form-label { font-size: 13px; color: var(--cb-text-2); margin-bottom: 4px; }
.cb .cb-auth-card .form-control, .cb .cb-auth-card .form-select,
.cb .cb-auth-card .input-group > .btn { height: 38px; }
.cb .cb-auth-card .btn-lg { height: 40px; font-size: 14px; }
.cb .cb-auth-foot { margin-top: 16px; font-size: 13px; color: var(--cb-text-2); text-align: center; }
/* 服务端拒了整张表单时的那一条（F2）。空的时候不占地方 —— 原来是塞一个 <br> 撑高度 */
.cb .cb-auth-err:empty { display: none; }
@media (max-width: 575.98px) {
  .cb-auth-main { padding: 0; }
  .cb-auth-card { max-width: none; border: 0; border-radius: 0; box-shadow: none; min-height: 100%; }
}

/* ==========================================================================
   §20 首页看板（factory/index.htm，R3）：快捷入口 · 今天 · 各线打包进度 · 排行
   看板的数据不在表格里，一格一格是卡片。卡片只有三种：数字格（.cb-stat）、
   进度格（.cb-lane，一条线的打包进度，整格是链接）、入口格（.cb-quick a）。
   ========================================================================== */
.cb .cb-dash { display: flex; flex-direction: column; gap: 16px; }
.cb .cb-dash-sec > h2 { font-size: 13px; font-weight: 600; color: var(--cb-text-2);
                        margin: 0 0 8px; display: flex; align-items: center; gap: 8px; }
.cb .cb-dash-sec > h2 .ms-auto { font-weight: 400; }

/* 快捷入口：按业务分组，一组一行，组名在左 */
.cb .cb-quick { display: grid; grid-template-columns: 96px 1fr; gap: 8px 12px; align-items: center; margin: 0; }
.cb .cb-quick dt { font-size: 12px; font-weight: 400; color: var(--cb-text-2); }
.cb .cb-quick dd { margin: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.cb .cb-quick a { display: inline-flex; align-items: center; gap: 6px; height: var(--cb-ctl-h);
                  padding: 0 12px; border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius);
                  background: var(--cb-surface); color: var(--cb-text); text-decoration: none; white-space: nowrap; }
.cb .cb-quick a:hover { border-color: var(--cb-accent); color: var(--cb-accent-text); background: var(--cb-accent-soft); }
.cb .cb-quick a .fa { color: var(--cb-accent); width: 14px; text-align: center; }
.cb .cb-quick a .fa-external-link { color: var(--cb-text-3); font-size: 11px; width: auto; }

/* 数字格 */
.cb .cb-stats { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.cb .cb-stat { background: var(--cb-surface); border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius-lg);
               padding: 12px 16px; box-shadow: var(--cb-shadow-1); }
.cb .cb-stat .k { font-size: 12px; color: var(--cb-text-2); display: flex; align-items: center; gap: 6px; }
.cb .cb-stat .v { font-size: 24px; font-weight: 600; color: var(--cb-heading); font-variant-numeric: tabular-nums;
                  line-height: 1.3; margin-top: 4px; }
.cb .cb-stat .v .of { font-size: 14px; font-weight: 400; color: var(--cb-text-2); }
.cb .cb-stat.ok { border-color: var(--cb-ok-line); background: var(--cb-ok-soft); }
.cb .cb-stat.ok .v { color: var(--cb-ok-text); }
.cb .cb-stat.wn { border-color: var(--cb-wn-line); background: var(--cb-wn-soft); }
.cb .cb-stat.wn .v { color: var(--cb-wn-text); }
/* 金额前的 $ 由样式出：老看板这一格印的是裸数字，文字内容保持原样 */
.cb .cb-stat .v.money::before { content: "$"; font-size: 16px; font-weight: 400; color: var(--cb-text-2); margin-right: 2px; }

/* 各线打包进度：没打完的是橙色并带一个会呼吸的点 —— 老看板这里是整格闪烁（blink-low），
   意思是「还有单没打包」。点只在没打完时出现；要求减少动效的系统上不动 */
.cb .cb-lanes { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 8px; }
.cb .cb-lane { display: block; padding: 10px 12px; border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius);
               background: var(--cb-surface); color: var(--cb-text); text-decoration: none; }
.cb .cb-lane:hover { border-color: var(--cb-accent); }
.cb .cb-lane .t { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.cb .cb-lane .t .name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb .cb-lane .t .n { font-variant-numeric: tabular-nums; font-weight: 600; }
.cb .cb-lane progress { display: block; width: 100%; height: 6px; margin-top: 8px; border: 0; border-radius: 3px;
                        overflow: hidden; appearance: none; background: var(--cb-surface-3); accent-color: var(--cb-ok-fill); }
.cb .cb-lane progress::-webkit-progress-bar { background: var(--cb-surface-3); }
.cb .cb-lane progress::-webkit-progress-value { background: var(--cb-ok-fill); }
.cb .cb-lane progress::-moz-progress-bar { background: var(--cb-ok-fill); }
.cb .cb-lane.wn { border-color: var(--cb-wn-line); background: var(--cb-wn-soft); }
.cb .cb-lane.wn .t .n { color: var(--cb-wn-text); }
.cb .cb-lane.wn progress { accent-color: var(--cb-wn-fill); }
.cb .cb-lane.wn progress::-webkit-progress-value { background: var(--cb-wn-fill); }
.cb .cb-lane.wn progress::-moz-progress-bar { background: var(--cb-wn-fill); }
.cb .cb-lane.ok .t .n { color: var(--cb-ok-text); }
.cb .cb-pulse { animation: cb-pulse 1.2s ease-in-out infinite; }
@keyframes cb-pulse { 0% { opacity: 1; } 50% { opacity: .25; } 100% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .cb .cb-pulse { animation: none; } }

/* 今天：左边数字与各线进度，右边今天前十；1200 以下上下叠 */
.cb .cb-dash-today { display: grid; grid-template-columns: minmax(0, 2fr) minmax(300px, 1fr); gap: 16px; align-items: start; }
@media (max-width: 1199.98px) { .cb .cb-dash-today { grid-template-columns: minmax(0, 1fr); } }
/* 排行：两张窄表并排，窄屏上下叠 */
.cb .cb-dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; }
.cb .cb-dash .table { margin: 0; }
.cb .cb-dash .card-header { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 13px; }
.cb .cb-dash .card-header a { margin-left: auto; font-weight: 400; }
@media (max-width: 575.98px) {
  .cb .cb-quick { grid-template-columns: 1fr; }
  .cb .cb-quick dt { margin-top: 4px; }
}

/* ==========================================================================
   §21 第三方：CKEditor 4（moono 皮肤）的夜间 —— 用户 2026-09-28：「暗色时还是白底，整个项目只要用它都这样」
   皮肤里的浅色是写死的，这里按 token 覆盖工具栏 / 边框 / 底栏 / 弹窗 / 下拉面板的外壳。
   ⚠ 编辑区本身和下拉面板的内容是【独立的 iframe 文档】，这份 CSS 和 --cb-* 变量都进不去 ——
     那一半在 _cb_js.htm 的 ⑥ 里：编辑器就绪时把 token 取成具体颜色注入进去，跟着白天 / 夜间切换重画。
   只在夜间生效，白天一个字节不变；只作用于新壳页面（.cb），老页面够不着。
   ========================================================================== */
.cb[data-bs-theme="dark"] .cke_chrome { border-color: var(--cb-line-strong); box-shadow: none; }
.cb[data-bs-theme="dark"] .cke_inner { background: var(--cb-surface); }
.cb[data-bs-theme="dark"] .cke_top,
.cb[data-bs-theme="dark"] .cke_bottom {
  background: var(--cb-surface-2); border-color: var(--cb-line); box-shadow: none;
}
.cb[data-bs-theme="dark"] .cke_toolgroup,
.cb[data-bs-theme="dark"] a.cke_combo_button {
  background: var(--cb-surface-3); border-color: var(--cb-line-strong); box-shadow: none;
}
.cb[data-bs-theme="dark"] .cke_toolgroup a.cke_button:hover,
.cb[data-bs-theme="dark"] .cke_toolgroup a.cke_button:focus,
.cb[data-bs-theme="dark"] .cke_toolgroup a.cke_button:active,
.cb[data-bs-theme="dark"] .cke_toolgroup a.cke_button_on,
.cb[data-bs-theme="dark"] .cke_combo_off a.cke_combo_button:hover,
.cb[data-bs-theme="dark"] .cke_combo_off a.cke_combo_button:focus,
.cb[data-bs-theme="dark"] .cke_combo_on a.cke_combo_button {
  background: var(--cb-accent-soft); box-shadow: none;
}
.cb[data-bs-theme="dark"] .cke_toolbar_separator { background: var(--cb-line-strong); box-shadow: none; }
.cb[data-bs-theme="dark"] .cke_button_label,
.cb[data-bs-theme="dark"] .cke_combo_text,
.cb[data-bs-theme="dark"] a.cke_path_item,
.cb[data-bs-theme="dark"] span.cke_path_empty { color: var(--cb-text); text-shadow: none; }
.cb[data-bs-theme="dark"] a.cke_path_item:hover,
.cb[data-bs-theme="dark"] a.cke_path_item:focus { background: var(--cb-surface-3); color: var(--cb-heading); box-shadow: none; }
/* 图标是一张深色雪碧图：翻成浅色，hue-rotate 把彩色图标（文字颜色 / 背景色）的色相转回原位 */
.cb[data-bs-theme="dark"] .cke_button_icon,
.cb[data-bs-theme="dark"] .cke_button_arrow,
.cb[data-bs-theme="dark"] .cke_combo_arrow,
.cb[data-bs-theme="dark"] .cke_resizer { filter: invert(.88) hue-rotate(180deg); }
.cb[data-bs-theme="dark"] .cke_combo_arrow { border-top-color: var(--cb-text); }
/* 链接 / 图片等弹窗（在主文档里，不是 iframe） */
.cb[data-bs-theme="dark"] .cke_dialog_body { background: var(--cb-surface); border-color: var(--cb-line-strong); }
.cb[data-bs-theme="dark"] .cke_dialog_title,
.cb[data-bs-theme="dark"] .cke_dialog_footer,
.cb[data-bs-theme="dark"] .cke_dialog_tabs {
  background: var(--cb-surface-2); color: var(--cb-heading); border-color: var(--cb-line); text-shadow: none; box-shadow: none;
}
.cb[data-bs-theme="dark"] .cke_dialog_contents { background: var(--cb-surface); border-color: var(--cb-line); }
.cb[data-bs-theme="dark"] .cke_dialog_ui_labeled_label,
.cb[data-bs-theme="dark"] .cke_dialog_ui_text,
.cb[data-bs-theme="dark"] .cke_dialog_contents_body label,
.cb[data-bs-theme="dark"] .cke_dialog_contents_body td { color: var(--cb-text); }
.cb[data-bs-theme="dark"] a.cke_dialog_tab { background: var(--cb-surface-3); color: var(--cb-text-2); border-color: var(--cb-line); }
.cb[data-bs-theme="dark"] a.cke_dialog_tab_selected { background: var(--cb-surface); color: var(--cb-heading); }
.cb[data-bs-theme="dark"] .cke_dialog_ui_input_text,
.cb[data-bs-theme="dark"] .cke_dialog_ui_input_textarea,
.cb[data-bs-theme="dark"] .cke_dialog_ui_input_select,
.cb[data-bs-theme="dark"] .cke_dialog_ui_input_text input,
.cb[data-bs-theme="dark"] .cke_dialog_ui_input_textarea textarea,
.cb[data-bs-theme="dark"] .cke_dialog_ui_input_select select {
  background: var(--cb-surface-2); color: var(--cb-text); border-color: var(--cb-line-strong);
}
.cb[data-bs-theme="dark"] a.cke_dialog_ui_button { background: var(--cb-surface-3); border-color: var(--cb-line-strong); box-shadow: none; }
.cb[data-bs-theme="dark"] a.cke_dialog_ui_button span { color: var(--cb-text); text-shadow: none; }
.cb[data-bs-theme="dark"] a.cke_dialog_ui_button_ok { background: var(--cb-accent-strong); border-color: var(--cb-accent-strong); }
.cb[data-bs-theme="dark"] a.cke_dialog_ui_button_ok span { color: var(--cb-on-fill); }
.cb[data-bs-theme="dark"] .cke_dialog_close_button { filter: invert(.88) hue-rotate(180deg); }
/* 下拉面板的外框（内容在 iframe 里，见 _cb_js ⑥） */
.cb[data-bs-theme="dark"] .cke_panel { background: var(--cb-surface); border-color: var(--cb-line-strong); box-shadow: var(--cb-shadow-3); }
/* 「源码」模式的文本框在主文档里 */
.cb[data-bs-theme="dark"] textarea.cke_source { background: var(--cb-surface); color: var(--cb-text); }

/* ==== 试用（S3，2026-09-28，未提交）：整页滚，表头钉住 =====================================
   用户：「如果右边向下滑动，可以一直看到订单」。桌面上不再让表卡自己滚 ——
   右边 .cb-main 整体滚（侧栏仍然自己滚自己的，不会跟着走），筛选栏随页面滚走，
   表头以 .cb-main 为滚动容器钉在顶上。表卡不能再是滚动容器（overflow 必须 visible），
   否则 sticky 只在卡片里面生效、滚页面时表头不会停。宽表的横向溢出由 .cb-main 整体横向滚。
   要撤回：删掉这一整块。 */
@media (min-width: 992px) {
  .cb .cb-page { height: auto; }
  .cb .table-sticky, .cb .table-sticky.flex-grow-1 { max-height: none; min-height: 0; overflow: visible; }
}

/* ==========================================================================
   §22 新订单列表（factory/order_live.htm，Vue 3 · 实时）
   只收这一页确实需要、Bootstrap 又没有的几样：实时指示 · 行变化高亮 · 状态标签的计数 ·
   勾选后浮出的批量条（日期控件在 §24，与老订单列表共用）。颜色一律走 §2 的语义 token。
   ========================================================================== */
/* 实时指示：一个点 + 一行字。绿 = 在跑；灰 = 关着 / 页面在后台；橙 = 连不上、正在退避 */
.cb .cb-lv-live { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--cb-text-2); white-space: nowrap; }
.cb .cb-lv-live .cb-lv-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--cb-line-strong); flex: 0 0 8px; }
.cb .cb-lv-live.on .cb-lv-dot { background: var(--cb-ok-fill); animation: cb-lv-beat 2s ease-in-out infinite; }
.cb .cb-lv-live.err .cb-lv-dot { background: var(--cb-wn-fill); }
.cb .cb-lv-live.err { color: var(--cb-wn-text); }
@keyframes cb-lv-beat { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* 别人改了、这一拍刚换上来的行：底色亮 3 秒。
   ⚠ 表格带斑马纹（table-striped，用户 2026-09-29 定），Bootstrap 的格子底色是用 inset box-shadow 画的，
   写 background-color 会被盖住 —— 要写它留的那个钩子 --bs-table-bg-state */
.cb .cb-lv-table > tbody > tr.cb-lv-flash > td { --bs-table-bg-state: var(--cb-accent-soft); }
/* 已经不符合当前筛选（被取消 / 换了日期 / 换了车）：灰掉，换页或刷新时才拿掉 —— 不让人正在点的行突然消失 */
.cb .cb-lv-table > tbody > tr.cb-lv-gone > td { color: var(--cb-text-3); }
.cb .cb-lv-table > tbody > tr.cb-lv-gone > td a { color: inherit; }

/* 状态标签（全部 / 待完成 / 已完成 / 已取消 · 未拣货 / 未打印）：按钮组，数字跟在后面 */
.cb .cb-lv-tabs { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.cb .cb-lv-count { display: inline-block; min-width: 20px; margin-left: 6px; padding: 0 6px; border-radius: 10px;
                   font-size: 11px; line-height: 16px; text-align: center; font-variant-numeric: tabular-nums;
                   background: var(--cb-surface-3); color: var(--cb-text-2); }
.cb .cb-lv-tabs .active > .cb-lv-count { background: var(--cb-on-fill); color: var(--cb-accent-strong); }

/* 筛选区：第一行常驻，其余收进「更多筛选」 */
.cb .cb-lv-filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.cb .cb-lv-filters .form-select, .cb .cb-lv-filters .form-control,
.cb .cb-lv-more .form-select, .cb .cb-lv-more .form-control { width: auto; min-width: 140px; }
.cb .cb-lv-filters .cb-lv-search { min-width: 220px; }
.cb .cb-lv-more { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding-top: 8px; margin-top: 8px; border-top: 1px dashed var(--cb-line); }

/* 日期控件已换成两页共用的 §24（js/cb_date_range.js）。行内改配送日的只读框仍要像可点的输入框，不许长得像禁用的 */
.cb .cb-lv-table .form-control[readonly] { background-color: var(--cb-surface); cursor: pointer; }

/* 勾选之后才出现的批量条：钉在可视区底部 */
.cb .cb-lv-bulk { position: sticky; bottom: 0; z-index: var(--cb-z-bar); display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
                  padding: 8px 12px; margin-top: 8px; background: var(--cb-surface); border: 1px solid var(--cb-line-strong);
                  border-radius: var(--bs-border-radius); box-shadow: var(--cb-shadow-3); }
.cb .cb-lv-bulk .cb-lv-sel { font-weight: 600; color: var(--cb-heading); margin-right: 4px; }

/* 表格里的小件 */
.cb .cb-lv-sub { display: block; font-size: 12px; color: var(--cb-text-2); }
.cb .cb-lv-cust { white-space: normal; }
/* 客户名限宽 520px、超出自动换行（用户 2026-09-29 定，与老订单列表同一个数）。表格单元格上的 max-width 不生效，写在里层 */
.cb .cb-lv-cust > .cb-lv-custw { min-width: 200px; max-width: 520px; }
.cb .cb-lv-cols { min-width: 220px; }
.cb .cb-lv-pager { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 8px; }
@media (prefers-reduced-motion: reduce) {
  .cb .cb-lv-live.on .cb-lv-dot { animation: none; }
}

/* ==========================================================================
   §23 工作台（home/index.htm，Vue 3）—— 按角色的桌面首页
   顶条 · 待我处理 · 按角色分区（区头 + 卡片网格）· 角色导航 · 角色顺序弹窗。
   卡片里只有五样：数字格 · 图表（ECharts，颜色运行时取 §2 的 token）· 各线进度 · 列表 · 部门灯。
   ========================================================================== */
.cb [v-cloak] { display: none; }
.cb .cb-hm { display: flex; flex-direction: column; gap: 16px; }

/* 顶条 */
.cb .cb-hm-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.cb .cb-hm-hello h1 { font-size: 20px; font-weight: 600; color: var(--cb-heading); margin: 0; line-height: 1.3; }
.cb .cb-hm-date { font-size: 12px; color: var(--cb-text-2); margin-top: 2px; display: flex; align-items: center; gap: 6px; }
.cb .cb-hm-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cb .cb-hm-live { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--cb-text-2); white-space: nowrap; }
.cb .cb-hm-live.bad { color: var(--cb-bad-text); }
.cb .cb-hm-classic { font-size: 12px; }
.cb .cb-hm-spin { display: inline-block; animation: cb-hm-spin .8s linear infinite; }
@keyframes cb-hm-spin { to { transform: rotate(360deg); } }

/* 常用功能：一行药丸，钉上的带图钉；右上「管理」开弹窗 */
.cb .cb-hm-favbox { background: var(--cb-surface); border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius-lg);
                    padding: 12px 16px; box-shadow: var(--cb-shadow-1); }
.cb .cb-hm-favbox h2 { font-size: 13px; font-weight: 600; color: var(--cb-text-2); margin: 0 0 10px; display: flex; align-items: center; gap: 6px; }
.cb .cb-hm-favs { display: flex; flex-wrap: wrap; gap: 8px; }
.cb .cb-hm-fav { position: relative; display: inline-flex; align-items: stretch; border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius);
                 background: var(--cb-surface); }
.cb .cb-hm-fav:hover { border-color: var(--cb-accent); }
.cb .cb-hm-fav.pinned { border-color: var(--cb-accent); background: var(--cb-accent-soft); }
.cb .cb-hm-fav > a { display: inline-flex; align-items: center; gap: 6px; height: 34px; padding: 0 6px 0 10px; color: var(--cb-text);
                     text-decoration: none; font-size: 13px; white-space: nowrap; }
.cb .cb-hm-fav > a .fa { color: var(--cb-accent); }
.cb .cb-hm-fav > a .gp { font-size: 11px; color: var(--cb-text-3); }
.cb .cb-hm-pinmark { font-size: 11px; color: var(--cb-accent-text); }
.cb .cb-hm-favx { border: 0; background: transparent; color: var(--cb-text-3); padding: 0 6px; font-size: 14px; }
.cb .cb-hm-favx:hover { color: var(--cb-bad-text); }
/* 管理弹窗：按一级菜单分组，两列 */
.cb .cb-hm-mgroups { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
.cb .cb-hm-mgroup h6 { font-size: 13px; font-weight: 600; color: var(--cb-heading); margin: 0 0 6px; display: flex; align-items: center; gap: 4px; }
.cb .cb-hm-mgroup h6 .fa { color: var(--cb-accent); }
.cb .cb-hm-mgroup ul { list-style: none; margin: 0; padding: 0; }
.cb .cb-hm-mgroup li { display: flex; align-items: center; gap: 8px; padding: 4px 0; border-bottom: 1px solid var(--cb-line); font-size: 13px; }
.cb .cb-hm-mgroup li:last-child { border-bottom: 0; }
.cb .cb-hm-mgroup li .nm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb .cb-hm-mgroup li .ht { font-size: 11px; color: var(--cb-text-3); white-space: nowrap; }

/* 待我处理 */
.cb .cb-hm-inbox { background: var(--cb-surface); border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius-lg);
                   padding: 12px 16px; box-shadow: var(--cb-shadow-1); }
.cb .cb-hm-inbox h2 { font-size: 13px; font-weight: 600; color: var(--cb-text-2); margin: 0 0 10px; display: flex; align-items: center; gap: 6px; }
.cb .cb-hm-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.cb .cb-hm-pill { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 15px;
                  border: 1px solid var(--cb-line); background: var(--cb-surface); color: var(--cb-text); text-decoration: none; font-size: 13px; }
.cb .cb-hm-pill b { font-variant-numeric: tabular-nums; font-weight: 600; margin-left: 2px; }
.cb .cb-hm-pill:hover { border-color: var(--cb-accent); }
.cb .cb-hm-pill.bad { border-color: var(--cb-bad-line); background: var(--cb-bad-soft); }
.cb .cb-hm-pill.bad b { color: var(--cb-bad-text); }
.cb .cb-hm-pill.wn { border-color: var(--cb-wn-line); background: var(--cb-wn-soft); }
.cb .cb-hm-pill.wn b { color: var(--cb-wn-text); }
.cb .cb-hm-pill.info { border-color: var(--cb-line); background: var(--cb-accent-soft); }
.cb .cb-hm-pill.info b { color: var(--cb-accent-text); }
.cb .cb-hm-clear { font-size: 13px; color: var(--cb-ok-text); display: flex; align-items: center; gap: 6px; }

/* 角色区块 */
.cb .cb-hm-sec { display: flex; flex-direction: column; gap: 12px; }
.cb .cb-hm-sechead { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cb .cb-hm-picon { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; flex: 0 0 36px;
                   border-radius: 10px; background: var(--cb-accent-soft); color: var(--cb-accent-text); font-size: 18px; }
.cb .cb-hm-ptxt h2 { font-size: 15px; font-weight: 600; color: var(--cb-heading); margin: 0; }
.cb .cb-hm-pdesc { font-size: 12px; color: var(--cb-text-2); }
/* 销售经理的「全部 / 我的」：挨着快捷入口，一起靠右 */
.cb .cb-hm-scope { margin-left: auto; }
.cb .cb-hm-scope + .cb-hm-quick { margin-left: 0; }
.cb .cb-hm-quick { margin-left: auto; display: flex; flex-wrap: wrap; gap: 6px; }
.cb .cb-hm-quick a { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; font-size: 12px;
                     border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius); background: var(--cb-surface);
                     color: var(--cb-text); text-decoration: none; white-space: nowrap; }
.cb .cb-hm-quick a:hover { border-color: var(--cb-accent); color: var(--cb-accent-text); background: var(--cb-accent-soft); }
.cb .cb-hm-quick a .bi { color: var(--cb-accent); }

/* 卡片网格：图表 / 各线进度 / 部门灯占两格 */
/* dense：宽卡放不下时后面的窄卡往前补洞，不留大块空白；stretch：同一行卡片等高 */
.cb .cb-hm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; align-items: stretch;
                  grid-auto-flow: row dense; }
/* 这个区块只有一张卡：占满整行，里面的图表和列表并排 */
.cb .cb-hm-grid > .cb-hm-card:only-child { grid-column: 1 / -1; }
.cb .cb-hm-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; align-content: start; }
.cb .cb-hm-grid > .cb-hm-card:only-child .cb-hm-body,
.cb .cb-hm-w .cb-hm-body { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.cb .cb-hm-w { grid-column: span 2; }
.cb .cb-hm-n { grid-column: auto; }
@media (max-width: 767.98px) { .cb .cb-hm-w { grid-column: auto; } .cb .cb-hm-grid { grid-template-columns: minmax(0, 1fr); } }
.cb .cb-hm-card { background: var(--cb-surface); border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius-lg);
                  padding: 14px 16px; box-shadow: var(--cb-shadow-1); display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.cb .cb-hm-cardhead { display: flex; align-items: baseline; gap: 8px; }
.cb .cb-hm-cardhead h3 { font-size: 13px; font-weight: 600; color: var(--cb-heading); margin: 0; }
.cb .cb-hm-cardhead a { margin-left: auto; font-size: 12px; white-space: nowrap; }
.cb .cb-hm-sub { font-size: 12px; font-weight: 400; color: var(--cb-text-2); margin-left: 8px; }
.cb .cb-hm-err, .cb .cb-hm-none { font-size: 12px; color: var(--cb-text-2); padding: 6px 0; }
.cb .cb-hm-err { color: var(--cb-wn-text); }

/* 数字格 */
.cb .cb-hm-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; }
.cb .cb-hm-kpi { display: flex; flex-direction: column; padding: 10px 12px; border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius);
                 background: var(--cb-surface-2); color: var(--cb-text); text-decoration: none; min-width: 0; }
.cb a.cb-hm-kpi:hover { border-color: var(--cb-accent); }
.cb .cb-hm-kpi .k { font-size: 12px; color: var(--cb-text-2); display: flex; align-items: center; gap: 4px; }
.cb .cb-hm-kpi .k .bi { font-size: 11px; color: var(--cb-text-3); }
.cb .cb-hm-kpi .v { font-size: 22px; font-weight: 600; color: var(--cb-heading); font-variant-numeric: tabular-nums; line-height: 1.3; margin-top: 2px;
                    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb .cb-hm-kpi .s { font-size: 11px; color: var(--cb-text-2); display: flex; flex-wrap: wrap; gap: 6px; min-height: 16px; }
.cb .cb-hm-kpi.ok .v { color: var(--cb-ok-text); }
.cb .cb-hm-kpi.wn { border-color: var(--cb-wn-line); background: var(--cb-wn-soft); }
.cb .cb-hm-kpi.wn .v { color: var(--cb-wn-text); }
.cb .cb-hm-kpi.bad { border-color: var(--cb-bad-line); background: var(--cb-bad-soft); }
.cb .cb-hm-kpi.bad .v { color: var(--cb-bad-text); }
.cb .cb-hm-delta { display: inline-flex; align-items: center; gap: 2px; font-weight: 600; }
.cb .cb-hm-delta.up { color: var(--cb-ok-text); }
.cb .cb-hm-delta.down { color: var(--cb-bad-text); }

/* 图表：一张卡一到两张；每种图一个固定高度（ECharts 要容器先有尺寸） */
.cb .cb-hm-charts { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
.cb .cb-hm-charts.two { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); }
@media (max-width: 991.98px) { .cb .cb-hm-charts.two { grid-template-columns: minmax(0, 1fr); } }
.cb .cb-hm-chtitle { font-size: 12px; color: var(--cb-text-2); margin-bottom: 4px; }
.cb .cb-hm-chartbody { position: relative; }
.cb .cb-hm-chart { width: 100%; height: 220px; }
.cb .cb-hm-chartwrap.trend .cb-hm-chart { height: 240px; }
.cb .cb-hm-chartwrap.hbar .cb-hm-chart { height: 290px; }
.cb .cb-hm-chartwrap.stack .cb-hm-chart, .cb .cb-hm-chartwrap.bars2 .cb-hm-chart { height: 210px; }
.cb .cb-hm-chartwrap.gauge .cb-hm-chartbody, .cb .cb-hm-chartwrap.donut .cb-hm-chartbody { display: flex; align-items: center; gap: 16px; }
.cb .cb-hm-chartwrap.gauge .cb-hm-chtitle { display: none; }
.cb .cb-hm-chartwrap.gauge .cb-hm-chart { width: 170px; height: 150px; flex: 0 0 170px; }
.cb .cb-hm-chartwrap.donut .cb-hm-chart { width: 150px; height: 150px; flex: 0 0 150px; }
.cb .cb-hm-gcap { display: flex; flex-direction: column; gap: 2px; font-size: 12px; color: var(--cb-text-2); }
.cb .cb-hm-gcap b { font-size: 13px; color: var(--cb-heading); }
/* 环形图的图例：色块 · 名称 · 数 · 占比 */
.cb .cb-hm-legend { list-style: none; margin: 0; padding: 0; flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.cb .cb-hm-legend li { display: grid; grid-template-columns: 10px minmax(0, 1fr) auto 38px; gap: 8px; align-items: center; font-size: 12px; }
.cb .cb-hm-legend .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--cb-text); }
.cb .cb-hm-legend b { font-variant-numeric: tabular-nums; color: var(--cb-heading); }
.cb .cb-hm-legend .pc { text-align: right; color: var(--cb-text-2); font-variant-numeric: tabular-nums; }
.cb .cb-hm-sw { width: 10px; height: 10px; border-radius: 3px; background: var(--cb-line-strong); }
.cb .cb-hm-sw.ok { background: var(--cb-ok-fill); }
.cb .cb-hm-sw.wn { background: var(--cb-wn-fill); }
.cb .cb-hm-sw.bad { background: var(--cb-bad-fill); }
.cb .cb-hm-sw.acc { background: var(--cb-accent); }
.cb .cb-hm-sw.star { background: var(--cb-star); }
.cb .cb-hm-sw.mut { background: var(--cb-line-strong); }
.cb .cb-hm-sw.mut2 { background: var(--cb-text-3); }

/* 各线进度 */
.cb .cb-hm-lanes { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; }
.cb .cb-hm-lane { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px 8px; align-items: center; padding: 8px 10px;
                  border: 1px solid var(--cb-line); border-radius: var(--bs-border-radius); background: var(--cb-surface);
                  color: var(--cb-text); text-decoration: none; font-size: 12px; }
.cb .cb-hm-lane:hover { border-color: var(--cb-accent); }
.cb .cb-hm-lane .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb .cb-hm-lane .n { font-variant-numeric: tabular-nums; font-weight: 600; }
.cb .cb-hm-lane progress { grid-column: 1 / -1; display: block; width: 100%; height: 6px; border: 0; border-radius: 3px; overflow: hidden;
                           appearance: none; background: var(--cb-surface-3); accent-color: var(--cb-ok-fill); }
.cb .cb-hm-lane progress::-webkit-progress-bar { background: var(--cb-surface-3); }
.cb .cb-hm-lane progress::-webkit-progress-value { background: var(--cb-ok-fill); }
.cb .cb-hm-lane progress::-moz-progress-bar { background: var(--cb-ok-fill); }
.cb .cb-hm-lane.wn { border-color: var(--cb-wn-line); background: var(--cb-wn-soft); }
.cb .cb-hm-lane.wn .n { color: var(--cb-wn-text); }
.cb .cb-hm-lane.wn progress { accent-color: var(--cb-wn-fill); }
.cb .cb-hm-lane.wn progress::-webkit-progress-value { background: var(--cb-wn-fill); }
.cb .cb-hm-lane.wn progress::-moz-progress-bar { background: var(--cb-wn-fill); }
.cb .cb-hm-lane.ok .n { color: var(--cb-ok-text); }

/* 列表：左边一个序号 */
.cb .cb-hm-listhead { font-size: 12px; color: var(--cb-text-2); margin-bottom: 4px; }
.cb .cb-hm-list ol { list-style: none; margin: 0; padding: 0; counter-reset: hm; }
.cb .cb-hm-list li { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--cb-line); font-size: 13px;
                     counter-increment: hm; }
.cb .cb-hm-list li:last-child { border-bottom: 0; }
.cb .cb-hm-list li::before { content: counter(hm); flex: 0 0 20px; font-size: 11px; color: var(--cb-text-3); font-variant-numeric: tabular-nums; }
.cb .cb-hm-list .nm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--cb-text); }
.cb .cb-hm-list a.nm:hover { color: var(--cb-accent-text); }
.cb .cb-hm-list .sb { font-size: 12px; color: var(--cb-text-2); white-space: nowrap; }
.cb .cb-hm-list .vl { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }

/* 各部门灯（老板） */
.cb .cb-hm-pulse { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 10px; }
.cb .cb-hm-dept { display: grid; grid-template-columns: 32px minmax(0, 1fr) auto; gap: 2px 10px; align-items: center; padding: 10px 12px;
                  border: 1px solid var(--cb-ok-line); border-radius: var(--bs-border-radius); background: var(--cb-ok-soft);
                  color: var(--cb-text); text-decoration: none; }
.cb a.cb-hm-dept:hover { border-color: var(--cb-accent); }
.cb .cb-hm-dicon { grid-row: span 2; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px;
                   border-radius: 8px; background: var(--cb-surface); color: var(--cb-ok-text); font-size: 16px; }
.cb .cb-hm-dname { font-size: 13px; font-weight: 600; color: var(--cb-heading); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb .cb-hm-dstat { font-size: 12px; color: var(--cb-ok-text); white-space: nowrap; }
.cb .cb-hm-dstat b { font-size: 18px; font-variant-numeric: tabular-nums; }
.cb .cb-hm-dtodo { grid-column: 2 / -1; font-size: 11px; color: var(--cb-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb .cb-hm-dept.wn { border-color: var(--cb-wn-line); background: var(--cb-wn-soft); }
.cb .cb-hm-dept.wn .cb-hm-dicon, .cb .cb-hm-dept.wn .cb-hm-dstat { color: var(--cb-wn-text); }
.cb .cb-hm-dept.bad { border-color: var(--cb-bad-line); background: var(--cb-bad-soft); }
.cb .cb-hm-dept.bad .cb-hm-dicon, .cb .cb-hm-dept.bad .cb-hm-dstat { color: var(--cb-bad-text); }
.cb .cb-hm-dept.info { border-color: var(--cb-line); background: var(--cb-accent-soft); }
.cb .cb-hm-dept.info .cb-hm-dicon, .cb .cb-hm-dept.info .cb-hm-dstat { color: var(--cb-accent-text); }
.cb .cb-hm-dept.mut { border-color: var(--cb-line); background: var(--cb-surface-2); }
.cb .cb-hm-dept.mut .cb-hm-dicon, .cb .cb-hm-dept.mut .cb-hm-dstat { color: var(--cb-text-2); }

/* 角色顺序弹窗的说明 */
.cb .cb-hm-pickhint { font-size: 13px; color: var(--cb-text-2); }
@media (prefers-reduced-motion: reduce) { .cb .cb-hm-spin { animation: none; } }

/* 角色导航：钉在内容区顶上（.cb-main 是滚动容器），一个角色一颗，带待办数 */
.cb .cb-hm-nav { position: sticky; top: 0; z-index: 5; display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 0;
                 margin: -8px 0 -8px; background: var(--cb-bg); }
.cb .cb-hm-navi { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 15px;
                  border: 1px solid var(--cb-line); background: var(--cb-surface); color: var(--cb-text); text-decoration: none;
                  font-size: 13px; white-space: nowrap; box-shadow: var(--cb-shadow-1); }
.cb .cb-hm-navi:hover { border-color: var(--cb-accent); color: var(--cb-accent-text); }
.cb .cb-hm-navi .bi { color: var(--cb-accent); }
.cb .cb-hm-navn { min-width: 20px; height: 18px; padding: 0 6px; border-radius: 9px; font-size: 11px; font-weight: 600;
                  display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums;
                  background: var(--cb-wn-soft); color: var(--cb-wn-text); border: 1px solid var(--cb-wn-line); }
.cb .cb-hm-navn.bad { background: var(--cb-bad-soft); color: var(--cb-bad-text); border-color: var(--cb-bad-line); }
.cb .cb-hm-sec { scroll-margin-top: 52px; }

/* 区块折叠 · 主角色标记 */
.cb .cb-hm-fold { width: 28px; height: 28px; flex: 0 0 28px; border: 0; border-radius: var(--bs-border-radius); background: transparent;
                  color: var(--cb-text-2); display: inline-flex; align-items: center; justify-content: center; }
.cb .cb-hm-fold:hover { background: var(--cb-surface-3); color: var(--cb-heading); }
.cb .cb-hm-main { display: inline-flex; align-items: center; height: 18px; padding: 0 7px; margin-left: 8px; border-radius: 9px;
                  background: var(--cb-accent-strong); color: var(--cb-on-fill); font-size: 11px; font-weight: 600; vertical-align: middle; }

/* 数字格的图标、提示、迷你趋势 */
.cb .cb-hm-kpi { position: relative; overflow: hidden; }
.cb .cb-hm-ki { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 6px;
                background: var(--cb-accent-soft); color: var(--cb-accent-text); font-size: 12px; margin-right: 2px; }
.cb .cb-hm-kpi.wn .cb-hm-ki { background: var(--cb-surface); color: var(--cb-wn-text); }
.cb .cb-hm-kpi.bad .cb-hm-ki { background: var(--cb-surface); color: var(--cb-bad-text); }
.cb .cb-hm-kpi.ok .cb-hm-ki { background: var(--cb-ok-soft); color: var(--cb-ok-text); }
.cb .cb-hm-hint { font-size: 11px; color: var(--cb-text-3); }
.cb .cb-hm-spark { display: block; width: 100%; height: 24px; margin-top: 6px; }
.cb .cb-hm-spark .line { fill: none; stroke: var(--cb-accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.cb .cb-hm-spark .area { fill: var(--cb-accent-soft); stroke: none; }

/* 角色顺序：拖动把手 · 拖动中的占位 · 顶上那一行「现在是哪种顺序」 */
.cb .cb-hm-grip { cursor: grab; color: var(--cb-text-3); font-size: 16px; display: inline-flex; align-items: center; }
.cb .cb-hm-grip:hover { color: var(--cb-text); }
.cb .cb-hm-ghost { opacity: .45; background: var(--cb-accent-soft); }
.cb .cb-hm-omode { font-size: 12px; color: var(--cb-text-2); margin: -4px 0 10px; display: flex; align-items: center; gap: 6px; }
.cb .cb-hm-omode.manual { color: var(--cb-accent-text); }
/* 角色顺序：一行一个角色，右边上移 / 下移 */
.cb .cb-hm-order { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.cb .cb-hm-order li { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--cb-line);
                      border-radius: var(--bs-border-radius-lg); background: var(--cb-surface); }
.cb .cb-hm-order li.first { border-color: var(--cb-accent); background: var(--cb-accent-soft); }
.cb .cb-hm-order .no { width: 20px; text-align: center; font-size: 12px; font-weight: 600; color: var(--cb-text-2); font-variant-numeric: tabular-nums; }
.cb .cb-hm-order .tx { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.cb .cb-hm-order .tx b { font-size: 14px; color: var(--cb-heading); font-weight: 600; }
.cb .cb-hm-order .tx span { font-size: 12px; color: var(--cb-text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cb .cb-hm-order .mv { display: inline-flex; gap: 4px; }

/* ==========================================================================
   §24 订单列表的日期控件（js/cb_date_range.js · 老订单列表与 order_live 共用）
   [日期类型 ▾] [‹] [一个日期 / 一个区间 ▾] [›] —— 同一时刻屏上只有一个日期。
   弹层是 Bootstrap 的 .dropdown-menu：左边预设，右边月历（配送日带每天的单数）。
   ========================================================================== */
.cb .cb-dr { display: inline-flex; align-items: center; gap: 4px; flex-wrap: nowrap; vertical-align: middle; }
.cb .cb-dr .cb-dr-kind { width: auto; min-width: 0; flex: 0 0 auto; }
.cb .cb-dr .cb-dr-pick { position: relative; display: inline-flex; align-items: center; gap: 4px; }
.cb .cb-dr .cb-dr-step { flex: 0 0 auto; padding-left: 8px; padding-right: 8px; }
/* 触发按钮长得像下拉（form-select 的箭头），但它是按钮 —— 宽度够放「明天 · 2026-10-06 周二」不跳动 */
.cb .cb-dr .cb-dr-btn { width: auto; min-width: 210px; text-align: left; white-space: nowrap; cursor: pointer; }
.cb .cb-dr .cb-dr-menu { padding: 0; min-width: 0; }
.cb .cb-dr .cb-dr-menu.show { display: flex; }

.cb .cb-dr-presets { display: flex; flex-direction: column; padding: 6px; border-right: 1px solid var(--cb-line); min-width: 150px; }
.cb .cb-dr-pre { display: flex; align-items: center; justify-content: space-between; gap: 8px; border: 0; background: transparent;
                 color: var(--cb-text); font-size: 14px; line-height: 20px; padding: 6px 10px; border-radius: 4px; text-align: left; white-space: nowrap; }
.cb .cb-dr-pre:hover { background: var(--cb-surface-2); color: var(--cb-accent-text); }
.cb .cb-dr-pre.active { background: var(--cb-accent-soft); color: var(--cb-accent-text); font-weight: 600; }
.cb .cb-dr-def { font-size: 11px; line-height: 16px; padding: 0 6px; border-radius: 8px; font-weight: 400;
                 border: 1px solid var(--cb-line-strong); color: var(--cb-text-2); }

.cb .cb-dr-cal { padding: 8px 10px 10px; width: 300px; }
.cb .cb-dr-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.cb .cb-dr-cal-head b { font-size: 14px; font-weight: 600; color: var(--cb-heading); }
.cb .cb-dr-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cb .cb-dr-wk { text-align: center; font-size: 12px; line-height: 24px; color: var(--cb-text-2); }
.cb .cb-dr-day { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 38px; padding: 0;
                 border: 1px solid transparent; border-radius: 4px; background: transparent; color: var(--cb-text); font-variant-numeric: tabular-nums; }
.cb .cb-dr-day .n { font-size: 14px; line-height: 16px; }
.cb .cb-dr-day .c { font-size: 11px; line-height: 13px; min-height: 13px; color: var(--cb-accent-text); }
.cb .cb-dr-day.has .n { font-weight: 600; }
.cb .cb-dr-day.out { color: var(--cb-text-3); }
.cb .cb-dr-day:hover { background: var(--cb-surface-2); border-color: var(--cb-line-strong); }
.cb .cb-dr-day.is-today { border-color: var(--cb-accent); }
.cb .cb-dr-day.in-range { background: var(--cb-accent-soft); border-radius: 0; }
.cb .cb-dr-day.is-sel { background: var(--cb-accent-strong); color: var(--cb-on-fill); }
.cb .cb-dr-day.is-sel .c { color: var(--cb-on-fill); }
.cb .cb-dr-foot { display: flex; align-items: center; gap: 8px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--cb-line); }
.cb .cb-dr-hint { font-size: 12px; line-height: 16px; color: var(--cb-text-2); }

/* 手机：预设在上、月历在下；下拉框收窄 */
@media (max-width: 575.98px) {
  .cb .cb-dr { flex-wrap: wrap; }
  .cb .cb-dr .cb-dr-btn { min-width: 0; max-width: calc(100vw - 120px); overflow: hidden; text-overflow: ellipsis; }
  .cb .cb-dr .cb-dr-menu.show { flex-direction: column; }
  .cb .cb-dr-presets { flex-direction: row; flex-wrap: wrap; border-right: 0; border-bottom: 1px solid var(--cb-line); min-width: 0; max-width: 300px; }
}
