:root {
  /* ---- 配色：藍（あい）と罫線（2026-10-27 改版）----
     世界：日系企业系统的「帳票」质感 —— 生成纸白的底、发丝罫线分格、
     藍（靛）当墨色，颜色只做状态、不做装饰。
     为什么不沿用 #1565c0 科技蓝：它在白底上是"通用后台"的默认色，
     而 #1F3E63 是日式靛蓝（藍染/紺），压得住大表格，也不与状态红抢注意力。
     所有配对已用 WCAG 公式核过（正文 ≥4.5:1，见 tests/contrast.node.test.js）：
       #1A1D21/白 16.9 · #5A6270/白 6.2 · #5A6270/#F4F4F1 5.6
       白/#1F3E63 10.9 · 白/#2C6B57 6.3 · 白/#A93226 6.6 */
  --c-primary: #1F3E63;
  --c-primary-dark: #162F4C;
  --c-accent: #2C6B57;
  --c-accent-dark: #245846;
  --c-danger: #A93226;
  --c-text: #1A1D21;
  /* 次要文字：旧 #5b6472 在 #f5f7fa 上 5.4:1，可读但偏冷灰；
     #5A6270 在新纸白底（#F4F4F1）上 5.58:1、白底 6.15:1，且带一点暖。 */
  --c-muted: #5A6270;
  --c-border: #E2E0D9;          /* 发丝罫线：比 #e0e0e0 暖、比纯灰细 */
  --c-bg: #F4F4F1;              /* 生成纸白：不是纯灰，也不是奶油色 */
  /* 全站数据表格统一尺寸：正文数据列最小宽 120、数据行高 64。
     历史：2026-09-28 拍板 95px；2026-10-27 用户要求"UI 密度一起修"后降到 64px。
     95px 的问题：一屏 1080p 只放得下 8 行，翻台账要滚很久；对"看数"又缺层级。
     64px 下 1080p 约 12 行，且缩略图按 calc(--row-h - 14) 自动缩到 50px 仍可辨。
     含缩略图的三张表（商品/销售明细/客户定制）单独保留 95px，见下方覆盖。
     窄列（checkbox/图/单号/日期等 .nw 与操作列 .act）豁免，保持紧凑。 */
  --col-w: 120px;
  --row-h: 64px;
  /* td 的列宽**硬底线**。与 --col-w 分开是有原因的（2026-10-07）：
     --col-w 还兼着「行内缩略图 max-width」的职责，改它会连带把图缩小；
     而真正需要按列数分档的只有这一条底线。两者语义不同，别再合并。 */
  --td-min: var(--col-w);
  --c-surface: #ffffff;
  /* ---- 语义色阶（2026-10-27 变量化）----
     为什么把原本写死的色提成变量：深色模式用 @media (prefers-color-scheme) 覆盖时，
     写死的浅色值会和深色块的背景**交叉配对**，静态检测（impeccable detect）会把
     "深色文字配浅色底"当成不达标的组合报出来，噪音盖住真问题。
     提成变量后，浅色/深色各给一套值，静态与运行时看到的是同一组配对。 */
  --c-on-primary: #fff;          /* 主按钮上的字 */
  --c-on-accent: #fff;           /* 强调按钮上的字 */
  --c-sunken: #EFEEE9;           /* 分组底（.subtabs / .seg / 下拉页脚） */
  --c-raised: #ffffff;           /* 分组里被选中项的底 */
  --c-thead-bg: #FAFAF7;
  --c-thead-text: #4A5260;
  --c-row-hover: #F8F8F4;
  --c-danger-soft: #F7E9E6;
  --c-danger-soft-text: #8F2B20;
  --c-ok-bg: #E8F0EA;
  --c-ok-text: #276352;
  --c-hint-blue-bg: #E9EEF4;  --c-hint-blue-line: #C3CEDC;  --c-hint-blue-text: #22405E;
  --c-hint-amber-bg: #F7F0DF; --c-hint-amber-line: #DFCBA5; --c-hint-amber-text: #6B4E18;
  --c-combo-hover: #F5F5F1;
  --c-combo-on: #E9EEF4;
  --c-sampbar-bg: #F8F2E4; --c-sampbar-line: #E3D2B0; --c-sampbar-text: #6B4E18;
  --c-readonly-bg: #F1F1ED; --c-readonly-text: #565D68;
  /* 罫线三档（2026-10-27）：
     --c-line 是浮层/卡片内部的分隔（比 --c-border 再淡一档）；
     --c-rule 是表头下那条「見出し罫」，带靛蓝、不抢但要能被看见；
     --c-rule-strong 给合计区的二重罫線（帳票里合计上方那道双线）。 */
  --c-line: #E7E5DE;
  --c-rule: #B7C2D2;
  --c-rule-strong: #2B3A4D;
  --c-focus: rgba(31, 62, 99, .18);
  /* 下拉箭头：内联 SVG（不引图标库），颜色做成变量，深色主题给另一份 */
  --select-arrow: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5' fill='none' stroke='%235A6270' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --radius: 8px;
  --radius-sm: 6px;
  /* 高度只在两处出现：浮层与模态。卡片一律用罫线分格、不用投影 —— 
     1px 边 + 大范围柔影是"幽灵卡片"，两个高度系统会互相打架。 */
  --shadow-pop: 0 6px 18px rgba(20, 23, 28, .10), 0 1px 2px rgba(20, 23, 28, .06);
  --shadow-modal: 0 24px 64px rgba(18, 21, 26, .18), 0 2px 8px rgba(18, 21, 26, .08);
  --ease: cubic-bezier(.2, .7, .2, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  /* 字体：**中文族优先**。日文字族（Hiragino Sans / Yu Gothic）会让漢字走日文字形
     （直/骨/写 这些字的中日字形差异肉眼可见），这是中文经营系统，字形正确比"日式味道"重要；
     日式的克制靠字距、字号阶与罫线表达，不靠换字族。 */
  font-family: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Yu Gothic UI",
               -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  letter-spacing: .01em;                 /* 中文默认偏挤，一点字距就透气 */
  font-variant-numeric: tabular-nums;    /* 全站数字等宽：金额列才对得齐 */
  color: var(--c-text);
  background: var(--c-bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* 标题兜底（2026-10-27 一致性巡检）：只有 .card > h2 被写成 13.5px，
   其它位置的 <h2>（例如 .toolbar 里的「可约时段」）会掉回浏览器默认 1.5em/700 = 21px，
   于是同一套界面里出现两种"卡片标题"。这里给 h1/h2 一条与系统同源的兜底。 */
h1 { font-size: 16px; font-weight: 600; letter-spacing: .02em; color: var(--c-text); margin: 0; }
h2 { font-size: 13.5px; font-weight: 600; letter-spacing: .04em; color: var(--c-text); margin: 0; }

/* ---- 浏览器自带的那几处表面（2026-10-27）----
   选区、光标、滚动条、焦点环都是设计系统之外的地盘，默认值会把新配色衬得廉价。
   这几条是"被认真做过"最便宜的证据，也是模型最容易漏的一格。 */
::selection { background: rgba(31, 62, 99, .16); }
::-moz-selection { background: rgba(31, 62, 99, .16); }
input, textarea, select, [contenteditable] { caret-color: var(--c-primary); }
html { scrollbar-color: #C9C8C1 transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: #C9C8C1; border: 3px solid transparent; background-clip: content-box;
  border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover { background: #ABA9A2; background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* ---- 动效（2026-10-28）----
   JS 那边只有 motion.js 一处（GSAP，本地 vendor）：页签切换、弹窗打开、
   可搜索下拉、指标/合计数字滚动。这里只做两件 CSS 该做的事：
   给会被动画的元素一个合成层提示，以及"系统要求减少动效"时的总开关。 */
.modal { will-change: transform, opacity; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* 键盘焦点规则在下方 input 段之后统一给（全站一条，包含 a/button/input/select/textarea）。 */

header.topbar {
  background: var(--c-surface);
  /* 见出し罫（signature）：顶栏不靠阴影分段，靠一条带靛蓝的 1px 罫线 ——
     整页的"分段语法"从这一条开始，往下每一道都同源。 */
  border-bottom: 1px solid var(--c-rule);
  padding: 0 24px;
  min-height: 56px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px 12px;
}
header.topbar h1 {
  font-size: 16px; font-weight: 600; color: var(--c-text);
  letter-spacing: .02em;
}
header.topbar .sub { font-size: 12px; color: var(--c-muted); letter-spacing: .02em; }
/* 健康状态（2026-10-27 顶栏收敛）：正常时只留一个 8px 绿点。
   它 99% 的时间都是绿的，占一整块「数据库已连接」的空间不值当；异常时才展开成
   胶囊把话说清楚。文字仍保留在元素里（font-size:0 只是不显示），悬停 title 与
   读屏都拿得到。 */
#health {
  font-size: 0; width: 8px; height: 8px; padding: 0; display: inline-block;
  flex: 0 0 auto; border-radius: 50%; background: var(--c-ok-text);
  box-shadow: 0 0 0 3px rgba(39, 99, 82, .10);
}
#health.bad {
  font-size: 12px; width: auto; height: auto; padding: 3px 10px; border-radius: 999px;
  background: var(--c-danger-soft); color: var(--c-danger-soft-text);
}

/* 顶栏「⋯」菜单（2026-10-27）：所有次要动作（返回主页 / 关于 / 退出登录）都收在这里。
   改之前每页顶栏挂 3 个可见动作，其中"返回"与导航重复、"关于"一年点不了几次。 */
.topbar-menu { position: relative; display: inline-flex; }
.menu-btn { font-size: 16px; line-height: 20px; padding: 6px 12px; letter-spacing: 1px; }
.menu-pop {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 70;
  min-width: 148px; padding: 4px; display: none;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 8px;
  box-shadow: var(--shadow-pop);
}
.topbar-menu.open .menu-pop { display: block; }
.menu-pop > * {
  display: block; width: 100%; text-align: left; padding: 8px 10px;
  font: inherit; font-size: 13px; color: var(--c-text); text-decoration: none;
  background: none; border: 0; border-radius: 6px; cursor: pointer; white-space: nowrap;
}
.menu-pop > *:hover { background: var(--c-sunken); }

/* 页面标题兼作「回开单主页」的入口（只在独立页，见 nav.js 的 renderHead） */
.page-home { color: inherit; text-decoration: none; }
.page-home:hover { color: var(--c-primary); }

nav.tabs {
  display: flex; gap: 0; padding: 0 16px;
  background: var(--c-surface); border-bottom: 1px solid var(--c-border);
  min-height: 44px;
}
/* 导航项：字距 .03em、行高撑满 44px，active 用 2px 藍罫而不是色块 ——
   下划线是"页签"这一控件本来就有的语言，不需要发明新形态。 */
nav.tabs button {
  border: none; background: none; cursor: pointer;
  padding: 0 16px; font-size: 13.5px; color: var(--c-muted); letter-spacing: .03em;
  border-bottom: 2px solid transparent; font-family: inherit;
  transition: color .15s var(--ease), border-color .15s var(--ease);
}
nav.tabs button:hover { color: var(--c-text); }
nav.tabs button.active { color: var(--c-primary); border-bottom-color: var(--c-primary); font-weight: 600; }
/* 导航链接（2026-10-27 收敛）：原 7 个独立页各自复制了一份 nav.tabs a 规则（每页 5 行），
   现统一到此处。观感必须与上面的 nav.tabs button 完全一致；改这里就是改 8 个页面。
   white-space: nowrap 是为了中等宽度下不把两行字挤成两行（见文件末尾 1180 断点）。 */
nav.tabs a.tab-link {
  display: flex; align-items: center; text-decoration: none; font-family: inherit;
  padding: 0 16px; font-size: 13.5px; color: var(--c-muted); letter-spacing: .03em;
  border-bottom: 2px solid transparent; white-space: nowrap;
  transition: color .15s var(--ease), border-color .15s var(--ease);
}
nav.tabs a.tab-link:hover { color: var(--c-text); }
nav.tabs a.tab-link.active {
  color: var(--c-primary); border-bottom-color: var(--c-primary); font-weight: 600;
}
nav.tabs a.tab-link.active:hover { color: var(--c-primary); }
nav.tabs button { white-space: nowrap; }

/* 顶栏右侧动作容器：健康徽标 / 返回首页 / 退出登录，由 /static/nav.js 注入 */
.topbar-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* 顶栏左侧：业务域切换 + 页面标题（2026-10-27 用户拍板 C 方案） */
.topbar-left { display: flex; align-items: center; gap: 14px; min-width: 0; }
.topbar-titles { min-width: 0; }

/* 业务域切换「批发 | 门店」：用它代替原来 13 项平铺的一级导航。
   13 项在 1440 宽下正好占满（navW=1440 = clientW）、1280 就得横滚；而且批发和门店
   是两条独立流程，混在一排里谁也扫不快。现在导航条只显示当前域的 6~7 项。
   当前域渲染成 <span>（不可点），另一个域是 <a>（点它才离开本域）。 */
.domain-switch {
  display: inline-flex; gap: 0; flex: 0 0 auto;
  background: transparent; padding: 0; border-radius: 6px;
  border: 1px solid var(--c-border);
}
.domain-switch > * {
  padding: 3px 11px; border-radius: 0; font-size: 12.5px; line-height: 18px;
  color: var(--c-muted); text-decoration: none; white-space: nowrap;
  letter-spacing: .04em;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.domain-switch > *:first-child { border-radius: 5px 0 0 5px; }
.domain-switch > *:last-child { border-radius: 0 5px 5px 0; }
.domain-switch > a:hover { color: var(--c-primary); background: var(--c-sunken); }
.domain-switch > .active {
  background: var(--c-primary); color: var(--c-on-primary); font-weight: 600;
  box-shadow: none;
}

/* 门店切换器（S7，2026-10-27）：**多店模式才存在**，由 nav.js 的 renderStoreSwitch()
   动态插在 [data-domains] 的父容器（顶栏左侧）。单店时一个节点都不创建 —— 顶栏
   「可见元素=2 / 可点=1」的断言（tests/nav_breakpoints）不能被它影响。
   配色只用变量：theme-dark.css 换一套变量就自动适配深色，不用重复组件规则。 */
.store-switch { position: relative; display: inline-flex; flex: 0 0 auto; min-width: 0; }
.store-switch-btn {
  display: inline-flex; align-items: center; gap: 6px; max-width: 220px; min-width: 0;
  padding: 4px 11px; font: inherit; font-size: 13px; line-height: 18px;
  color: var(--c-text); background: var(--c-sunken);
  border: 1px solid var(--c-border); border-radius: 8px; cursor: pointer;
  white-space: nowrap; font-family: inherit;
}
.store-switch-btn:hover { color: var(--c-primary); border-color: var(--c-primary); }
.store-switch-btn:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
.store-switch-btn .store-name { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.store-switch-btn .store-caret { color: var(--c-muted); font-size: 12px; line-height: 1; }
.store-switch.open .store-switch-btn { color: var(--c-primary); border-color: var(--c-primary); }
.store-menu {
  position: absolute; left: 0; top: calc(100% + 6px); z-index: 70;
  min-width: 180px; max-width: 260px; padding: 4px; display: none;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
}
.store-switch.open .store-menu { display: block; }
.store-menu > button {
  display: block; width: 100%; text-align: left; padding: 8px 10px;
  font: inherit; font-size: 13px; color: var(--c-text);
  background: none; border: 0; border-radius: 6px; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.store-menu > button:hover { background: var(--c-sunken); }
.store-menu > .store-menu-cur {
  display: block; padding: 8px 10px; font-size: 13px; font-weight: 600;
  color: var(--c-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.store-switch-err {
  display: none; padding: 6px 10px; font-size: 12px; line-height: 1.6;
  color: var(--c-danger); white-space: normal; word-break: break-all;
}
.store-switch-err.show { display: block; }
/* 1024/768 这类窄视口：店名先截断，别把顶栏挤到换行（页面不允许横向溢出） */
@media (max-width: 1180px) { .store-switch-btn { max-width: 132px; } }

/* 主区：宽度跟着屏幕走，只在超宽屏留一点边距。
   历史是固定 max-width（1400/1560/1840/2100px），4K 上表格只占屏幕 52%，
   右边整片空白 —— 实测 3840px 时 table=2100 / viewport=3840。 */
main { padding: 20px 24px 64px; max-width: 100%; margin: 0 auto; }
.panel { display: none; }
.panel.active { display: block; }

.subtabs-wrap { display: block; margin-bottom: 16px; }
.subtabs { display: inline-flex; gap: 2px; background: transparent; padding: 4px;
  border: 1px solid var(--c-border); border-radius: var(--radius-sm); overflow: hidden; }
.subtabs button {
  border: none; background: none; cursor: pointer; padding: 0 13px; height: 26px;
  border-radius: 4px; font-size: 12.5px; color: var(--c-muted); font-family: inherit;
  letter-spacing: .03em;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.subtabs button:hover { color: var(--c-text); background: var(--c-sunken); }
.subtabs button.active { background: var(--c-sunken); color: var(--c-primary); font-weight: 600; box-shadow: none; }

.card {
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius); padding: 18px 20px 20px; margin-bottom: 16px;
}
/* 卡片标题下压一道发丝罫：帳票里「見出し」与内容本来就分开，
   也用这条线替掉原来靠"蓝色标题"区分层级的做法（颜色留给状态）。 */
.card > h2 {
  font-size: 13.5px; font-weight: 600; color: var(--c-text); letter-spacing: .04em;
  margin: -2px 0 16px; padding-bottom: 10px; border-bottom: 1px solid var(--c-line);
}
/* 有些卡片的标题写在 .toolbar 里（预约页「营业时间 / 员工排班」、员工页「员工档案」）。
   那种卡片的标题拿不到上面那条罫线，于是同一套界面里出现"有罫线的卡"和"没罫线的卡"。
   这里给「首行是含标题的工具条」的卡片补同一条罫线，观感与 .card > h2 一致。 */
.card > .toolbar:has(h2) { padding-bottom: 12px; border-bottom: 1px solid var(--c-line); margin-bottom: 14px; }
.card > .toolbar:has(h2) > h2 { margin: 0; padding: 0; border: 0; }

.toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; }
.toolbar .spacer { flex: 1; }

input, select, textarea {
  font-family: inherit; font-size: 13px; color: var(--c-text);
  border: 1px solid var(--c-border); border-radius: var(--radius-sm);
  padding: 0 10px; background: var(--c-surface); outline: none;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
input, select { height: 32px; }
textarea { padding: 7px 10px; min-height: 64px; line-height: 1.6; }
input:hover, select:hover, textarea:hover { border-color: #CFCDC5; }
input:focus, select:focus, textarea:focus {
  border-color: var(--c-primary); box-shadow: 0 0 0 3px var(--c-focus);
}
input::placeholder, textarea::placeholder { color: var(--c-muted); opacity: .82; }

/* 键盘焦点（2026-10-27）：鼠标点击不触发，只有键盘 Tab 才画。
   之前全站只有 stock/members 的表格行有 :focus-visible，按钮/输入框/链接一个都没有 ——
   用键盘操作时完全看不出焦点在哪。outline 不占布局空间，不会引发任何尺寸回归。 */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--c-primary);
  outline-offset: 2px;
}
/* 文本框/下拉/多行文本的焦点：边框转墨 + 3px 柔光环已经足够醒目，
   再叠一圈 2px outline 会变成"双线框"（截图里像两个框套在一起）。
   按钮与链接仍保留 outline —— 它们没有别的焦点表达。 */
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):focus-visible,
select:focus-visible, textarea:focus-visible { outline: none; }
input[type="date"] { min-width: 140px; }

/* 按钮：与输入框同高（32px）才排得齐 —— 原来 7px 内边距把按钮撑到 37px，
   和输入框差 5px，工具条一排控件上下错位，是"没做过"最明显的痕迹。 */
button.btn {
  font-family: inherit; font-size: 13px; cursor: pointer;
  border: 1px solid var(--c-border); background: var(--c-surface); color: var(--c-text);
  border-radius: var(--radius-sm); height: 32px; padding: 0 13px;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  letter-spacing: .02em; white-space: nowrap;
  transition: background .15s var(--ease), border-color .15s var(--ease), color .15s var(--ease);
}
button.btn:hover { border-color: #BDBAB0; color: var(--c-text); background: #FBFAF7; }
button.btn:active { background: var(--c-sunken); }
button.btn.primary { background: var(--c-primary); border-color: var(--c-primary); color: var(--c-on-primary); font-weight: 500; }
button.btn.primary:hover { background: var(--c-primary-dark); border-color: var(--c-primary-dark); color: var(--c-on-primary); }
button.btn.danger { color: var(--c-danger); border-color: #E0C6BF; }
button.btn.danger:hover { background: var(--c-danger-soft); border-color: #D3AFA6; color: var(--c-danger-soft-text); }
button.btn.mini { height: 26px; padding: 0 8px; font-size: 12px; letter-spacing: .01em; }
/* 次要按钮：视觉上退后一步（同一工具条里若有主操作按钮时用） */
button.btn.quiet { color: var(--c-muted); }
button.btn.quiet:hover { color: var(--c-text); }
/* 主操作按钮：开单页最高频的「添加明细行」。用松葉绿而不是主藍，
   好和藍色「保存销售单」区分开——同屏两个主色= 不知道该点哪个，等于没突出。 */
button.btn.accent {
  background: var(--c-accent); border-color: var(--c-accent); color: var(--c-on-accent);
  font-size: 13.5px; font-weight: 600; height: 32px; padding: 0 16px;
}
button.btn.accent:hover { background: var(--c-accent-dark); border-color: var(--c-accent-dark); color: var(--c-on-accent); }

/* ---- 表格：只画横罫，不画竖格（2026-10-27）----
   竖格线是 Excel 的语法；网页上的密集数据靠行距 + 横罫就够分栏，
   去掉竖线后同一屏能读到的列反而更多。开单明细表（输入型）例外，见 #ord-items-table。 */
table.grid { width: 100%; border-collapse: collapse; font-size: 12.5px; }
table.grid th, table.grid td {
  border: 0; border-bottom: 1px solid var(--c-border);
  padding: 7px 10px; text-align: left; vertical-align: middle;
}
/* 表头吸顶（2026-10-27）：数据行高 64px 后一屏能放 12 行，翻到第 10 行就看不到列名，
   吸顶把表头钉在滚动容器顶部。
   ⚠️ 之所以以前注入 position:sticky 无效，是因为 .table-wrap 只有 overflow-x，
   它自己成了滚动上下文却不滚动（见下方 .table-wrap 的 max-height）。
   border-collapse: collapse 下 sticky 单元格的 border 会跟随滚动消失，用 inset 阴影补底边。 */
table.grid thead th {
  background: var(--c-thead-bg); font-weight: 600; color: var(--c-thead-text); white-space: nowrap;
  position: sticky; top: 0; z-index: 3;
  font-size: 12px; letter-spacing: .05em;
  /* 見出し罫：表头下这条带靛蓝的罫线是全站签名的起点 */
  box-shadow: inset 0 -1px 0 var(--c-rule);
}
table.grid tbody tr:hover { background: var(--c-row-hover); }
table.grid tbody tr:last-child td { border-bottom: 0; }
table.grid td.num, table.grid th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.grid td.act { white-space: nowrap; }

/* 应收台账（弹窗内的宽表，2026-10-29）：加「对账单」按钮后 max-content 涨到 1187，
   在 1440 宽弹窗里溢出 32px（modal_overflow 回归会红）。
   弹窗内的表默认 min-width:max-content，列宽形同建议；这里改成 fixed + 100%，
   让 th 上写的宽度真正生效，长文本用省略号（title 里仍有全文）。 */
#rv-box table.grid {
  table-layout: fixed; width: 100%; min-width: 0 !important;
}
#rv-box table.grid th,
#rv-box table.grid td {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#rv-box table.grid td.act { padding-left: 6px; padding-right: 6px; }
#rv-box table.grid td.act .btn { margin-left: 2px; }

/* 看板 KPI 可点（O1，2026-10-29）：点数字跳销售明细并带当前区间。
   用 cursor + hover 提升可点性，键盘用 Enter/Space（role="button" tabindex="0"）。 */
.metric.kpi-link { cursor: pointer; }
.metric.kpi-link:hover { border-color: var(--c-primary); }
.metric.kpi-link:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }
.metric.kpi-link:hover .v { color: var(--c-primary); }

/* 手机专用元素（≤640 才显示）：批量「全选本页」按钮、明细「更多参数」按钮。
   桌面端表头复选框/整行展开已经够用，多出来只是噪音。 */
.mobile-only { display: none !important; }
.it-more { display: none; }

/* 行操作「⋯」菜单（2026-10-29，B7 D4/S2）：常用 3 个留行内，其余（复制/作废/删除/变更历史、
   打码版/利润版/版本）收进 <details>。用原生 details 而不是自己写 open/close：
   点外面自动收起、键盘可达、不额外占 app.js 的事件委托。 */
/* 可排序表头（2026-10-29，B8 X1）：点击按该列**服务端**排序，再点切方向。
   箭头用 aria-sort 驱动，屏幕阅读器也能读到「升序/降序」，不是纯视觉装饰。 */
table.grid th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
table.grid th.sortable:hover { color: var(--c-primary); }
table.grid th.sortable:focus-visible { outline: 2px solid var(--c-primary); outline-offset: -2px; }
table.grid th.sortable[aria-sort] { color: var(--c-primary); }
table.grid th.sortable[aria-sort="ascending"]::after { content: " ▲"; font-size: 9px; }
table.grid th.sortable[aria-sort="descending"]::after { content: " ▼"; font-size: 9px; }

details.more { display: inline-block; position: relative; vertical-align: middle; }
details.more > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 30px; letter-spacing: 1px;
}
details.more > summary::-webkit-details-marker { display: none; }
details.more[open] > summary { background: var(--c-row-hover); border-color: var(--c-border-strong, #b8c0cc); }
details.more .more-menu {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 30;
  display: flex; flex-direction: column; gap: 4px; padding: 6px;
  background: var(--c-card, #fff); border: 1px solid var(--c-border, #dfe3e8);
  border-radius: 8px; box-shadow: 0 8px 24px rgba(15, 23, 42, .16);
  min-width: 120px;
}
details.more .more-menu .btn { width: 100%; justify-content: flex-start; }
/* 表格容器是 overflow:auto —— 菜单会被裁掉，所以 .act 单元格必须允许溢出
   （已有规则 table.grid tbody td.act { overflow: visible }）；给弹窗/窄屏兜底：
   菜单贴近右缘时不允许撑破视口。 */
details.more .more-menu { max-width: 220px; }

.table-wrap { overflow-x: auto; }
.empty { text-align: center; color: var(--c-muted); padding: 32px 0; font-size: 12.5px; letter-spacing: .04em; }

/* 指标带（2026-10-27 改造）：原来每项各挂一张 3px 彩条卡片，是"图标+数字"的通用模板；
   现在收成一条由罫线分格的 KPI 带 —— 一个容器、一条上罫、一条下罫，四段并排，
   读起来是一张表的表头，而不是四张卡。彩色只在有语义时落在数字上。 */
.metrics {
  display: flex; flex-wrap: wrap; gap: 0; margin-bottom: 16px;
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius); overflow: hidden;
}
.metric {
  /* flex-basis 0 + 等分：不按内容宽度分配，否则「累计销售金额」这种长数字会把
     自己那一段撑得比别人宽一倍，整条带子的节奏就散了。 */
  flex: 1 1 0; min-width: 140px; background: transparent; border: 0;
  border-left: 1px solid var(--c-line); border-radius: 0; padding: 13px 18px 14px;
}
.metric:first-child { border-left: 0; }
.metric .k {
  font-size: 12px; color: var(--c-muted); margin-bottom: 6px;
  letter-spacing: .05em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.metric .v {
  font-size: 21px; font-weight: 600; color: var(--c-text);
  font-variant-numeric: tabular-nums; line-height: 1.2; letter-spacing: .01em;
}
.metric.green .v { color: var(--c-accent); }

.modal-mask {
  position: fixed; inset: 0; background: rgba(24, 27, 31, .38);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 50px 16px; overflow: auto; z-index: 50;
}
.modal-mask.hidden { display: none; }
.modal {
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: 10px; width: 100%; max-width: 620px;
  padding: 22px 24px; box-shadow: var(--shadow-modal);
  /* 🔴 弹窗里的宽表（应收台账 / 版本历史）列宽合计 ~1090px，
     原来 .wide 只有 1000px、扣掉 22×2 内边距只剩 956px → 表格硬溢出 113px，
     最右侧「回款」按钮被白底切一半（2026-10-06 用户报"超出背景层"）。
     .wide 现在按「列宽合计 + 内边距 + 余量」给足，并且大屏继续放宽。 */
  min-width: 0;
}
.modal.wide { max-width: 1180px; }
/* 弹窗内的表格统一给一层横向滚动兜底：列宽是硬性下限，压不动时
   应该出现滚动条，而不是把内容顶出白底。（用 :has 避免影响普通弹窗） */
.modal:has(table) table.grid { min-width: max-content; }
.modal .table-wrap { margin-top: 10px; }
@media (min-width: 1700px) { .modal.wide { max-width: 1400px; } }
@media (min-width: 2200px) { .modal.wide { max-width: 1700px; } }
.modal h3 {
  font-size: 15px; font-weight: 600; color: var(--c-text); letter-spacing: .03em;
  margin: 0 0 16px; padding-bottom: 12px; border-bottom: 1px solid var(--c-line);
}
.form-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.form-item { flex: 1 1 200px; display: flex; flex-direction: column; gap: 4px; }
.form-item label { font-size: 12px; color: var(--c-muted); }

/* 修改单据时的提示条 */
.edit-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 12.5px; line-height: 1.6;
  background: var(--c-hint-amber-bg); border: 1px solid var(--c-hint-amber-line);
  border-radius: var(--radius-sm); padding: 9px 12px; margin-bottom: 12px;
  color: var(--c-hint-amber-text);
}
.edit-banner b { color: var(--c-hint-amber-text); font-weight: 600; }

/* 收货信息智能识别框（新建/编辑客户表单顶部） */
.smart-fill {
  border: 1px dashed var(--c-border);
  border-radius: var(--radius);
  background: var(--c-sunken);
  padding: 12px;
  margin-bottom: 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.smart-fill > label { font-size: 12px; color: var(--c-muted); }
.smart-fill textarea { width: 100%; resize: vertical; line-height: 1.5; }
.smart-fill-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.smart-fill-foot span { font-size: 12px; color: var(--c-muted); }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }

.order-foot {
  display: flex; gap: 26px; flex-wrap: wrap; align-items: baseline; justify-content: flex-end;
  margin-top: 18px; padding-top: 12px; font-size: 12.5px; color: var(--c-muted);
  /* 二重罫線：帳票里「合計」上方那道双线。1px 双线比任何色块都更像账本，
     也比加粗一行字更能把"这一段是合计"说清楚。 */
  border-top: 3px double var(--c-rule);
}
.order-foot b { color: var(--c-text); font-weight: 600; font-variant-numeric: tabular-nums; }
/* 金额合计：用主色而不是朱 —— 朱在这套系统里只表示欠款/退货/失败，
   把全单金额标成朱色，等于告诉老板"这单出问题了"。 */
.order-foot .big { font-size: 19px; font-weight: 600; color: var(--c-primary); font-variant-numeric: tabular-nums; letter-spacing: .01em; }
.order-foot .words { color: var(--c-muted); font-size: 12px; }
/* 利润/利润率：正=绿、负=红（与单据页 setStatus 的语义色一致） */
.order-foot b.num-pos { color: var(--c-ok-text); }
.order-foot b.num-neg { color: var(--c-danger); }
/* 应收净额（= 金额合计 − 整单优惠）：这是客户真正要付的钱，给它主色 + 稍重的字重，
   与「金额合计(毛)」那个大号数形成"毛 / 净"的层级；0 优惠时两数相同、视觉不动。 */
.order-foot b.net { color: var(--c-primary); font-weight: 700; }

/* ---- 整单优惠（B3，2026-10-28）三向联动输入区 ----
   放在明细表与合计条之间：优惠是"整单级"的调整，先于合计条读；
   样式沿用卡片的发丝罫线（不做色块），一排放下 率 / 金额 / 只读净额 + 口径小字。 */
.ord-discount {
  display: flex; flex-wrap: wrap; gap: 6px 20px; align-items: baseline;
  margin-top: 14px; padding: 9px 12px;
  font-size: 12.5px; color: var(--c-muted);
  border: 1px solid var(--c-border); border-radius: var(--radius-sm);
  background: var(--c-thead-bg);
}
.ord-discount .od-title {
  font-size: 13px; font-weight: 600; color: var(--c-text); letter-spacing: .02em;
}
.ord-discount label { display: inline-flex; align-items: baseline; gap: 5px; white-space: nowrap; }
.ord-discount input {
  width: 88px; padding: 4px 7px; font-size: 13px; font-variant-numeric: tabular-nums;
  border: 1px solid var(--c-border); border-radius: var(--radius-sm);
  background: var(--c-surface); color: var(--c-text);
}
.ord-discount input:focus { outline: 2px solid var(--c-focus); outline-offset: 1px; border-color: var(--c-primary); }
/* 「优惠后金额」只读：视觉上不能像输入框（用户会试着在只读格里改，以为坏了） */
.ord-discount .od-readonly { white-space: nowrap; }
.ord-discount .od-readonly b {
  color: var(--c-primary); font-weight: 700; font-variant-numeric: tabular-nums;
  font-size: 14px;
}
/* 优惠金额超过行金额合计：整块转朱（保存会被拦），提示文字说明为什么 */
.ord-discount.err { border-color: var(--c-danger); background: var(--c-danger-soft); }
.ord-discount.err #ord-disc-hint { color: var(--c-danger-soft-text); }

.toast {
  position: fixed; right: 24px; bottom: 24px; z-index: 99;
  background: #1E2226; color: #F4F4F1; font-size: 12.5px; letter-spacing: .02em;
  padding: 11px 16px; border-radius: var(--radius-sm); opacity: 0;
  transition: opacity .18s var(--ease), transform .18s var(--ease);
  transform: translateY(4px);
  box-shadow: var(--shadow-pop); max-width: 420px; line-height: 1.5;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast.err { background: var(--c-danger); }

/* ⚠️ 选择器不能只写 span.lbl：勾选框行用的是 <label class="lbl">，
    span 前缀会让它掉回 body 的 14px 墨黑（同一页两种"勾选框旁的说明文字"）。 */
.lbl { font-size: 13px; color: var(--c-muted); white-space: nowrap; }
p.note { font-size: 12px; color: var(--c-muted); line-height: 1.6; margin-top: 10px; }
td.cell-void { color: var(--c-danger); }
tr.row-void td { background: #FBF3F1; }
span.badge {
  display: inline-block; font-size: 12px; line-height: 1.6;
  padding: 0 6px; margin: 0 4px; border-radius: 4px;
  background: var(--c-combo-on); color: var(--c-primary); white-space: nowrap;
}
span.selinfo { font-size: 13px; color: var(--c-primary); white-space: nowrap; }
/* 全站复选框统一 16px（2026-10-07 巡检）：此前工具条是浏览器默认 13px、
   表格内 15px，同一屏三种大小。这条是兜底，下面几条更特异的规则只补充光标/对齐，
   尺寸一律跟这里走 —— 改尺寸只改这一处。 */
input[type="checkbox"], input[type="radio"] {
  width: 16px; height: 16px; cursor: pointer; vertical-align: middle;
  accent-color: var(--c-primary);       /* 勾选态用藍，而不是浏览器默认蓝 */
  margin: 0; flex: 0 0 auto;
}

/* 下拉框（2026-10-27 一致性）：原生箭头在 Windows / macOS 长得完全不同，
   和这套罫线控制不住；统一换成自绘 chevron（内联 SVG，不引图标库）。 */
select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 26px;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 12px 12px;
  cursor: pointer;
}
select:hover { border-color: #CFCDC5; }
select:disabled { color: var(--c-muted); cursor: not-allowed; opacity: .7; }

/* 日期框的日历小图标也压到次要色，别抢主色 */
input[type="date"]::-webkit-calendar-picker-indicator { opacity: .55; cursor: pointer; }
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: .9; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { opacity: .35; }
button.btn.danger:disabled { opacity: .45; cursor: not-allowed; }

/* 窄屏 / 高缩放比兜底：flex 子项默认 min-width:auto 会把控件顶出容器 */
.form-item, .form-row > * { min-width: 0; }
input, select, textarea { min-width: 0; }
/* 表格滚动容器（2026-10-27）：加了 max-height 才让表头 sticky 真正生效 ——
   没有高度上限时容器自身不滚动，表头跟着页面滚走（实测 sticky 注入后滚 600px，
   表头 top 从 361 变 -239）。上限 = 视口高 − 顶栏/导航/页脚分页的占用；
   短表内容不足 max-height，不会凭空出现滚动条。单据纸四页不用 .table-wrap，不受影响。 */
.table-wrap {
  overflow: auto; -webkit-overflow-scrolling: touch;
  max-height: calc(100vh - 250px);
}
body { overflow-x: hidden; }

/* 中等宽度（2026-10-27）：笔记本 1280 / 分屏 1024 这一档原先没有任何断点 ——
   13 项导航（6 panel + 7 独立页）在这个宽度会挤压、文字折行把导航条顶高。
   这里改为「单行 + 横向滚动」，不换行、不改高度：这是成本最低且可回退的做法
   （改成「更多 ▾」收纳要额外写开合逻辑，属另一次改动）。 */
@media (max-width: 1180px) {
  nav.tabs { overflow-x: auto; overflow-y: hidden; flex-wrap: nowrap; scrollbar-width: thin; }
  nav.tabs > * { flex: 0 0 auto; }
  nav.tabs::-webkit-scrollbar { height: 4px; }
  nav.tabs::-webkit-scrollbar-thumb { background: var(--c-border); border-radius: 2px; }
}

@media (max-width: 720px) {
  main { padding: 14px; }
  header.topbar, nav.tabs { padding-left: 14px; padding-right: 14px; }
  .metric { flex: 1 1 45%; }
}

/* ------------------------------------------------ 可搜索下拉（combobox） */
#combo-list {
  position: absolute; z-index: 90; display: none;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-pop);
  max-height: 320px; overflow-y: auto; font-size: 13px;
}
#combo-list.show { display: block; }
#combo-list .cl-row {
  padding: 7px 10px; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-bottom: 1px solid #f5f7f8;
}
#combo-list .cl-row:last-child { border-bottom: none; }
#combo-list .cl-row:hover { background: var(--c-combo-hover); }
#combo-list .cl-row.on { background: var(--c-combo-on); }
#combo-list .cl-row b { color: var(--c-primary); font-weight: 600; }
#combo-list .cl-sub { color: var(--c-muted); font-size: 12px; margin-left: 10px; }
/* 下拉项缩略图（商品下拉）：26px 方图贴行首，文字自然右移；无图商品不输出，不占位 */
#combo-list .cl-thumb {
  width: 26px; height: 26px; object-fit: cover; border-radius: 3px;
  margin-right: 7px; vertical-align: middle; background: #f5f7f8;
}
/* 定制款标记：红字，和灰色的规格/价格区分开 */
#combo-list .cl-custom { color: var(--c-danger); font-size: 12px; margin-left: 10px; }
#combo-list .cl-empty { color: var(--c-muted); }
#combo-list .cl-note { cursor: default; color: var(--c-muted); }
#combo-list .cl-note:hover { background: transparent; }
/* 分页进度条：贴在浮层底部，滚动时始终可见，用户知道"还能往下拉" */
#combo-list .cl-foot {
  position: sticky; bottom: 0; text-align: center;
  padding: 6px 10px; font-size: 12px; color: var(--c-muted);
  background: var(--c-sunken); border-top: 1px solid var(--c-border);
}
#combo-list .cl-off {
  color: #fff; background: var(--c-muted); font-size: 12px;
  padding: 0 5px; border-radius: 3px; margin-left: 8px;
}

input.combo {
  width: 100%; height: 32px;
  padding: 0 10px;
  border: 1px solid var(--c-border);
  border-radius: var(--radius-sm);
  font-family: inherit; font-size: 13px;
}
input.combo:focus { outline: none; border-color: var(--c-primary); box-shadow: 0 0 0 3px var(--c-focus); }
td input.combo { min-width: 170px; }

/* ============================================================ 屏幕尺寸适配
   目标：1080P(1920×1080)、2K(2560×1440)、4K 下都不出现横向滚动条把控件顶出去，
   也不把内容挤在屏幕正中的一条窄带里。 */

/* ① 主区宽度：**跟着屏幕走**（`main` 已改为 max-width:100%）。
   这里只做两件补充：
   - 超宽屏留边距：100% 全铺到边会让表格贴住屏幕两侧、观感差，留 2~3% 呼吸位；
   - 大屏同步放大字号与内边距：屏幕越大、像素密度越低，13px 的字在 4K 上偏小，
     表格被拉宽后行高不变会显得"空而小"。这两条不配套就会从"太窄"变成"太空"。 */
@media (min-width: 1700px) {
  main { max-width: 97%; }
  body { font-size: 14px; }
  table.grid th, table.grid td { padding: 9px 12px; }
  .metric { padding: 14px 18px; }
  .metric .v { font-size: 22px; }
}
@media (min-width: 2200px) {
  main { max-width: 96%; }
  body { font-size: 15px; }
  table.grid { font-size: 14px; }
  table.grid th, table.grid td { padding: 11px 14px; }
  input, select, textarea { font-size: 14px; }
  nav.tabs button, nav.tabs a.tab-link { font-size: 15px; padding: 13px 22px; }
  .metric { padding: 16px 22px; }
  .metric .v { font-size: 26px; }
  .metric .k { font-size: 13px; }
  /* 开单明细表在大屏上跟着放松一点，别把输入框挤成细条。
     ⚠️ td 与 input 的 padding 是**叠加**的：两边一起放大会让输入框高度失控
     （4K 档实测单行飙到 40px+，一个空表页看着全是空白）。所以这里只放大
     input 的内边距来增大可点区域，td 保持紧凑。 */
  #ord-items-table th, #ord-items-table td { padding: 6px 8px; }
  #ord-items-table input { padding: 7px 9px; }
}
@media (min-width: 3200px) {
  main { max-width: 95%; }
  body { font-size: 16px; }
  table.grid { font-size: 15px; }
  table.grid th, table.grid td { padding: 13px 16px; }
  input, select, textarea { font-size: 15px; }
  nav.tabs button, nav.tabs a.tab-link { font-size: 16px; padding: 15px 26px; }
  .metric { padding: 20px 28px; }
  .metric .v { font-size: 30px; }
  .metric .k { font-size: 14px; }
  #ord-items-table th, #ord-items-table td { padding: 8px 10px; }
  #ord-items-table input { padding: 9px 12px; }
}

/* ② 开单明细表（12 列）
   之前列宽合计 1447px，超过 1080P 下可用宽度，导致最右侧的"删除行"按钮
   被推到横向滚动条外面 —— 用户会以为没有删除功能。这里压缩内边距与列宽，
   并把"删"列固定在最右（即使真的需要横向滚动，它也一直可见）。
   2026-10-18 新增独立「图片」列（48px）：缩略图原来塞在商品格里，
   而商品输入框是 width:100%，两者同格时图片被挤到上一行（上图下字、行变高）。 */
#ord-items-table { table-layout: fixed; min-width: 1128px; }
#ord-items-table th, #ord-items-table td {
  padding: 5px 6px; border-right: 1px solid var(--c-line);
}
#ord-items-table th:last-child, #ord-items-table td:last-child { border-right: 0; }
#ord-items-table th.col-img, #ord-items-table td.it-img { text-align: center; padding: 4px 2px; }
/* 表头与下方控件同向：退货/赠品列下面是居中勾选框，表头却右对齐（.num），
   # 列下面是居中的行号、表头却左对齐 —— 三种方向在一行里打架。统一居中。 */
#ord-items-table thead th.col-no,
#ord-items-table tbody td.ln,
#ord-items-table thead th.num:nth-of-type(7),
#ord-items-table thead th.num:nth-of-type(8) { text-align: center; }
#ord-items-table thead th.act { text-align: center; }
#ord-items-table tbody td.it-return-cell,
#ord-items-table tbody td.it-gift-cell { text-align: center; }
#ord-items-table td.it-img img.it-row-thumb {
  display: block; margin: 0 auto; width: 28px; height: 28px;
  object-fit: cover; border-radius: 4px; border: 1px solid var(--c-border);
}
/* 数字输入框右对齐（2026-10-27 一致性）：表头「数量/单价/金额」都是右对齐，
   输入框却默认左对齐 —— 同一列里数字和表头两个方向，扫码/核对时最费眼。
   金额与数量一律右对齐 + 等宽数字，和只读的金额单元格对齐到同一条右边线。 */
input[type="number"], input.num, .it-qty, .it-price, .it-pqty, .it-pprice {
  text-align: right; font-variant-numeric: tabular-nums;
}

/* 表内输入框（2026-10-27 统一）：与全站输入框同一内距 10px。
   表格是 table-layout:fixed，内距不改变列宽，只影响可用文字宽度。 */
#ord-items-table input { padding: 0 10px; width: 100%; min-width: 0; }
#ord-items-table .it-product { min-width: 0; padding: 5px 7px; }
#ord-items-table .it-unit, #ord-items-table .it-amount, #ord-items-table .it-costamt {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 退货行视觉提示：数量/金额/成本金额变红，行号后加「退」字 */
#ord-items-table tbody tr.return-row .it-qty,
#ord-items-table tbody tr.return-row .it-amount,
#ord-items-table tbody tr.return-row .it-costamt {
  color: var(--c-danger);
  font-weight: 600;
}
#ord-items-table tbody tr.return-row .ln::after {
  content: '退';
  margin-left: 4px;
  font-size: 12px;
  color: var(--c-danger);
  border: 1px solid var(--c-danger);
  border-radius: 4px;
  padding: 0 3px;
  vertical-align: middle;
}

#ord-items-table td.act, #ord-items-table th.act {
  position: sticky; right: 0;
  background: var(--c-surface);
  box-shadow: -1px 0 0 var(--c-border);
  text-align: center; z-index: 2;
}
#ord-items-table tbody tr:hover td.act { background: var(--c-row-hover); }
/* 数字输入框去掉上下小箭头，省出来的宽度留给内容 */
#ord-items-table input[type="number"]::-webkit-inner-spin-button,
#ord-items-table input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none; margin: 0;
}
#ord-items-table input[type="number"] { -moz-appearance: textfield; }

