/* ============================================================================
   应用样式：把现成库的皮肤改成我们的设计令牌
   ----------------------------------------------------------------------------
   「库是现成的，皮肤是自己的」——本文件只做两件事：
     1. 覆盖 Bootstrap 5 与 Tabulator 的主题变量/关键选择器（不改库源码）
     2. 补充本应用的布局（顶栏 / 侧栏 / 内容区 / 底部状态栏）与业务组件

   本文件禁止出现裸色值，一律引用 tokens.css 的变量。
   ========================================================================= */

/* ==========================================================================
   1. Bootstrap 5 变量覆盖
   ========================================================================== */
:root,
[data-bs-theme="dark"] {
  --bs-body-bg: var(--bg-page);
  --bs-body-color: var(--text-primary);
  --bs-body-font-family: var(--font-sans);
  --bs-body-font-size: var(--fs-md);
  --bs-emphasis-color: var(--text-primary);
  --bs-secondary-color: var(--text-secondary);
  --bs-tertiary-color: var(--text-muted);
  --bs-border-color: var(--border);
  --bs-border-color-translucent: var(--border-muted);
  --bs-primary: var(--accent);
  /* 裸 RGB 三元组必须与 --accent 同步（禁裸十六进制的检查覆盖不到这里，最易漏） */
  --bs-primary-rgb: 11, 127, 110;
  --bs-link-color: var(--info);
  --bs-link-hover-color: var(--info-strong);
  /* 同上：必须与 --info 同步 */
  --bs-link-color-rgb: 47, 111, 143;
  --bs-success: var(--ok);
  --bs-warning: var(--warn);
  --bs-danger: var(--danger);
  --bs-info: var(--info);
  --bs-secondary-bg: var(--bg-elevated);
  --bs-tertiary-bg: var(--bg-inset);
  --bs-card-bg: var(--card-bg);
  --bs-card-border-color: var(--card-border);
  --bs-modal-bg: var(--bg-elevated);
  --bs-modal-border-color: var(--border);
  --bs-dropdown-bg: var(--bg-elevated);
  --bs-dropdown-border-color: var(--border);
  --bs-dropdown-link-hover-bg: var(--bg-hover);
  --bs-dropdown-link-active-bg: var(--accent);
  --bs-dropdown-link-active-color: var(--on-accent);
  --bs-table-bg: transparent;
  --bs-table-border-color: var(--tbl-border);
  --bs-focus-ring-color: var(--accent-border);
}

html { color-scheme: light; }

body {
  background-color: var(--bg-page);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  line-height: var(--lh-base);
  margin: 0;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--info); text-decoration: none; }
a:hover { color: var(--info-strong); text-decoration: underline; }

code, kbd, .num, .mono { font-family: var(--font-mono); }

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-xs);
}

/* Windows 高对比度模式兜底：系统会丢弃 box-shadow，上面那条焦点环会整个消失，
   键盘用户就再也看不到焦点落点。这里用系统色关键字换回真实 outline。
   CanvasText 是系统前景色，跟着用户的高对比度配色走，不能写死颜色。 */
@media (forced-colors: active) {
  :focus-visible {
    outline: 2px solid CanvasText;
    outline-offset: 2px;
    box-shadow: none;
  }
}

::selection { background: var(--accent-weak); color: var(--text-primary); }

/* 滚动条 */
* { scrollbar-color: var(--c-neutral-300) transparent; scrollbar-width: thin; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--c-neutral-300);
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--c-neutral-400); background-clip: padding-box; }

/* ==========================================================================
   2. 应用骨架
   ========================================================================== */
.app-shell { display: flex; flex-direction: column; min-height: 100vh; }

/* --- 顶栏：全站唯一的品牌识别 —— 一条青绿细线（取代原黄黑斜条纹） --- */
.topbar {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  height: var(--layout-topbar-h);
  padding: 0 var(--sp-4);
  background: var(--bg-sidebar);
  border-bottom: 1px solid var(--border);
}
.topbar::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--brand-line-h);
  background: var(--brand-line);
}
.topbar__brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  white-space: nowrap;
}
.topbar__brand svg { width: 20px; height: 20px; color: var(--accent); }
.topbar__version {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
}
.topbar__nav { display: flex; gap: 2px; flex-wrap: wrap; margin-left: var(--sp-4); }
.topbar__nav a {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 var(--sp-3);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  border: 1px solid transparent;
  transition: color var(--dur-base), background var(--dur-base), border-color var(--dur-base);
}
.topbar__nav a:hover { color: var(--text-primary); background: var(--bg-hover); text-decoration: none; }
.topbar__nav a.is-active {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: var(--fw-bold);
  border-color: transparent;
}
.topbar__spacer { margin-left: auto; }
.topbar__user {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
}
.topbar__user b { color: var(--text-primary); font-weight: var(--fw-medium); }
/* 顶栏「退出」走 POST 表单（2026-10-19）：flex 容器里不能让它撑成块级 */
.topbar__logout { display: flex; margin: 0; }

/* --- 内容区 --- */
.app-body { display: flex; flex: 1; min-height: 0; }
.app-main {
  flex: 1;
  min-width: 0;
  padding: var(--sp-6) var(--sp-6) calc(var(--layout-statusbar-h) + var(--sp-6));
  max-width: var(--layout-content-max);
}

/* --- 页面标题 --- */
.page-head {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-3);
  /* 2026-09-30：为「一屏多看几行」收紧（sp-5 → sp-3），把纵向空间让给表格 */
  margin-bottom: var(--sp-3);
  flex-wrap: wrap;
}
.page-head h1 { font-size: var(--fs-xl); font-weight: var(--fw-bold); margin: 0; }
.page-head p { margin: 0 0 2px; color: var(--text-secondary); font-size: var(--fs-sm); }

/* --- 底部状态栏（桌面工具型 Web 用它替代移动端底部导航） --- */
.statusbar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  height: var(--layout-statusbar-h);
  padding: 0 var(--sp-4);
  background: var(--bg-sidebar);
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}
.statusbar__spacer { margin-left: auto; }
.statusbar b { font-family: var(--font-mono); color: var(--text-primary); font-weight: var(--fw-medium); }

/* ==========================================================================
   3. 业务组件
   ========================================================================== */
.card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
  padding: var(--card-pad);
}
.card--flush { padding: 0; overflow: hidden; }

.grid { display: grid; gap: var(--sp-4); }
/* auto-fit 的 min 一律用 min(…, 100%) 兜底：容器本身比 min 还窄时，
   minmax 会强行按 min 撑宽并把内容顶出卡片（登记页中间栏在高 DPI 缩放的窄窗口下
   曾把输入框挤出栏外 100px）。加了这个兜底就只会退化成单列，不会溢出。 */