/* ③ 单据列表/销售明细这类宽表：首列（单号 / 复选框）固定，横向滚动时仍能定位 */
#ol-body tr td:first-child, #it-body tr td:first-child { left: 0; }

/* ④ 表格与控件在大屏上放松一点。
   注：字号/内边距的分档已并入上方「屏幕尺寸适配 ①」统一管理，
   这里只留容器/工具栏间距，别处再写一份同样的 table.grid 规则会互相覆盖。 */
@media (min-width: 1700px) {
  .card { padding: 20px; }
  .toolbar { gap: 12px; }
}

/* ⑤ 销售明细台账有 18 列，1080P 下差几十像素就要出现横向滚动条 —— 稍微收紧即可放下。
   屏幕够宽时（≥1700）解除压缩：这里的 12px 是为「塞进 1080P」服务的，
   在 4K 上只会让这一页比别的页小一号。上面的断点管通用字号，
   但本页的选择器更特异，必须在这里显式覆盖回去。 */
#panel-items table.grid { font-size: 12px; }
#panel-items table.grid th,
#panel-items table.grid td { padding: 6px 7px; }
#panel-items table.grid thead th { white-space: normal; }
@media (min-width: 1700px) {
  #panel-items table.grid { font-size: 14px; }
  #panel-items table.grid th,
  #panel-items table.grid td { padding: 10px 11px; }
}
@media (min-width: 2200px) {
  #panel-items table.grid { font-size: 15px; }
  #panel-items table.grid th,
  #panel-items table.grid td { padding: 12px 13px; }
}