.grid--kpi { grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }

/* 指标卡：数值放大、四周留足，讲究"一眼看清" */
.kpi { position: relative; overflow: hidden; }
.kpi::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--accent);
}
.kpi--warn::after { background: var(--warn); }
.kpi__label { font-size: var(--fs-xs); color: var(--text-secondary); letter-spacing: .02em; }
.kpi__value {
  font-family: var(--font-mono);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  margin-top: var(--sp-2);
}
.kpi__sub { font-size: var(--fs-2xs); color: var(--text-muted); margin-top: var(--sp-2); }

/* ---- 紧凑概览数字条（仅库存总表用）----
   旧实现是 5 张 .card.kpi 大卡片（约 137px，窄窗口折两行再翻倍），把表格挤到
   一屏只剩 7 行。用户要求「表格上面的数据项搞小点，不要喧宾夺主」——
   这里换成一条约 40px 的数字条，纵向空间还给表格。
   ⚠ .kpi* 那套仍被 io.html / movements.html 使用，上面的定义不要动。 */
.statstrip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-1) var(--sp-5);
  padding: var(--sp-2) var(--sp-4);
  margin-bottom: var(--sp-3);
  background: var(--bg-card);
  border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
}
.statstrip__item { display: inline-flex; align-items: baseline; gap: var(--sp-2); }
.statstrip__label { font-size: var(--fs-2xs); color: var(--text-secondary); letter-spacing: .02em; }
.statstrip__value {
  font-family: var(--font-mono);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  line-height: 1.3;
}
.statstrip__note { font-size: var(--fs-2xs); color: var(--text-muted); }
.statstrip__item--warn .statstrip__value { color: var(--warn); }
.statstrip__item--danger .statstrip__value { color: var(--danger); }
/* 窄窗口先丢掉最占宽度的那句辅助说明（数字本身一个都不少），
   否则数字条会折成两行、把刚省下来的行数又吃回去 */
/* 统计条备注比顶栏更早收起（它更占横向空间）——这个 1120 与下面 1100 的顶栏
   换行是两个组件各自的阈值，不是重复档位，别合并（详见文末手机断点的说明）。 */
@media (max-width: 1120px) {
  .statstrip__note { display: none; }
}

/* 状态胶囊：三色语义 + 钢蓝「参考」档 */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: var(--badge-py) var(--badge-px);
  border-radius: var(--badge-radius);
  font-size: var(--badge-font-size);
  font-weight: var(--fw-medium);
  border: 1px solid transparent;
  white-space: nowrap;
}
.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.badge--ok     { color: var(--c-moss-500);  background: var(--ok-weak);     border-color: var(--ok-border); }
.badge--warn   { color: var(--c-rust-500);  background: var(--warn-weak);   border-color: var(--warn-border); }
.badge--danger { color: var(--c-alarm-500); background: var(--danger-weak); border-color: var(--danger-border); }
.badge--info   { color: var(--c-steel-600); background: var(--info-weak);   border-color: var(--info-border); }
.badge--muted  { color: var(--text-secondary); background: var(--bg-elevated); border-color: var(--border); }
/* 主色徽标：用于「管理登录」这类主入口的标签，与 info/muted 明显区分。
   ⚠ 文字取 --accent-active（700 档）而不是 --accent：13px 小字压在 --accent-weak
   上只有 4.30:1，差 0.2 到 AA；换 700 档后约 7.1:1。弱底仍用 --accent-weak，
   保持与主色的家族关系。回归由 test_tokens_contrast.py 守门。 */
.badge--accent { color: var(--accent-active); background: var(--accent-weak); border-color: var(--accent-border); }

/* ---- 筛选栏 ---- */
.filterbar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  padding: var(--sp-4) var(--sp-5);
  background: var(--bg-card);
  border: 1px solid var(--card-border);
  border-radius: var(--card-radius) var(--card-radius) 0 0;
  border-bottom: none;
}
.filterbar .form-control { max-width: 360px; }
/* 品类下拉（主要筛选维度）：宽度按内容走，与搜索框同高同框 */
.filterbar .form-select {
  width: auto;
  min-width: 216px;
  min-height: var(--input-h);
  padding-top: 0;
  padding-bottom: 0;
  background-color: var(--input-bg);
  border-color: var(--input-border);
  border-radius: var(--input-radius);
  color: var(--input-fg);
  box-shadow: none;
}
.filterbar__spacer { margin-left: auto; }
.filterbar__count { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-secondary); }
/* 列可见性说明：靠 flex-basis:100% 换行独占一行，小号弱化灰字。
   不复用 .filterbar__count —— 那个是等宽字体，用来排中文整句不合适。 */
.filterbar__note {
  flex-basis: 100%;
  margin: 0;
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  line-height: var(--lh-base);
}

/* ---- 「显示列」勾选框弹窗（SweetAlert2 的 html 内容） ---- */
.colpick { display: flex; flex-direction: column; gap: var(--sp-3); text-align: left; }
.colpick__hint { margin: 0; font-size: var(--fs-2xs); color: var(--text-muted); }
.colpick__list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: var(--sp-1) var(--sp-4);
  max-height: 46vh;
  overflow-y: auto;
}
.colpick__row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--radius-xs);
  font-size: var(--fs-sm);
  color: var(--text-primary);
  cursor: pointer;
}
.colpick__row:hover { background: var(--bg-hover); }
.colpick__row input { accent-color: var(--accent); width: 15px; height: 15px; flex: none; }
.switch {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
}
.switch input { accent-color: var(--accent); width: 14px; height: 14px; }

/* 表格容器与筛选栏拼成一张卡片 */
.table-card { border-top-left-radius: 0; border-top-right-radius: 0; position: relative; }
.table-card .tabulator { border-radius: 0 0 var(--card-radius) var(--card-radius); border-top: none; }

/* ---- 整页滚动 + 表头吸顶（2026-09-30）----
   表格不再设固定高度（stock-table.js 已删掉 height），随内容自然增高、由**页面**滚动
   承载：一路滚到第 2747 行，而不是在几百像素高的小框里滚。表头连同列筛选行吸在
   顶栏下方，滚到哪都能排序/筛选。
   ⚠ position: sticky 会被任何 overflow≠visible 的祖先**静默废掉**（不报错、只是不生效），
     这里有两处必须放开：
       1) 上方 .card--flush { overflow: hidden }（原本用来裁卡片圆角）
       2) vendor 皮肤 .tabulator { overflow: hidden }
     只放开库存总表这张卡片的作用域，不影响其他页面；圆角改由 .tabulator 自身
     与其 tableholder 给出，视觉不变。 */
.table-card.card--flush { overflow: visible; }
.table-card > .tabulator { overflow: visible; }
.table-card .tabulator .tabulator-tableholder {
  border-bottom-left-radius: var(--card-radius);
  border-bottom-right-radius: var(--card-radius);
}
.table-card .tabulator .tabulator-header {
  position: sticky;
  top: var(--layout-topbar-h);
  /* 必须低于顶栏（--z-sticky = 20），否则滚起来把顶栏盖住 */
  z-index: calc(var(--z-sticky) - 1);
}

/* ---- 详情抽屉 ---- */
.drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(720px, 92vw);
  z-index: var(--z-drawer);
  background: var(--bg-card);
  border-left: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  animation: drawer-in var(--dur-slow) var(--ease-out);
}
.drawer[hidden] { display: none; }
@keyframes drawer-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
.drawer__head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated);
}
.drawer__head h2 {
  font-size: var(--fs-md);
  margin: 0;
  font-family: var(--font-mono);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.drawer__body { padding: var(--sp-4); overflow: auto; flex: 1; }
.drawer-h3 {
  font-size: var(--fs-sm);
  margin: var(--sp-5) 0 var(--sp-2);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
  color: var(--text-secondary);
}
/* 详情抽屉字段清单：两列网格（2026-10-08 仓管建议），长值字段独占整行。
   item = div 包裹的一对 dt/dd（HTML 规范允许 dl 内 div 分组）；
   movements 页 .item-info .detail-kv 是独立作用域覆盖，不受影响。 */
.detail-kv {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px var(--sp-3);
  margin: 0;
  font-size: var(--fs-sm);
}
.detail-kv__item { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.detail-kv__item dt { color: var(--text-secondary); flex: 0 0 auto; white-space: nowrap; }
.detail-kv__item dd { margin: 0; word-break: break-all; min-width: 0; flex: 1 1 auto; }
.detail-kv__item--wide { grid-column: 1 / -1; }
.detail-kv dt { color: var(--text-secondary); }
.detail-kv dd { margin: 0; word-break: break-all; }
.drawer-table { max-height: 420px; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); }
/* 字号走 --tbl-fs-html（默认=--fs-xs）：显示设置改字号时与 Tabulator 表同步放大 */
.drawer-table table {
  width: 100%; border-collapse: collapse;
  font-size: var(--tbl-fs-html); font-family: var(--tbl-font);
}
.drawer-table thead th {
  position: sticky; top: 0; background: var(--bg-elevated); color: var(--text-secondary);
  text-align: left; padding: 6px var(--sp-3); border-bottom: 1px solid var(--border);
  font-weight: var(--fw-medium);
}
.drawer-table tbody td { padding: 5px var(--sp-3); border-bottom: 1px solid var(--tbl-border); }
.drawer-table tbody tr:hover { background: var(--bg-hover); }

/* ==========================================================================
   4. 表单
   ========================================================================== */
.form-label { font-size: var(--fs-xs); color: var(--text-secondary); margin-bottom: var(--sp-1); }
.form-label .req { color: var(--danger); margin-left: 2px; }
.form-hint { font-size: var(--fs-2xs); color: var(--text-muted); margin-top: 3px; }
.form-error { font-size: var(--fs-2xs); color: var(--c-alarm-500); margin-top: 3px; }

.form-control, .form-select {
  height: var(--input-h);
  background: var(--input-bg);
  color: var(--input-fg);
  border: 1px solid var(--input-border);
  border-radius: var(--input-radius);
  font-size: var(--fs-sm);
  padding: 0 var(--sp-2);
}
.form-control::placeholder { color: var(--input-placeholder); }
.form-control:focus, .form-select:focus {
  background: var(--input-bg-focus);
  color: var(--input-fg);
  border-color: var(--input-border-focus);
  box-shadow: var(--focus-ring);
}
textarea.form-control { height: auto; padding: var(--sp-2); }
.form-check-input { background-color: var(--input-bg); border-color: var(--input-border); }
.form-check-input:checked { background-color: var(--accent); border-color: var(--accent); }

/* 按钮：主按钮用安全黄（绝不用蓝） */
.btn {
  height: var(--btn-h);
  padding: 0 var(--btn-px);
  border-radius: var(--btn-radius);
  font-size: var(--btn-font-size);
  font-weight: var(--fw-medium);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  border: 1px solid transparent;
  transition: background var(--dur-base), color var(--dur-base), border-color var(--dur-base);
}
.btn svg { width: 14px; height: 14px; flex: none; }
.btn-primary {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-fg);
  border-color: var(--btn-primary-bg);
}
.btn-primary:hover { background: var(--btn-primary-bg-hover); border-color: var(--btn-primary-bg-hover); }
.btn-primary:active { background: var(--btn-primary-bg-active); }
.btn-primary:disabled { background: var(--bg-elevated); color: var(--text-disabled); border-color: var(--border-muted); }
.btn-outline-secondary {
  background: var(--btn-ghost-bg);
  color: var(--btn-ghost-fg);
  border-color: var(--btn-ghost-border);
}
.btn-outline-secondary:hover { background: var(--btn-ghost-bg-hover); color: var(--text-primary); border-color: var(--border-strong); }
.btn-outline-secondary:disabled { color: var(--text-disabled); border-color: var(--border-muted); }
.btn-danger { background: var(--btn-danger-bg); color: var(--btn-danger-fg); border-color: var(--btn-danger-bg); }
.btn-danger:hover { filter: brightness(.92); }
.btn-sm { height: 26px; font-size: var(--fs-xs); padding: 0 var(--sp-2); }
.btn-link { color: var(--info); background: none; border: none; padding: 0; height: auto; }
.btn-link:hover { color: var(--info-strong); text-decoration: underline; }

/* ==========================================================================
   5. Tabulator 皮肤覆盖（在浅色皮肤之上改色，不改库源码）
   ========================================================================== */