/* ------------------------------------------- 销售明细：视图切换 + 按单据合并行 */
.seg {
  display: inline-flex; gap: 2px; background: transparent; padding: 4px;
  border: 1px solid var(--c-border); border-radius: var(--radius-sm); overflow: hidden;
}
.seg button {
  border: none; background: none; cursor: pointer; font-family: inherit; height: 26px;
  font-size: 12.5px; color: var(--c-muted); padding: 0 13px; border-radius: 4px;
  letter-spacing: .03em; transition: background .15s var(--ease), color .15s var(--ease);
}
.seg button:hover { color: var(--c-text); background: var(--c-sunken); }
.seg button.on { background: var(--c-sunken); color: var(--c-primary); font-weight: 600; box-shadow: none; }

/* 日期快捷档（2026-10-29，B8 X2）：紧跟在「截至」输入框后，比普通 seg 小一号，
   五处（看板/单据/明细/寄样/商品统计）共用，避免占掉一行工具条。 */
.seg.seg-date { margin-left: 4px; }
.seg.seg-date button { font-size: 12px; padding: 2px 7px; height: 26px; }

/* 按单据合并的行：稍高一点、加下分隔线，读起来是一张张单 */
#panel-items tr.grp td { padding: 9px 10px; border-bottom: 1px solid var(--c-border); }
#panel-items tr.grp:hover td { background: var(--c-row-hover); }
#panel-items td.dim { color: var(--c-muted); font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#panel-items td.nw { white-space: nowrap; }

/* 赠品 / 样品行：整行淡淡标出来，避免和普通收费行看混 */
#ord-items-table tr.is-gift { background: #FAF6EA; }
#ord-items-table tr.is-gift .it-price,
#ord-items-table tr.is-gift .it-pprice { background: var(--c-sunken); color: var(--c-muted); }
.it-gift-cell { text-align: center; }
.it-gift { cursor: pointer; width: 16px; height: 16px; }

/* 勾选框命中区（2026-10-27 巡检）：裸的 16px 勾选框不满足 WCAG 2.5.8「可点目标 ≥24×24」，
   平板上尤其点不准。⚠️ 给 input 加 padding **没用** —— Chrome 对 checkbox 这种
   replaced element 会忽略 padding，盒子仍是 16×16（实测过）。正确做法是用
   <label class="chk-hit"> 包住：label 的 padding 会真正扩大命中区，点标签文字也能勾。 */
.chk-hit {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 4px; padding: 4px; margin: -4px; cursor: pointer; white-space: nowrap;
  min-width: 24px; min-height: 24px;
  font-size: 13px; color: var(--c-muted);
}
/* 明细行的「删除」按钮：27×25 → 28 高 */
#ord-items-table .it-del { min-height: 28px; padding: 5px 10px; }

/* 退货勾选框（2026-10-04，位置在赠品左边）：勾上后数量/包裹数量转为负数 */
.it-return-cell { text-align: center; }
.it-return { cursor: pointer; width: 16px; height: 16px; }
#ord-items-table tr.is-return { background: #FAF0EE; }
#ord-items-table tr.is-return .it-qty,
#ord-items-table tr.is-return .it-pqty { color: var(--c-danger); font-weight: 700; }