.tabulator {
  background-color: var(--tbl-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
  font-size: var(--tbl-fs);
  /* 字体族走 --tbl-font（默认=--font-sans）：「显示设置」个性化只覆写表格字体，
     不波及整页 UI 文案；等宽数字列/型号列仍由 .col-num/.col-model 保持 --font-mono。 */
  font-family: var(--tbl-font);
  color: var(--text-primary);
}
.tabulator .tabulator-header {
  background-color: var(--tbl-header-bg);
  border-bottom: 1px solid var(--border-strong);
  color: var(--tbl-header-fg);
}
.tabulator .tabulator-header .tabulator-col {
  background-color: var(--tbl-header-bg);
  border-right: 1px solid var(--tbl-border);
  color: var(--tbl-header-fg);
  font-weight: var(--fw-medium);
}
.tabulator .tabulator-header .tabulator-col:hover { background-color: var(--c-neutral-150); }
.tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title { padding-right: 16px; }
.tabulator .tabulator-col-content { padding: 6px var(--tbl-cell-px); }
/* 列头随数据一起居中（hozAlign='center' 会给 .tabulator-col-content 的 justifyContent，
   这里兜底那些没有显式 hozAlign 的列） */
.tabulator .tabulator-header .tabulator-col .tabulator-col-title { text-align: center; }
.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input,
.tabulator .tabulator-header .tabulator-col .tabulator-header-filter select {
  background: var(--input-bg);
  color: var(--input-fg);
  border: 1px solid var(--input-border);
  border-radius: var(--radius-xs);
  height: 24px;
  font-size: var(--fs-xs);
  font-family: var(--font-sans);
}
.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input:focus,
.tabulator .tabulator-header .tabulator-col .tabulator-header-filter select:focus {
  border-color: var(--input-border-focus);
  box-shadow: none;
}
.tabulator-row {
  background-color: var(--tbl-row-bg);
  color: var(--text-primary);
  min-height: var(--layout-table-row-h);
  /* ⚠ 实测禁区（2026-10-13）：不要给行加 content-visibility: auto——
     Tabulator 的 fitData 布局在 setData 后逐格测量列宽，与 CV 的「跳过渲染」
     相互触发强制布局，全显示 2713 行从基线 2.4s 恶化到 43s（20 倍）。
     整页滚动模式的优化空间见 HANDOVER §9.13（列目录+子集已做），勿再尝试 CV。 */
  border-bottom: 1px solid var(--tbl-border);
}
.tabulator-row.tabulator-row-even { background-color: var(--tbl-row-alt-bg); }
.tabulator-row.tabulator-selectable:hover { background-color: var(--tbl-row-hover-bg) !important; }
.tabulator-row.tabulator-selected { background-color: var(--tbl-row-selected-bg) !important; }
.tabulator-cell {
  border-right: 1px solid var(--tbl-border);
  padding: 5px var(--tbl-cell-px);
  font-size: var(--tbl-fs);
  /* 表内数据居中（2026-09-30 用户要求）。注意：列定义里的 hozAlign 会被 Tabulator
     写成**行内** text-align，行内样式压过 CSS —— 所以 stock-table.js 那边也把
     columnDefaults.hozAlign 设成了 'center'，这里只是没有显式 hozAlign 时的兜底。 */
  text-align: center;
}
.tabulator .tabulator-footer {
  background-color: var(--bg-elevated);
  border-top: 1px solid var(--border);
  color: var(--text-secondary);
}
.tabulator .tabulator-footer .tabulator-page {
  background: var(--bg-inset);
  border: 1px solid var(--border);
  color: var(--text-primary);
  border-radius: var(--radius-xs);
}
.tabulator .tabulator-footer .tabulator-page.active { background: var(--accent); color: var(--on-accent); }
.tabulator .tabulator-footer .tabulator-page:disabled { color: var(--text-disabled); }
/* 冻结列投影：滚动时一眼看出哪几列钉住了 */
.tabulator .tabulator-cell.tabulator-frozen,
.tabulator .tabulator-header .tabulator-col.tabulator-frozen {
  background-color: inherit;
}
.tabulator-row .tabulator-cell.tabulator-frozen { background-color: var(--bg-card); }
.tabulator-row.tabulator-row-even .tabulator-cell.tabulator-frozen { background-color: var(--bg-card); }
.tabulator-row:hover .tabulator-cell.tabulator-frozen { background-color: var(--bg-page); }
.tabulator .tabulator-cell.tabulator-frozen.tabulator-frozen-left { box-shadow: var(--tbl-frozen-shadow); }
/* 数字列：等宽数字 + 对齐走令牌（2026-09-30 用户要求表内数据居中，故为 center） */
.tabulator .tabulator-cell.col-num, .tabulator .tabulator-col.col-num .tabulator-col-title {
  text-align: var(--tbl-num-align);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
/* 型号列：字号与单元格同字号（2026-10-08 跟单主管要求「型号清楚点」）。
   字体可由「显示设置」按本机覆写（--tbl-font-model）；2026-10-13 用户要求
   默认改黑体——回落表格字体（--tbl-font 默认=黑体），未设置时与表格一致 */
.tabulator .tabulator-cell.col-model { font-family: var(--tbl-font-model, var(--tbl-font)); font-size: var(--tbl-fs); }
.tabulator .tabulator-col.col-model { font-family: var(--tbl-font-model, var(--tbl-font)); }
.tabulator .tabulator-placeholder { color: var(--text-muted); font-size: var(--fs-sm); }

/* 按型号分组（v3.3）：组头=型号（N 行）· 数量合计 · 余额合计。
   Tabulator 组头容器是 .tabulator-group；这里覆盖底色与排布，只消费既有令牌。 */
.tabulator .tabulator-group {
  background: var(--bg-inset);
  border-bottom: 1px solid var(--border);
}
.specagg__name { font-weight: var(--fw-medium); color: var(--text-primary); }
.specagg__count {
  margin-left: var(--sp-2);
  font-size: var(--fs-xs); color: var(--text-muted);
}
.specagg__sum {
  margin-left: var(--sp-4);
  font-size: var(--fs-xs); color: var(--text-secondary);
}
.specagg__sum b {
  font-family: var(--font-mono); font-weight: var(--fw-medium);
  color: var(--text-primary);
}

/* Tabulator 深层部件（下拉菜单 / 弹出层 / 计算行 / 加载态 / 每页条数 / 排序箭头）：
   官方浅色皮肤只给了灰阶默认值，这里按令牌收口，避免与全站配色不一致。
   注：官方皮肤是「替换」而非叠加，所以只需收口这些边角，不必清底。 */
.tabulator .tabulator-menu,
.tabulator-popup-container {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  color: var(--text-primary);
}
.tabulator .tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover,
.tabulator-popup-container .tabulator-edit-list .tabulator-edit-list-item.active {
  background: var(--bg-hover);
  color: var(--text-primary);
}
.tabulator .tabulator-menu .tabulator-menu-separator { border-top: 1px solid var(--border); }
.tabulator .tabulator-calcs-holder,
.tabulator .tabulator-calcs-holder .tabulator-row {
  background: var(--bg-inset) !important;
  color: var(--text-secondary);
}
.tabulator .tabulator-loader-msg {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.tabulator .tabulator-loader-msg .tabulator-loader-progress { background: var(--accent); }
.tabulator .tabulator-footer .tabulator-page-size {
  background: var(--bg-inset);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
}
.tabulator .tabulator-header .tabulator-col .tabulator-arrow {
  border-bottom-color: var(--c-neutral-400);
}
.tabulator .tabulator-col.tabulator-sortable[aria-sort="ascending"] .tabulator-arrow {
  border-bottom-color: var(--accent);
}
.tabulator .tabulator-col.tabulator-sortable[aria-sort="descending"] .tabulator-arrow {
  border-top-color: var(--accent);
  border-bottom-color: transparent;
}

/* ---- 小工具类 ---- */
.card__title { font-size: var(--fs-md); font-weight: var(--fw-bold); margin: 0 0 var(--sp-3); }
.list-note { font-size: var(--fs-sm); color: var(--text-secondary); margin: 0; padding-left: var(--sp-5); }
.list-note li { margin: 4px 0; }
.u-ml-3 { margin-left: var(--sp-3); }
.card--notice {
  border-color: var(--warn-border);
  background: var(--warn-weak);
  margin-bottom: var(--sp-4);
}

/* ---- 提示条 ---- */
.alert {
  display: flex;
  gap: var(--sp-2);
  align-items: flex-start;
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-xs);
  border: 1px solid transparent;
  margin-bottom: var(--sp-4);
}
.alert svg { flex: none; margin-top: 1px; }
.alert--danger { background: var(--danger-weak); border-color: var(--danger-border); color: var(--c-alarm-500); }
.alert--ok { background: var(--ok-weak); border-color: var(--ok-border); color: var(--c-moss-500); }
.alert--info { background: var(--info-weak); border-color: var(--info-border); color: var(--c-steel-600); }

/* ---- 登录页 ---- */
.login-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - var(--layout-topbar-h) - var(--layout-statusbar-h) - 64px);
}
.login-card { width: 100%; max-width: 380px; }
.login-card__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding-bottom: var(--sp-4); margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--border);
}
.login-card__head svg { width: 30px; height: 30px; color: var(--accent); }
.login-card__head h1 { font-size: var(--fs-lg); margin: 0; }
.login-card__head p { margin: 2px 0 0; font-size: var(--fs-2xs); color: var(--text-muted); }
.login-brand {
  height: var(--brand-line-h);
  background: var(--brand-line);
  border-radius: var(--radius-xs);
  margin-bottom: var(--sp-5);
}
.login-card .btn-primary { width: 100%; height: 38px; margin-top: var(--sp-4); }

/* 双入口：查看模式（共享口令）与管理登录 */
.login-mode { display: block; }
.login-mode > .form-label { margin-top: var(--sp-3); }
.login-mode .btn { width: 100%; height: 38px; margin-top: var(--sp-4); }
.login-mode__title {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2);
  font-size: var(--fs-xs);
}
.login-divider {
  display: flex; align-items: center; gap: var(--sp-2);
  margin: var(--sp-5) 0 var(--sp-2);
  font-size: var(--fs-2xs); color: var(--text-muted);
}
.login-divider::before,
.login-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.login-foot {
  margin-top: var(--sp-4);
  font-size: var(--fs-2xs);
  color: var(--text-muted);
  text-align: center;
}

/* ---- 错误页 ---- */
.error-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-12) var(--sp-4);
  text-align: center;
}
.error-page svg { width: 36px; height: 36px; opacity: .5; }
.error-page__code {
  font-family: var(--font-mono);
  font-size: var(--fs-3xl);
  color: var(--text-secondary);
  line-height: 1.2;
}
.error-page__title { font-size: var(--fs-lg); margin: var(--sp-2) 0 0; }
.error-page__detail { max-width: 460px; color: var(--text-secondary); margin: 0; }
.error-page__actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-4); }

/* ---- 空状态 ---- */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-10) var(--sp-4);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.empty-state svg { width: 32px; height: 32px; opacity: .5; }

/* ---- 加载失败（整块替换表格）----
   目的：服务停止 / 接口报错时给出**明确原因 + 重试入口**，
   不能只是空表 + 一排「—」——那样与「本来就没有数据」无法区分。

   2026-09-30 改整页滚动后重做：表格可能有几万像素高，原来的
   `position: absolute; inset: 0` 会把提示层垂直居中到视口之外（根本看不见）。
   现在改成 sticky，贴在表头下方，滚到哪都看得见。 */
.load-error {
  position: sticky;
  top: var(--layout-topbar-h);
  /* 比吸顶表头高一级（表头是 --z-sticky - 1），否则被表头盖住 */
  z-index: var(--z-sticky);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 240px;
  padding: var(--sp-8) var(--sp-4);
  text-align: center;
  background: var(--bg-card);
  border-bottom: 1px solid var(--card-border);
}
/* 失败时把表格（连同表头）整块藏起来：否则 Tabulator 会同时显示
   「没有符合条件的记录」占位文案，与上方的失败原因自相矛盾 */
.table-card .load-error:not([hidden]) ~ .tabulator { display: none; }
/* 商业报表页专用：三个 Tabulator 分散在三张卡里，load-error 是它们的共同兄弟节点。
   失败时整段收起，避免「错误提示 + 三张旧数据表」同屏——留着旧数据就是假装还有数据。 */
.load-error:not([hidden]) ~ .analytics-panels { display: none; }
.load-error[hidden] { display: none; }
.load-error svg { width: 34px; height: 34px; color: var(--danger); opacity: .8; }
.load-error__title { margin: 0; font-size: var(--fs-md); font-weight: var(--fw-medium); color: var(--text-primary); }
.load-error__detail {
  margin: 0; max-width: 54ch;
  font-size: var(--fs-sm); color: var(--text-secondary);
}

/* ==========================================================================
   6. 第三方组件暗色适配
   ========================================================================== */
/* Tom-Select */
.ts-wrapper .ts-control {
  background: var(--input-bg);
  border-color: var(--input-border);
  color: var(--input-fg);
  border-radius: var(--input-radius);
  min-height: var(--input-h);
  box-shadow: none;
}
.ts-wrapper.focus .ts-control { border-color: var(--input-border-focus); box-shadow: var(--focus-ring); }
.ts-wrapper .ts-dropdown {
  background: var(--bg-elevated);
  border-color: var(--border);
  color: var(--text-primary);
}
.ts-wrapper .ts-dropdown .option { color: var(--text-primary); }
.ts-wrapper .ts-dropdown .option.active { background: var(--accent-weak); color: var(--text-primary); }
.ts-wrapper .ts-dropdown .option:hover { background: var(--bg-hover); }
.ts-control > input { color: var(--input-fg) !important; }
.ts-control input::placeholder { color: var(--input-placeholder) !important; }