/* 行内「清空商品」按钮：选错商品时一键重选，不用全选再删 */
/* 定位容器放在 div 上，不要放在 <td> 上 —— td 的 position:relative
   在部分浏览器里不生效，绝对定位的清空按钮会跳到别的参照系去（就是那个错位）。 */
.it-prod-wrap { position: relative; }
.it-prod-wrap .it-product { width: 100%; padding-right: 26px; box-sizing: border-box; }
/* 20px 而不是 16px：这是「选错商品一键重选」的高频按钮，
   16px 在触屏上基本点不准（实测巡检里它是全站最小的可点目标）。 */
.it-clear {
  position: absolute; right: 2px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; line-height: 22px; padding: 0;
  border: 1px solid var(--c-border); border-radius: 50%; background: var(--c-surface);
  color: var(--c-muted); font-size: 14px; cursor: pointer;
  transition: color .15s var(--ease), border-color .15s var(--ease), background .15s var(--ease);
}
.it-clear:hover { color: var(--c-danger); border-color: #D3AFA6; background: var(--c-danger-soft); }
.it-prod-wrap .it-product:placeholder-shown + .it-clear { opacity: .35; }

/* ---- 开单表头：等宽栅格 ----
   之前每个字段各写一个 flex 宽度（260 / 170 / 150 / 130 / 180px），六个框宽窄不一；
   而且 .form-item 是 flex-direction:column，「+新建客户」按钮被挤到输入框下面去了。
   改成栅格后列宽一致，按钮用 .inline-field 拉回同一行。 */
.ord-head-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 10px 12px;
  align-items: start;
}
.ord-head-form > .form-item { flex: none; }
.ord-head-form > .span2 { grid-column: span 2; }
.ord-head-form .inline-field { display: flex; gap: 6px; align-items: center; }
.ord-head-form .inline-field .combo { flex: 1 1 auto; min-width: 0; }
.ord-head-form .inline-field > .btn { flex: 0 0 auto; white-space: nowrap; }
/* 控件高度统一 —— 光有等宽不够，高度不齐照样看着乱 */
.ord-head-form input { width: 100%; height: 32px; box-sizing: border-box; }
/* select 之前没被上面那条覆盖到，宽度/高度和文本框对不齐 —— 一并统一 */
.ord-head-form select { width: 100%; height: 32px; box-sizing: border-box; }