/* SweetAlert2 */
.swal2-popup {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--card-radius);
  font-family: var(--font-sans);
}
.swal2-title, .swal2-html-container { color: var(--text-primary); }
.swal2-html-container { font-size: var(--fs-sm); }
.swal2-input, .swal2-select, .swal2-textarea {
  background: var(--input-bg);
  color: var(--input-fg);
  border-color: var(--input-border);
}
.swal2-confirm { background: var(--accent) !important; color: var(--on-accent) !important; }
.swal2-cancel { background: var(--bg-elevated) !important; color: var(--text-primary) !important; }
.swal2-validation-message { background: var(--danger-weak); color: var(--c-alarm-500); }

/* Bootstrap 模态框与下拉 */
.modal-content { background: var(--bg-elevated); border-color: var(--border); color: var(--text-primary); }
.modal-header, .modal-footer { border-color: var(--border); }
.dropdown-menu { background: var(--bg-elevated); border-color: var(--border); font-size: var(--fs-sm); }
.dropdown-item { color: var(--text-primary); }
.dropdown-item:hover { background: var(--bg-hover); color: var(--text-primary); }
.dropdown-divider { border-color: var(--border); }

/* ==========================================================================
   7. 工具类
   ========================================================================== */
.text-secondary { color: var(--text-secondary) !important; }
.text-muted { color: var(--text-muted) !important; }
.text-accent { color: var(--accent) !important; }
.text-ok { color: var(--ok) !important; }
.text-warn { color: var(--warn) !important; }
.text-danger { color: var(--danger) !important; }
.num { font-variant-numeric: tabular-nums; }
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0; padding: 0;
}
.mono-sm { font-family: var(--font-mono); font-size: var(--fs-xs); }

/* ==========================================================================
   8. 响应式兜底（本应用是桌面工具，仅保证手机可查看）
   ========================================================================== */
@media (max-width: 1100px) {
  .app-main { padding: var(--sp-4) var(--sp-4) calc(var(--layout-statusbar-h) + var(--sp-5)); }
  .topbar { flex-wrap: wrap; height: auto; padding: var(--sp-2) var(--sp-3); }
  .topbar__nav { margin-left: 0; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* ==========================================================================
   9. 出入库登记页
   ========================================================================== */
.card__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
}
.card__head h2 { margin: 0; font-size: var(--fs-md); font-weight: var(--fw-medium); }
.card__head .form-control { max-width: 150px; }
.card__body { padding: var(--sp-4); }
.card__body--flush { padding: 0; }

/* 三栏始终横排（用户明确要求：不要竖着堆叠）。
   旧版有 ≤1280px 折两栏、≤860px 折单列的断点，普通笔记本 / 高 DPI 缩放正好踩进去，
   三块卡片就被拆成了上下排列。现在把 min 值压到刚好放得下的程度
   （180+220+280+间距+页面内边距 ≈ 760px），只有真正的窄屏才退化成单列。 */
.mov-col { min-width: 0; }   /* 三列 grid 子项防长内容撑破（类名同时是结构语义标记） */
.movements-grid {
  display: grid;
  grid-template-columns: minmax(180px, 4fr) minmax(220px, 5fr) minmax(280px, 7fr);
  gap: var(--sp-4);
  align-items: start;
}
@media (max-width: 760px) {
  .movements-grid { grid-template-columns: 1fr; }
}

/* 方向选择：分段控件（出库/入库一眼可辨） */
.seg { display: flex; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.seg__opt { flex: 1; margin: 0; position: relative; }
.seg__opt input { position: absolute; opacity: 0; pointer-events: none; }
.seg__opt span {
  display: block; text-align: center; padding: 8px var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-secondary); cursor: pointer; user-select: none;
  font-size: var(--fs-sm);
  transition: border-color .12s ease, background-color .12s ease, color .12s ease;
}
.seg__opt input:checked + span {
  border-color: var(--accent); background: var(--accent-weak);
  /* 同 .badge--accent：弱底上的 14px 字取 700 档才够 4.5:1 */
  color: var(--accent-active); font-weight: var(--fw-medium);
}
/* 焦点反馈必须由 span 承担——radio 本身是 opacity:0 的隐藏元素，全局
   :focus-visible 的 box-shadow 画在它身上等于没画。这里复用全局 --focus-ring，
   保证与全站其它控件的焦点外观一致（此前用 --accent-border 仅 1.72:1，基本看不见）。 */
.seg__opt input:focus-visible + span { box-shadow: var(--focus-ring); }

/* ---- 抽屉「快速登记」表单（stock-table.js 动态渲染进 #detailBody）----
   双击库存总表行 → 看到结存 → 顺手补一笔；样式全部走令牌，与登记页同观感。 */
.quickmv { display: flex; flex-direction: column; gap: var(--sp-3); margin: var(--sp-3) 0 var(--sp-4); }
.quickmv .seg { margin-bottom: 0; }
.quickmv__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: var(--sp-2) var(--sp-3);
}
.quickmv__grid label {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}
.quickmv__grid .form-control { min-height: var(--input-h); }
.quickmv__err { margin: 0; font-size: var(--fs-sm); color: var(--danger); }
.quickmv__hint { margin: 0; font-size: var(--fs-xs); color: var(--text-muted); }

.item-info { margin-top: var(--sp-4); border-top: 1px dashed var(--border); padding-top: var(--sp-3); }
.item-info .detail-kv {
  display: grid; grid-template-columns: auto 1fr; gap: 2px var(--sp-3);
  margin: 0 0 var(--sp-3); font-size: var(--fs-sm);
}
.item-info__nums { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-2); }
.item-info__nums .kpi__value { font-size: var(--fs-lg); }
.item-info__status { margin-top: var(--sp-2); }

/* 当天流水筛选行：方向分段 + 料号关键词（纯本地过滤，不动接口） */
.mov-filters {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); align-items: center;
  padding: var(--sp-2) var(--sp-4);
  border-bottom: 1px solid var(--border);
}
.mov-filters .seg { flex: 0 1 auto; margin-bottom: 0; }
.mov-filters .seg__opt { flex: 0 0 auto; }
.mov-filters .seg__opt span { padding: 4px var(--sp-3); font-size: var(--fs-xs); }
.mov-filters__item { flex: 1 1 auto; min-width: 0; height: auto; }

/* 显示设置弹窗（v3.3 显示设置）：表格字体/字号个性化，本机 localStorage 保存。
   结构与观感对齐 .colpick（同为 Swal 内嵌表单）；字号档位复用 .seg 的
   「隐藏 radio + 焦点环画相邻 span」范式。 */