/* 税点三格并进表头同一栅格后，宽度自然与「发货人」等格一致（同为 auto-fit 的 1fr），
   窄屏时也会跟着自动换行、填满上一行的空位 —— 不需要再单独规定列宽。 */
.ord-head-form .hint { font-weight: 400; }

/* ---- 列表行高统一 ----
   带「编辑/删除」按钮的行会被按钮撑高，和纯文字行高低不一。
   给 td 一个 height（表格里等同最小高度）即可把所有行拉齐。 */
table.grid tbody td, table.grid thead th {
  height: 36px; padding: 5px 10px; line-height: 1.35;
  /* 行高要齐，前提是内容不换行：长文本截断成省略号，完整内容放在 title 里（悬停可见）。
     只设 height 没用 —— 地址/名称折行照样会把行撑高。 */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
table.grid tbody td.act { overflow: visible; }
table.grid tbody td.act > * { vertical-align: middle; }

/* 分页条：列表改成真分页后，指标卡仍按全量显示（不随页码变） */
.pager {
  display: flex; align-items: center; gap: 10px;
  margin-top: 8px; font-size: 13px; color: var(--c-muted);
}
.pager .pg-info { margin-left: auto; }
.pager button {
  height: 28px; padding: 0 11px; border: 1px solid var(--c-border);
  border-radius: var(--radius-sm); background: var(--c-surface); cursor: pointer; font-size: 12.5px;
  color: var(--c-text); transition: border-color .15s var(--ease), color .15s var(--ease);
}
.pager button:hover:not(:disabled) { border-color: #BDBAB0; color: var(--c-primary); }
.pager button:disabled { opacity: .45; cursor: not-allowed; }

/* 客户定制：两个相似页面的用途提示条（蓝=按客户的核算，黄=全局预设） */
.card-hint {
  padding: 10px 13px; border-radius: var(--radius-sm); font-size: 12.5px; line-height: 1.65;
  margin: 4px 0 12px; border: 1px solid; letter-spacing: .01em;
}
.card-hint.blue {
  background: rgba(31, 62, 99, .045); border-color: rgba(31, 62, 99, .16);
  color: #2A3B52;
}
.card-hint.amber {
  background: var(--c-hint-amber-bg); border-color: var(--c-hint-amber-line);
  color: var(--c-hint-amber-text);
}
/* 折叠式凡例（开单页的计价口径）：摘要行字距收紧、箭头留给浏览器，不另造图标 */
.card-hint > summary { font-weight: 600; letter-spacing: .03em; }

/* 表单小标签 / 说明文字（2026-10-27）：原来是 10 处逐字重复的内联
   style="font-size:13px;color:（旧的灰）"（筛选标签、日期区间标签、单号预览…）。
   抽成一个类，改观感只改这里。 */
.muted-sm { font-size: 13px; color: var(--c-muted); }
.muted-xs { font-size: 12px; color: var(--c-muted); }
/* ⚠️ 原来是 #9AA0A8：#FFFFFF 上只有 2.9:1，而这几个字是**真的给人读的提示**
   （「先勾选左侧复选框」「点击任意一行可展开」），不是装饰。
   模糊感改用字号/字重表达，颜色仍走 --c-muted（6.15:1）。 */
.muted-faint { color: var(--c-muted); }

/* 开单页「行标记与计价口径」折叠说明（2026-10-27）：退货/赠品/含税/运费四组口径
   原先只在 title 上（触屏拿不到）。摘要常显、点开看全，不占首屏高度。 */
.line-hints { padding: 8px 12px; }
.line-hints > summary { cursor: pointer; font-weight: 600; outline-offset: 2px; }
.line-hints > ul { margin: 8px 0 2px; padding-left: 20px; line-height: 1.7; }
.line-hints > ul li + li { margin-top: 4px; }

/* ===================== 寄样管理（2026-09-22 新增） ===================== */
/* 状态角标 chip：所有状态字眼都带 title 悬停提示（硬性要求，见 sample.js SAMPLE_STATUS） */
.chip {
  display: inline-block; padding: 1px 7px; border-radius: 3px;
  font-size: 12px; font-weight: 600; line-height: 18px; white-space: nowrap;
  letter-spacing: .04em; border: 1px solid transparent;
}
.chip.st-gray  { background: var(--c-sunken); color: #4E5663; border-color: #DEDCD4; }
.chip.st-blue  { background: var(--c-hint-blue-bg); color: var(--c-hint-blue-text); border-color: var(--c-hint-blue-line); }
.chip.st-red   { background: var(--c-danger-soft); color: var(--c-danger-soft-text); border-color: #E6C9C2; }
.chip.st-amber { background: var(--c-hint-amber-bg); color: var(--c-hint-amber-text); border-color: var(--c-hint-amber-line); }
.chip.st-green { background: var(--c-ok-bg); color: var(--c-ok-text); border-color: #C7DCD0; }
.chip.st-dark  { background: var(--c-primary); color: var(--c-on-primary); border-color: var(--c-primary); }

/* 指标带补充色：语义只落在数字上（条带上不再挂彩条） */
.metric.red .v { color: var(--c-danger); }
.metric.amber .v { color: var(--c-hint-amber-text); }

/* 客户聚合行 / 详情 */
.ok-tag  { color: var(--c-accent); font-weight: 600; }
.err-tag { color: var(--c-danger); font-weight: 600; }

.samp-card {
  border: 1px solid var(--c-border); border-radius: 8px;
  margin: 12px 0; padding: 10px 14px; background: var(--c-surface);
}
.samp-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.samp-head b { font-size: 14px; color: var(--c-primary-dark); }
.samp-head .spacer, .toolbar .spacer { flex: 1; }
.samp-actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.samp-items { margin: 6px 0; }
.samp-fees { display: flex; gap: 18px; flex-wrap: wrap; font-size: 13px; margin: 6px 0; }
.samp-fees .big { font-size: 15px; color: var(--c-primary-dark); }
.samp-line { font-size: 13px; color: var(--c-text); margin: 4px 0; line-height: 1.6; }
.samp-link { color: var(--c-primary); text-decoration: underline; cursor: pointer; margin-right: 8px; }

/* 回访记录 */
.samp-fus { border-top: 1px dashed var(--c-border); margin-top: 8px; padding-top: 8px; font-size: 13px; }
.samp-fu { padding: 6px 0 2px; border-bottom: 1px dotted var(--c-border); }
.samp-fu-date { font-weight: 600; margin-right: 10px; }
.samp-fu-con { margin: 0 10px; }
.samp-fu-result { color: var(--c-muted); margin: 2px 0 4px 0; white-space: pre-wrap; }

/* 客户管理页的寄样角标列 */
.samp-badge { text-decoration: none; cursor: pointer; }

/* ---- 悬停浮层卡片 .hover-pop（2026-10-04 用户需求；2026-10-27 类名解耦）----
   ⚠️ 类名从 samp-hover* 改为 hover-pop*（2026-10-27）：这是**通用**的"鼠标悬停
   弹出小卡片"组件，不该以寄样（samp）命名 —— 别的列表要加同款浮层时可以直接复用，
   不必再复制一份样式。业务专属语义留在 markSampleHover 的数据结构里，不在 class 上。
   注意：tests/sample_hover.node.test.js 按 .hover-pop 断言，改名要同步该测试。

   当前用例：鼠标停在客户列表「寄样」列的角标上，浮出一张小卡片：最近几张寄样的
   产品 / 快递 / 备注 / 最新一条回访；点卡片进入该客户的寄样详情。
   实现要点见 sample.js 的 bindSampleHoverCard：
     · 用**一个全局复用**的浮层（不是每行各建一个），跟着鼠标定位；
     · 延迟 ~250ms 才显示（划过时不该闪一下），移出即隐藏；
     · 卡片自己也能承接鼠标，否则鼠标从角标移到卡片的空隙卡片就消失了。 */
.hover-pop {
  position: fixed; z-index: 1200; max-width: 380px; min-width: 280px;
  padding: 10px 12px; border-radius: 10px; cursor: pointer;
  background: var(--c-surface); border: 1px solid var(--c-border);
  box-shadow: var(--shadow-pop);
  font-size: 12px; line-height: 1.6; color: var(--c-text);
}
.hover-pop-title {
  display: flex; align-items: center; gap: 6px;
  padding-bottom: 6px; margin-bottom: 6px; border-bottom: 1px solid var(--c-line);
  font-size: 12px; color: var(--c-muted);
}
.hover-pop-title b { color: var(--c-text); font-size: 13px; }
.hover-pop-card {
  padding: 5px 0; border-bottom: 1px dashed var(--c-line);
}
.hover-pop-card:last-child { border-bottom: none; }
.hover-pop-card > .r1 {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 2px;
}
.hover-pop-card .r1 .no { font-weight: 600; }
.hover-pop-card .r2 { color: var(--c-muted); }
.hover-pop-card .r2 b { color: var(--c-text); font-weight: 500; }
.hover-pop-card .r3 {
  margin-top: 3px; padding: 3px 6px; border-radius: 4px;
  background: var(--c-hint-amber-bg); color: var(--c-hint-amber-text);
}
.hover-pop-card .r3 .fu-d { font-weight: 600; }
.hover-pop-more {
  margin-top: 6px; padding-top: 5px; border-top: 1px solid var(--c-line);
  text-align: center; color: var(--c-muted); font-size: 12px;
}
.hover-pop-empty { color: var(--c-muted); padding: 4px 0; }
#cust-body td.sample-badge-cell { font-size: 13px; }

/* 表单里的"勾选框 + 文字"：字号/颜色与页面其它勾选框（如「含作废」）保持一致，
   只额外给个 32px 行高与旁边的 input/select 对齐 —— 别自己发明尺寸，会显得又大又不搭。 */
.chk-inline {
  display: inline-flex; align-items: center; gap: 4px; height: 32px;
  font-size: 13px; color: var(--c-muted); font-weight: 400; cursor: pointer;
  letter-spacing: .01em;
  white-space: nowrap;          /* 「含税（需开票）」别在中间折行 */
}
/* ⚠️ .ord-head-form input 那条规则是给文本框/日期框撑满用的（width:100%; height:32px），
   勾选框会被一起撑成一个 32px 的蓝色大方块（用户截图反馈"勾选开关太大"）。
   这里单独复位成与页面其它勾选框一致（表格里是 15px）。 */
.ord-head-form input[type="checkbox"] { width: 16px; height: 16px; padding: 0; }

/* 商品主图缩略图：行内 36px，点击（编辑弹窗里）看大图 */
img.prod-thumb { width: 36px; height: 36px; object-fit: cover; border-radius: 4px;
  border: 1px solid var(--c-border); display: block; }
.prod-img-preview { max-width: 200px; max-height: 200px; border-radius: 6px;
  border: 1px solid var(--c-border); display: block; margin-bottom: 6px; }

/* 商品图片悬停预览：鼠标悬停缩略图时弹出的**网页内浮动卡片**
   （区别于 GLightbox 的全屏遮罩 —— 那个仍是点击触发）。
   单例常驻、hover 时换内容，避免每行都挂一个弹层。 */
#img-hover-pop {
  position: fixed; z-index: 9999; display: none; cursor: zoom-in;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: 8px;
  box-shadow: var(--shadow-pop); padding: 8px;
}
#img-hover-pop img { max-width: 320px; max-height: 320px; display: block; border-radius: 4px; }
#img-hover-pop .ihp-name { font-size: 12px; color: var(--c-muted); margin-top: 6px; text-align: center; }

/* 商品下拉里的缩略图（开单/客户定制/寄样共用） */
.combo-thumb { width: 28px; height: 28px; object-fit: cover; border-radius: 4px;
  border: 1px solid var(--c-border); margin-right: 8px; vertical-align: middle; }
.combo-thumb-none { display: inline-block; width: 28px; height: 28px;
  background: var(--c-sunken); border-radius: 4px; margin-right: 8px; vertical-align: middle; }
/* 表格行内小图（台账/统计/客户定制列表） */
img.row-thumb { width: 32px; height: 32px; object-fit: cover; border-radius: 4px;
  border: 1px solid var(--c-border); display: block; }
td.cell-thumb { width: 40px; }

/* ---------------------------------------------------------------- 全站表格统一尺寸（2026-09-28）
   --col-w/--row-h 见 :root。只作用于数据行 td：正文列宽 ≥120、行高统一 95。
   豁免：.nw（checkbox/图/单号/日期等窄列）与 .act（操作按钮列）保持紧凑；
   thead th 不加高度（表头随内容）。既有 th 内联 width 与 cols 数组作为建议值共存。 */
table.grid tbody td { min-width: var(--td-min); height: var(--row-h); }
table.grid tbody td.nw,
table.grid tbody td.act { min-width: 0; height: auto; }
/* 🔴 正文字列 max-width 封顶（2026-10-14 用户报「1080P 下操作区被挡住、缩小页面才看得见」）：
   table.grid 是 table-layout:auto，td 上的 overflow:hidden + ellipsis **拦不住 max-content
   撑宽列** —— 实测 1920 视口注入长文本：客户管理 14 列 +6460px、销售明细 15 列 +6219px，
   最右「操作」列被 .table-wrap 横向滚动区整体切走（删除/编辑点不到）。
   max-width 给列的 max-content 贡献封顶：Σmin-width ≤ 容器宽时 auto 布局必然把表格收进容器，
   长文本按既有口径截省略号（各列表渲染时已带 title，悬停见全值）。
   ⚠️ .act 必须豁免：按钮组 nowrap（sheets 操作列 7 个按钮 ≈ 400px），封顶会把按钮切一半；
      .nw 豁免：单号/日期列内容定长，不撑宽。 */
table.grid tbody td { max-width: 240px; }
table.grid tbody td.act,
table.grid tbody td.nw { max-width: none; }
/* 🔴 正文列 min-content 塌陷（接上一条，2026-10-14）：
   只封 max-width 还不够 —— white-space:nowrap 让 td 的 min-content = 全文本宽，
   auto 布局压缩阶段被 min-content 卡住（实测 1920 视口仍溢出 1356px，操作列照样被切）。
   中文没有空格，唯一能让 min-content 塌下来的是 word-break:break-all（逐字换行）。
   窄列（数字 .num / 单号 .nw / 操作 .act）恢复 nowrap：它们内容定长、min-content 本来就小。
   正文长文本改为多行显示 + 行高裁切（比单行省略号显示的字更多），全值悬停 title 见。 */
/* anywhere 与 break-all 的差别（2026-10-27）：两者都能让 min-content 塌陷（这是防溢出的关键），
   但 anywhere 会**先找合法断点**（连字符、空格、CJK 字间），实在放不下才硬断 ——
   于是 "020-88886666" 断在连字符后，而不是断成 "020-888866 / 66" 这种像错账的样子。 */
table.grid tbody td { white-space: normal; word-break: normal; overflow-wrap: anywhere; }
table.grid tbody td.nw,
table.grid tbody td.act,
table.grid tbody td.num { white-space: nowrap; }
/* 结构豁免补充：勾选列（>input.rowchk）与缩略图列（>img.row-thumb）按子元素自动豁免，
   覆盖未带 .nw class 的列表（商品管理/客户管理等）。 */
table.grid tbody td:has(> input.rowchk),
table.grid tbody td:has(> img.row-thumb) { min-width: 0; height: auto; }

/* 🔴 宽表分档底线（2026-10-07 巡检）：14~15 列的表用 120px 底线必然溢出。
   实测 1920 屏容器 1772px：客户定制 15 列 1849（+77）、销售明细 15 列 1828（+56）、
   客户管理 14 列 1809（+37）—— 最右侧「操作」列被切，删除按钮点不到。
   根因是 120 × 列数 这条硬地板：th 上的内联 width 只是**建议值**，
   输给 td 的 min-width，所以怎么调列宽都压不下去。
   这里只放宽这三张宽表的底线（其他 11 列以下的表继续保持 120，用户 09-28 定的口径不动），
   且只动 --td-min、不动 --col-w —— 后者还兼着缩略图 max-width 的职责。
   2026-10-27 改版：96 → 88。原因同上但更硬 —— 实测 1440（最常见的笔记本宽度）下
   14 列 × 96 = 1344 已经超过容器 1350 − 表格内留白，最右「操作」列被切。
   88 是 --col-w 120 的 73%，仍在"能读一列数字"的范围内，而 1440 下不再横向溢出。 */
#panel-customers table.grid tbody td { --td-min: 88px; }
#panel-items table.grid tbody td { --td-min: 76px; }
/* 客户定制 15 列 + 操作列 7 个按钮：1440 下 88 还差 80px，再收到 78 */
#panel-sheets table.grid tbody td { --td-min: 78px; }

/* 销售明细的操作列（单据 / 客户版）在 1440 下差 ~19px 才放得下：
   只收这一列的左右内边距，**不动按钮内距**（按钮内距全站只有一种）。 */
#panel-items table.grid tbody td.act { padding-left: 4px; padding-right: 4px; }

/* 含缩略图的三张表保持 95px 行高（2026-10-27 随 --row-h 降到 64 时配套）：
   缩略图高度 = calc(--row-h − 14)，64px 下会缩到 50px；这三张表以"看图认货"为主，
   宁可每屏少放几行。客户管理 / 销售单 / 寄样管理没有图，走 64px 默认值。
   注意 --row-h 要在 td 上覆盖（不能只在 table 上设），因为 height 读的是 td 自己的值；
   图在 td 内部，按继承拿到 95 后 calc 结果仍是 81px。 */
#panel-products table.grid tbody td,
#panel-items table.grid tbody td,
#panel-sheets table.grid tbody td { --row-h: 95px; }
/* 商品档案 11 列：默认底线 120 在 1440 下多出 22px（最右「操作」被切半个按钮）。
   100 够放 0.0000 这种四位小数，也够「停用/启用」两个字。 */
#panel-products table.grid tbody td { --td-min: 100px; }

/* 窄屏（≤1600：笔记本 1440 / 分屏 1280）客户表 14 列放不下，名称/地址会被挤成 3 行。
   微信 / 旺旺 是「扫一眼」用得最少的两个字段（要查详情点「编辑」就有，导出/客户版照旧），
   先在窄屏收起这两列，把宽度让给名称、地址和操作列。1920 及以上不受影响。 */
@media (max-width: 1600px) {
  #panel-customers table.grid th:nth-child(5),
  #panel-customers table.grid td:nth-child(5),
  #panel-customers table.grid th:nth-child(6),
  #panel-customers table.grid td:nth-child(6) { display: none; }
}

/* ---------------------------------------------------------------- 行内缩略图自适应表格尺寸（2026-09-28）
   「适应表格」的本质是变量联动：高度 = 行高变量 --row-h 减去 td 上下内边距（7px×2=14），
   宽 auto 保比例、超列宽 --col-w 封顶。改 :root 的 --row-h/--col-w，图随表格自动缩放。
   覆盖前面 img.row-thumb / img.prod-thumb 的固定像素旧规则（同选择器、后文生效）。
   注意：各 JS 模板里的行内固定 width/height 已删除，由本类接管（开单行 it-row-thumb、
   下拉项 cl-thumb、悬停浮层 #img-hover-pop 不适用）。 */
img.row-thumb,
img.prod-thumb {
  height: calc(var(--row-h) - 14px);
  width: auto;
  max-width: var(--col-w);
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--c-border);
  display: block;
}
/* 商品管理图格外层是 a.prod-thumb-link（内含 block 级 img）：inline-block 收缩包裹，
   避免行内 a 的基线空隙把图挤出格外。 */
a.prod-thumb-link { display: inline-block; line-height: 0; }

/* ---------------------------------------------------------------- 寄样联动条（2026-10-08）
   开单页选中客户后，把他的进行中寄样单摆在这。刻意用「琥珀」而不是蓝色：
   它是**提醒**（别忘了寄过样），不是操作项 —— 用主色会跟下面的表单抢注意力。
   #ord-sample-bar 条件渲染（无进行中寄样单时整条 display:none），不占空白。 */
.samp-link-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--c-sampbar-bg); border: 1px solid var(--c-sampbar-line);
  border-radius: var(--radius-sm); padding: 9px 12px; margin-bottom: 12px; font-size: 12.5px;
}
.samp-link-bar .slb-title { font-weight: 600; color: var(--c-sampbar-text); }
.samp-link-bar .slb-card {
  display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap;
  background: var(--c-surface); border: 1px solid var(--c-sampbar-line); border-radius: 6px;
  padding: 4px 10px; color: var(--c-sampbar-text);
}
.samp-link-bar .slb-card .no { font-weight: 600; }
.samp-link-bar .slb-card .meta { color: var(--c-hint-amber-text); font-size: 12px; }
.samp-link-bar .slb-spacer { flex: 1; }
.samp-link-bar .slb-hint { color: var(--c-sampbar-text); font-size: 12px; }


   只读态灰底示意"这是展示不是输入框"；编辑态琥珀色边框提示"正在编辑、自动保存中"。 */
#srm-text[readonly] { background: var(--c-readonly-bg); color: var(--c-readonly-text); cursor: default; }
#srm-text.editing   { background: var(--c-hint-amber-bg); border-color: var(--c-hint-amber-line); }

/* ============================================================================
   深色模式（2026-10-27）
   ----------------------------------------------------------------------------
   策略：只覆盖语义变量 + 少数硬编码的浅色底，不重写任何组件规则 ——
   组件全部走 var()，变量一换，卡片/表格/弹窗/输入框自动跟随。
   刻意不动的：状态色（.chip.st-*、退货红底、寄样琥珀条）在深色下偏亮但可读，
   逐个调色属于"重新设计"，不在本次改造范围。

   ⚠️ 打印必须保持白底：单据纸要能白纸黑字打出来，所以下面 @media print 把
   变量全部重置回浅色。缺这一段，系统处于深色模式时打出来的销售单会是深灰底。
   ========================================================================== */
/* 打印一律白底黑字（单据纸要拿这张纸跟客户对账） */
@media print {
  :root {
    --c-primary: #1F3E63; --c-primary-dark: #162F4C;
    --c-accent: #2C6B57; --c-accent-dark: #245846;
    --c-danger: #A93226; --c-text: #1A1D21; --c-muted: #5A6270;
    --c-border: #B9B7AF; --c-bg: #fff; --c-surface: #fff;
    --c-line: #B9B7AF; --c-rule: #1F3E63;
  }
  body { background: #fff; }
  .table-wrap { max-height: none; overflow: visible; }
  table.grid thead th { position: static; box-shadow: none; }
}

/* ============================================================================
   批发页·照抄同行（2026-10-28）
   ----------------------------------------------------------------------------
   这一节只给「本轮照抄 vendor/jshERP 的批发页改造」新增的结构加样式：
   A1 查询区折叠 / A2 列设置 / A4 扫码条 / A5-A7 明细工具条 / A0 上次成交价小字 /
   分页跳页。既有选择器一个都不改（只在末尾追加），颜色一律走 :root 语义变量，
   深色模式自动跟随；不引第三方库。
   ========================================================================== */

/* ---- A1 查询区两行制：折叠区（客户 / 状态 / 每页条数） ----
   <details class="adv-filter">：不展开时只占一个「展开」按钮，展开后关键词浮在
   工具条下方（absolute），不把下面第二行工具条推走 —— 展开/收起不会让整页跳一下。 */
.adv-filter { position: relative; }
.adv-filter > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: 5px;
  height: 28px; padding: 0 12px; font-size: 12.5px; color: var(--c-muted);
  border: 1px solid var(--c-border); border-radius: var(--radius-sm);
  background: var(--c-surface);
  transition: color .15s var(--ease), border-color .15s var(--ease);
}
.adv-filter > summary::-webkit-details-marker { display: none; }
.adv-filter > summary::before { content: '▾'; font-size: 10px; line-height: 1; }
.adv-filter:not([open]) > summary::before { content: '▸'; }
.adv-filter > summary:hover { color: var(--c-primary); border-color: #BDBAB0; }
.adv-filter > summary:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
.adv-filter .adv-body {
  position: absolute; left: 0; top: calc(100% + 6px); z-index: 30;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px; min-width: 480px;
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-pop);
}
.adv-filter .adv-body input,
.adv-filter .adv-body select { height: 28px; }
.adv-filter .adv-body .muted-xs { flex-basis: 100%; }

/* ---- A2 列设置 ----
   与查询区折叠同一套交互；浮层里按 2 列排 10 个复选，勾太窄会有两处折行。 */
.col-set { position: relative; }
.col-set > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: 5px;
  height: 28px; padding: 0 12px; font-size: 12.5px; color: var(--c-muted);
  border: 1px solid var(--c-border); border-radius: var(--radius-sm);
  background: var(--c-surface);
  transition: color .15s var(--ease), border-color .15s var(--ease);
}
.col-set > summary::-webkit-details-marker { display: none; }
.col-set > summary::before { content: '▾'; font-size: 10px; line-height: 1; }
.col-set:not([open]) > summary::before { content: '▸'; }
.col-set > summary:hover { color: var(--c-primary); border-color: #BDBAB0; }
.col-set > summary:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 1px; }
.col-set .col-set-body {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 30;
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px 16px;
  padding: 10px 12px; min-width: 260px;
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-pop);
}
.col-set .col-set-body label {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; color: var(--c-text); cursor: pointer;
  min-height: 24px;                    /* 触屏命中 ≥24px */
}
.col-set .col-set-body input { accent-color: var(--c-primary); width: 15px; height: 15px; }
.col-set .col-set-body .btn { grid-column: 1 / -1; margin-top: 4px; justify-self: start; }