.dispset { text-align: left; }
.dispset__group { margin-bottom: var(--sp-4); }
.dispset__group > .form-label { display: block; margin-bottom: var(--sp-2); }
.dispset__hint { margin: var(--sp-2) 0 0; font-size: var(--fs-2xs); color: var(--text-muted); }
.dispset__sizes { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.dispset__opt { position: relative; margin: 0; }
.dispset__opt input { position: absolute; opacity: 0; pointer-events: none; }
.dispset__opt span {
  display: block; padding: 4px var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-secondary); cursor: pointer; user-select: none;
  font-size: var(--fs-sm); font-family: var(--font-mono);
  transition: border-color .12s ease, background-color .12s ease, color .12s ease;
}
.dispset__opt input:checked + span {
  border-color: var(--accent); background: var(--accent-weak);
  /* 弱底上的 13–14px 字必须用 700 档（7.12:1）——同 .seg 的对比度门槛 */
  color: var(--accent-active); font-weight: var(--fw-medium);
}
.dispset__opt input:focus-visible + span { box-shadow: var(--focus-ring); }

.mov-summary {
  padding: var(--sp-2) var(--sp-4);
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border); background: var(--bg-inset);
}
.mov-table { max-height: 560px; border: none; border-radius: 0; }
.mov-table td .btn { padding: 1px 8px; font-size: var(--fs-2xs); }
.mov-row--voided td { opacity: .55; }
.mov-row--voided td.no-line { text-decoration: none; }

.form-control--date { font-family: var(--font-mono); }
.ta-r { text-align: right; }
.ta-c { text-align: center; }
.u-hidden { display: none !important; }
.u-mt-3 { margin-top: var(--sp-3); }

/* ============================================================================
   导入导出页（/io）
   ========================================================================= */
.dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); text-align: center; cursor: pointer;
  padding: var(--sp-5) var(--sp-4);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-inset);
  transition: border-color .15s ease, background-color .15s ease;
}
.dropzone:hover, .dropzone.is-over {
  border-color: var(--accent-border);
  background: var(--bg-hover);
}
/* 焦点态交给全局 :focus-visible 的 --focus-ring（实心主色，对白 4.91:1）。
   此前这里另画一条 --accent-border（38% 透明，压白仅约 1.7:1）的描边：既远低于
   WCAG 3:1，又和全局环叠成双环。dropzone 是真实可聚焦元素（tabindex="0"），
   全局环本来就画得到，删掉冗余的这条。 */
.dropzone svg { width: 34px; height: 34px; color: var(--accent); opacity: .9; }
.dropzone__title { margin: 0; font-size: var(--fs-sm); color: var(--text-primary); }
.dropzone__hint {
  margin: 0; max-width: 62ch;
  font-size: var(--fs-2xs); color: var(--text-muted);
}

.io-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.io-sub {
  margin: var(--sp-4) 0 var(--sp-2);
  font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--text-primary);
}
.io-sub .badge { margin-left: var(--sp-1); }

/* 品类多选 chips（/io「库存数据」导出卡）：可点选切换，选中态用青绿弱底 */
.chip-group { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-2) 0; }
.chip {
  padding: 3px var(--sp-3);
  background: var(--bg-inset);
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: var(--fs-xs);
  line-height: 1.6;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.chip:hover { background: var(--bg-hover); border-color: var(--accent-border); }
/* 同 .dropzone：chip 是真实 <button>，全局 --focus-ring 已经画得到，不再叠弱描边。 */
.chip.is-on {
  background: var(--accent-weak);
  border-color: var(--accent-border);
  /* 同 .badge--accent：弱底上的 13px 字取 700 档才够 4.5:1 */
  color: var(--accent-active);
  font-weight: var(--fw-medium);
}

/* TomSelect 料号候选行 */
.ts-option-line { display: flex; justify-content: space-between; gap: var(--sp-3); align-items: baseline; }
.ts-option-spec { white-space: normal; }
.ts-option-meta { color: var(--text-muted); white-space: nowrap; }
.ts-item-meta { color: var(--text-muted); }
.no-results { padding: 6px var(--sp-2); color: var(--text-muted); }

/* ============================================================================
   在途采购 / 销售预定 / 商业报表 / 物料修正（2026-10-06 新域页面）
   颜色一律走 tokens.css 令牌，禁止裸十六进制（test_tokens_contrast.py 守门）。
   ========================================================================= */

/* 导航红点（页面内提醒 D4：在途采购入口的待到货计数） */
.nav-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: var(--sp-1);
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: var(--c-neutral-000);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  line-height: 1;
  vertical-align: middle;
}
.topbar__nav a.is-active .nav-dot { color: var(--c-neutral-000); }

/* 详情抽屉加宽（D1：承载在途时间线 + 销售预定 + 编辑物料） */
.drawer--wide { width: min(880px, 94vw); }

/* 低库存整行高亮（F1.5）：盖过斑马纹，hover 反馈保留 */
.tabulator-row.row-lowstock { background-color: var(--danger-weak) !important; }
.tabulator-row.row-lowstock .tabulator-cell.tabulator-frozen {
  background-color: var(--bg-card);
}
/* 个人醒目标记（2026-10-08，仓管确认单黄色，按登录账号隔离）：人工意图优先于
   低库存提醒（rowFormatter 里标记行不再加 row-lowstock）。色值令牌单点 tools/theme.py。 */
.tabulator-row.row-mark-yellow { background-color: var(--mark-yellow-weak) !important; }
.tabulator-row.row-mark-yellow .tabulator-cell.tabulator-frozen {
  background-color: var(--bg-card);
}
/* /admin 数据备份：下载链接 */
.adm-backup-dl { color: var(--info); }

/* 抽屉顶部的标识条：单个「标记为醒目」按钮（已标记时为取消态） */
.markbar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 0 var(--sp-3);
  margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border);
}
.markbar__label { font-size: var(--fs-sm); color: var(--text-secondary); }
.markbar__btn {
  background: var(--bg-inset);
  border: 1px solid var(--border-strong);
  color: var(--text-primary);
  font-size: var(--fs-sm);
  padding: 3px 10px;
  border-radius: var(--radius-sm, 6px);
  cursor: pointer;
}
.markbar__btn:hover { background: var(--bg-hover); }
.markbar__btn.is-active {
  background: var(--mark-yellow-weak);
  border-color: var(--warn-border);
  color: var(--warn);
  font-weight: var(--fw-medium);
}
.markbar__btn.is-active::before { content: "★ "; }
.markbar__hint { font-size: var(--fs-xs); color: var(--text-muted); }

/* 编辑物料 / 到货确认等 Swal 表单（F4/D12） */
.edititem { display: flex; flex-direction: column; gap: var(--sp-3); text-align: left; }
.edititem__field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--fs-xs);
  color: var(--text-secondary);
}
.edititem__field .form-control { min-height: var(--input-h); }

/* 预计库存时间线（F1.4）：紧凑表 */
.proj-table { max-height: 300px; border: none; border-radius: 0; }
.proj-table td, .proj-table th { padding: 4px var(--sp-2); }

/* 条形图（商业报表 F3：纯 CSS，零依赖） */
.barchart {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-4);
}
.bar-row { display: flex; align-items: center; gap: var(--sp-3); }
.bar-rank { width: 24px; text-align: right; color: var(--text-muted); flex: none; }
.bar-label {
  width: min(320px, 34vw);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--fs-xs);
  color: var(--text-primary);
  flex: none;
}
.bar-track {
  flex: 1;
  height: 14px;
  background: var(--bg-inset);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.bar-fill {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: inherit;
}
.bar-value { width: 72px; text-align: right; flex: none; color: var(--text-secondary); }

/* 预定页「已失效」KPI 的中性档 */
.statstrip__item--muted .statstrip__value { color: var(--text-muted); }

/* ==================================================================
   系统管理页（/admin）：模块标签、期间状态卡、时间线、审计 JSON
   配色只消费 tokens.css 令牌（test_tokens_contrast.py 契约）。
   ================================================================== */

/* 筛选栏里的窄输入（操作人等短字段） */
.form-control--narrow { max-width: 10rem; }

/* 期间状态卡：本页唯一有性格的地方——当前期间就是页面的主角 */
.adm-period-card { padding: var(--sp-5) var(--sp-5) var(--sp-4); }
.adm-period-card__main {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
.adm-period-card__period {
  font-family: var(--font-mono);
  font-size: var(--fs-3xl);
  /* ⚠ 原来写的是 --fw-semibold，而 tokens.css 只定义了 normal/medium/bold，
     整条声明一直静默失效、回退到继承字重——36px 的「主角」反而最没分量。
     --fw-bold 就是 600 档（= semibold 的常规叫法），直接用它。 */
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  letter-spacing: 0.02em;
}
.adm-period-card__impact {
  margin: var(--sp-2) 0 var(--sp-3);
  color: var(--text-secondary);
  font-size: var(--fs-sm);
  max-width: 60ch;
}

/* 期间历史时间线：一列「点 + 期间 + 状态」，替代千篇一律的表格 */
.adm-timeline { padding: var(--sp-3) var(--sp-4); }
.adm-timeline__row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border);
  min-height: var(--layout-table-row-h);
}
.adm-timeline__row:last-child { border-bottom: none; }
.adm-timeline__note {
  padding: 0 0 var(--sp-2) calc(var(--sp-3) + 18px);
  font-size: var(--fs-xs);
}
.adm-dot {
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--border-strong);
  background: var(--bg-card);
}
.adm-dot--current { background: var(--accent); border-color: var(--accent); }
.adm-dot--open { background: var(--bg-card); border-color: var(--ok); }
.adm-dot--closed { background: var(--border-strong); border-color: var(--border-strong); }

/* 审计详情里的 JSON 快照 */
.adm-json {
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  background: var(--bg-inset);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  line-height: 1.6;
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-all;
}

/* 管理页区块尾部的口径说明 */
.adm-note {
  margin: var(--sp-3) var(--sp-1) 0;
  color: var(--text-muted);
  font-size: var(--fs-xs);
}

/* ==========================================================================
   10. 手机端适配（2026-10-10，仅库存总表：用户要求「只适配库存总表」）
   断点 720px：
     * 顶栏不再吸顶（随页滚走），表头/失败提示层改为贴视口顶——
       避免顶栏折行后高度不定、把 sticky 偏移算错的问题；
     * 导航 8 项改横向滑动（不折行，滑动查看其余项）；
     * 品类下拉 / 关键词搜索占满整行，按钮行自动换行；
     * 输入框 16px —— 防 iOS Safari 聚焦时自动放大整页；
     * 抽屉全宽、SweetAlert 弹窗限宽 92vw。
   只动既有类的断点表现，不新增结构类（契约测试安全）。
   ========================================================================== */
/* ----------------------------------------------------------------------------
   手机断点（≤720px）
   ⚠ 整站宽度断点共 4 个：1120 / 1100 / 760 / 720。其中 1120 与 1100 是**按组件**
   各自定的阈值（1120 收起统计条备注、1100 顶栏换行），不是同一套栅格的两个档位，
   **不要合并**——合了会让顶栏在 1101–1120px 之间提前换行。760 是「登记页三栏
   拆单列」的组件局部断点，就地放在 .movements-grid 旁边。
   ---------------------------------------------------------------------------- */
@media (max-width: 720px) {
  :root { --fs-2xl: 22px; --fs-xl: 18px; }
  .topbar__brand { font-size: var(--fs-md); }
  .topbar__version { display: none; }
  .statusbar { gap: var(--sp-3); font-size: var(--fs-2xs); }
  .statusbar__optional { display: none; }

  .app-main { padding: var(--sp-3) var(--sp-3) calc(var(--layout-statusbar-h) + var(--sp-4)); }

  .topbar { position: static; height: auto; padding: var(--sp-2) var(--sp-3); row-gap: var(--sp-1); }
  .topbar__nav {
    flex-wrap: nowrap;
    overflow-x: auto;
    margin: 0 calc(-1 * var(--sp-3));
    padding: 0 var(--sp-3);
    width: calc(100% + 2 * var(--sp-3));
    scrollbar-width: none;
  }
  .topbar__nav::-webkit-scrollbar { display: none; }
  .topbar__nav a { flex: none; height: 34px; }

  .page-head p { display: none; }   /* 说明句在手机上是噪音 */
  .statstrip { padding: var(--sp-2) var(--sp-3); gap: var(--sp-1) var(--sp-3); }

  .filterbar { padding: var(--sp-3); gap: var(--sp-2); }
  .filterbar .form-control { max-width: none; flex: 1 1 100%; }
  .filterbar .form-select { width: 100%; min-width: 0; flex: 1 1 100%; }
  .filterbar .btn { flex: 1 1 auto; }
  .filterbar__note { display: none; }   /* 列说明长文案：显示列弹窗里本就有 */

  /* iOS Safari：输入字号 <16px 聚焦时会自动放大整页 */
  .form-control, .form-select { font-size: 16px; }

  /* 顶栏不吸顶后，表头 / 失败提示层贴视口顶 */
  .table-card .tabulator .tabulator-header { top: 0; }
  .load-error { top: 0; }

  .drawer { width: 100vw; border-left: none; }
  .drawer__body { padding: var(--sp-3); }

  /* Swal 表单弹窗（编辑物料 / 新增料号 / 显示列 等）：限宽防横向溢出 */
  .swal2-popup { max-width: 92vw; }
}