/* ---- 分页条「跳页」 ----
   只加在分页条末尾；其它列表（不传 per）也共用这一套，行为不变。 */
.pg-jump { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.pg-jump .pg-jump-inp {
  width: 56px; height: 28px; padding: 0 6px; text-align: center;
  font-family: inherit; font-size: 12.5px; color: var(--c-text);
  border: 1px solid var(--c-border); border-radius: var(--radius-sm);
  background: var(--c-surface);
}
.pg-jump .pg-jump-inp:focus { outline: none; border-color: var(--c-primary); box-shadow: 0 0 0 3px var(--c-focus); }
/* 页码 spinner 在 56px 里挤掉数字，去掉 */
.pg-jump .pg-jump-inp::-webkit-outer-spin-button,
.pg-jump .pg-jump-inp::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ---- A4-A7 明细工具条 / 扫码条 ----
   工具条里的按钮等高；扫码条单独一行（浅底 + 发丝线），只占开单页。 */
.ord-tools { margin-top: 2px; margin-bottom: 8px; }
.ord-scan-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 12px; margin: 0 0 10px;
  background: var(--c-sunken); border: 1px solid var(--c-border);
  border-radius: var(--radius-sm);
}
.ord-scan-bar .ord-scan-lbl { font-size: 12.5px; color: var(--c-muted); white-space: nowrap; }
.ord-scan-bar #ord-scan {
  flex: 1 1 260px; min-width: 180px; height: 30px; padding: 0 10px;
  font-family: inherit; font-size: 13px; color: var(--c-text);
  border: 1px solid var(--c-border); border-radius: var(--radius-sm);
  background: var(--c-surface);
}
.ord-scan-bar #ord-scan:focus { outline: none; border-color: var(--c-primary); box-shadow: 0 0 0 3px var(--c-focus); }
.ord-scan-bar .btn { min-height: 28px; }

/* ---- A0 「上次成交价」行内小字 ----
   挂在商品单元格下方：字号小、颜色走 --c-muted，不抢单价输入框的注意力。 */
.last-price-hint {
  margin-top: 2px; font-size: 11.5px; line-height: 1.35;
  color: var(--c-muted); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
#ord-items-table td .last-price-hint { max-width: 100%; }

/* ---- 扫码命中的行闪一下（300ms）----
   告诉人"落到哪一行了"，尤其连续扫码时视线还在扫码框上。 */
@keyframes ord-row-flash {
  0%   { background: var(--c-combo-on); }
  100% { background: transparent; }
}
#ord-items-table tr.flash > td { animation: ord-row-flash .3s var(--ease); }

/* ---- A7 行首复选框列 ----
   与开单表头其它 .col-* 一致；勾选框套 .chk-hit 保证 24px 命中区。 */
#ord-items-table th.col-sel,
#ord-items-table td.col-sel { text-align: center; padding-left: 4px; padding-right: 4px; }

/* hidden 属性必须真的隐藏：.ord-scan-bar / 按钮都有 display 规则，会盖掉 [hidden] 的 UA 样式 */
#ord-scan-bar[hidden], #ord-clear-sel[hidden] { display: none !important; }

/* ============================================================
   经营看板（2026-10-28）
   ----------------------------------------------------------------------------
   信息架构照抄同行 dashboard（vendor/jshERP 的 IndexChart.vue）：
   KPI 带 + 月度趋势 + 客户 TOP10 排行 + 最近单据。
   只复用既有 token 与类（.card / .metrics / .metric / .grid / .toolbar），
   本节点**不新增色板**、也不改任何既有选择器的语义。
   ============================================================ */

/* 工具条右侧「数据截至：业务今天」：小字弱色，不抢查询按钮 */
.ov-asof {
  font-size: 12px; color: var(--c-muted); letter-spacing: .03em;
  white-space: nowrap;
}
.ov-asof b {
  font-weight: 600; color: var(--c-text);
  font-variant-numeric: tabular-nums;
}

/* 成本 / 毛利旁的 ⓘ 口径说明：不是按钮、不进 Tab 序，悬停看 title */
.ov-info {
  display: inline-block; margin-left: 3px; cursor: help;
  font-size: 11px; color: var(--c-muted); vertical-align: 1px;
}

/* 两张图并排；<1100 折成一列（12 个月柱 + TOP10 横向条都需要宽度） */
.ov-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
  margin-bottom: 16px;
}
.ov-grid .card { margin-bottom: 0; }
.ov-chart-card > h2 {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
}
/* 卡片副标题：口径说明（全年 / TOP10 口径 / 是否等于本期销售额） */
.ov-note {
  font-size: 11.5px; font-weight: 400; color: var(--c-muted);
  letter-spacing: .02em; line-height: 1.4;
}
/* 图表容器固定高度：Chart.js 用 maintainAspectRatio:false 填满它，
   切页签 / 窗口 resize 只重排画布，不把页面高度撑来撑去。 */
.ov-chart { position: relative; height: 272px; }
.ov-chart canvas { display: block; width: 100% !important; height: 100% !important; }
/* 空态 / 图表库缺失：盖住画布只留一行说明（数字仍在 KPI 带里，不是整页报错） */
.ov-chart .ov-chart-empty {
  display: none; position: absolute; inset: 0;
  align-items: center; justify-content: center;
  padding: 0 14px; text-align: center;
  color: var(--c-muted); font-size: 12.5px; letter-spacing: .04em;
}
.ov-chart.is-empty canvas { display: none; }
.ov-chart.is-empty .ov-chart-empty { display: flex; }

/* 最近单据：沿用 .grid；只压客户列宽度，避免长客户名把金额挤到第二屏 */
.ov-recent .table-wrap { overflow-x: auto; }

@media (min-width: 641px) {
  .ov-recent table.grid td { white-space: nowrap; }
  .ov-recent table.grid td:nth-child(3) {
    max-width: 420px; overflow: hidden; text-overflow: ellipsis;
  }
}

@media (max-width: 1099px) {
  .ov-grid { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  /* 手机：图矮一点，一屏里 KPI + 第一张图能同屏；表格交给 mobile.css 卡片化 */
  .ov-chart { height: 224px; }
  .ov-chart-card > h2 { display: block; }
  .ov-note { display: block; margin-top: 2px; }
}
