:root {
  --primary: #2563eb;
  --primary-dark: #1d4ed8;
  --primary-soft: #eff4ff;
  --danger: #dc2626;
  --danger-soft: #fdecec;
  --warn: #d97706;
  --warn-soft: #fff4e5;
  --ok: #059669;
  --ok-soft: #e8f7f1;
  --purple: #7c4dff;
  --teal: #0f9d8f;
  --orange: #e2661a;

  --bg: #f5f7fb;
  --panel: #ffffff;
  --border: #e4e8f0;
  --border-strong: #d3d9e5;
  --text: #1f2733;
  --text-2: #5b6478;
  --text-3: #939db0;
  --shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 4px 16px rgba(16, 24, 40, .05);
  --radius: 10px;
  --sidebar-w: 208px;
}

* { box-sizing: border-box; }
html, body { min-height: 100%; }
html { background: var(--bg); }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Arial, sans-serif;
  font-size: 14px;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
a { color: var(--primary); text-decoration: none; }
button, input, select, textarea { font-family: inherit; font-size: 14px; }
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-thumb { background: #cdd4e0; border-radius: 6px; }
::-webkit-scrollbar-thumb:hover { background: #b6bfd0; }

/* ---------------- 布局 ---------------- */
.layout { display: flex; min-height: 100vh; }
.sidebar {
  width: var(--sidebar-w); flex: none; background: #1b2333; color: #c8d0e0;
  display: flex; flex-direction: column; position: fixed; top: 0; bottom: 0; left: 0; z-index: 40;
}
.sidebar .brand {
  padding: 18px 16px 14px; display: flex; align-items: center; gap: 9px;
  border-bottom: 1px solid rgba(255,255,255,.07);
}
.sidebar .brand .logo {
  width: 30px; height: 30px; border-radius: 8px; flex: none;
  background: linear-gradient(135deg, #3b82f6, #22d3ee);
  display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 15px;
}
.sidebar .brand b { color: #fff; font-size: 14px; line-height: 1.3; font-weight: 600; }
.nav { padding: 10px 8px; overflow-y: auto; flex: 1; }
.nav .group {
  padding: 16px 12px 8px; font-size: 12px; color: #9aa5b8; letter-spacing: .04em;
  font-weight: 600;
}
.nav .group-items { padding-left: 2px; }
.nav a {
  display: flex; align-items: center; gap: 9px; padding: 9px 11px; border-radius: 8px;
  color: #c2cadb; font-size: 13.5px; margin-bottom: 2px; transition: .15s; cursor: pointer;
}
.nav a:hover { background: rgba(255,255,255,.07); color: #fff; }
.nav a.active { background: var(--primary); color: #fff; font-weight: 500; }
.nav a .ico { width: 16px; text-align: center; opacity: .95; font-size: 14px; }

/* 导航分组（折叠 / 展开） */
.nav .nav-group { margin-bottom: 2px; }
.nav .nav-group-head {
  display: flex; align-items: center; gap: 8px; padding: 8px 12px 6px;
  color: #fff; font-size: 12.5px; font-weight: 700;
  letter-spacing: .04em; cursor: pointer; user-select: none;
}
.nav .nav-group-head:hover { color: #fff; }
.nav .nav-group-icon { width: 18px; text-align: center; font-size: 14px; opacity: .9; }
.nav .nav-group-title { flex: 1; color: #fff; font-weight: 700; }
.nav .nav-group-arrow {
  font-size: 10px; color: #fff; font-weight: 700; transition: transform .15s;
  width: 12px; text-align: center;
}
.nav .nav-group.collapsed .nav-group-items { display: none; }
.nav .nav-item { padding-left: 22px; }

/* 新版 Tailwind 分组导航 */
#nav .tw-nav-group { margin-bottom: 2px; }
#nav .tw-nav-group-head {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; border-radius: 8px;
  color: #475569; font-size: 13.5px; font-weight: 600;
  transition: background .15s;
}
#nav .tw-nav-group-head:hover { background: #f1f5f9; }
#nav .tw-nav-items {
  position: relative;
  margin-left: 12px; padding-left: 12px;
  border-left: 2px solid #e2e8f0;
  padding-top: 2px; padding-bottom: 2px;
}
#nav .tw-nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px 8px 14px; border-radius: 8px;
  color: #64748b; font-size: 13.5px;
  transition: .15s; cursor: pointer;
}
#nav .tw-nav-item:hover { background: #eff6ff; color: #2563eb; }
#nav .tw-nav-item.active { background: #eff6ff; color: #2563eb; font-weight: 500; }
#nav .tw-nav-item .ico { width: 18px; text-align: center; opacity: .95; font-size: 14px; }

/* ==================== 导航图标（SVG，见 js/icons.js） ====================
 * 设计：统一的「圆角方块底 + 线性图标」= 双色（duotone）效果。
 *   - 默认：浅灰底 + 灰图标
 *   - hover：浅蓝底 + 品牌蓝图标
 *   - 选中：实心品牌蓝底 + 白色图标（最醒目，一眼看出当前页）
 * 颜色全部走 var()，所以换主题色时图标自动跟随。
 * 注意：这类 DOM 由 JS 字符串拼接生成，**必须**用下面的自定义类名，
 * 不要改成 Tailwind 类（内容扫描扫不到，会丢样式）。 */
.nav-ico {
  flex: none;
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px;
  background: #f1f5f9;
  color: #64748b;
  transition: background .15s, color .15s, box-shadow .15s;
}
.nav-ico .nav-ico-svg { width: 16px; height: 16px; display: block; }
#nav .tw-nav-item:hover .nav-ico { background: #dbeafe; color: #2563eb; }
#nav .tw-nav-item.active .nav-ico {
  background: var(--primary); color: #fff;
  box-shadow: 0 2px 8px rgba(37, 99, 235, .32);
}
/* 分组标题的图标底略大一点、底色略深，形成层级差 */
.nav-ico-g { width: 28px; height: 28px; border-radius: 9px; background: #eef2f7; color: #475569; }
.nav-ico-g .nav-ico-svg { width: 17px; height: 17px; display: block; }
#nav .tw-nav-group-head:hover .nav-ico-g { background: #dbeafe; color: #2563eb; }
/* 折叠状态下分组图标更醒目 */
#nav .tw-nav-group.tw-collapsed .nav-ico-g { background: #e2e8f0; }

/* 侧边栏拖拽条 */
#sidebarResizer {
  position: absolute; top: 0; right: 0; bottom: 0; width: 6px;
  cursor: col-resize; z-index: 50;
  background: transparent;
  transition: background .15s;
}
#sidebarResizer:hover { background: rgba(37, 99, 235, .18); }
#sidebarResizer:active { background: rgba(37, 99, 235, .32); }

/* 高级筛选通用样式（Excel 风格） */
.ws-col-filter-cell { padding: 4px 6px !important; vertical-align: middle; }
.ws-col-filter-cell .input { display: block; width: 100%; min-width: 0; height: 26px; padding: 0 6px; font-size: 12px; box-sizing: border-box; }
.ws-col-filter-cell .ws-date-pair { display: flex; gap: 4px; }
.ws-col-filter-cell .ws-date-pair input[type="date"] { flex: 1; min-width: 0; }

/* 列表表格最小宽度（防止列被压缩导致高级筛选行换行） */
.tb-wo-list { min-width: 2000px; }
.tb-wo-list thead th { white-space: nowrap; }
/* 工单列表「反馈内容」列限宽：长文本不再把整列撑开（仍单行显示，超出省略，hover 看完整内容）
   ⚠ 内层必须用 display:block 跟着单元格内容区走（td 是 border-box，600px 里含左右各 12px padding，
     若内层再写 max-width:600px 会横向溢出 24px 被 td 的 overflow:hidden 裁掉，省略号就看不见了） */
.tb-wo-list th.wo-col-fb, .tb-wo-list td.wo-col-fb { max-width: 600px; }
.wo-cell-cap {
  display: block; max-width: 600px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nav a .badge {
  margin-left: auto; background: var(--danger); color: #fff; font-size: 11px;
  padding: 1px 6px; border-radius: 9px; font-weight: 600;
}
.sidebar .foot { padding: 10px 14px; border-top: 1px solid rgba(255,255,255,.07); font-size: 11.5px; color: #6b7689; }

.main { flex: 1; margin-left: var(--sidebar-w); min-width: 0; display: flex; flex-direction: column; }
.topbar {
  height: 56px; background: #fff; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 14px; padding: 0 20px;
  position: sticky; top: 0; z-index: 30;
}
.topbar .page-title { font-size: 16px; font-weight: 600; white-space: nowrap; }
.searchbox { position: relative; margin-left: auto; width: 340px; }
.searchbox input {
  width: 100%; height: 34px; border: 1px solid var(--border-strong); border-radius: 18px;
  padding: 0 34px 0 34px; background: #f7f9fc; outline: none; transition: .15s;
}
.searchbox input:focus { background: #fff; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.searchbox .icon { position: absolute; left: 12px; top: 8px; color: var(--text-3); }
.suggest {
  position: absolute; top: 40px; left: 0; right: 0; background: #fff; border: 1px solid var(--border);
  border-radius: 10px; box-shadow: var(--shadow); max-height: 380px; overflow: auto; z-index: 60;
}
.suggest div.item { padding: 9px 12px; cursor: pointer; display: flex; align-items: center; gap: 8px; }
.suggest div.item:hover { background: var(--primary-soft); }
.suggest .tag { font-size: 11px; padding: 1px 6px; border-radius: 4px; background: #eef1f6; color: var(--text-2); flex: none; }
.suggest .c { font-family: ui-monospace, Menlo, Consolas, monospace; color: var(--primary); font-size: 12.5px; }
.suggest .n { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.userbox { display: flex; align-items: center; gap: 8px; }
.avatar {
  width: 30px; height: 30px; border-radius: 50%; background: var(--primary); color: #fff;
  display: grid; place-items: center; font-size: 13px; font-weight: 600;
}
.content { padding: 18px 20px 40px; flex: 1; }

/* ---------------- 通用组件 ---------------- */
.card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
.card .card-hd {
  padding: 13px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 10px;
}
.card .card-hd h3 { margin: 0; font-size: 14.5px; font-weight: 600; display: inline-flex; align-items: center; gap: 8px; }
.card .card-hd h3::before {
  content: ''; display: inline-block; width: 4px; height: 1em; border-radius: 2px; background: var(--primary);
}
.card .card-hd .sub { color: var(--text-3); font-size: 12.5px; }
.card .card-bd { padding: 14px 16px; }
.card .card-bd.nopad { padding: 0; }

/* 系统设置：每张卡片右上角的「编辑 / 保存」按钮对（只提交该卡片内的配置）。
   用淡蓝底 + 主色字，避免一屏多个实心蓝按钮太抢眼。 */
.card .card-hd .card-hd-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.btn.card-save-btn { border-color: var(--primary); color: var(--primary); background: var(--primary-soft); font-weight: 500; }
.btn.card-save-btn:hover:not(:disabled) { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn.card-save-btn:disabled { background: var(--primary-soft); border-color: var(--border); color: var(--text-3); }
.btn.card-edit-btn { border-color: var(--border); color: var(--text-2); background: var(--panel); font-weight: 500; }
.btn.card-edit-btn:hover:not(:disabled) { border-color: var(--primary); color: var(--primary); }
.btn.card-edit-btn:disabled { opacity: .55; }

/* 卡片编辑态高亮 —— 一眼看出哪张卡片被「编辑」解锁了（默认全站卡片都是只读灰底，
   灰底样式由上面的 .input:disabled 统一负责，这里不必重复定义）。 */
.card.set-card:not(.is-locked) { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }

/* ===== 系统设置顶部横向导航（2026-11-03 用户要求）=====
   按业务把卡片归成 5 类，点标签只显示该类，不用再滚十几屏。
   ⚠ 卡片用 hidden 隐藏；`.card` 本身没有 display 声明，UA 的 `[hidden]{display:none}` 能生效，
     但为防以后给 `.card` 加 display 把它盖掉（项目里踩过 .btn 的同类坑），这里显式补一条。 */
.set-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.set-tab {
  padding: 7px 15px; border-radius: 9px; border: 1px solid var(--border);
  background: #fff; color: var(--text-2); font-size: 13.5px; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.set-tab:hover { border-color: var(--primary); color: var(--primary); }
.set-tab.active { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600; }
.set-card[hidden], #updCard[hidden] { display: none !important; }
@media (max-width: 767.98px) {
  /* 手机端一行放不下 5 个标签 → 横向滚动，不折成两行（折行会把内容继续往下推） */
  .set-tabs { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; -webkit-overflow-scrolling: touch; }
  .set-tab { flex: 0 0 auto; white-space: nowrap; }
}

/* SMTP 卡片内的「校验 SMTP / 发送测试邮件」动作按钮（2026-09-25 从页面底部移上来）。
   虚线与上方字段隔开，表示这是「对整张卡片做诊断」的动作、不是可填字段；
   ⚠ 与卡片只读锁无关，锁定态照样可点，所以不参与 disabled 逻辑。 */
.card .card-bd .smtp-actions {
  display: flex; gap: 8px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--border);
}

/* 系统设置：这几张卡片的字段在**移动端一行一个**（用户 2026-09-25 要求）——
   基本设置 / 编号规则 / 工单管理默认筛选 / 预警与邮件 / SMTP 邮件服务器 / 登录安全。
   其余卡片（界面与导航、预警阈值、自动备份）在移动端保持默认两列。
   ⚠ 移动端那条全局 `.form-grid { grid-template-columns: 1fr 1fr }` 会把这些卡片也压成两列，
     这里靠**更高的选择器权重**盖回单列（与源码顺序无关，不必挪到文件末尾）。
   ⚠ 断点必须与那条全局规则**完全一致**（max-width: 768px），否则「正好 768px」时会出现
     两列/单列各生效一半的错位。 */
@media (max-width: 768px) {
  .card.set-card.mobile-1col .card-bd .form-grid { grid-template-columns: 1fr; }
  .card.set-card.mobile-1col .card-bd .form-grid .half,
  .card.set-card.mobile-1col .card-bd .form-grid .full { grid-column: auto; }
}

/* 工作台顶部公告条 */
.notice-topbar {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 8px 12px; background: #fffbeb; border: 1px solid #fde68a; border-radius: 8px;
}
.notice-topbar-label {
  flex: 0 0 auto; margin-top: 1px;
  padding: 2px 8px; background: #f59e0b; color: #fff; border-radius: 4px;
  font-size: 12px; font-weight: 600; line-height: 1.5;
}
.notice-topbar-list {
  flex: 1 1 auto; display: flex; flex-direction: column; gap: 6px;
  max-height: 60px; overflow-y: auto; scrollbar-width: thin;
}
.notice-topbar-list::-webkit-scrollbar { width: 5px; }
.notice-topbar-list::-webkit-scrollbar-thumb { background: #fcd34d; border-radius: 3px; }
.notice-topbar-item {
  font-size: 13px; line-height: 1.55; color: #92400e;
}
.notice-topbar-item p { margin: 0; }
.notice-topbar-item a { color: var(--primary); text-decoration: underline; }

/* 简易富文本编辑器 */
.rte-wrap { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.rte-toolbar {
  display: flex; gap: 6px; padding: 6px 8px; background: #f8fafc;
  border-bottom: 1px solid var(--border); flex-wrap: wrap;
}
.rte-toolbar button {
  min-width: 28px; height: 28px; padding: 0 6px; border: 1px solid var(--border);
  background: #fff; border-radius: 5px; cursor: pointer; font-size: 13px; line-height: 1;
}
.rte-toolbar button:hover { border-color: var(--primary); color: var(--primary); }
.rte-editor {
  min-height: 180px; max-height: 260px; padding: 10px 12px; overflow-y: auto;
  line-height: 1.6; outline: none;
}
.rte-editor p { margin: 0 0 8px; }
.rte-editor p:last-child { margin-bottom: 0; }
.rte-editor ul, .rte-editor ol { margin: 0 0 8px 18px; padding: 0; }
.rte-editor a { color: var(--primary); }
.rte-editor:empty::before { content: '请输入公告内容…'; color: var(--text-3); }

.btn {
  display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 13px;
  border: 1px solid var(--border-strong); background: #fff; border-radius: 7px;
  cursor: pointer; color: var(--text); transition: .15s; white-space: nowrap; font-size: 13.5px;
}
.btn:hover { border-color: var(--primary); color: var(--primary); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
/* .btn 自己声明了 display:inline-flex，会盖掉浏览器对 [hidden] 的默认 display:none，
   于是「设了 hidden 的按钮照样显示」。这里显式补上（踩过一次：升级卡片的「刷新页面」按钮）。 */
.btn[hidden] { display: none; }
.btn.primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn.primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); color: #fff; }
.btn.danger { background: #fff; border-color: #f0c2c2; color: var(--danger); }
/* 兼容 Tailwind 的 hidden/md:table-cell：app.css 的 table.tb td { display: table-cell } 覆盖了 .hidden。
   提升 .hidden 优先级：.hidden{display:none} 即可。 */
.hidden { display: none; }
/* 列表列移动端显隐助手：hide-mobile 在 <768 隐藏，show-mobile 在 >=768 隐藏
   （用 !important 确保压过 table.tb td { display:table-cell }） */
@media (max-width: 767.98px) {
  .hide-mobile { display: none !important; }
}
@media (min-width: 768px) {
  .show-mobile { display: none !important; }
}
/* 设备档案页：移动端把"主体信息 + 一物一码"两卡改为上下排列（一物一码卡再加 .hide-mobile 完全隐藏）。
   inline style 写了 grid-template-columns:1fr 268px，需 !important 覆盖。 */
@media (max-width: 767.98px) {
  .detail-grid { grid-template-columns: 1fr !important; }
}
@media (min-width: 768px) {
  .md\:table-cell { display: table-cell; }
  .md\:inline-flex { display: inline-flex; }
}
.btn.danger:hover { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn.wechat { background: #07C160; border-color: #07C160; color: #fff; }
.btn.wechat:hover { background: #06ad56; border-color: #06ad56; color: #fff; }
.btn.sm { height: 27px; padding: 0 10px; font-size: 12.5px; border-radius: 6px; }
.btn.ghost { border-color: transparent; background: transparent; }
.btn.ghost:hover { background: var(--primary-soft); }
.btn.pill { height: 34px; border-radius: 17px; padding: 0 18px; font-size: 13.5px; }
.btn.pill:hover { border-color: var(--primary); color: var(--primary); }
.btn.pill.primary:hover { border-color: var(--primary-dark); color: #fff; }

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

.field { display: flex; flex-direction: column; gap: 5px; }
.field > label { font-size: 12.5px; color: var(--text-2); font-weight: 500; }
.field > label .req { color: var(--danger); margin-left: 2px; }
.input, select.input, textarea.input {
  height: 33px; border: 1px solid var(--border-strong); border-radius: 7px; padding: 0 10px;
  outline: none; background: #fff; width: 100%; transition: .15s; color: var(--text);
}
textarea.input { height: auto; padding: 8px 10px; resize: vertical; min-height: 62px; line-height: 1.6; }
.input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.input:disabled,
.input[readonly] { background: #f5f7fa; color: var(--text-3); cursor: not-allowed; }
.btn.locked-note { background: #fff8e1; border-color: #ffe0a3; color: #8a6d1a; font-weight: 500; }
.btn.locked-note:hover { background: #fff3cf; border-color: #ffd27a; color: #8a6d1a; }
select.input { appearance: none; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'><path d='M0 0l5 6 5-6z' fill='%23939db0'/></svg>"); background-repeat: no-repeat; background-position: right 11px center; padding-right: 28px; }
select.input.multi-select { appearance: auto; background-image: none; padding-right: 10px; min-height: 90px; height: auto; }
select.input.multi-select option { padding: 5px 8px; border-radius: 4px; margin: 2px 0; }
select.input.multi-select option:checked { background: var(--primary-soft); color: var(--primary); }
.input-wrap { position: relative; display: flex; align-items: center; }
.input-wrap .input { padding-right: 34px; }
.input-suffix { position: absolute; right: 0; top: 0; bottom: 0; width: 34px; border: none; background: transparent; cursor: pointer; color: var(--text-3); font-size: 14px; display: flex; align-items: center; justify-content: center; padding: 0; }
.input-suffix:hover { color: var(--primary); }
.input-suffix:disabled { cursor: not-allowed; opacity: .5; }
.filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.filters .field { width: 168px; }

.grid { display: grid; gap: 14px; }
/* 设备档案顶部的「租赁到期 / 下次保养」两张卡：桌面并排两列，**手机端一行一个**（2026-09-30 用户要求）。
   ⚠ 列数原来写在这两个卡片的**内联** `grid-template-columns:1fr 1fr` 上 —— 内联优先级最高，
     媒体查询/类都盖不住 → 已把列数挪到这条规则里由类控制，**别再写回内联**。
   ⚠ `@media (max-width: 767.98px)` = `<768`，与 `.hide-mobile` 同一口径。 */
.dev-big-row { grid-template-columns: 1fr 1fr; }
@media (max-width: 767.98px) {
  .dev-big-row { grid-template-columns: 1fr; }
}
.form-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 13px; }
.form-grid.single-col { grid-template-columns: 1fr; }
.form-grid.single-col .full, .form-grid.single-col .half { grid-column: 1 / -1; }
.form-grid .full { grid-column: 1 / -1; }
.form-grid .half { grid-column: span 2; }
.form-grid .field.break-row { grid-column-start: 1; }
.form-grid.two-col { grid-template-columns: repeat(2, 1fr); }
.form-grid.two-col .half { grid-column: span 1; }
.form-grid.two-col .full { grid-column: 1 / -1; }
@media (max-width: 860px) { .form-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 860px) { .form-grid.two-col { grid-template-columns: 1fr; } }

/* 高级筛选行：表头下方的输入框行（参考工单结算页样式） */
.advanced-filter-row td { background: #fafbfe; padding: 6px 8px; border-bottom: 1px solid var(--border); }
.advanced-filter-row td input[data-af-filter] { width: 100%; min-width: 100px; padding: 4px 8px; font-size: 12.5px; }
.advanced-filter-row td .ws-date-pair { display: flex; gap: 4px; align-items: center; }
.advanced-filter-row td .ws-date-pair input { flex: 1; min-width: 95px; padding: 4px 8px; font-size: 12.5px; }

/* 表格 */
.table-wrap { overflow: auto; }
table.tb { width: 100%; border-collapse: collapse; }
table.tb th {
  text-align: left; font-size: 12.5px; color: var(--text-2); font-weight: 600;
  padding: 10px 12px; background: #f8fafc; border-bottom: 1px solid var(--border); white-space: nowrap;
  position: sticky; top: 0; z-index: 2;
}
table.tb td { padding: 10px 12px; border-bottom: 1px solid #f0f3f8; vertical-align: middle; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
table.tb tbody tr:hover { background: #fafbfe; }
/* 选中行：整行统一浅金色（包含序号、滚动固定列、操作列） */
table.tb tbody tr.selected,
table.tb tbody tr.selected td,
table.tb tbody tr.selected td.sticky-left,
table.tb tbody tr.selected td.sticky-right { background: #f1dd9b; }
table.tb td.nowrap, table.tb th.nowrap { white-space: nowrap; }
/* 可点击整行（2026-10-20：保养记录列表移动端隐藏「操作」列后，靠点整行进入详情） */
table.tb tbody tr.clickable { cursor: pointer; }

/* 收款周期的发票附件（2026-10-21）：期次表单里的文件胶囊 + 列表里的「N 个」链接 */
.inv-files { margin-top: 4px; padding-top: 12px; border-top: 1px dashed var(--border); }
.inv-files-label { font-size: 13px; color: var(--text-2); font-weight: 500; margin-bottom: 6px; }
.inv-files-list { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.inv-file {
  display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px; border-radius: 8px;
  background: var(--bg-soft); border: 1px solid var(--border); font-size: 12.5px; max-width: 100%;
}
.inv-file .code-click { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inv-file-x { border: 0; background: transparent; color: var(--text-3); cursor: pointer; font-size: 14px; line-height: 1; padding: 0 2px; }
.inv-file-x:hover { color: var(--danger); }
.inv-files-act { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
table.tb.compact th, table.tb.compact td { padding: 7px 10px; font-size: 12.5px; }

/* 子表按产品分组 */
.sub-table-groups { display: flex; flex-direction: column; gap: 14px; }
.sub-group-card { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.sub-group-header { padding: 10px 12px; background: var(--bg-soft); display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sub-group-title { font-weight: 500; font-size: 14px; }
.sub-group-meta { font-size: 12.5px; color: var(--text-2); }
.sub-group-body .table-wrap { border-radius: 0; }
.sub-group-body table.tb th { background: #fff; }

/* BOM 多选搜索 */
.bom-multi-select { margin-top: 8px; border: 1px solid var(--border-strong); border-radius: 8px; padding: 10px; background: #fff; }
.bm-search { margin-bottom: 8px; }
.bm-search .input { height: 32px; }
.bm-options { max-height: 220px; overflow-y: auto; border: 1px solid var(--border); border-radius: 6px; padding: 4px; margin-bottom: 8px; }
.bm-options,
.bm-options-head,
.bm-option { white-space: normal; word-break: break-word; }
.bm-options-head { display: flex; align-items: center; gap: 8px; padding: 6px 8px; font-size: 12px; color: var(--text-3); font-weight: 600; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.bm-options-head .bm-col-sel { width: 16px; flex-shrink: 0; text-align: left; }
.bm-options-head .bm-col-loc { width: 56px; flex-shrink: 0; text-align: left; color: var(--text-3); }
.bm-options-head .bm-col-info { flex: 1 1 auto; min-width: 0; text-align: left; color: var(--text-3); }
.bm-options-list { display: flex; flex-direction: column; gap: 2px; }
.bm-option { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 5px; cursor: pointer; font-size: 13px; line-height: 1.5; }
.bm-option span { min-width: 0; overflow-wrap: break-word; }
.bm-option:hover { background: var(--bg-soft); }
.bm-option.selected { background: var(--primary-soft); }
.bm-option input[type="checkbox"] { margin: 0; flex-shrink: 0; width: 16px; height: 16px; min-width: 16px; }
.bm-option .bm-col { flex-shrink: 0; }
.bm-option .bm-col-loc { width: 56px; text-align: left; color: var(--text-2); }
/* 编码 + 简称 同一行显示（用简称后宽度足够一行放下，过长才回退换行） */
.bm-option .bm-col-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: row; align-items: center; gap: 8px; flex-wrap: wrap; }
.bm-option .bm-col-code { align-self: center; flex: 0 0 auto; padding: 2px 8px; border-radius: 4px; background: #e7f3ff; color: var(--primary); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 12px; word-break: break-all; }
.bm-option .bm-col-name { flex: 1 1 auto; min-width: 0; text-align: left; color: var(--text-2); word-break: break-word; }
/* 价格列（2026-10-27 用户要求：添加滤芯 / 配件时，在「编码 / 简称」右侧显示单价） */
.bm-options-head .bm-col-price { width: 76px; flex: 0 0 76px; text-align: right; color: var(--text-3); }
.bm-option .bm-col-price {
  width: 76px; flex: 0 0 76px; text-align: right; color: var(--text-2);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px;
}
.bm-empty { color: var(--text-3); font-size: 12.5px; padding: 10px; text-align: center; }
.bm-custom { margin-bottom: 8px; }
.bm-custom .input { height: 32px; }
.bm-selected { display: flex; flex-direction: column; gap: 8px; min-height: 26px; }
.bm-placeholder { color: var(--text-3); font-size: 12.5px; }
.bm-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; background: var(--bg-soft); border-radius: 6px; font-size: 13px; }
.bm-item-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bm-item-fields { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.bm-item-fields label { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--text-2); white-space: nowrap; }
.bm-item-fields .input { width: 70px; height: 28px; padding: 4px 6px; text-align: right; }
.bm-item-fields .input.bm-amount { background: var(--bg-soft); color: var(--text-1); }
.bm-remove { cursor: pointer; font-style: normal; color: var(--text-2); font-size: 18px; line-height: 1; padding: 0 4px; }
.bm-remove:hover { color: var(--danger); }

/* BOM 清单 */
#bomBox .hint { color: var(--text-3); font-size: 12.5px; }
#bomBox .hint-warn { color: var(--warning); font-size: 12.5px; padding: 8px 0; }

.image-count { color: var(--primary); cursor: pointer; font-size: 13px; }
.image-count:hover { text-decoration: underline; }
.preview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }

/* 固定列：左侧/右侧吸附 */
table.tb th.sticky-left,
table.tb td.sticky-left { position: sticky; left: 0; background: #f8fafc; z-index: 3; }
table.tb th.sticky-right,
table.tb td.sticky-right {
  position: sticky; right: 0; background: #fff; z-index: 3;
  /* ⚠⚠ 分隔线**不能**用 border-left 画（2026-09-30 实测修复）：
     `table.tb` 是 `border-collapse: collapse`，折叠边框由**表格**在「该列的静态位置」绘制，
     **不跟随 sticky 单元格移动** —— 横向滚动后这条线就原地留在视口外了。
     实测证据：把 sticky 单元格的 border-left 设成 `3px solid red`，
     滚动到表格中间截图，红线**一条都看不到**（未滚动时同样看不到，被相邻单元格盖住）；
     而给同一个单元格加 `outline` 则清清楚楚跟着走。
     症状 = 用户反馈的「看不清操作列在哪个位置」。
     ⇒ 改用 **inset box-shadow**：由单元格自己绘制，永远贴着自己的左边缘。
     外侧那条 `-3px 0 6px` 的柔和阴影同理，一起保留做层次。 */
  box-shadow: inset 1px 0 0 var(--border-strong), -3px 0 6px rgba(16,24,40,.06);
}
table.tb th.sticky-left,
table.tb th.sticky-right { z-index: 4; }
table.tb tbody tr:hover td.sticky-left,
table.tb tbody tr:hover td.sticky-right { background: #fafbfe; }

/* 出入库列表（成品/配件/耗材/滤芯，入库与出库）：操作列冻结在最右侧 + 固定宽度。
   2026-09-24 用户要求「查看/编辑/删除 固定在可视窗口最右侧，宽度固定」。
   复用上面的 sticky-right 机制（hover / 选中态的底色都已处理），这里只补两点：
   ① 固定列宽 —— 三个按钮的宽度稳定，不会因某行的按钮多少而左右抖动；
   ② 表头底色与相邻表头接缝 —— 既有规则把 th.sticky-right 设成 #fff，
      而普通表头是 #f8fafc，不修正的话这一列表头会比旁边白一块。 */
table.tb th.inv-op.sticky-right,
table.tb td.inv-op.sticky-right { width: 180px; }
table.tb th.sticky-right { background: #f8fafc; }
@media (max-width: 767.98px) {
  /* 手机端：整列隐藏（用户 2026-09-24 要求「防止手机端误操作」）。
     原来这里是「收窄到 168px」，现在直接不显示 —— 手机端点按钮太容易误触删除。
     ⚠ 两个都必须一起隐藏：只隐 td 会在表头留一个空的「操作」列。
     ⚠ 必须 !important：td.inv-op.inv-row-actions 是 display:flex（见下方 1384 行），
       不加压不过；同时 table.tb td 的 display:table-cell 也要压过。
     入库 / 出库两个列表共用这两个类，所以这一处同时管住它们。
     注：调拨单列表的表头是普通 `inv-op`（没有 sticky-right），不受影响。 */
  table.tb th.inv-op.sticky-right,
  table.tb td.inv-op.sticky-right { display: none !important; }
}

/* 库存查询列表（成品 / 配件 / 滤芯）：操作列也冻结在最右侧（2026-09-30 用户要求）。
   复用同一套 sticky-right 机制，只覆盖上面那两条「出入库列表专用」的规则：
   ① 宽度 —— 上面 `td.inv-op.sticky-right { width:180px }` 是给「查看/编辑/删除」三按钮留的，
      这里只有一个「查看」，整列被撑到 180px 很空。压回 auto，让 <th> 的行内 width
      （成品 100px / 配件·滤芯 80px）说了算。
      ⚠ 必须写成 `table.tb.inv-stock-table …`（多一个类）才压得过，靠顺序放后面是没用的。
   ② 手机端 —— ~~不隐藏~~ **改为隐藏（2026-11-02 用户要求）**：手机端字段全保留、向右滚动，
      操作列的「查看」按钮收进「点整行查看」（`views/inventory.js` 给行绑了 click），
      列本身不再占位置。此前 2026-09-30 要求「一直在最右侧显示」，现已按新要求作废。 */
table.tb.inv-stock-table td.inv-op.sticky-right { width: auto; }
@media (max-width: 767.98px) {
  table.tb.inv-stock-table th.inv-op.sticky-right,
  table.tb.inv-stock-table td.inv-op.sticky-right { display: none !important; }
}

/* 字典管理记录列表的操作列：4 个文字按钮（查看/编辑/BOM 配置/删除）挤在一格，
   实测整列宽 256px，2026-09-30 用户要求「紧凑一点，占的空间太大」。
   收三样：按钮内边距 / 字号 / 按钮之间的间距，另外把单元格自身左右 padding 也收窄。
   ⚠ 按钮之间**不留 HTML 空白节点**（dicts.js 用数组 join('') 拼），
     间距统一交给 `.btn + .btn` 的 margin —— 否则空白节点会再白撑 ~4px/处。
   ⚠ 必须写成 `table.tb td.dict-act .btn` 才压得过 `.btn.sm`（(0,3,2) > (0,2,0)）。 */
table.tb td.dict-act { padding-left: 6px; padding-right: 10px; }
table.tb td.dict-act .btn { height: 24px; padding: 0 6px; font-size: 12px; border-radius: 5px; }
table.tb td.dict-act .btn + .btn { margin-left: 4px; }
/* 手机端整列隐藏（用户 2026-09-30 要求）。⚠ th 与 td **必须一起隐**，
   只隐 td 会在表头留一个空的「操作」列。直接复用现成的 .hide-mobile（<768 生效）。 */

/* 表头下拉筛选 */
table.tb th.filterable { cursor: pointer; }
table.tb th.filterable::after { content: '▼'; font-size: 9px; color: var(--text-3); margin-left: 5px; vertical-align: middle; }
table.tb th.filterable.active-filter::after { color: var(--primary); }
table.tb th.filterable:hover { color: var(--primary); }
.header-filter-pop {
  position: fixed; background: #fff; border: 1px solid var(--border); border-radius: 8px;
  box-shadow: var(--shadow); min-width: 140px; max-width: 260px; max-height: 320px; overflow: auto; z-index: 100; padding: 4px 0;
}
.header-filter-pop .hf-item { padding: 8px 12px; cursor: pointer; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.header-filter-pop .hf-item:hover { background: var(--primary-soft); color: var(--primary); }
.header-filter-pop .hf-item.active { color: var(--primary); font-weight: 600; background: #f5f7ff; }

/* 保持 dict-picker 行 hover 在最后，避免被覆盖 */
table.tb tbody tr.dict-picker-row:hover { background: var(--primary-soft); }
table.tb .code {
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12.5px;
  color: var(--primary); cursor: pointer;
}
table.tb .code:hover { text-decoration: underline; }
table.tb tbody tr[data-open] { cursor: pointer; }
.empty { padding: 46px 20px; text-align: center; color: var(--text-3); }
.empty .big { font-size: 34px; opacity: .35; margin-bottom: 8px; }

.pager { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--border); }
.pager-wrap { margin-top: 8px; }
.pager .info { color: var(--text-3); font-size: 12.5px; }

/* 徽标 */
.tag {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: 11px;
  font-size: 12px; font-weight: 500; background: #eef1f6; color: var(--text-2); white-space: nowrap;
}
.tag.blue { background: var(--primary-soft); color: var(--primary); }
.tag.green { background: var(--ok-soft); color: var(--ok); }
.tag.red { background: var(--danger-soft); color: var(--danger); }
.tag.orange { background: var(--warn-soft); color: var(--warn); }
.tag.gray { background: #f0f2f6; color: #7b8496; }
.tag.purple { background: #f1ecff; color: var(--purple); }
.tag.yellow { background: #fff8e1; color: #b7900b; }
.tag.teal { background: #e6f7f5; color: var(--teal); }
.tag.bw { background: var(--primary); color: #fff; }

.dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }

/* 统计卡 */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); gap: 12px; }
.stat {
  background: #fff; border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 15px;
  box-shadow: var(--shadow); position: relative; overflow: hidden;
}
.stat .lbl { font-size: 12.5px; color: var(--text-2); }
.stat .val { font-size: 25px; font-weight: 700; margin-top: 5px; line-height: 1.15; letter-spacing: -.5px; }
.stat .sub { font-size: 11.5px; color: var(--text-3); margin-top: 3px; }
.stat .bar { position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--primary); }
.stat.clickable { cursor: pointer; transition: .15s; }
.stat.clickable:hover { transform: translateY(-1px); box-shadow: 0 4px 18px rgba(16,24,40,.1); }

/* 弹窗 */
.mask {
  position: fixed; inset: 0; background: rgba(20, 26, 38, .45); z-index: 100;
  display: flex; align-items: flex-start; justify-content: center; padding: 40px 16px; overflow: auto;
  animation: fade .15s;
}
@keyframes fade { from { opacity: 0 } to { opacity: 1 } }
.modal {
  background: #fff; border-radius: 12px; width: 100%; max-width: 1280px; box-shadow: 0 20px 60px rgba(16,24,40,.24);
  animation: pop .18s ease-out;
}
@keyframes pop { from { transform: translateY(-10px); opacity: .6 } to { transform: none; opacity: 1 } }
.modal.sm { max-width: 460px; }
.modal.lg { max-width: 1280px; }
.modal .m-hd { padding: 15px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; }
.modal .m-hd h3 { margin: 0; font-size: 15.5px; }
.modal .m-hd .m-x { margin-left: 8px; cursor: pointer; background: var(--danger); color: #fff; border: none; height: 32px; padding: 0 16px; border-radius: 6px; font-size: 13px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; gap: 4px; transition: background .15s; }
.modal .m-hd .m-x:hover { background: #b91c1c; }
.modal .m-hd .x { margin-left: auto; cursor: pointer; color: var(--text-3); font-size: 20px; line-height: 1; padding: 0 4px; }
.modal .m-bd { padding: 18px 20px; max-height: calc(100vh - 220px); overflow: auto; }
.modal .m-bd,
.modal .m-bd table.tb { max-width: 100%; }
.modal .m-bd .form-grid { max-width: 100%; }
.modal .m-bd .form-grid .input,
.modal .m-bd .form-grid select,
.modal .m-bd .form-grid textarea { max-width: 100%; box-sizing: border-box; }
.modal .m-ft { padding: 13px 20px; border-top: 1px solid var(--border); display: flex; gap: 9px; justify-content: flex-end; }
/* 模态框右下角拖拽手柄：拉伸宽度 */
.modal { position: relative; }
.modal .m-resize {
  position: absolute; right: 2px; bottom: 2px; width: 14px; height: 14px;
  cursor: ew-resize; opacity: .35; user-select: none;
  background: linear-gradient(135deg, transparent 50%, var(--text-3) 50%, var(--text-3) 60%, transparent 60%, transparent 75%, var(--text-3) 75%, var(--text-3) 85%, transparent 85%);
}
.modal .m-resize:hover { opacity: .8; }

/* toast */
#toasts { position: fixed; top: 16px; right: 16px; z-index: 200; display: flex; flex-direction: column; gap: 9px; }
.toast {
  background: #fff; border: 1px solid var(--border); border-left: 3px solid var(--primary);
  border-radius: 8px; padding: 11px 15px; box-shadow: 0 8px 26px rgba(16,24,40,.14);
  min-width: 250px; max-width: 420px; animation: slide .2s; font-size: 13.5px;
}
@keyframes slide { from { transform: translateX(20px); opacity: 0 } to { transform: none; opacity: 1 } }
.toast.error { border-left-color: var(--danger); }
.toast.success { border-left-color: var(--ok); }
.toast.warn { border-left-color: var(--warn); }

/* 明细页 */
.detail-head { display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.detail-head .dh-main { flex: 1; min-width: 0; }
.detail-head .dh-code { font-family: ui-monospace, Menlo, monospace; color: var(--primary); font-size: 14px; font-weight: 600; margin-bottom: 4px; }
.detail-head .dh-title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.detail-head .dh-title { font-size: 22px; font-weight: 700; color: var(--text); }
.detail-stats-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin: 10px 0 14px; }
.detail-stats-left { display: flex; flex-wrap: wrap; gap: 10px; }
.stat-pill { display: inline-flex; align-items: center; gap: 4px; background: #e8f0fe; color: var(--primary); padding: 4px 12px; border-radius: 20px; font-size: 13.5px; font-weight: 500; }
.stat-pill b { font-weight: 650; }
.detail-stats-right { font-size: 12.5px; color: var(--text-3); white-space: nowrap; }
.detail-divider { height: 1px; background: var(--border); margin: 14px 0; }
.detail-info { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px 24px; }
.detail-info .info-block.wide { grid-column: span 2; }
.detail-info .info-block.full { grid-column: 1 / -1; }
.detail-info .info-label { font-size: 12px; color: var(--text-3); margin-bottom: 5px; }
.detail-info .info-value { font-size: 14px; font-weight: 650; color: var(--text); word-break: break-all; }
.kv { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px 20px; }
.kv .k { font-size: 12px; color: var(--text-3); margin-bottom: 3px; }
.kv .v { font-size: 13.5px; word-break: break-all; }
.tabs { display: flex; gap: 3px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.tabs .tab {
  padding: 9px 15px; cursor: pointer; border-bottom: 2px solid transparent; color: var(--text-2); font-size: 13.5px;
}
.tabs .tab.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; }

/* 字典管理：字典类型切换（分段按钮组 - 白底描边 + 选中实心蓝） */
.dicts-page .tabs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  border-bottom: none; margin-bottom: 0;
}
.dicts-page .tabs .tab {
  display: inline-flex; align-items: center; justify-content: center;
  height: 36px; padding: 0 20px; line-height: 1;
  background: #fff; border: 1px solid var(--border-strong); border-radius: 10px;
  color: var(--text); font-size: 13.5px; font-weight: 500; white-space: nowrap;
  cursor: pointer; box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
  transition: background .15s, border-color .15s, color .15s, box-shadow .15s;
}
.dicts-page .tabs .tab:hover:not(.active) {
  background: var(--primary-soft); border-color: var(--primary); color: var(--primary);
}
.dicts-page .tabs .tab.active {
  background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 600;
  box-shadow: 0 2px 8px rgba(37, 99, 235, .28);
}
.dicts-page .tabs .tab:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-soft); }
@media (max-width: 767.98px) {
  .dicts-page .tabs { gap: 6px; }
  .dicts-page .tabs .tab { height: 33px; padding: 0 13px; font-size: 13px; border-radius: 9px; }
}

.timeline { position: relative; padding-left: 20px; }
.timeline::before { content: ''; position: absolute; left: 5px; top: 5px; bottom: 5px; width: 2px; background: var(--border); }
.timeline .ti { position: relative; padding: 0 0 16px 0; }
.timeline .ti::before {
  content: ''; position: absolute; left: -19px; top: 5px; width: 9px; height: 9px; border-radius: 50%;
  background: #fff; border: 2px solid var(--primary);
}
.timeline .ti.warn::before { border-color: var(--warn); }
.timeline .ti.red::before { border-color: var(--danger); }

/* 进度条 */
.progress { height: 6px; background: #eef1f6; border-radius: 4px; overflow: hidden; }
.progress > i { display: block; height: 100%; background: var(--primary); border-radius: 4px; }

/* 二维码标签 */
.labels { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: 10px; }
.label-card {
  border: 1px dashed var(--border-strong); border-radius: 8px; padding: 10px;
  display: flex; gap: 10px; align-items: center; background: #fff; break-inside: avoid;
}
.label-card img { width: 82px; height: 82px; flex: none; }
.label-card .info { min-width: 0; font-size: 11.5px; line-height: 1.55; }
.label-card .info .n { font-size: 13px; font-weight: 600; margin-bottom: 2px; }
.label-card .info .c { font-family: ui-monospace, Menlo, Consolas, monospace; color: var(--primary); }
.label-card .info div { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-2); }

@media print {
  .sidebar, .topbar, .toolbar, .no-print { display: none !important; }
  .main { margin-left: 0; }
  .content { padding: 0; }
  .card { border: none; box-shadow: none; }
  .labels { grid-template-columns: repeat(3, 1fr); }
}

/* 图表 */
.chart-bars { display: flex; align-items: flex-end; gap: 8px; height: 148px; padding-top: 10px; }
.chart-bars .cb { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 5px; min-width: 0; }
.chart-bars .cb .bar-out { width: 100%; height: 112px; display: flex; align-items: flex-end; }
.chart-bars .cb .bar-in { width: 100%; background: linear-gradient(180deg, #60a5fa, #2563eb); border-radius: 4px 4px 0 0; min-height: 2px; position: relative; transition: .3s; }
.chart-bars .cb .bar-in:hover { filter: brightness(1.08); }
.chart-bars .cb .bar-in span { position: absolute; top: -17px; left: 0; right: 0; text-align: center; font-size: 11px; color: var(--text-2); }
.chart-bars .cb .lb { font-size: 11px; color: var(--text-3); white-space: nowrap; }

.hbar { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; font-size: 13px; }
.hbar .nm { width: 108px; flex: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-2); }
.hbar .tr { flex: 1; height: 15px; background: #f2f4f8; border-radius: 4px; overflow: hidden; }
.hbar .tr > i { display: block; height: 100%; border-radius: 4px; }
.hbar .vv { width: 38px; text-align: right; font-weight: 600; font-size: 12.5px; }

.checkbox { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; font-size: 13px; }
.checkbox input { width: 15px; height: 15px; accent-color: var(--primary); cursor: pointer; }

.alert-row-overdue { background: #fff7f7 !important; }
.hint { font-size: 12.5px; color: var(--text-3); line-height: 1.6; }
.divider { height: 1px; background: var(--border); margin: 16px 0; }
.section-title { font-size: 13px; font-weight: 600; color: var(--text-2); margin: 4px 0 10px; display: flex; align-items: center; gap: 7px; }
.section-title::before { content: ''; width: 3px; height: 13px; background: var(--primary); border-radius: 2px; }

/* ---------------- 移动端抽屉菜单按钮 ---------------- */
.menu-toggle {
  display: none; flex: none; width: 36px; height: 36px; border: 1px solid var(--border-strong);
  background: #fff; border-radius: 8px; font-size: 18px; line-height: 1; color: var(--text-2);
  cursor: pointer; align-items: center; justify-content: center;
}
.menu-toggle:active { background: var(--primary-soft); }
.sidebar-overlay { display: none; }

/* ---------------- 响应式：手机端 ---------------- */
@media (max-width: 820px) {
  .menu-toggle { display: inline-flex; }

  /* 侧边栏改为抽屉 */
  .sidebar {
    width: 240px; transform: translateX(-100%); transition: transform .25s ease;
    box-shadow: 0 0 32px rgba(0, 0, 0, .35);
  }
  .sidebar.open { transform: none; }
  .main { margin-left: 0; }

  .sidebar-overlay {
    position: fixed; inset: 0; background: rgba(15, 23, 42, .45); z-index: 35; display: none;
  }
  .sidebar-overlay.show { display: block; }

  /* 顶部栏适配 */
  .topbar { padding: 0 12px; gap: 10px; position: sticky; }
  .topbar .page-title { font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .searchbox { display: none; }
  .userbox #btnPwd, .userbox #btnLogout { padding: 0 8px; }
  .content { padding: 12px 12px 32px; }

  /* 手机端隐藏：系统设置、备份与恢复 */
  .nav a[data-key="settings"], .nav a[data-key="backup"] { display: none; }

  /* 统计卡 / 网格自适应单列 */
  .stats { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
  .form-grid { grid-template-columns: 1fr; }
  .form-grid .half, .form-grid .full { grid-column: auto; }
  .filters { gap: 8px; }
  .filters .field { width: 100%; }
  .toolbar { gap: 7px; }
  .toolbar .btn { flex: none; }

  /* 明细页 */
  .detail-head { gap: 10px; }
  .detail-head .dh-title { font-size: 18px; }
  .detail-stats-row { flex-direction: column; align-items: flex-start; gap: 8px; }
  .detail-info { grid-template-columns: repeat(2, 1fr); gap: 14px 16px; }
  .detail-info .info-block.wide { grid-column: 1 / -1; }
  .detail-info .info-block.full { grid-column: 1 / -1; }
  .kv { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

  /* 弹窗接近全宽、底部弹出 */
  .mask { padding: 0; align-items: flex-end; }
  .modal { max-width: 100%; border-radius: 16px 16px 0 0; max-height: 94vh; }
  .modal .m-bd { max-height: calc(100vh - 150px); }
  .modal .m-ft { padding: 12px 16px; }

  /* 表格字号略缩，依赖 .table-wrap 横向滚动 */
  table.tb th, table.tb td { padding: 9px 10px; font-size: 12.5px; }

  /* 二维码标签单列 */
  .labels { grid-template-columns: 1fr; }
}

@media (max-width: 480px) {
  .topbar .userbox #userName, .topbar .userbox #userRole { display: none; }
  .userbox { gap: 4px; }
  .page-title { font-size: 14px; }
}

/* ============== 移动端适配（≤768px） ============== */
@media (max-width: 768px) {
  .layout { grid-template-columns: 1fr; }
  .sidebar { display: none; }
  /* 移动端顶部 header 改为深蓝固定顶部栏（参考截图） */
  .topbar {
    background: linear-gradient(135deg, #1e3a8a 0%, #1d4ed8 100%) !important;
    color: #fff; padding: 12px 14px; gap: 10px;
    border-radius: 0 0 14px 14px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, .08);
    margin-bottom: 0;
  }
  .topbar .menu-toggle { color: #fff; font-size: 20px; padding: 4px 8px; }
  .topbar .page-title { color: #fff; font-size: 16px; font-weight: 600; }
  .topbar .userbox .avatar { background: rgba(255, 255, 255, .2); color: #fff; }
  .topbar .searchbox { display: none; }  /* 移动端隐藏全局搜索 */
  .topbar .menu-toggle { display: inline-block; }
  .main { padding: 0; }
  .content { padding: 10px 12px 84px; background: #f4f6fa; }
  .toolbar { flex-wrap: wrap; gap: 6px; }
  .toolbar .input, .toolbar select { flex: 1 1 140px; min-width: 0; font-size: 13px; }
  .form-grid { grid-template-columns: 1fr 1fr; }
  .form-grid.two-col { grid-template-columns: 1fr; }
  .acc-form .form-grid .full { grid-column: 1 / -1; }
  .modal { max-width: 100vw; border-radius: 12px 12px 0 0; max-height: 94vh; }
  .modal .m-bd { max-height: calc(100vh - 180px); }
  .modal .m-resize { display: none; }
  .card { padding: 12px; border-radius: 12px; box-shadow: 0 2px 12px rgba(0, 0, 0, .04); }
  /* 表格自适应 */
  .table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  /* 选中行整行高亮（移动端更明显） */
  table.tb tbody tr.selected, table.tb tbody tr.selected td,
  table.tb tbody tr.selected td.sticky-left, table.tb tbody tr.selected td.sticky-right { background: #f1dd9b; }
  /* 顶部提示横幅 */
  .m-tip-banner {
    display: flex; align-items: center; gap: 8px;
    background: #fff7ed; color: #c2410c; border: 1px solid #fdba74;
    padding: 10px 14px; border-radius: 10px; margin: 10px 0;
    font-size: 13.5px;
  }
  .m-tip-banner::before { content: '📍'; font-size: 16px; }
  /* 移动端统计卡片：4 个排成一行（参考截图） */
  .m-stat-row {
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
    margin: 10px 0;
  }
  .m-stat-card {
    background: #fff; border-radius: 10px; padding: 10px 6px;
    text-align: center; box-shadow: 0 2px 10px rgba(0, 0, 0, .05);
  }
  .m-stat-card .lbl { font-size: 11.5px; color: var(--text-2); margin-bottom: 4px; }
  .m-stat-card .val { font-size: 17px; font-weight: 700; color: #111; }
  .m-stat-card .badge { display: inline-block; background: #eff4ff; color: var(--primary); border-radius: 8px; padding: 1px 5px; font-size: 10.5px; margin-top: 2px; }
  /* 预警卡片化（每条预警独立卡片，按等级配色边框） */
  .m-alert-list { display: flex; flex-direction: column; gap: 10px; }
  .m-alert-card {
    background: #fff; border-radius: 10px; padding: 12px 14px;
    box-shadow: 0 1px 6px rgba(0, 0, 0, .04);
    display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center;
    font-size: 13px;
  }
  .m-alert-card.warn { border-left: 4px solid var(--warn); background: #fff8e6; }
  .m-alert-card.overdue { border-left: 4px solid var(--danger); background: #fff1f0; }
  .m-alert-tag {
    padding: 2px 10px; border-radius: 4px; font-size: 12px; font-weight: 600;
    color: #fff; text-align: center; min-width: 50px;
  }
  .m-alert-tag.warn { background: var(--warn); }
  .m-alert-tag.overdue { background: var(--danger); }
  .m-alert-card .info { color: var(--text-2); font-size: 12px; }
  .m-alert-card .go { color: var(--primary); cursor: pointer; font-weight: 600; }
  /* 底部移动端 Tab 栏 */
  .m-bottom-nav {
    position: fixed; left: 0; right: 0; bottom: 0; height: 64px;
    background: #fff; border-top: 1px solid var(--border);
    display: flex; align-items: stretch; justify-content: space-around;
    z-index: 30; box-shadow: 0 -4px 14px rgba(0, 0, 0, .05);
    padding: 0 4px env(safe-area-inset-bottom, 0);
  }
  .m-tab {
    flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
    color: var(--text-2); text-decoration: none; font-size: 11.5px; gap: 2px; padding: 6px 0;
    position: relative;
  }
  .m-tab-ico { font-size: 20px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; height: 22px; }
  .m-tab-ico .nav-ico-svg { width: 21px; height: 21px; display: block; }
  .m-tab.active { color: var(--primary); }
  .m-tab.active .m-tab-ico { transform: translateY(-2px); }
  /* 中央 FAB「+」按钮（合同） */
  .m-tab-fab {
    position: relative;
  }
  .m-tab-fab-plus {
    position: absolute; top: -22px; left: 50%; transform: translateX(-50%);
    width: 52px; height: 52px; border-radius: 50%;
    background: var(--primary); color: #fff; font-size: 28px; line-height: 52px;
    text-align: center; box-shadow: 0 4px 14px rgba(37, 99, 235, .35);
  }
  .m-tab-fab.active .m-tab-fab-plus { background: var(--primary); }
}

@media (min-width: 769px) {
  .m-bottom-nav { display: none; }
}

/* 大屏：sidebar 在左、main 在右 */
@media (min-width: 769px) {
  .m-bottom-nav { display: none; }
}

/* ---------------- 饼图 ---------------- */
.pie-wrap {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}
.pie-svg {
  width: 110px;
  height: 110px;
  flex: none;
}
.pie-legend-list {
  flex: 1;
  min-width: 140px;
}
.pie-legend {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  line-height: 22px;
  color: var(--text);
}
.pie-legend i {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex: none;
}
.pie-legend span {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pie-legend b {
  font-weight: 600;
  color: var(--text);
}
.pie-legend small {
  color: var(--text-3);
  font-size: 11.5px;
}

/* ---------- 工单管理 ---------- */
.wo-header {
  padding: 16px 20px;
}
.wo-header-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}
.wo-header-title {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
}
/* 移动端：账号 + 服务单号 上下排（缩小字号） */
.wo-header-title-stack {
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  font-size: 16px;
}
.wo-header-title-stack .wo-header-code { font-size: 16px; }
.wo-header-title-stack .wo-header-divider { display: none; }
.wo-header-label {
  color: var(--text);
}
.wo-header-divider {
  color: var(--border);
  font-weight: 400;
}
.wo-header-code {
  color: var(--primary);
}
.wo-service-no {
  color: var(--primary);
  font-weight: 700;
}
.wo-header-tags {
  display: flex;
  gap: 8px;
  margin-top: 12px;
  flex-wrap: wrap;
}
.wo-header-meta {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.wo-header-meta span {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  color: var(--text-2);
}
.wo-header-meta b {
  color: var(--text);
  font-weight: 600;
}
/* 工单详情各子表（产品/收费/配件/滤芯）的「操作」列固定到表格最右 */
.wo-sub-table .tb td:last-child,
.wo-sub-table .tb th:last-child {
  position: sticky; right: 0; background: var(--panel); z-index: 1; box-shadow: -1px 0 0 var(--border);
  white-space: nowrap;
}
.wo-sub-table .tb td:last-child { display: flex; justify-content: flex-end; gap: 4px; align-items: center; }
/* 移动端隐藏操作列：上面 td:last-child{display:flex} 优先级高于 .hidden，需同优先级覆盖 */
.wo-sub-table .tb td:last-child.hidden { display: none; }
.wo-sub-table .tb th:last-child.hidden { display: none; }
@media (min-width: 768px) {
  .wo-sub-table .tb td:last-child.md\:table-cell { display: flex; justify-content: flex-end; gap: 4px; align-items: center; }
  .wo-sub-table .tb th:last-child.md\:table-cell { display: table-cell; }
}
/* 工单详情 - 产品信息表「序号」列：固定在最左（横向滚动时不动）。
   宽度与工单管理列表的序号列一致（56px），对齐/字色/内边距全部沿用 table.tb 默认值。
   ⚠ .sticky-left 默认给底色 #f8fafc，子表行是白底，必须盖回白色，否则序号列带一层灰底。 */
.wo-sub-table .tb .wo-seq-col { width: 56px; min-width: 56px; }
.wo-sub-table .tb tbody td.wo-seq-col { background: var(--panel); }
.wo-sub-table .tb tbody tr:hover td.wo-seq-col { background: #fafbfe; }
/* 产品信息标题右侧「已添加 N 台设备」 */
.wo-prod-count { font-size: 13.5px; color: var(--text-2); }
.wo-prod-count b { color: var(--primary); font-weight: 600; font-size: 14px; }
.wo-tabs {
  display: flex;
  gap: 2px;
  margin-top: 14px;
  border-bottom: 1px solid var(--border);
}
.wo-tabs button {
  padding: 10px 10px;
  border: 1px solid transparent;
  border-bottom: none;
  background: #f1f4f9;
  color: var(--text-2);
  border-radius: 8px 8px 0 0;
  cursor: pointer;
}
.wo-tabs button.active {
  background: #fff;
  color: var(--primary);
  border-color: var(--border);
  font-weight: 600;
}
.wo-tabs button:hover:not(.active) {
  background: #e8edf5;
}

/* 验收单详情顶部：单号信息 + 验证码 */
.acc-detail-header {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
}
.acc-detail-header .acc-detail-title {
  flex: 1;
  min-width: 0;
}
.acc-detail-header .acc-verify-box {
  background: #f8fafc;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 16px;
  text-align: center;
  min-width: 240px;
}
@media (max-width: 767.98px) {
  .acc-detail-header {
    flex-direction: column;
    gap: 12px;
  }
  .acc-detail-header .acc-detail-title,
  .acc-detail-header .acc-verify-box {
    width: 100%;
    box-sizing: border-box;
  }
  .acc-detail-header .acc-verify-box {
    min-width: auto;
  }
}

/* 产品行「查看」弹窗：分组紧凑布局（2026-11-05 重排）
   旧版是 .product-view-grid 两列平铺，每个字段 label 压在值上方（白占两行高），
   18 个字段 + 大量空值 ⇒ 弹窗近 600px 高、有效信息不到三分之一。
   现在按业务分组，组内 label 左 / 值右同一行，长值独占整行。 */
.pv { display: flex; flex-direction: column; gap: 8px; }
.pv-group {
  border: 1px solid var(--border);
  border-radius: 10px;
  background: #fff;
  overflow: hidden;
}
.pv-hd {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  background: #f7f9fc;
  border-bottom: 1px solid #eef2f8;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.pv-hd::before {
  content: '';
  width: 3px;
  height: 13px;
  border-radius: 2px;
  background: var(--primary);
}
.pv-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 22px;
  padding: 4px 14px 6px;
}
/* 3 列（保养：近期 / 周期 / 下次 —— 只有 3 个字段，2 列会留下一条整行空白） */
.pv-grid.pv-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.pv-item { display: flex; align-items: baseline; gap: 10px; padding: 4px 0; min-width: 0; }
.pv-item.full { grid-column: 1 / -1; }
/* 完工附件那一格是块级内容（缩略图网格 / 「暂无」），不能用 baseline 对齐 */
.pv-item.pv-item-files { align-items: flex-start; }
.pv-item-files .pv-k { padding-top: 2px; }
.pv-item-files .empty-sm { padding: 2px 0; }
.pv-k { flex: 0 0 84px; font-size: 12.5px; color: var(--text-3); line-height: 1.5; }
.pv-v {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  color: var(--text);
  line-height: 1.5;
  word-break: break-word;
}
.pv-empty { color: #b9c1ce; }
/* 编码 / 物流码：等宽 + 浅底，方便逐位核对 */
.pv-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  background: #f4f6fa;
  border-radius: 4px;
  padding: 1px 6px;
  color: #3f4a5c;
}
/* 物流码：蓝色（用户指定），现场扫码核对时最需要一眼看到 */
.pv-code.pv-code-blue { background: #eff5ff; color: var(--primary); font-weight: 600; }
.pv-fee { color: var(--primary); font-weight: 600; font-size: 14px; }
.pv-note {
  padding: 10px 14px 12px;
  font-size: 13.5px;
  color: var(--text);
  line-height: 1.7;
  white-space: pre-wrap;
  word-break: break-word;
}
@media (max-width: 767.98px) {
  /* ⚠ 必须带 .pv-grid-3：它的特异性 (0,2,0) 高过这里的 (0,1,0)，漏了会让 3 列布局留在手机上 */
  .pv-grid,
  .pv-grid.pv-grid-3 { grid-template-columns: minmax(0, 1fr); column-gap: 0; }
  .pv-item.full { grid-column: auto; }
  .pv-k { flex: 0 0 76px; }
}
.finish-images-edit .field-label {
  display: block;
  font-size: 12.5px;
  color: var(--text-3);
  margin-bottom: 6px;
}
.finish-images-edit { margin-top: 8px; }
.product-two-col { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 24px; align-items: start; }
.product-two-col .prod-col { display: flex; flex-direction: column; gap: 12px; }
.product-two-col .prod-col .form-field,
.product-two-col .prod-col .form-grid { width: 100%; }
.product-two-col .prod-col input.input,
.product-two-col .prod-col textarea.input,
.product-two-col .prod-col select.input { width: 100%; box-sizing: border-box; }
.finish-images-grid,
.finish-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
}
.finish-thumb {
  width: 100%;
  height: 120px;
  object-fit: cover;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: #f8f9fb;
}
.finish-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.finish-item .finish-name {
  font-size: 12px;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.finish-thumb-wrap {
  position: relative;
  border-radius: 6px;
  overflow: hidden;
}
.finish-thumb-wrap .pending-badge {
  position: absolute;
  top: 6px;
  left: 6px;
  background: var(--warn);
  color: #fff;
  font-size: 11px;
  padding: 1px 6px;
  border-radius: 4px;
}
.finish-images-edit-inner.drag-over {
  outline: 2px dashed var(--primary);
  outline-offset: 4px;
  background: var(--primary-soft);
  border-radius: 6px;
}
.finish-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.empty-sm {
  color: var(--text-3);
  font-size: 13px;
  padding: 10px 0;
}
.hint-warn {
  color: var(--warning);
  font-size: 12.5px;
  margin-top: 6px;
}

/* 工单详情编辑态：保持 kv 网格位置不变，输入框右侧带笔图标 */
.wo-edit-grid .kv {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.wo-edit-grid .kv.full {
  grid-column: 1 / -1;
}
.wo-edit-grid .kv label .req {
  color: var(--danger);
  margin-left: 2px;
}
.edit-input-wrap {
  position: relative;
  display: flex;
  align-items: center;
}
.edit-input-wrap .input {
  width: 100%;
  padding-right: 28px;
}
.edit-input-wrap .pen {
  position: absolute;
  right: 9px;
  color: var(--text-3);
  font-style: normal;
  font-size: 13px;
  pointer-events: none;
}
.edit-input-wrap textarea.input + .pen {
  top: 9px;
}

.section {
  margin-bottom: 22px;
}
.section + .section {
  padding-top: 16px;
}
.sec-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 12px;
  padding: 0 12px;
  line-height: 1.4em;
  border-left: 5px solid var(--primary);
  border-radius: 0 6px 6px 0;
}
.kv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 14px 20px;
}
.kv-grid .kv.break-row {
  grid-column-start: 1;
}
.kv {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* 收款管理汇总（2026-10-29 用户要求：PC 端横排一行）
   ⚠ 全局 `.kv` 在本文件后面被改成 `flex-direction: column`（一个 kv = 一个字段），
     这里当**容器**用时必须显式改回 grid，否则 5 个字段全竖着堆成一列。
     5 个字段 → PC 一行 5 列；手机 2 列。
   2026-10-29 追加：整行加**淡背景**，和下面的期次表区分开（用户截图要求）。 */
.kv.pp-summary {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 10px 24px;
  background: #f6f8fc; border-radius: 8px; padding: 10px 14px;
}
@media (max-width: 767.98px) {
  .kv.pp-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 12px; padding: 10px 12px; }
}
.kv label {
  font-size: 12.5px;
  color: var(--text-3);
}
.kv span {
  font-size: 13.5px;
  color: var(--text);
  word-break: break-word;
}
.text-block {
  background: #f8fafc;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  color: var(--text-2);
  line-height: 1.65;
  white-space: pre-wrap;
}

/* ============== 库存管理（移动端优先） ============== */
.inv-page { max-width: none; margin: 0; }
.inv-hero {
  background: linear-gradient(135deg, #1e3a8a, #2563eb); color: #fff;
  border-radius: 14px; padding: 18px 20px; margin-bottom: 14px;
}
.inv-hero-title { font-size: 20px; font-weight: 700; }
.inv-hero-sub { font-size: 13px; opacity: .85; margin-top: 3px; }
.inv-stat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 16px; }
.inv-stat {
  background: #fff; border-radius: 12px; padding: 14px 8px; text-align: center;
  box-shadow: 0 2px 10px rgba(0,0,0,.05); cursor: pointer;
}
.inv-stat-val { font-size: 22px; font-weight: 800; color: #1e3a8a; }
.inv-stat-lbl { font-size: 12px; color: var(--text-2); margin-top: 2px; }
.inv-section-title { font-size: 14px; font-weight: 700; color: var(--text-1); margin: 16px 0 10px; }
.inv-btn-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.inv-quick {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  background: #fff; border: none; border-radius: 12px; padding: 16px 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,.05); text-decoration: none; color: #333;
  font-size: 13px; font-weight: 600; cursor: pointer;
}
.inv-quick-ico { font-size: 24px; line-height: 1; }
.inv-quick-blue { color: #2563eb; } .inv-quick-teal { color: #0d9488; }
.inv-quick-green { color: #16a34a; } .inv-quick-orange { color: #ea580c; }
.inv-quick-red { color: #dc2626; } .inv-quick-purple { color: #7c3aed; }
.inv-quick-indigo { color: #4f46e5; } .inv-quick-gray { color: #64748b; }
.inv-type-tabs { display: flex; gap: 8px; margin: 12px 0; }
.inv-tab {
  flex: 0 0 auto; padding: 10px 18px; border-radius: 10px; border: 1px solid var(--border);
  background: #fff; font-size: 13.5px; font-weight: 600; color: var(--text-2); cursor: pointer;
  white-space: nowrap;
}
.inv-tab.active { background: #2563eb; color: #fff; border-color: #2563eb; }
.inv-order-card {
  background: #fff; border-radius: 12px; padding: 14px 16px; margin-bottom: 10px;
  box-shadow: 0 2px 10px rgba(0,0,0,.05);
}
.inv-order-card.done { border-left: 4px solid #16a34a; }
.inv-order-card.pending { border-left: 4px solid #f59e0b; }

/* 入库/出库列表：可点击选中行 + 下方独立子表（参考工单管理设计） */
.tb .inv-table-row.clickable { cursor: pointer; transition: background 0.1s; }
.tb .inv-table-row.clickable:hover { background: #f8fafc; }
.tb .inv-table-row.selected { background: #eef6ff !important; }
.tb .inv-table-row.selected > td { border-bottom-color: #c7d2fe; }
.tb .inv-table-row.selected > td:first-child { box-shadow: inset 3px 0 0 0 #2563eb; }
/* 子表（独立卡片内） */
.inv-sub-table { width: 100%; background: #fff; font-size: 12.5px; border-collapse: separate; border-spacing: 0; }
.inv-sub-table th { background: #f1f5f9; color: #475569; font-weight: 600; text-align: left; padding: 8px 12px; border-bottom: 1px solid #e2e8f0; font-size: 12px; }
.inv-sub-table td { padding: 8px 12px; border-bottom: 1px solid #f1f5f9; color: #334155; }
.inv-sub-table tbody tr:last-child td { border-bottom: 0; }
.inv-sub-table tbody tr:hover { background: #f8fafc; }
.inv-sub-codes { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 11.5px; color: #2563eb; line-height: 1.7; max-width: 480px; word-break: break-all; }
.inv-sub-empty { padding: 14px; color: #94a3b8; font-size: 12.5px; text-align: center; }
.inv-sn-chip { white-space: nowrap; }
.inv-order-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.inv-order-head .code { font-size: 14px; }
.inv-order-meta { font-size: 12.5px; color: var(--text-2); line-height: 1.6; }
.inv-order-actions { display: flex; gap: 10px; margin-top: 10px; }
.inv-line {
  display: flex; gap: 10px; align-items: flex-start; background: var(--bg-soft);
  border-radius: 10px; padding: 10px 12px; margin-bottom: 8px;
}
.inv-line-idx { width: 22px; height: 22px; border-radius: 50%; background: #2563eb; color: #fff; font-size: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.inv-line-body { flex: 1; font-size: 13.5px; }
.inv-form-row { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
.inv-scan-row { display: flex; gap: 8px; align-items: center; flex: 1 1 100%; }
.inv-scan-row .input { flex: 1; }
.inv-order-info { background: var(--bg-soft); border-radius: 10px; padding: 10px 14px; margin-bottom: 12px; }
.inv-timeline { display: flex; flex-direction: column; gap: 0; padding-left: 4px; }
.inv-tl-item { display: flex; gap: 10px; position: relative; padding-bottom: 16px; }
.inv-tl-item::before { content: ''; position: absolute; left: 5px; top: 14px; bottom: 0; width: 2px; background: var(--border); }
.inv-tl-item:last-child::before { display: none; }
.inv-tl-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; margin-top: 3px; }
.inv-tl-dot.green { background: #16a34a; } .inv-tl-dot.red { background: #dc2626; } .inv-tl-dot.blue { background: #2563eb; }
.inv-tl-body { flex: 1; font-size: 13px; }
.muted { color: var(--text-2); font-size: 12px; }

/* 库存字段配置 */
.inv-field-list { display: flex; flex-direction: column; gap: 8px; }
.inv-field-row { display: flex; align-items: center; gap: 10px; background: var(--bg-soft); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; }
.inv-field-row.dragging { opacity: .5; }
.inv-field-drag { color: var(--text-3); cursor: grab; font-size: 18px; line-height: 1; user-select: none; }
.inv-field-row-label { flex: 1; font-size: 13.5px; }
.inv-field-row-tag { padding: 2px 8px; border-radius: 4px; font-size: 12px; background: var(--primary-soft); color: var(--primary); }
.inv-field-row-tag.half { background: #fef3c7; color: #b45309; }
.inv-field-row-tag.required { background: #fee2e2; color: #b91c1c; }
.inv-field-actions { display: flex; gap: 4px; }

/* 表单宽度一致性（inv-* 表单内） */
.inv-page .form-grid { grid-template-columns: repeat(2, 1fr) !important; }
.inv-page .form-grid .field.half, .inv-page .form-grid .half { grid-column: span 1; }
.inv-page .form-grid .field.full, .inv-page .form-grid .full { grid-column: 1 / -1; }
.inv-page .form-grid .field input,
.inv-page .form-grid .field select,
.inv-page .form-grid .field textarea { width: 100%; box-sizing: border-box; }

/* 库存入库明细：产品型号行 */
.inv-mat-row { display: flex; align-items: center; gap: 10px; background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; margin-bottom: 8px; }
.inv-mat-idx { width: 24px; height: 24px; border-radius: 50%; background: #2563eb; color: #fff; font-size: 12px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.inv-mat-body { flex: 1; font-size: 13.5px; }
.inv-mat-actions { display: flex; gap: 6px; flex-shrink: 0; }

/* 选产品 */
.inv-pick-item { display: flex; align-items: center; gap: 10px; padding: 8px 6px; border-bottom: 1px solid #f0f3f8; }
.inv-pick-item:last-child { border-bottom: none; }

/* 物流码列表行 */
.inv-code-row { display: flex; align-items: center; gap: 10px; padding: 8px 10px; background: var(--bg-soft); border-radius: 6px; margin-bottom: 6px; font-size: 13px; }

/* 库存入库明细：物流码行内列表 */
.inv-codes { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.inv-code-line { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 12.5px; color: var(--text-1); padding: 3px 0; text-align: left; word-break: break-all; }

/* 库存明细：产品行 + 物流码紧凑布局 */
.inv-mat-block { background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; margin-bottom: 10px; }
.inv-mat-row { display: flex; align-items: center; gap: 10px; }
.inv-mat-block .inv-mat-row { margin-bottom: 8px; }
.inv-codes { display: flex; flex-direction: column; gap: 2px; padding: 4px 0 2px; }
.inv-code-line { display: flex; align-items: center; gap: 8px; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 12.5px; color: var(--text-1); padding: 1px 0; text-align: left; word-break: break-all; }
.inv-code-idx { width: 22px; height: 22px; border-radius: 50%; background: #16a34a; color: #fff; font-size: 11px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
/* .inv-code-text { flex: 1; } */

/* 入库明细：产品卡片去掉内部边框，保留外部淡背景 + 阴影 */
.inv-mat-block { background: #fafbfd !important; border: none !important; border-radius: 12px !important; padding: 14px 16px !important; margin-bottom: 12px !important; box-shadow: 0 1px 4px rgba(0,0,0,.04); }
.inv-mat-row { display: flex; align-items: center; gap: 10px; border: none !important; background: transparent !important; padding: 0 !important; }
.inv-mat-actions { display: flex; gap: 6px; flex-shrink: 0; }

/* 库存管理首页（PC 端） */
.inv-home { max-width: none; margin: 0; }
.inv-stat-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 14px; }
.inv-stat-card { background: #fff; border-radius: 12px; padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 1px 4px rgba(0,0,0,.04); cursor: pointer; }
.inv-stat-info { flex: 1; }
.inv-stat-card .inv-stat-lbl { font-size: 13px; color: var(--text-2); }
.inv-stat-card .inv-stat-val { font-size: 24px; font-weight: 800; color: var(--text-1); margin-top: 4px; }
.inv-stat-ico { width: 56px; height: 56px; background: #eff4ff; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 26px; }
.inv-stat-spark { background: transparent; padding: 0; width: 88px; height: 32px; }
.inv-home-body { display: grid; grid-template-columns: 1.6fr 1fr; gap: 14px; }
.inv-card { background: #fff; border-radius: 12px; padding: 14px 18px; box-shadow: 0 1px 4px rgba(0,0,0,.04); }
.inv-card-hd { display: flex; align-items: center; justify-content: space-between; padding-bottom: 10px; }
.inv-card-title { font-size: 15px; font-weight: 700; color: var(--text-1); }
.inv-btn-grid-4 { grid-template-columns: repeat(4, 1fr); }
.inv-btn-grid-3 { grid-template-columns: repeat(3, 1fr); }
.inv-quick { background: #fff; border: 1px solid var(--border); border-radius: 10px; padding: 18px 10px; text-align: center; cursor: pointer; transition: box-shadow .15s, transform .15s; }
.inv-quick:hover { box-shadow: 0 4px 16px rgba(0,0,0,.08); transform: translateY(-1px); }
.inv-quick-flat { border: 1px solid var(--border); }
.inv-quick-ico { display: block; font-size: 28px; line-height: 1; margin-bottom: 6px; }

/* ---------- 库存管理模块内的图标尺寸（SVG，见 js/icons.js） ----------
 * 这些图标由 JS 字符串拼接生成，**必须**用这里的自定义类名（不要改用 Tailwind 类）。 */
.inv-ico    { width: 24px; height: 24px; display: block; }                       /* 统计卡 / 快捷入口大图标 */
.inv-ico-sm { width: 14px; height: 14px; display: inline-block; vertical-align: -2px; margin-right: 5px; } /* 按钮内联图标 */
.inv-ico-meta { width: 14px; height: 14px; display: inline-block; vertical-align: -2px; margin-right: 3px; opacity: .7; } /* 日期/类型/经办人等元信息前缀 */
.inv-card-query .inv-quick-form { display: flex; flex-direction: column; gap: 8px; }

/* 库存查询行操作按钮 */
.inv-row-actions { display: flex; gap: 4px; justify-content: flex-end; }
.inv-row-actions .btn { padding: 2px 10px; font-size: 12px; }
/* 库存查询列表：操作列右对齐 */
table.tb th.inv-op, table.tb td.inv-op { text-align: right; }

/* 出入库列表的「值标签」：仓库 / 入库类型 / 出库类型 / 领用人
   （2026-09-24 用户要求：这些值用不同底色区分，一眼看出是哪个仓库 / 哪种类型）
   底色与字色由 inventory.js 的 valueTag() 按「值」哈希出的色相决定 ——
   renderCell 是出入库共用的，所以两边配色天然一致。
   这里只负责形状；颜色写在行内 style（每个值不同）。 */
.inv-val-tag {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 5px;
  font-size: 12px;
  line-height: 1.75;
  white-space: nowrap;
}

/* 出库单列表的「服务单号」超链接（2026-09-24 用户要求：蓝色 + 下划线，表示可以点）
   点击 → 按服务单号在工单管理里反查工单；命中则新开一页展示工单详情。
   注意：全局 `a` 只设了颜色、没有下划线（app.css:38），所以下划线必须在这里显式声明。 */
a.inv-svc-link {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
a.inv-svc-link:hover { color: var(--primary-dark); }

/* 提示型弹框垂直居中：.mask 默认是 align-items:flex-start（长表单弹窗更需要顶对齐、
   避免顶部被裁），但「暂未匹配到工单信息」这类短提示居中才醒目 —— 由调用方加 is-center 类。 */
.mask.is-center { align-items: center; }

/* 库存管理面包屑 */
.inv-breadcrumb { display: flex; align-items: center; gap: 6px; font-size: 13.5px; color: var(--text-2); margin-bottom: 12px; }
.inv-breadcrumb a { color: var(--primary); cursor: pointer; text-decoration: none; }
.inv-breadcrumb a:hover { text-decoration: underline; }
.inv-breadcrumb span { color: var(--text-3); }

/* 库存查询筛选表单 */
.inv-stock-form { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 12px 0; padding: 12px 14px; background: #fff; border-radius: 10px; box-shadow: 0 1px 4px rgba(0,0,0,.04); }

/* 日期区间范围选择器（紧凑双 date input） */
.inv-date-range { display: inline-flex; align-items: center; gap: 0; background: #fff; border: 1px solid var(--border); border-radius: 8px; padding: 0; height: 32px; }
.inv-date-range input[type="date"] { border: none; padding: 0 10px; height: 100%; font-size: 13px; background: transparent; min-width: 130px; }
.inv-date-range input[type="date"]:focus { outline: none; }
.inv-date-range input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .5; }
.inv-date-sep { color: var(--text-3); padding: 0 4px; font-size: 13px; }

/* 入库明细新设计 */
.inv-detail-head { padding: 4px 0 12px; }
.inv-detail-meta { display: flex; flex-wrap: wrap; gap: 16px; font-size: 13.5px; color: var(--text-2); }
.inv-detail-meta .inv-detail-code { font-weight: 700; color: var(--primary); font-size: 14px; }
.inv-detail-rule { border: none; border-top: 2px solid var(--primary); margin: 10px 0 0; }
.inv-mat-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.inv-cat-tag { display: inline-block; padding: 1px 8px; border-radius: 4px; background: #fef3c7; color: #b45309; font-size: 12px; margin-left: 4px; }
.inv-icon-btn { width: 32px; height: 32px; border: none; background: transparent; border-radius: 6px; cursor: pointer; padding: 4px; display: inline-flex; align-items: center; justify-content: center; transition: background .15s; }
.inv-icon-btn:hover { background: rgba(0,0,0,.05); }
.inv-icon-btn svg { width: 20px; height: 20px; }
.inv-icon-scan { color: #2563eb; }
.inv-icon-edit { color: #6366f1; }
.inv-icon-del { color: #ef4444; }
.inv-mat-block .inv-mat-row { align-items: flex-start; }
.inv-mat-block .inv-mat-body { padding-right: 8px; }
.inv-mat-block .inv-cat-sel { max-width: 110px; }
/* 成品「类型」下拉与类别下拉同宽，否则 flex 行里会撑满整行、换行占位 */
.inv-mat-block .inv-pt-sel { max-width: 110px; }

/* 设备类型 tag 按内容变色 */
.inv-cat-tag-green { background: #dcfce7; color: #16a34a; }
.inv-cat-tag-orange { background: #fef3c7; color: #b45309; }
.inv-cat-tag-blue { background: #dbeafe; color: #2563eb; }
/* 未填写的「类型」用中性灰，别和「类别」的彩色 tag 混在一起 */
.inv-cat-tag-none { background: #f1f5f9; color: #64748b; }
/* 库存查询「类型」列 / 详情弹窗标题：同一编码可能并存多个类型，tag 并排、窄列自动换行。
   行内并排时用 gap 控距，抵消 .inv-cat-tag 自带的 margin-left，避免首个 tag 缩进。 */
.inv-pt-tags { display: inline-flex; flex-wrap: wrap; gap: 4px; vertical-align: middle; }
.inv-pt-tags .inv-cat-tag { margin-left: 0; }

/* 入库单备注 tag */
.inv-detail-remark { background: #fff7ed; color: #c2410c; padding: 2px 10px; border-radius: 4px; font-size: 12.5px; }
/* 单条物流码删除按钮 */
.inv-code-line { display: flex; align-items: center; gap: 8px; padding: 1px 0; }
.inv-code-del { margin-left: 30px; width: 20px; height: 20px; border: none; background: transparent; border-radius: 4px; cursor: pointer; padding: 0; display: inline-flex; align-items: center; justify-content: center; color: var(--danger); }
.inv-code-del:hover { background: #fee2e2; }
.inv-code-del svg { width: 14px; height: 14px; }

/* ============== 滤芯出入库：移动端优先新流程样式 ============== */
.inv-filter-page {
  max-width: 720px;
  margin: 0 auto;
  padding: 14px 14px 96px;
}
.inv-filter-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}
.inv-back {
  border: 0;
  background: transparent;
  font-size: 14.5px;
  color: var(--primary);
  padding: 6px 8px;
  border-radius: 8px;
  cursor: pointer;
}
.inv-back:hover { background: #eff6ff; }
.inv-filter-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--text-1);
}
/* 单据编号（入库单号 / 出库单号）—— 标题右侧的只读标签。
   刻意做成「静态小标签」而不是输入框：编号由服务端自动生成、不允许修改（用户 2026-09-25 要求）。 */
.inv-filter-code {
  display: inline-block;
  margin-left: 10px;
  padding: 2px 9px;
  border-radius: 6px;
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-2);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .2px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  vertical-align: 2px;
}
.inv-filter-code[hidden] { display: none; }
@media (max-width: 768px) {
  /* 手机端标题栏窄：编号换到标题下方一行，别把标题挤断 */
  .inv-filter-code { display: block; margin: 4px 0 0; width: fit-content; }
}
.inv-big-btn {
  display: inline-block;
  width: auto;
  min-width: 140px;
  padding: 12px 22px;
  border-radius: 10px;
  border: 0;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  margin-bottom: 10px;
  transition: opacity 0.15s;
}
.inv-big-btn:active { opacity: 0.7; }
.inv-big-btn.primary { background: var(--primary); color: #fff; }
.inv-big-btn.ghost { background: #f1f5f9; color: var(--text-1); }
/* 「暂存」按钮（2026-09-29）：白底 + 描边 = 次要操作，与「取消」（灰底实心）「提交入库/出库」（实心主色）区分开。
   ⚠ .inv-big-btn 是 display:inline-block，会把 UA 的 [hidden] 盖掉 → 必须显式写下面这条，
      否则「已入库/已出库」与只读态下隐藏暂存按钮不生效（本项目既有坑）。 */
.inv-big-btn.draft { background: #fff; color: var(--primary); border: 1px solid var(--primary); }
.inv-big-btn.draft:hover:not(:disabled) { background: var(--primary-soft); }
.inv-big-btn.draft:disabled { opacity: 0.6; cursor: default; }
.inv-big-btn[hidden] { display: none !important; }
.inv-filter-list { display: flex; flex-direction: column; gap: 10px; }
.inv-order-card {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 14px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.inv-order-card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.inv-order-card-head .code { font-size: 14.5px; font-weight: 600; }
.inv-order-card-meta {
  display: flex; flex-wrap: wrap; gap: 4px 10px;
  font-size: 12.5px; color: var(--text-2); line-height: 1.7;
}
.inv-order-card-remark {
  margin-top: 6px; font-size: 12.5px; color: var(--text-3);
  background: #f8fafc; padding: 6px 8px; border-radius: 6px;
}
.inv-order-card-actions { display: flex; gap: 8px; margin-top: 10px; }
.inv-order-card-actions .btn { flex: 1; padding: 8px 10px; font-size: 13.5px; }
.inv-order-card-actions .btn.danger { color: var(--danger); }

/* ---------------- 物料图片：字典「图片」字段 + 图片画廊（2026-10-19） ---------------- */
/* 表单里的多图控件（骨架由 ui.js: formFieldHtml 的 images 分支渲染，接线在 views/dicts.js） */
.imgs-field { display: flex; flex-direction: column; gap: 8px; }
.imgs-list { display: flex; flex-wrap: wrap; gap: 8px; min-height: 0; }
.imgs-item { position: relative; width: 84px; height: 84px; border-radius: 8px; overflow: hidden;
  border: 1px solid var(--border); background: #f8fafc; cursor: pointer; }
.imgs-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.imgs-del { position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; border: 0; border-radius: 50%;
  background: rgba(15, 23, 42, .62); color: #fff; font-size: 13px; line-height: 20px; padding: 0; cursor: pointer; }
.imgs-del:hover { background: var(--danger); }
.imgs-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.imgs-hint { font-size: 12px; }
.imgs-empty { font-size: 12.5px; color: var(--text-3); }
/* 字典列表里的缩略图列 */
.dict-thumb { width: 46px; height: 46px; border-radius: 6px; object-fit: cover; display: block;
  border: 1px solid var(--border); cursor: zoom-in; background: #f8fafc; }
.dict-thumb-wrap { display: inline-flex; align-items: center; gap: 4px; }
.dict-thumb-more { font-size: 11.5px; color: var(--text-3); }
.dict-noimg { color: var(--text-3); font-size: 12.5px; }
/* 库存里「可点看图片」的编码 */
.code-click { color: var(--primary); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.code-click:hover { text-decoration-thickness: 2px; }
/* 图片画廊 */
.imgv { display: flex; flex-direction: column; gap: 10px; }
.imgv-stage { background: #f4f6fa; border-radius: 10px; padding: 10px; display: flex;
  align-items: center; justify-content: center; min-height: 240px; }
.imgv-img { max-width: 100%; max-height: 62vh; object-fit: contain; display: block; }
.imgv-bar { display: flex; align-items: center; justify-content: center; gap: 12px; }
.imgv-idx { font-size: 12.5px; color: var(--text-2); }
.imgv-thumbs { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.imgv-thumb { width: 62px; height: 62px; object-fit: cover; border-radius: 6px; cursor: pointer;
  border: 2px solid transparent; opacity: .72; }
.imgv-thumb.on { border-color: var(--primary); opacity: 1; }
@media (max-width: 768px) {
  .imgv-img { max-height: 54vh; }
  .imgs-item { width: 68px; height: 68px; }
}

/* 单据查询：入库单「逐码出库状态」（2026-10-19）
   —— 列表卡片上的统计徽标 + 就地展开的明细面板（不用弹窗，避免挡住列表）。 */
.inv-os-card { cursor: pointer; }
.inv-os-card.is-open { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.inv-os-toggle { font-size: 12.5px; color: var(--primary); white-space: nowrap; }
.inv-os-stats { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.inv-os-chip { font-size: 12px; padding: 2px 9px; border-radius: 10px; background: #f0f2f6; color: #5a6478; }
.inv-os-chip.out { background: var(--primary-soft); color: var(--primary); }
.inv-os-chip.left { background: var(--warn-soft); color: var(--warn); }
.inv-os-chip.muted { background: #f8fafc; color: var(--text-3); }
.inv-os-panel {
  margin: -6px 0 12px; padding: 12px 14px 4px; background: #fff;
  border: 1px solid var(--primary); border-top: 0; border-radius: 0 0 12px 12px;
}
.inv-os-hd { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.inv-os-sum { font-size: 13px; color: var(--text-2); }
.inv-os-sum b { color: var(--text); }
.inv-os-out b { color: var(--primary); }
.inv-os-left-t b { color: var(--warn); }
.inv-os-onlyw { font-size: 12.5px; color: var(--text-2); display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
.inv-os-table { font-size: 13px; }
.inv-os-link { color: var(--primary); cursor: pointer; }
.inv-os-link:hover { text-decoration: underline; }
.inv-os-date { font-size: 11.5px; margin-left: 6px; }
/* 「只看未出库」：已出库的行直接不显示（不重排 DOM，取消勾选就回来） */
.inv-os-panel.only-left .inv-os-table tbody tr[data-out="1"] { display: none; }
/* 同一张表放进**弹窗**时用 .inv-os-dialog（入库详情页「查看出库」，2026-11-02）：
   不能复用 .inv-os-panel —— 那套是贴着卡片下沿的（负 margin + 去上边框），放弹窗里会缺边框。
   hd / sum / table / link 这些子样式是独立的，照常复用。 */
.inv-os-dialog.only-left .inv-os-table tbody tr[data-out="1"] { display: none; }
.inv-os-dialog .inv-os-hd { margin-bottom: 8px; }
/* ⚠ 2026-11-03 用户要求：**名称不能再被截断**。
   昨天为「8 列全塞进一屏」把 .tb-cell-clip 压到 240px，45 个字的长名称只剩 15 个字，
   用户直接指出「名称被截断了，最大宽度被限制了 240px」。
   现在改成「固定列宽 + 自动换行」：
     · 不截断 —— 名称全部显示（长规格串折成 2~4 行，行高变大，但一行一台设备看得清清楚楚）；
     · 不横向滚动 —— 列宽定死 360px，8 列总和仍在弹窗内宽里，
       「是否出库 / 出库单号」不用滑也能看到（这正是这个弹窗存在的意义）。
   ⚠ 不能改成「不限宽、单行」：滤芯名称有 100+ 字，那样会把表格撑到 2000px+，
     出库状态又被推到滚动条外（试过，2026-11-03）。
   ⚠ 只作用于弹窗容器；单据查询里的就地展开保持原样（那边列少、宽度够）。 */
.inv-os-dialog .inv-os-table th:nth-child(3),
.inv-os-dialog .inv-os-table td:nth-child(3) { width: 360px; min-width: 360px; }
.inv-os-dialog .inv-os-table td:nth-child(3) .tb-cell-clip {
  max-width: 100%; white-space: normal; overflow: visible; text-overflow: clip;
  word-break: break-word; line-height: 1.45;
}
/* 手机上 360px 的名称列会把整张表挤成横向滚动条，压到 220px（名称照样换行完整显示） */
@media (max-width: 767.98px) {
  .inv-os-dialog .inv-os-table th:nth-child(3),
  .inv-os-dialog .inv-os-table td:nth-child(3) { width: 220px; min-width: 220px; }
}

/* 滤芯单据全屏表单 */
.inv-filter-sheet {
  max-width: 1200px;
  margin: 0;
  padding: 14px 14px 100px;
  display: flex; flex-direction: column; gap: 14px;
}
.inv-filter-sheet-head {
  display: flex; align-items: center; gap: 10px;
}
/* 标题栏右侧 actions 容器（编辑基本信息等按钮） */
.inv-filter-sheet-head .inv-filter-head-actions {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 8px;
}
/* 标题栏右侧的浅色小按钮：用于「编辑基本信息」「复制单据」等次级操作 */
.inv-filter-head-btn {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 6px 12px;
  font-size: 13px; line-height: 1.2;
  background: #eff6ff; color: #1d4ed8;
  border: 1px solid #bfdbfe; border-radius: 8px;
  cursor: pointer; transition: background .15s, border-color .15s;
}
.inv-filter-head-btn:hover { background: #dbeafe; border-color: #93c5fd; }
/* 危险次级按钮（撤销出库等不可逆操作）：与「编辑基本信息」同尺寸，仅换配色 */
.inv-filter-head-btn.danger { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.inv-filter-head-btn.danger:hover { background: #fee2e2; border-color: #fca5a5; }
/* 已提交单据改明细的说明条：插在基本信息表单与「单据明细」之间 */
.inv-edit-tip {
  margin: 12px 0 0;
  padding: 10px 12px;
  font-size: 12.5px; line-height: 1.7;
  color: #92400e; background: #fffbeb;
  border: 1px solid #fde68a; border-left: 3px solid #f59e0b;
  border-radius: 8px;
}
.inv-edit-tip b { color: #b45309; }
/* 只读查看模式（列表页「查看」）：整页沿用编辑页布局，字段浅灰底、明细增删按钮与底部保存按钮全部隐藏 */
.inv-filter-sheet.is-readonly .inv-filter-form .field .input,
.inv-filter-sheet.is-readonly .inv-filter-form .field input,
.inv-filter-sheet.is-readonly .inv-filter-form .field select,
.inv-filter-sheet.is-readonly .inv-filter-form .field textarea {
  background: #f5f7fa !important;
  color: var(--text-1) !important;
  border-color: #e2e8f0 !important;
  cursor: default;
  box-shadow: none !important;
}
.inv-filter-sheet.is-readonly #saveOrder,
.inv-filter-sheet.is-readonly #draftOrder,
.inv-filter-sheet.is-readonly #addItem,
.inv-filter-sheet.is-readonly #quickOutByCode,
.inv-filter-sheet.is-readonly .inv-item-card-actions { display: none !important; }
/* ⚠ 只读模式说明条（.inv-view-tip）已于 2026-09-25 按用户要求删除（连同 JS 里的插入逻辑），
   这里不再保留样式，避免留下死 CSS。 */
/* 编辑基本信息弹窗内的分组小标题（基本 / 联系与地址 / 发货） */
/* 编辑基本信息弹窗：与主表单同款三列分组卡片布局（移动端单列），字段顺序与页面一致 */
.inv-info-editor {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  align-items: start;
}
@media (max-width: 900px) { .inv-info-editor { grid-template-columns: 1fr; } }
.inv-info-editor .inv-filter-section {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px;
  display: flex; flex-direction: column; gap: 10px;
  min-width: 0;
}
.inv-info-editor .form-grid { display: flex; flex-direction: column; gap: 10px; }
.inv-info-editor .form-field { width: 100%; }
.inv-info-editor .form-field label {
  display: block; font-size: 13px; color: var(--text-2); margin-bottom: 4px; font-weight: 500;
}
.inv-info-editor .form-field .input,
.inv-info-editor .form-field textarea.input,
.inv-info-editor .form-field select.input { width: 100%; }
.inv-info-editor .inv-info-editor-title {
  font-size: 12.5px; font-weight: 600; color: var(--text-2);
  margin: 0 0 8px; padding-left: 8px; border-left: 3px solid var(--primary);
}
.inv-info-editor .inv-info-editor-title + .form-grid { margin-top: 0; }
.inv-info-editor .inv-info-editor-tip {
  grid-column: 1 / -1;
  margin-top: 0; padding: 10px 12px;
  font-size: 12.5px; line-height: 1.7;
  color: #92400e; background: #fffbeb;
  border: 1px solid #fde68a; border-left: 3px solid #f59e0b; border-radius: 8px;
}
.inv-filter-form {
  background: #fff;
  border-radius: 12px;
  padding: 14px;
  border: 1px solid var(--border);
  /* 滤芯单据分组卡片（按 group 分组）：PC 端一行 3 列，移动端（< 900px）单列 */
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
@media (max-width: 900px) {
  .inv-filter-form { grid-template-columns: 1fr; }
}
/* 滤芯单据分组卡片（按 group 分组）：每个分组一张白卡 */
.inv-filter-form .inv-filter-section {
  background: #fff;
  border-radius: 10px;
  padding: 12px;
  display: flex; flex-direction: column; gap: 10px;
  min-width: 0; /* 防止 grid item 被内容撑大超出列宽 */
}
.inv-filter-form .form-grid { display: flex; flex-direction: column; gap: 10px; }

/* ---------- 入库单主表：横排（2026-09-22 用户要求） ----------
   PC 一行 4 个字段（仓库 / 入库类型 / 入库日期 / 经办人），备注独占一行全宽；≤900px 回落成一行一个字段。
   ⚠ 三条覆盖缺一不可，少一条就还是竖排：
     ① 父级 .inv-filter-form 是 3 列 grid —— 主表必须 grid-column:1/-1 才跨满整行，否则只有 1/3 宽装不下 4 列；
     ② .form-grid 全局是 3 列 grid、且 .form-grid .full { grid-column:1/-1 } ——
        而入库字段定义里全是 span:'full'，不把 .full 还原成「占 1 列」，改了列数也依然每个字段独占一行；
     ③ .inv-filter-form .form-grid 本身被设成 flex column，要显式改回 grid。
   只有「入库侧第一个分组」加 .is-inline-main（出库有 basic/contact/ship 三组、每张卡仅 1/3 宽，横排会挤死）。 */
.inv-filter-form .inv-filter-section.is-inline-main {
  grid-column: 1 / -1;
}
.inv-filter-form .inv-filter-section.is-inline-main .form-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px 14px;
}
.inv-filter-form .inv-filter-section.is-inline-main .form-grid > .field.full { grid-column: auto; }
.inv-filter-form .inv-filter-section.is-inline-main .form-grid > .field.break-row { grid-column: 1 / -1; }
/* 窄屏（手机 / 窄窗口）：回到竖版，一行一个字段。断点与 .inv-filter-form 自身保持一致。 */
@media (max-width: 900px) {
  .inv-filter-form .inv-filter-section.is-inline-main .form-grid { grid-template-columns: 1fr; }
}
.inv-filter-form .form-field { width: 100%; }
.inv-filter-form .form-field label {
  display: block; font-size: 13px; color: var(--text-2); margin-bottom: 4px; font-weight: 500;
}
.inv-filter-form .form-field .input,
.inv-filter-form .form-field textarea.input,
.inv-filter-form .form-field select.input {
  width: 100%; box-sizing: border-box; padding: 10px 12px; font-size: 14.5px; min-height: 42px;
}
.inv-filter-form .form-field textarea.input { min-height: 70px; }
.inv-filter-items {
  background: #fff;
  border-radius: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
}
.inv-filter-items-head {
  display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;
}
.inv-filter-items-head .t { font-size: 14.5px; font-weight: 600; }
/* 单据明细的汇总（标题右侧「共 N 项，共入库 M」）+ 数量高亮（2026-10-30 用户要求：
   明细里的数量要用显眼的颜色标出来，方便验收核对） */
.inv-items-head-left { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.inv-items-sum { font-size: 12.5px; color: var(--text-2); }
.inv-items-sum b { color: var(--danger); font-size: 14.5px; font-weight: 700; }
.inv-item-row .inv-qty-hl {
  display: inline-block; min-width: 28px; text-align: center;
  padding: 1px 9px; border-radius: 6px;
  background: #fdecec; color: var(--danger); font-weight: 700; font-size: 13.5px;
}
.inv-filter-item-list { display: flex; flex-direction: column; gap: 10px; }
.inv-item-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
  background: #f8fafc;
}
.inv-item-card-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 6px;
}
.inv-item-card-head .idx { font-weight: 700; color: var(--primary); font-size: 13.5px; }
.inv-item-card-head .one-code-tag {
  font-size: 11.5px; padding: 2px 8px; border-radius: 999px;
}
.inv-item-card-head .one-code-tag.yes { background: #dbeafe; color: #1d4ed8; }
.inv-item-card-head .one-code-tag.no { background: #f1f5f9; color: var(--text-3); }
.inv-item-card-actions { margin-left: auto; display: inline-flex; gap: 6px; align-items: center; }
.inv-item-edit {
  width: 26px; height: 24px; border: 0; background: #e7f3ff; color: var(--primary);
  border-radius: 6px; cursor: pointer; display: inline-flex;
  align-items: center; justify-content: center;
  transition: background .15s;
}
.inv-item-edit:hover { background: #dbeafe; }
.inv-item-del {
  width: 24px; height: 24px; border: 0; background: #fee2e2;
  color: var(--danger); border-radius: 6px; cursor: pointer; font-size: 16px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
.inv-item-row {
  display: flex; gap: 8px; font-size: 13.5px; line-height: 1.7;
}
/* 标签列宽（2026-09-29 用户要求：原来固定 78px 太宽，压到 35px）：
   绝大多数标签是 2 个字（编码/名称/简称/单价/数量/备注）→ 恰好 35px；
   「物流码」「适用型号」是 3~4 字，用 width:auto + nowrap 让它们按内容放宽，
   避免被 35px 挤成两行（⚠ 别改回固定 width：那样长标签会折行）。 */
.inv-item-row .lab { min-width: 35px; width: auto; white-space: nowrap; color: var(--text-3); flex-shrink: 0; }
.inv-item-row .val { flex: 1; color: var(--text-1); word-break: break-all; }
.inv-filter-foot {
  position: fixed; left: 0; right: 0; bottom: 0;
  background: #fff; border-top: 1px solid var(--border);
  padding: 10px 14px;
  display: flex; gap: 10px; z-index: 50;
  justify-content: flex-end; /* 按钮组放右下角 */
}
/* 底部按钮：宽度自适应内容，不再 flex:1 平分 */
.inv-filter-foot .inv-big-btn { margin-bottom: 0; flex: 0 0 auto; }
/* 手机端底部按钮太大放不下（2026-09-29 用户反馈：iPhone 393 宽时「取消」被挤出屏幕）。
   原因 = 每个 .inv-big-btn 带 `min-width:140px` + `padding:12px 22px`，三个按钮合计约 468px > 视口；
   而 footer 是 `justify-content:flex-end`，溢出的部分正好落在左边的「取消」上。
   改法 = 等分剩余宽度 + 允许收缩 + 缩小字号/内边距。
   ⚠ max-width:220px 是为「只读态底部只剩一个「返回列表」」准备的：那种情况 flex:1 会把它拉满整行，
     而只读态 DOM 里其实仍有 3 个按钮（#saveOrder / #draftOrder 是 display:none），
     不能用 :only-child 之类按「可见兄弟数」判断。
   ⚠ 断点用 767.98px（= <768，与 .hide-mobile 同一口径），别写成 768px（那样 768 宽的视口也会命中）。 */
@media (max-width: 767.98px) {
  .inv-filter-foot { padding: 8px 10px; gap: 8px; }
  .inv-filter-foot .inv-big-btn {
    flex: 1 1 0; min-width: 0; max-width: 220px;
    padding: 10px 6px; font-size: 14px; border-radius: 8px;
  }
}
/* 库存列表页（入库/出库）筛选区在手机端的重排（2026-09-29 用户要求）：
   搜索框独占一整行，仓库 / 类型 / 起始日期 / 结束日期各占半行（两两一行）。
   ⚠ 这几个控件都带**内联 width**（240 / 280 / 140px）→ 必须 `!important` 才盖得住；
     半行宽用 `calc(50% - 4px)` 抵消工具栏的 `gap: 8px`，两个正好凑满一行。
   ⚠ 只作用于 `.inv-list-toolbar`（给这两处工具栏新加的专属类）——`.toolbar` 是全站通用类，
     直接写 `.toolbar #kw` 会连带影响库存首页 / 库存查询页。 */
@media (max-width: 767.98px) {
  .inv-list-toolbar #kw { flex: 1 1 100%; width: auto !important; }
  .inv-list-toolbar #filterWh,
  .inv-list-toolbar #filterType,
  .inv-list-toolbar #dateFrom,
  .inv-list-toolbar #dateTo { flex: 1 1 calc(50% - 4px); width: auto !important; min-width: 0; }
  /* 日期之间的「至」在窄屏让位：两个日期框各半行本身已表达区间，
     留着它会把「结束日期」挤到下一行（半行 + 至 + 半行 > 100%）。 */
  .inv-list-toolbar .inv-date-sep { display: none; }
  /* 撑开器在窄屏收起：否则「重置」贴左、「+ 新建…」被推到最右，中间空一大截 */
  .inv-list-toolbar .inv-tb-spacer { display: none; }
}

/* ==================== 入库 / 出库列表顶部合并成一行（2026-11-02 用户要求） ====================
   一行 = 左 3 个分类按钮 + 右（搜索框 → 新增 → 更多）；其余筛选项与低频按钮全在「更多」菜单里。
   ⚠ 筛选控件仍留在 DOM（只是藏在菜单内），`loadList` 按 id 取值不受影响。 */
.inv-head-bar { margin: 10px 0; flex-wrap: wrap; gap: 8px; }
/* ⚠ 撑开器必须有 `flex:1` 才推得动：它只带 `.inv-tb-spacer` 时**没有任何 flex 规则**
   （那条只在窄屏媒体查询里 `display:none`），右侧那组会紧贴分类按钮、看起来像"没右对齐"。
   现在同时挂了全局 `.spacer`（`.toolbar .spacer{flex:1}`），这里再兜一层。 */
.inv-head-bar .inv-tb-spacer { flex: 1 1 auto; }
.inv-head-bar .inv-type-tabs-inline { margin: 0; gap: 6px; }
/* 分类按钮从「大胶囊」压到与 input 同高（33px = 6+6 + 19 行高 + 2 边框），否则一行里高低不齐 */
.inv-head-bar .inv-type-tabs-inline .inv-tab {
  padding: 6px 14px; font-size: 13px; border-radius: 8px; line-height: 19px;
}
.inv-head-bar .inv-head-kw { width: 260px; flex: none; }
.inv-more-wrap { position: relative; }
/* 有筛选生效时给「更多」上主色（菜单收起来后仍能看出列表被筛过） */
.inv-more-wrap > .btn.has-filter {
  border-color: var(--primary); color: var(--primary); background: var(--primary-soft);
}
.inv-more-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; margin-left: 5px; padding: 0 4px;
  border-radius: 99px; background: var(--primary); color: #fff;
  font-size: 11px; font-weight: 700; line-height: 1;
}
.inv-more-badge[hidden] { display: none; }
/* ⚠ 必须写 `.wo-more-menu.inv-more-menu`（0,2,0）：本文件**后面**还有一条
   `.wo-more-menu { min-width:158px }`，同特异性下后写者胜 —— 只写 `.inv-more-menu` 会被它盖掉，
   菜单只剩 158px 宽、里面两个日期框挤成一团（实测 2026-11-02）。 */
.wo-more-menu.inv-more-menu { min-width: 250px; padding: 8px; }
.inv-more-filter { display: flex; flex-direction: column; gap: 7px; }
.inv-more-lab {
  display: flex; align-items: center; font-size: 12px; font-weight: 600;
  color: var(--text-2); padding: 0 2px;
}
.inv-more-dates { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 4px; }
.inv-more-sep { height: 1px; background: var(--border); margin: 8px 0; }
/* ⚠ 菜单里的筛控件要盖掉上面那条窄屏规则（`flex: 1 1 calc(50% - 4px)` 在 column 里会撑高）。
   那条用的是 `#id` 选择器（1,1,0）→ 这里也必须带 id 且写在更后面，否则压不住。 */
.inv-more-menu #filterWh,
.inv-more-menu #filterType,
.inv-more-menu #dateFrom,
.inv-more-menu #dateTo {
  width: 100% !important; flex: none; height: 32px; min-width: 0;
}
.inv-more-menu .inv-date-sep { display: inline; padding: 0 2px; font-size: 12px; }

/* ---------------- 库存查询顶部合并成一行（2026-11-02 用户要求） ----------------
   与入库/出库列表同款：左 3 个分类按钮，右「搜索框 → 仓库 → 查询 → 重置 → 更多」，
   状态 / 入库日期区间 / 安全库存 / 导出 Excel 全在「更多」菜单里。
   ⚠ 表单本身必须做成 inline 容器：原来用的 `.inv-stock-form` 自带白底卡片 + 12px 内边距，
     套进 `.toolbar` 里会变成"第二张卡片"，一行就破了。 */
.inv-head-bar .stock-query-form {
  display: flex; align-items: center; gap: 8px;
  margin: 0; padding: 0; background: none; box-shadow: none; border: none;
}
.inv-head-bar .stock-query-form #whFilter { width: 150px; flex: none; }
/* 菜单里的控件撑满一列（同入库/出库侧）。⚠ 行内 width 必须 !important 才压得住 */
.inv-more-menu #statusFilter,
.inv-more-menu #startDate,
.inv-more-menu #endDate { width: 100% !important; flex: none; height: 32px; min-width: 0; }
.inv-more-menu #safetyBtn { width: 100%; justify-content: flex-start; }
@media (max-width: 767.98px) {
  .inv-head-bar .stock-query-form { flex: 1 1 auto; flex-wrap: wrap; }
  .inv-head-bar .stock-query-form .inv-head-kw { flex: 1 1 140px; width: auto; min-width: 0; }
  .inv-head-bar .stock-query-form #whFilter { width: auto; flex: 1 1 120px; }
}
@media (max-width: 767.98px) {
  .inv-head-bar .inv-more-wrap { margin-left: auto; }
  .inv-head-bar .inv-head-kw { width: auto; }
  .inv-more-menu { min-width: 0; width: min(272px, calc(100vw - 28px)); }
}

/* ==================== 单据查询：一行搜索栏（2026-11-02 用户要求删掉三个分类按钮） ====================
   一行 = 输入框 → 扫码图标按钮 → 查询。输入后回车或点「查询」都能查；
   有关键词时**三类单据一起搜**并分组显示（见 views/inventory.js: orderGroupHtml）。 */
.inv-oc-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.inv-oc-bar #orderSearch { flex: 1 1 auto; min-width: 0; }
/* 扫码是纯图标按钮，左右内边距收窄一点，别比输入框高出一截 */
.inv-oc-scan { padding: 0 10px; flex: none; }
.inv-oc-tip { font-size: 12.5px; color: var(--text-3); margin-bottom: 10px; }
.inv-oc-group-hd {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 600; color: var(--text-2);
  margin: 16px 0 8px;
}
.inv-oc-group:first-child .inv-oc-group-hd { margin-top: 0; }
.inv-oc-group-hd b { color: var(--primary); font-weight: 700; }
@media (min-width: 720px) {
  .inv-filter-foot { position: sticky; bottom: 0; }
}

/* 名称行：label + 右侧库存 tag（仅出库 + 已选物料时显示） */
.inv-item-lab-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.inv-item-stock-tag {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px; border-radius: 999px;
  background: #ecfdf5; color: #047857; font-size: 12px; line-height: 18px;
  border: 1px solid #a7f3d0; user-select: none;
}
.inv-item-stock-tag .ico { font-size: 12px; }
.inv-item-stock-tag .lbl { font-size: 11px; color: #047857; opacity: 0.85; }
.inv-item-stock-tag .num { font-weight: 700; font-size: 13px; color: #047857; }
.inv-item-stock-tag.is-empty { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.inv-item-stock-tag.is-empty .lbl, .inv-item-stock-tag.is-empty .num { color: #b91c1c; }

/* 添加入库信息 modal */
.inv-item-form { display: flex; flex-direction: column; gap: 12px; }
.inv-item-step { display: flex; flex-direction: column; gap: 4px; }
.inv-item-lab { font-size: 13px; color: var(--text-2); font-weight: 500; }
.inv-item-input-wrap { display: flex; gap: 8px; align-items: stretch; }

/* 首页库存查询：编码输入框 + 扫码按钮同行 */
.inv-qq-row { display: flex; gap: 6px; align-items: stretch; }
.inv-qq-scan {
  flex-shrink: 0; width: 38px; border: 0; border-radius: 6px;
  background: #eff6ff; color: var(--primary);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; transition: background .15s;
}
.inv-qq-scan:hover { background: #dbeafe; }
.inv-qq-row .input { flex: 1; min-width: 0; }

/* 首页物流码查询：结果卡片（一个码可流转多轮 → 每轮一张卡片） */
.inv-trace-meta {
  font-size: 12.5px; color: var(--text-3); margin-bottom: 8px;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}
.inv-trace-list { margin-top: 4px; }
.inv-trace-card {
  border: 1px solid var(--border); border-radius: 8px;
  padding: 10px 12px; margin-top: 8px; background: #f8fafc;
  font-size: 12.5px;
}
.inv-trace-card.in_stock { border-left: 3px solid #16a34a; background: #f0fdf4; }
.inv-trace-card.out_stock { border-left: 3px solid #f59e0b; background: #fffbeb; }
.inv-trace-card.not_found { border-left: 3px solid #94a3b8; background: #f8fafc; }
/* 卡片头：左侧轮次、右侧状态标签 */
.inv-trace-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.inv-trace-round { font-weight: 600; font-size: 13px; color: var(--text); }
.inv-trace-tag {
  font-weight: 600; font-size: 12px; padding: 1px 8px; border-radius: 10px;
  background: #e2e8f0; color: var(--text-3); white-space: nowrap;
}
.inv-trace-card.in_stock .inv-trace-tag { background: #dcfce7; color: #15803d; }
.inv-trace-card.out_stock .inv-trace-tag { background: #fef3c7; color: #b45309; }
.inv-trace-card.not_found .inv-trace-tag { background: #e2e8f0; color: var(--text-3); }
/* 顶部「当前状态」徽标（不在卡片内，配色单独给） */
.inv-trace-meta .inv-trace-tag.in_stock { background: #dcfce7; color: #15803d; }
.inv-trace-meta .inv-trace-tag.out_stock { background: #fef3c7; color: #b45309; }
.inv-trace-meta .inv-trace-tag.not_found { background: #e2e8f0; color: var(--text-3); }
.inv-trace-row { display: flex; gap: 8px; line-height: 1.7; }
.inv-trace-row .lab { width: 70px; color: var(--text-3); flex-shrink: 0; }
.inv-trace-row .val { flex: 1; min-width: 0; word-break: break-all; }
.inv-item-input-wrap .input { flex: 1; min-width: 0; }
.inv-scan-btn {
  border: 0; background: #eff6ff; color: var(--primary);
  width: 42px; border-radius: 8px; cursor: pointer; font-size: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .15s;
}
.inv-scan-btn:hover { background: #dbeafe; }
.inv-item-hint { font-size: 11.5px; color: var(--text-3); margin-top: 4px; line-height: 1.5; }
.inv-radio { display: flex; gap: 16px; padding: 6px 0; }
.inv-radio label { display: flex; align-items: center; gap: 6px; font-size: 14px; cursor: pointer; }
.inv-item-form .readonly-bg { background: #f1f5f9 !important; color: var(--text-2) !important; }

.inv-item-suggest {
  max-height: 180px; overflow: auto; border: 1px solid var(--border);
  border-radius: 8px; background: #fff;
}
.inv-suggest-item { padding: 8px 10px; border-bottom: 1px solid #f1f5f9; cursor: pointer; }
.inv-suggest-item:hover { background: #eff6ff; }
.inv-suggest-item:last-child { border-bottom: 0; }
.inv-suggest-item .r1 { font-size: 13.5px; }
.inv-suggest-item .r2 { font-size: 12px; color: var(--text-3); margin-top: 2px; }
/* 建议列表底部「命中数 > 展示数」的说明行（2026-09-27）：
   不是可点项，只是告知「共 N 条、只显示了前 M 条」—— 用 sticky 钉在滚动区底部，
   否则内容一多它会被挤到下面看不见，等于没提示。 */
.inv-suggest-more {
  position: sticky; bottom: 0;
  padding: 7px 10px; font-size: 12px; color: var(--text-3);
  background: #f8fafc; border-top: 1px solid #e2e8f0;
}
.inv-suggest-more b { color: var(--text-2); }

/* 「常用滤芯」快捷行（明细弹窗「编码」下方、分隔线上方，2026-09-27）：
   整行用浅底与下面字段分开；每个标签**底色相同**（值来自「选项设置 → 常用滤芯」）。 */
.inv-common-row {
  display: flex; align-items: flex-start; gap: 8px;
  margin-top: 8px; padding: 8px 10px;
  background: #f8fafc; border-radius: 8px;
}
/* ⚠ display:flex 会盖掉 UA 的 [hidden] —— 没维护选项时要能整行隐藏，必须显式写这条 */
.inv-common-row[hidden] { display: none; }
.inv-common-lab { flex: 0 0 auto; font-size: 12.5px; color: var(--text-3); line-height: 24px; }
.inv-common-chips { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.inv-common-chip {
  background: #eff4ff; color: var(--primary);
  border: 1px solid #dbeafe; border-radius: 6px;
  padding: 2px 9px; font-size: 12.5px; line-height: 20px;
  cursor: pointer; transition: background 0.12s;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.inv-common-chip:hover { background: #dbeafe; }
.inv-common-chip:active { background: #c7ddff; }
.empty.sm { padding: 14px; text-align: center; color: var(--text-3); font-size: 13px; }

/* 滤芯出入库单查看弹窗 */
.inv-detail-box { max-height: 70vh; overflow-y: auto; padding-right: 4px; }
.inv-detail-head { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.inv-detail-row { display: flex; gap: 10px; font-size: 13.5px; line-height: 1.6; }
.inv-detail-row .lab { width: 72px; color: var(--text-3); flex-shrink: 0; }
.inv-detail-row .val { flex: 1; color: var(--text-1); word-break: break-all; }
.inv-detail-row .val.code { font-weight: 600; }
/* 重点标注字段（入库数量等关键信息） */
.inv-detail-row.inv-detail-row-emph {
  background: linear-gradient(90deg, #e7f3ff 0%, #f0f7ff 100%);
  margin: 0 -10px;
  padding: 4px 10px;
  border-radius: 6px;
}
.inv-detail-row.inv-detail-row-emph .lab { color: var(--primary); font-weight: 600; }
.inv-detail-row.inv-detail-row-emph .val { color: var(--primary); font-weight: 700; font-size: 14.5px; }
.inv-detail-items-head { font-size: 14px; font-weight: 600; margin-bottom: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.inv-detail-items { display: flex; flex-direction: column; gap: 10px; }
.inv-detail-item { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: #f8fafc; }
.inv-detail-item-head { font-size: 13.5px; font-weight: 600; margin-bottom: 8px; color: var(--text-1); }
.inv-detail-item-body { display: flex; flex-direction: column; gap: 6px; }
.inv-detail-code-empty { color: var(--text-3); font-size: 13px; }
.inv-detail-codes { display: flex; flex-direction: column; gap: 5px; }
.inv-detail-code { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.inv-detail-code .idx { width: 20px; height: 20px; border-radius: 50%; background: var(--primary-soft); color: var(--primary); font-size: 11px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.inv-detail-code .txt { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; word-break: break-all; }

/* 滤芯一物一码扫码录入弹窗 */
.scan-acc-body { padding: 8px 0; }
.scan-acc-count-box { text-align: center; margin-bottom: 14px; }
.scan-acc-label { font-size: 14px; color: var(--text-3); }
.scan-acc-num { font-size: 42px; font-weight: 700; color: var(--primary); margin-top: 6px; line-height: 1; }
.scan-acc-tip {
  background: #f8fafc; border-radius: 8px; padding: 16px;
  text-align: center; color: var(--text-3); font-size: 13.5px; margin-bottom: 14px;
}
.scan-acc-tip span { font-size: 12px; opacity: .85; }
.scan-acc-list {
  max-height: 240px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 6px;
}
.scan-code-row {
  display: flex; align-items: center; gap: 10px;
  background: #f1f5f9; border-radius: 8px;
  padding: 9px 10px; font-size: 13.5px;
}
.scan-code-idx {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--primary); color: #fff;
  font-size: 11px; display: inline-flex;
  align-items: center; justify-content: center; flex-shrink: 0;
}
.scan-code-text {
  flex: 1; min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  word-break: break-all; color: var(--text);
}
.scan-code-del {
  width: 24px; height: 24px; border: 0; border-radius: 6px;
  background: #fee2e2; color: var(--danger);
  font-size: 18px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; transition: background .15s;
}
.scan-code-del:hover { background: #fecaca; }

/* 添加明细中数量下方展示已扫物流码 */
.inv-code-preview { margin-top: 10px; }
.inv-code-preview-title { font-size: 12.5px; color: var(--text-3); margin-bottom: 6px; }
.inv-code-preview-list { display: flex; flex-wrap: wrap; gap: 6px; }

/* 一物一码行：是/否 + 扫码按钮 同一行右对齐 */
.inv-item-step--onecode .inv-item-oc-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.inv-item-step--onecode .inv-item-oc-row .inv-radio { padding: 6px 0; }
.inv-item-step--onecode .inv-item-oc-row .inv-item-oc-actions {
  display: inline-flex; gap: 6px; align-items: center;
}
.inv-item-step--onecode .inv-scan-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 36px; border: 0; border-radius: 8px;
  background: #eff6ff; color: var(--primary); cursor: pointer;
  transition: background .15s;
}
.inv-item-step--onecode .inv-scan-btn:hover { background: #dbeafe; }

.inv-code-chip {
  display: inline-flex; align-items: center;
  background: #e7f3ff; color: var(--primary);
  border-radius: 999px; padding: 4px 10px;
  font-size: 12.5px; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  word-break: break-all; max-width: 100%;
}

/* ===== 出库「按物流码出库」（一物一码场景：只给物流码，自动带出物料） ===== */
.inv-filter-items-head { flex-wrap: wrap; gap: 8px; }
.inv-filter-items-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* 明细卡片里的物流码一行（chip 展示，便于核对扫码结果） */
.inv-item-codes { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.inv-item-codes .more { font-size: 12px; color: var(--text-3); }

.inv-qb { display: flex; flex-direction: column; gap: 10px; }
.inv-qb-tip {
  font-size: 12.5px; line-height: 1.6; color: var(--text-2);
  background: var(--primary-soft); border-radius: 8px; padding: 8px 10px;
}
.inv-qb-input {
  width: 100%; resize: vertical;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 13px;
}
.inv-qb-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.inv-qb-hint { font-size: 12.5px; color: var(--text-3); }
.inv-qb-result { display: flex; flex-direction: column; gap: 10px; }
.inv-qb-summary { display: flex; flex-wrap: wrap; gap: 8px; }
.inv-qb-pill {
  display: inline-flex; align-items: center; height: 24px; padding: 0 10px;
  border-radius: 999px; background: #f1f5f9; color: var(--text-2); font-size: 12.5px;
}
.inv-qb-pill.ok { background: #e7f3ff; color: var(--primary); font-weight: 600; }
.inv-qb-pill.warn { background: #fff7e6; color: #b45309; }
.inv-qb-note {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  font-size: 12.5px; line-height: 1.6; color: #b45309;
  background: #fff7e6; border: 1px solid #fde68a; border-radius: 8px; padding: 8px 10px;
}
.inv-qb-groups { display: flex; flex-direction: column; gap: 8px; max-height: 42vh; overflow-y: auto; }
.inv-qb-group { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: #f8fafc; }
.inv-qb-group-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.inv-qb-code { font-weight: 600; font-size: 13px; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
.inv-qb-qty { font-size: 13px; font-weight: 700; color: var(--primary); }
.inv-qb-name { margin-top: 4px; font-size: 12.5px; line-height: 1.55; color: var(--text-2); }
.inv-qb-meta { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 5px; font-size: 12.5px; color: var(--text-3); }
.inv-qb-codes { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 7px; }
.inv-qb-bad { border: 1px solid #fde68a; background: #fffdf5; border-radius: 10px; padding: 10px 12px; }
.inv-qb-bad-title { margin-bottom: 6px; font-size: 12.5px; font-weight: 600; color: #b45309; }
.inv-qb-bad-row { display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: 12.5px; }
.inv-qb-bad-code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; color: var(--text-2); }
.inv-qb-bad-reason { color: #b45309; }
@media (max-width: 767px) {
  .inv-qb-groups { max-height: none; }
  .inv-filter-items-actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* 库存详情弹窗：表格形态，单号信息列含出入库两行，行高一致 */
.inv-stock-detail-table td { vertical-align: middle; }
.inv-stock-detail-table .inv-order-cell {
  display: flex; flex-direction: column; gap: 4px; min-width: 260px;
}
.inv-stock-detail-table .inv-oc-row {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; line-height: 1.4; min-height: 20px;
  white-space: nowrap;
}
.inv-stock-detail-table .inv-oc-lab {
  flex-shrink: 0; width: 30px; color: var(--text-3); font-size: 12px;
}
/* 单号不伸缩，日期紧跟单号右侧（间距 12px，需求要求「最大 30px」）。
   ⚠ 这里曾经是 flex:1，会把日期一路推到单元格最右端，看着和单号脱节。 */
.inv-stock-detail-table .inv-oc-val { flex: 0 0 auto; min-width: 0; }
.inv-stock-detail-table .inv-oc-date {
  flex-shrink: 0; color: var(--text-3); font-size: 12px; margin-left: 12px;
}

/* 弹窗头信息（编码/仓库/数量）背景色标注 */
.inv-order-info .inv-info-tag {
  display: inline-block;
  background: #e7f3ff; color: var(--primary);
  border-radius: 4px; padding: 1px 8px;
  font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  margin: 0 2px;
}

/* 工单信息看板 · 月度快捷按钮
   用独立 CSS 类避开 Tailwind 的 :hover 类覆盖
   特异性：.wob-preset-active:hover 高于 .wob-preset-idle:hover，确保激活态在 hover 时仍保持蓝底白字 */
.wob-preset-idle { background: #fff; color: #475569; border-color: #e2e8f0; }
.wob-preset-idle:hover { color: #1d4ed8; border-color: #1d4ed8; }
.wob-preset-active {
  background: #2563eb; color: #fff; border-color: #2563eb;
}
.wob-preset-active:hover {
  background: #1d4ed8; color: #fff; border-color: #1d4ed8;
}

/* 滤芯出库单：提交出库后按钮变 "已出库" + 禁用样式 */
.inv-big-btn.is-done,
.inv-big-btn.is-done:hover {
  background: #94a3b8 !important;
  color: #fff !important;
  cursor: not-allowed !important;
  opacity: 0.85;
  border-color: #94a3b8 !important;
}
.inv-filter-done-tag {
  margin-left: 10px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  background: #16a34a;
  color: #fff;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.5px;
}

/* 工单详情移动端：整个「操作」列（th + td）连同行内编辑/删除按钮一起隐藏，防止误触；
   表格会自然收缩，不留空列与边框。仅 ≤768px 生效，PC 端不受影响。 */
@media (max-width: 768px) {
  [data-hide-on-mobile] { display: none !important; }
}

/* ================= 多标签页导航（Tab 栏，≥768px） =================
 * 位于顶栏之下、内容区之上。每个标签 = 一个已打开的页面，可点击切换、点 × 关闭。
 * 用独立类名而非 Tailwind（标签 HTML 由 JS 拼接，Tailwind 内容扫描会漏掉，需 build:css）。 */
#tabbar {
  display: none;                 /* 默认隐藏，≥768px 由下方媒体查询打开 */
  flex: none;
  align-items: center;
  gap: 4px;
  padding: 6px 8px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  overflow-y: hidden;
  white-space: nowrap;
  scrollbar-width: thin;
}
#tabbar::-webkit-scrollbar { height: 5px; }
#tabbar::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }

#tabbar .wb-tab {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex: none;
  max-width: 220px;
  padding: 5px 6px 5px 12px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: #e9edf4;
  color: var(--text-2);
  font-size: 12.5px;
  line-height: 1.5;
  cursor: pointer;
  user-select: none;
  transition: background .15s, color .15s, border-color .15s;
}
#tabbar .wb-tab:hover { background: #dfe6f1; color: var(--text); }
#tabbar .wb-tab.active {
  background: #fff;
  color: var(--primary);
  font-weight: 600;
  border-color: var(--border);
  box-shadow: 0 1px 2px rgba(16, 24, 40, .07);
}
#tabbar .wb-tab-ico { font-size: 12px; opacity: .85; flex: none; display: inline-flex; align-items: center; }
#tabbar .wb-tab-ico .nav-ico-svg { width: 14px; height: 14px; display: block; }
#tabbar .wb-tab.active .wb-tab-ico { opacity: 1; }
#tabbar .wb-tab-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#tabbar .wb-tab-x {
  flex: none;
  width: 18px; height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: none; border-radius: 50%;
  background: transparent;
  color: var(--text-3);
  font-size: 14px; line-height: 1;
  cursor: pointer;
}
#tabbar .wb-tab-x:hover { background: rgba(15, 23, 42, .12); color: var(--danger); }
#tabbar .wb-tab-spacer { flex: 1 0 4px; }

/* 右键标签弹出的操作菜单 */
.wb-tabmenu {
  position: fixed;
  z-index: 300;
  min-width: 122px;
  padding: 4px;
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 9px;
  box-shadow: var(--shadow);
}
.wb-tabmenu button {
  border: none;
  background: transparent;
  text-align: left;
  padding: 7px 10px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
}
.wb-tabmenu button:hover { background: var(--primary-soft); color: var(--primary); }

/* ---- 工单列表工具栏「更多」下拉（收纳低频按钮，节省横向空间） ---- */
.wo-more-wrap { position: relative; }
.wo-more-caret { width: 13px; height: 13px; flex: none; opacity: .7; }
.wo-more-wrap > .btn[aria-expanded="true"] {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-soft);
}
.wo-more-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 40;
  min-width: 158px;
  padding: 5px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow);
}
.wo-more-menu[hidden] { display: none; }
.wo-more-menu .btn {
  width: 100%;
  justify-content: flex-start;
  text-align: left;
  border-color: transparent;
}
.wo-more-menu .btn:hover { background: var(--primary-soft); }
.wo-more-menu .btn.active {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-soft);
  font-weight: 500;
}

@media (min-width: 768px) {
  #tabbar { display: flex; }
}

/* 每个标签的内容容器（切换时整体挂载/卸载，DOM 与滚动位置都保留） */
.tab-pane { display: block; }

/* ---------------- 系统升级卡片 + 侧边栏新版本徽标 ---------------- */
.upd-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  margin-bottom: 10px;
}
.upd-kv { display: flex; align-items: baseline; gap: 6px; font-size: 13px; }
.upd-k { color: var(--text-3); }
.upd-kv b { color: var(--text); font-weight: 500; }

.upd-state {
  padding: 9px 12px;
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text-2);
  font-size: 13px;
  line-height: 1.6;
}
.upd-state.checking { background: var(--primary-soft); color: var(--primary-dark); }
.upd-state.ok { background: var(--ok-soft); color: var(--ok); }
.upd-state.new { background: var(--warn-soft); color: var(--warn); }
.upd-state.warn { background: var(--danger-soft); color: var(--danger); }
.upd-state .upd-sub { color: inherit; opacity: .75; font-size: 12px; }

.upd-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.upd-hint { flex: 1 1 260px; color: var(--text-3); font-size: 12px; line-height: 1.6; }
.upd-warn {
  margin-top: 8px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--warn-soft);
  color: var(--warn);
  font-size: 12px;
  line-height: 1.6;
}

/* 侧边栏底部的「有新版本」徽标（#sideFoot 里，块级换行显示） */
.upd-badge {
  display: inline-block;
  margin-top: 6px;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--primary);
  background: var(--primary-soft);
  color: var(--primary);
  font-size: 11px;
  font-weight: 500;
}
.upd-badge:hover { background: var(--primary); color: #fff; }

/* ---------------- 用户管理：模块权限勾选表（2026-09-22） ----------------
   一个模块一行 × 四档（查看/编辑/删除/导出）。分组行做浅底分隔，与左侧导航分组对应。 */
.perm-table th, .perm-table td { padding: 6px 10px; }
.perm-table .perm-group-row td {
  background: #f8fafc;
  font-weight: 600;
  font-size: 12.5px;
  color: var(--text-2);
  padding: 6px 10px;
}
.perm-table input[type=checkbox] { width: 15px; height: 15px; cursor: pointer; }

/* ---------------- 站内消息（通知中心）2026-09-30 ----------------
   顶栏铃铛 + 右上角固定面板。类名一律 n-* 前缀，避免与既有样式撞车。
   ⚠ 面板是「固定定位 + 点击外部关闭 + Esc 关闭」（app.js: initNotif），
     不用 ui.js 的 modal —— 那个是居中遮罩弹窗，与参考的「贴右上角下拉面板」不是一回事。 */

/* 铃铛按钮 */
.n-bell {
  position: relative; width: 36px; height: 36px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: transparent; border-radius: 9px; cursor: pointer; color: #64748b;
}
.n-bell:hover { background: #f1f5f9; color: var(--primary); }
.n-bell.is-open { background: var(--primary-soft); color: var(--primary); }
.n-bell-ico { width: 21px; height: 21px; display: block; }
/* 未读徽标：贴铃铛右上角；数字超过 99 显示 99+ */
.n-bell-badge {
  position: absolute; top: 1px; right: 1px; min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 9px; background: #e5484d; color: #fff; font-size: 11px; font-weight: 600;
  line-height: 17px; text-align: center; box-shadow: 0 0 0 2px #fff; box-sizing: border-box;
}
.n-bell-badge.is-warn { background: #d97706; }

/* ---------------- 可搜索下拉（combobox） ----------------
   给选项很多的字段用（工单的「项目」是第一个用户）。
   ⚠ 浮层是**相对 .ssel 绝对定位**（不是 body 定位），所以要求祖先别裁剪：
     目前用它的两个表单位于页面里的 .card 内（无 overflow）→ 安全。
     若以后把该字段放进 overflow:hidden 的容器，需要改成 body + fixed 定位。
   ⚠ 类名是自定义的，不走 Tailwind —— JS 里拼的 HTML 不要依赖扫描器。 */
.ssel { position: relative; }
.ssel-native { display: none; }                    /* 只作取值/事件载体，见 ui.js 注释 */
.ssel-inp { padding-right: 30px; cursor: text; }   /* 右侧给箭头留位 */
.ssel-caret {
  position: absolute; right: 11px; top: 50%; width: 0; height: 0; margin-top: -2px;
  border-left: 4.5px solid transparent; border-right: 4.5px solid transparent;
  border-top: 5px solid var(--text-3); cursor: pointer; transition: transform .15s;
}
.ssel.is-open .ssel-caret { transform: rotate(180deg); }
.ssel-pop {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 60; margin-top: 4px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 10px 26px rgba(17, 24, 39, .14); overflow: hidden;
}
.ssel-pop.up { top: auto; bottom: 100%; margin-top: 0; margin-bottom: 4px; }
.ssel-list { max-height: 264px; overflow-y: auto; padding: 4px; scrollbar-width: thin; }
.ssel-opt {
  padding: 7px 10px; border-radius: 6px; font-size: 13.5px; color: var(--text); cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ssel-opt.on { background: var(--primary-soft); }
.ssel-opt.cur { color: var(--primary); font-weight: 600; }
.ssel-opt.cur::after { content: '✓'; float: right; margin-left: 8px; }
.ssel-opt-new { color: var(--primary); margin-top: 4px; border-top: 1px solid var(--border); border-radius: 0 0 6px 6px; }
.ssel-hit { color: var(--primary); }
.ssel-none { padding: 12px; text-align: center; font-size: 12.5px; color: var(--text-3); }

/* 面板 */
.n-panel {
  position: fixed; top: 62px; right: 14px; z-index: 120;
  width: 440px; max-width: calc(100vw - 20px);
  max-height: min(72vh, 620px);
  background: #fff; border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 12px 34px rgba(16, 24, 40, .14);
  display: flex; flex-direction: column; overflow: hidden;
}
.n-hd { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--border); }
.n-tabs { display: flex; gap: 2px; }
.n-tab {
  border: 0; background: transparent; cursor: pointer; padding: 6px 10px; border-radius: 8px;
  font-size: 13.5px; color: var(--text-2); display: inline-flex; align-items: center; gap: 5px;
}
.n-tab:hover { background: #f6f8fb; }
.n-tab.is-active { color: var(--primary); font-weight: 600; background: var(--primary-soft); }
.n-tab i { font-style: normal; font-size: 12px; color: var(--text-3); }
.n-tab.is-active i { color: var(--primary); }
.n-hd-act { margin-left: auto; display: flex; align-items: center; gap: 4px; }
.n-link {
  border: 0; background: transparent; cursor: pointer; font-size: 12.5px; color: var(--text-2);
  padding: 5px 8px; border-radius: 7px;
}
.n-link:hover { background: #f6f8fb; color: var(--primary); }
.n-link.primary { color: var(--primary); font-weight: 600; }
.n-link.primary:hover { background: var(--primary-soft); }
/* 「全部删除」= 破坏性操作 → 红色（hover 也保持红，别被 .n-link:hover 的 primary 盖掉） */
.n-link.danger { color: var(--danger); }
.n-link.danger:hover { background: #fdf2f4; color: var(--danger); }
.n-x {
  width: 28px; height: 28px; border: 0; background: transparent; border-radius: 7px;
  cursor: pointer; color: var(--text-3); display: inline-flex; align-items: center; justify-content: center;
}
.n-x:hover { background: #f6f8fb; color: var(--text); }
.n-x svg { width: 16px; height: 16px; }

/* 列表 */
.n-list { flex: 1 1 auto; overflow-y: auto; padding: 4px 0; }
.n-item { padding: 10px 14px; border-bottom: 1px solid #f2f5f9; position: relative; }
.n-item:last-child { border-bottom: 0; }
.n-item:hover { background: #fafbfe; }
.n-item.is-clickable { cursor: pointer; }
/* ⚠ 右侧留出 46px 给绝对定位的「删除」按钮 —— 否则它会压住右端的时间。
   桌面端平时 opacity:0、hover 才显形，但一显形就重叠，所以**常留**这段空隙。 */
.n-item-hd { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-3); padding-right: 46px; }
.n-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--primary); flex: 0 0 auto; }
.n-dot.lv-overdue { background: #d83a4b; }
.n-dot.lv-urgent { background: #d97706; }
.n-dot.lv-warning { background: #2563eb; }
.n-dot.lv-info { background: #94a3b8; }
.n-sender { color: var(--text-2); font-weight: 500; }
.n-time { margin-left: auto; }
.n-item-title { font-size: 13.5px; font-weight: 600; color: var(--text); margin: 4px 0 2px; word-break: break-word; }
.n-item-body { font-size: 13px; color: var(--text-2); white-space: pre-wrap; word-break: break-word; }
.n-item-del {
  position: absolute; top: 8px; right: 10px; border: 0; background: transparent; cursor: pointer;
  color: var(--text-3); font-size: 12.5px; padding: 3px 7px; border-radius: 6px; opacity: 0;
}
.n-item:hover .n-item-del { opacity: 1; }
.n-item-del:hover { background: #fdeaec; color: #d83a4b; }
.n-empty { padding: 42px 16px; text-align: center; color: var(--text-3); font-size: 13px; }
.n-tag {
  display: inline-block; padding: 1px 7px; border-radius: 9px; font-size: 11.5px;
  background: #eef1f6; color: var(--text-2);
}
.n-tag.lv-overdue { background: #fdeaec; color: #d83a4b; }
.n-tag.lv-urgent { background: #fff1e0; color: #c9740a; }
.n-tag.lv-warning { background: #e8f0ff; color: #2563eb; }

/* 底部：总数 + 翻页 */
.n-ft {
  display: flex; align-items: center; gap: 8px; padding: 8px 12px;
  border-top: 1px solid var(--border); background: #fbfcfe; font-size: 12.5px; color: var(--text-3);
}
.n-pager { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.n-pg {
  border: 1px solid var(--border-strong); background: #fff; border-radius: 7px;
  padding: 3px 9px; font-size: 12.5px; cursor: pointer; color: var(--text-2);
}
.n-pg:hover:not(:disabled) { border-color: var(--primary); color: var(--primary); }
.n-pg:disabled { opacity: .5; cursor: not-allowed; }

/* 发消息弹窗里的收件人多选 */
.n-pick { display: flex; flex-wrap: wrap; gap: 6px; max-height: 168px; overflow-y: auto; padding: 2px 0; }
.n-pick label {
  display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; cursor: pointer;
  border: 1px solid var(--border-strong); border-radius: 20px; font-size: 12.5px; color: var(--text-2);
  background: #fff;
}
.n-pick label:hover { border-color: var(--primary); color: var(--primary); }
.n-pick input { accent-color: var(--primary); }
.n-pick label.is-on { border-color: var(--primary); background: var(--primary-soft); color: var(--primary); font-weight: 500; }
.n-pick .n-pick-off { opacity: .5; }

/* 移动端：面板铺满可视宽度、贴到顶栏下沿 */
@media (max-width: 767.98px) {
  .n-panel { top: 60px; right: 8px; left: 8px; width: auto; max-width: none; max-height: 78vh; }
  .n-item-del { opacity: 1; }   /* 手机上没有 hover，删除按钮常显 */
}

/* ---------------- 维修工单「现场图片」列（2026-09-30） ----------------
   客户扫码报修时上传的现场图片/视频，原先在工单里完全看不到。
   列表里只放最多 3 张 36px 缩略图 + 「+N」，点击弹查看器看全部。
   ⚠ 缩略图类名统一 rp-*，与移动端扫码页的样式互不影响。 */
.rp-pics { display: flex; align-items: center; gap: 4px; cursor: pointer; }
.rp-pics:hover .rp-thumb { border-color: var(--primary); }
.rp-thumb {
  width: 36px; height: 36px; border-radius: 6px; object-fit: cover; display: block;
  border: 1px solid var(--border); background: #f2f5f9; flex: 0 0 auto;
}
.rp-thumb-video {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; color: var(--text-2);
}
.rp-thumb-more { font-size: 12px; color: var(--text-3); flex: 0 0 auto; }

/* 维修工单列表「操作」列：冻结在最右侧 + 固定宽度（2026-10-10 用户要求，参照工单管理列表页）。
   ① 冻结 = 复用 `.sticky-right` 机制（分隔线/hover/表头底色都已在上面处理过），这里只挂类名；
   ② 固定宽度 = `width:1%` + `nowrap`：`table.tb` 是 width:100% 的 auto 布局，
      最后一列（自动宽度）会把剩余空间全吸走、被拉得很宽 —— 压成「恰好等于按钮内容宽」，
      窗口再宽这一列也不变形（同设备台账 `dev-op` 的写法，见下方 2575 行附近）。
   ③ 按钮之间的 `|` 分隔符：与工单管理列表一致（纯装饰，别让它被当成可点内容）。 */
table.tb th.rp-op,
table.tb td.rp-op { width: 1%; white-space: nowrap; }
.rp-op-sep { color: var(--border-strong); margin: 0 4px; }

/* 维修工单详情「报修信息」等信息网格（2026-10-10 用户要求：横排）。
   基础 `.kv-grid` 是 `auto-fill minmax(220px,1fr)`，列数随容器宽度浮动（窄一点就掉成 4 列，
   用户要的分行分组就散了）⇒ 这里**固定 5 列**，与「工单管理 → 工单详情 → 工单信息」一致；
   行分组由每行首格的 `.break-row`（`grid-column-start:1`）决定，与列数无关。
   ⚠ 手机端必须收窄，否则 5 列挤成一条看不清。 */
.rp-kv-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }

/* 信息网格里的**可点编号**（项目编号 / 合同编号 / 设备编号）：蓝色 + 下划线 + 手型
   （2026-10-12 用户要求）。
   ⚠ 基础 `.code` 只在 `table.tb` 里定义了样式（app.css:558），放进 `.kv` 里就是**纯文本** ——
     所以这里补一套。作用域限定在 `.rp-kv-grid`（维修工单详情的信息网格），
     不去改设备档案等页面既有的观感。 */
.rp-kv-grid .code {
  color: var(--primary); cursor: pointer; text-decoration: underline;
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12.5px;
}
.rp-kv-grid .code:hover { opacity: .75; }
@media (max-width: 767.98px) {
  /* 手机端：**一行只放一个字段**（2026-10-12 用户要求）——
     窄屏放两列时，长值（如「CON-2026-015 御泥坊前滩中心直饮水买断合同」）会折成两三行、
     和左边的短值挤在一起很难读。三个页签（报修信息/设备信息/处理信息）共用这个类，一处改全生效。 */
  .rp-kv-grid { grid-template-columns: minmax(0, 1fr); }

  /* 「处理信息」编辑态的表单同样一行一个（它用的是通用 `.form-grid`，窄屏默认两列）。
     ⚠ 两种写法都要覆盖：早先编辑态是「外层包一层 .rp-proc-edit」，现在改成了
       直接把类加在 `.form-grid` 自己身上（`class="form-grid rp-proc-edit"`）——
       只写后代选择器会**匹配不到**，手机端就悄悄变回两列了。 */
  .rp-proc-edit .form-grid,
  .rp-proc-edit.form-grid { grid-template-columns: minmax(0, 1fr); }
  .rp-proc-edit .form-grid .half,
  .rp-proc-edit .form-grid .full,
  .rp-proc-edit.form-grid .half,
  .rp-proc-edit.form-grid .full { grid-column: auto; }
}

/* ---------- 维修工单「上门处理记录」卡片（2026-10-12） ----------
   行业惯例：一次上门 = 一条服务履历（不是工单上几个可覆盖的字段）。
   一张卡 = 一次上门：标题行（第 N 次 / 时间 / 工程师 / 结果标签 / 编辑删除）+ 正文（处理内容…）。

   ⚠ 区分手段两重（2026-10-12 用户第一版反馈后定稿）：
     ① 斑马纹：`.is-alt`（偶数次）换一档底色 —— 相邻两张即使结果相同也能分开；
     ② hover：描边 + 阴影 + 上浮 1px，且**描边颜色按本次结果给**（未完工=蓝、已完工=绿）。
   ⛔ 首版还做过「左侧 4px 色条」（未完工蓝 / 已完工绿），**用户明确要求删掉**（2026-10-12）——
     别再把它加回来；`.is-open` / `.is-done` 两个类现在**只服务于 hover 描边配色**。
   ⚠ `:hover` 里写 `border-color` 是**四边一起设**。这里用「状态类 + `:hover`」两条规则
     （3 个选择器，特异性高于基础 `:hover`）来实现按状态变色 ——
     **不要**退回「基础 `:hover` 改 border-color、再补一条 `border-left-color` 覆盖」的老写法，
     那种写法依赖**源顺序**，加一行 CSS 就可能被静默改掉（首版就是这么翻车的）。 */
.rp-visit-bar { display: flex; align-items: center; gap: 10px; margin: 18px 0 12px; }
.rp-visit-bar .sec-title { margin: 0; }
.rp-visit-bar .btn { margin-left: auto; flex: 0 0 auto; }
.rp-visit {
  border: 1px solid var(--border-strong);
  border-radius: 10px; padding: 12px 14px;
  margin-top: 12px;
  background: var(--panel);
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
  transition: border-color .18s ease, box-shadow .18s ease, transform .18s ease;
}
.rp-visit.is-alt { background: #f5f8fd; }
.rp-visit:hover { box-shadow: 0 6px 18px rgba(37, 99, 235, .14); transform: translateY(-1px); }
.rp-visit.is-open:hover { border-color: var(--primary); }
.rp-visit.is-done:hover { border-color: var(--ok); box-shadow: 0 6px 18px rgba(5, 150, 105, .16); }

.rp-visit-hd {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding-bottom: 9px; border-bottom: 1px dashed var(--border);
}
.rp-visit-no { font-weight: 600; font-size: 13.5px; color: var(--text); }
.rp-visit-time { font-size: 12.5px; color: var(--text-2); }
.rp-visit-eng { font-size: 12.5px; color: var(--text-2); }
.rp-visit-ops { margin-left: auto; display: flex; gap: 6px; }
.rp-visit-bd { margin-top: 11px; }
.rp-visit-item { margin-top: 10px; }
.rp-visit-item:first-child { margin-top: 0; }
.rp-visit-item > label { display: block; font-size: 12px; color: var(--text-3); margin-bottom: 4px; }
.rp-visit-item .text-block { margin: 0; }
.rp-visit-empty {
  margin-top: 10px; padding: 18px; border: 1px dashed var(--border-strong); border-radius: 10px;
  font-size: 13px; color: var(--text-3); line-height: 1.7; background: #fcfdff;
}
@media (max-width: 767.98px) {
  /* 手机端：操作按钮换行独占一行，别把「第 N 次上门 · 时间」挤成两行；
     附件缩略图两列铺满（180px 固定宽在 390px 屏上会溢出）；
     ⚠ 触屏没有 hover ⇒ 手机端**唯一的区分手段就是斑马纹**，别把区分全押在 hover 上。 */
  .rp-visit { padding: 10px 12px; }
  .rp-visit-ops { margin-left: 0; width: 100%; }
  .rp-visit-bar { flex-wrap: wrap; }
  .rp-visit .rp-vw-item { width: calc(50% - 4px); }
  .rp-visit .rp-vw-item img,
  .rp-visit .rp-vw-item video { height: 120px; }
}

/* 查看器（弹窗内）：每张 180px 宽，图片按 contain 完整显示 */
.rp-viewer { display: flex; flex-wrap: wrap; gap: 12px; }
.rp-vw-item { width: 180px; }
.rp-vw-item img,
.rp-vw-item video {
  width: 100%; height: 150px; object-fit: contain; display: block;
  background: #f2f5f9; border: 1px solid var(--border); border-radius: 8px;
}
.rp-vw-name { font-size: 11.5px; color: var(--text-3); margin-top: 4px; word-break: break-all; }

/* ---------------- 设备台账「操作」列（2026-10-08 用户要求） ----------------
   ① 按钮紧凑：高度 27→24px、字号 12.5→12px、左右内边距 10→6px、单元格 padding 12→6/10px；
      三个按钮之间**不留空白节点**（JS 侧已改成数组 join('')），间距统一交给下面的 `+` 选择器。
   ② 固定列宽：`table.tb` 是 width:100% 的 auto 布局，最后这个「自动宽度」的列会吸走剩余空间，
      宽屏显示器上被撑得特别大 → 用 `width:1%` + `nowrap` 把列宽压到「恰好等于内容宽」。
      ⚠ 必须写成 `table.tb th.dev-op` / `table.tb td.dev-act`（(0,3,2)）才压得过
        `.btn.sm`（(0,2,0)）与 `table.tb th`（(0,1,2)）的基础规则。
   ③ 移动端隐藏：th/td 都在 JS 里挂了 `hide-mobile`（<768）；只隐 td 会留一个空的「操作」表头，
      两边必须一起加。移动端仍可点「设备编号」进档案，不牺牲可用性。 */
table.tb th.dev-op { width: 1%; white-space: nowrap; }
table.tb td.dev-act { width: 1%; padding-left: 6px; padding-right: 10px; }
table.tb td.dev-act .btn { height: 24px; padding: 0 6px; font-size: 12px; border-radius: 5px; }
table.tb td.dev-act .btn + .btn { margin-left: 4px; }

/* ---------------- 设备档案三张履历表的「操作」列（2026-10-13 用户要求） ----------------
   需求：保养履历 / 维修履历 / 租赁履历 的操作按钮**固定在最右侧** + **按钮间距小一点**。
   ① 冻结 = 复用全站通用的 `.sticky-right`（分隔线 / 表头底色 / hover 底色都已在那套规则里处理过）；
   ② 紧凑 = **刻意不复用上面的 `.dev-act`** —— 那是「设备台账列表」共用的，改它会连带改那一页
      （用户没提那里）。这里独立一套 `arch-op` / `arch-act`，比设备台账再收一档：
      单元格 padding 12→4/8px、按钮左右内边距 6→5px、高度 24→23px、**按钮间距 4→2px**。
      ⚠ 实测过：只把按钮间的 HTML 空白节点换成 margin，间距**还是 4px**（空格本身约 4px 宽）——
        真要更小只能改 margin 自身，别只做「去空白节点」。
   ③ 固定列宽 `width:1%` + `nowrap`（同 dev-op：`table.tb` 是 width:100% 的 auto 布局，
      最后一列会把剩余空间全吸走、被拉得很宽）。
   ⚠ th / td 必须**成对**加类；只加一侧会出现「表头冻了、数据列没冻」的错位。 */
table.tb th.arch-op { width: 1%; white-space: nowrap; }
table.tb td.arch-act { width: 1%; padding-left: 4px; padding-right: 8px; }
table.tb td.arch-act .btn { height: 23px; padding: 0 5px; font-size: 12px; border-radius: 5px; }
table.tb td.arch-act .btn + .btn { margin-left: 2px; }

/* 设备档案页的四张表（保养计划 / 保养履历 / 维修履历 / 租赁履历）：
   **手机端把「操作」列整列隐掉**（2026-10-13 用户要求：手机屏窄，操作列把内容列挤没了）。
   操作改由「点整行查看」提供 —— 保养履历 → 保养详情、维修履历 → 工单详情、
   保养计划 / 租赁履历 → 只读信息弹窗（这两个没有独立详情页）。
   ⚠ 只改手机端，**PC 端一切照旧**（按钮照常显示，不绑行点击，见 deviceDetail.js: bindRowTap）。
   ⚠ th / td **必须成对隐**，只隐一侧会留个空表头（同 `.hide-mobile` 的既有约定）。
   ⚠ `!important` 不可省：`table.tb td { display: table-cell }` 会盖掉普通 `display:none`。 */
@media (max-width: 767.98px) {
  table.tb th.arch-op, table.tb td.arch-act { display: none !important; }
  table.tb tr.tappable { cursor: pointer; }
}

/* ---------------- BOM 表单「编码」字段的字典搜索下拉（2026-10-08） ----------------
   在「新增/编辑 配件BOM·滤芯BOM」弹窗里，编码框输入关键词 → 弹出字典候选，
   选中自动带出 名称 / 简称 / 参考单价。类名统一 dict-*，避免与库存页的 .inv-suggest 撞车。
   ⚠ 容器 `.m-bd` 是 overflow:auto，向下空间不足时给 `.dict-suggest` 加 `.drop-up` 改为向上弹，
     否则下拉会被滚动容器裁掉（只有一半能看见）。 */
.dict-pick-field { position: relative; }
.dict-suggest {
  position: absolute; left: 0; right: 0; top: calc(100% + 2px); z-index: 70;
  background: #fff; border: 1px solid var(--border-strong); border-radius: 10px;
  box-shadow: 0 12px 28px rgba(16, 24, 40, .14); max-height: 268px; overflow-y: auto;
}
.dict-suggest.drop-up { top: auto; bottom: calc(100% + 2px); box-shadow: 0 -10px 26px rgba(16, 24, 40, .14); }
.dict-suggest-hd {
  padding: 6px 10px; font-size: 12px; color: var(--text-3);
  border-bottom: 1px solid #f2f5f9; background: #fbfcfe; position: sticky; top: 0;
}
.dict-suggest-item { display: flex; align-items: center; gap: 8px; padding: 7px 10px; cursor: pointer; font-size: 13px; }
.dict-suggest-item:hover, .dict-suggest-item.hi { background: #f4f8ff; }
.dict-suggest-item .ds-code {
  flex: 0 0 auto; font-family: ui-monospace, Menlo, monospace; color: var(--primary);
}
.dict-suggest-item .ds-name {
  flex: 1 1 auto; min-width: 0; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dict-suggest-item .ds-short {
  flex: 0 0 auto; max-width: 38%; color: var(--text-3); font-size: 12.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dict-suggest-item .ds-price { flex: 0 0 auto; color: var(--text-2); font-size: 12.5px; }
.dict-suggest-empty { padding: 14px 12px; text-align: center; color: var(--text-3); font-size: 12.5px; }

/* ---------------- 「标签显示内容」弹窗里的勾选项（2026-10-09） ----------------
   两列网格排布（8 个字段一屏看完），手机上单列。项本身复用 .checkbox 既有样式。 */
.label-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px; }
@media (max-width: 767.98px) { .label-fields { grid-template-columns: 1fr; } }

/* ---------------- 库存查询「名称」列限宽（2026-10-09 用户要求） ----------------
   名称最长可达两三百字（滤芯名称里带整串规格/批号），会把整张表撑得极宽。
   限制 900px、超出省略号、hover 用 title 看全文。
   ⚠ 必须套一层**块级元素**：`td { max-width }` 在 `table-layout: auto` 下**不生效**
     （规范里 max-width 对 table-cell 无效），作用在 td 里的 div 上才可靠。 */
/* ⚠ 2026-10-09 二次调整：原先写死 `max-width: 900px`，但列宽一旦超过 900（宽屏）文字区就只占 900、
   右侧留出大片空白（用户反馈「名称右边还是大面积空白」）。现在列宽由「表头 width + table-layout:fixed」
   统一控制，名称列吃满剩余空间 ⇒ 这里改成 `max-width: 100%`：文字铺满列宽、超出照样截断，
   既没有空白，也不会再出现「名称把整表撑宽」。 */
.tb-cell-clip { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------- 库存查询表格：固定布局 + 「数量」列右侧冻结（2026-10-09） ----------------
   用户诉求：小屏电脑上**不横向滚动也能看到数量**。
   ⚠ 之前只给名称列限了 900px，但 `table-layout: auto` 下**列宽仍由内容决定** →
     整表宽度 > 视口，数量列被推到右边，照样得滑动（用户反馈「右侧仍是空白 + 要看数量要滑」）。
   现在三件事一起做：
     ① 表格改 `table-layout: fixed`：列宽由**表头 width** 决定，名称列吃满剩余空间并可自由收缩；
     ② 「数量」列在「操作」列左侧**再冻结一份**（sticky right = 操作列宽），怎么滚都在眼前；
     ③ 窄屏（≤1150px）隐藏最占地的次要列（型号 / 类型），把宽度让给名称列。
   ⚠⭐ 数量列的 `right` 偏移**必须等于操作列的实际宽度** —— 所以这里把操作列钉死为 80px，
     以后改操作列宽度**必须同步改这里的 right 值**，否则两列会叠在一起。 */
table.tb.inv-stock-table { table-layout: fixed; }
table.tb.inv-stock-table th.inv-op,
table.tb.inv-stock-table td.inv-op { width: 80px; }
table.tb.inv-stock-table th.inv-qty-col,
table.tb.inv-stock-table td.inv-qty-col {
  position: sticky; right: 80px; z-index: 3;
  box-shadow: -6px 0 8px -8px rgba(16, 24, 40, .18);
}
/* ⚠ 2026-10-09 用户要求「数量列背景色去掉」（附截图，红框圈整列）：
   原先这两格各写 `background: #fff` —— 数据格其实看不出差别（相邻 td 是 transparent、
   透出白底，视觉同为白），真正扎眼的是**表头**那格：其它表头是 `#f8fafc` 浅灰，
   只有「数量」这格是纯白 ⇒ 表头行凭空多出一块白。
   ⇒ 表头改回与其它表头相同的 `#f8fafc`；数据格改 `transparent`（与其它 td 同口径）。
   ⚠ sticky 列理论上要有不透明背景来遮住滚过来的内容，但本表是 `table-layout: fixed` +
     `width:100%` 且含弹性列（名称列），**不会横向溢出**（实测 scrollMax = 0），故 transparent 安全。
   ⚠ hover 不再单独写色：`tr:hover` 的 #fafbfe 会透过 transparent 自然显示，与邻列完全一致。 */
table.tb.inv-stock-table th.inv-qty-col { z-index: 4; background: #f8fafc; }
table.tb.inv-stock-table td.inv-qty-col { background: transparent; }
@media (max-width: 1150px) {
  table.tb.inv-stock-table th.col-secondary,
  table.tb.inv-stock-table td.col-secondary { display: none; }
}

/* ---------------- 库存查询：手机端「字段全保留 + 不换行 + 向右滚动」（2026-11-02 用户要求） ----------------
   历史：同一位置上一版是「隐藏 序号/仓库/简称/参考单价 + 名称折 2 行」（起因：名称列被压成 0px）。
   用户看过效果后改要求 ⇒ 本段整体重写：所有字段都保留、文本不换行、横向滚动，
   操作列的「查看」按钮改为「点整行查看」（按钮列在上一段 @media 里已 display:none）。
   机制：
     ① **取消固定布局**（`table-layout: auto`）：各列按内容宽度排开，表格总宽 > 视口时由
        `.table-wrap` 的 `overflow:auto` 横向滚动。
        ⚠ 别用「fixed + width:auto」：实测 Chrome 会把表格撑到 1284px、名称列 647px（要滚 926px），
          因为 fixed 布局下 auto 宽度按内容固有宽度算，列上的 width 根本不作数。
          上一版「名称被压成 0px」也是 fixed 布局的锅（负剩余宽度会给 auto 列 0）。
     ② 文本一律 nowrap；名称列宽用**内部 div 的 max-width** 限死 240px ——
        auto 布局下 `td { max-width }` 对 table-cell 无效（规范如此），必须作用在 div 上。
     ③ 数量列取消 sticky：操作列已隐藏，原来的 `right:62px/80px` 偏移会让它在滚动时悬空错位。
     ④ tfoot 恢复默认显示：列全保留，colspan 与表头一致，不再错位。 */
@media (max-width: 767.98px) {
  table.tb.inv-stock-table { table-layout: auto; }
  /* ≤1150px 那条「隐藏 类型 / 适用型号」的规则在手机上失效（用户要求字段全保留） */
  table.tb.inv-stock-table th.col-secondary,
  table.tb.inv-stock-table td.col-secondary { display: table-cell; }
  /* 名称：单行 + 固定 240px（≈16 个汉字），超出省略、title 看全文 */
  table.tb.inv-stock-table td.col-name { white-space: nowrap; }
  table.tb.inv-stock-table td.col-name .tb-cell-clip {
    max-width: 240px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* 数量列不再冻结（跟着表格横向滚动） */
  table.tb.inv-stock-table th.inv-qty-col,
  table.tb.inv-stock-table td.inv-qty-col { position: static; right: auto; }
  /* 整行可点查看 → 触屏给一点按压反馈（触屏没有 hover，不反馈就像"点不动"） */
  table.tb.inv-stock-table tbody tr:active { background: var(--primary-soft); }
}

/* ---------------- 库存查询：数量汇总 + 安全库存预警（2026-10-09 用户要求，仅滤芯） ----------------
   用户三点：① 简称列不要压缩、完整显示；② 数量加一个汇总（求和）；③ 给滤芯加「安全库存」预警。
   本段负责 ②③ 的视觉（① 是 JS 侧动态算列宽 shortColWidth，见 inventory.js）。 */

/* 表格上方统计条：共 N 条 · 合计 X 件（原先只有「共 N 条」，且用内联样式右对齐） */
.inv-stock-stats { text-align: right; margin-bottom: 6px; font-size: 12.5px; }
.inv-stock-stats b { color: var(--text); font-size: 13.5px; }

/* 底部合计行（tfoot）。⚠ tfoot 不受 sticky 表头影响；列宽由 thead 决定，自动对齐 */
table.tb tfoot td {
  background: #f6f8fb; border-top: 1px solid var(--border-strong);
  font-size: 12.5px; color: var(--text-2); padding: 8px 12px;
}
table.tb tfoot td.inv-sum-label { text-align: right; }
table.tb tfoot td.inv-sum-qty { font-weight: 700; font-size: 13.5px; color: var(--text); }
table.tb tfoot td.inv-sum-qty.low { color: #d83a4b; }

/* 安全库存警示条（仅滤芯页）。有低库存 = 橙色；全部充足 = 绿色 */
.inv-safety-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 12px; margin-bottom: 10px; border-radius: 10px;
  background: #fff7e8; border: 1px solid #f6d9a8; color: #8a5a00; font-size: 13px;
}
.inv-safety-banner b { color: #b06a00; }
.inv-safety-banner .inv-safety-ico { width: 18px; height: 18px; flex: 0 0 auto; color: #d97706; }
.inv-safety-banner .inv-safety-sub { color: #9a7020; font-size: 12.5px; }
.inv-safety-banner .btn { margin-left: auto; }
.inv-safety-banner.ok { background: #eefaf1; border-color: #bfe6c9; color: #1a7a3c; }
.inv-safety-banner.ok b { color: #15703a; }
.inv-safety-banner.ok .inv-safety-ico { color: #1a9d54; }
.inv-safety-banner.ok .inv-safety-sub { color: #3f8a5c; }

/* 低库存行：数字变红 + 「低」徽标；整行给一层极淡的红底（不喧宾夺主）
   ⚠ 给 `tr.low td` 设背景会盖掉 tr:hover 的底色 → 必须补一条 hover 规则，
     否则鼠标划过低库存行时「没有 hover 反馈」，看起来像卡住了。 */
.inv-low-num { color: #d83a4b; }
.inv-low-tag {
  display: inline-block; margin-left: 5px; padding: 0 5px; border-radius: 8px;
  background: #fdeaec; color: #d83a4b; font-size: 11px; font-weight: 600;
  border: 1px solid #f7c9ce; vertical-align: 1px;
}
table.tb tbody tr.inv-low-row td { background: #fffafa; }
table.tb tbody tr.inv-low-row:hover td { background: #fdf2f3; }
/* ⚠⚠ 数量列是 sticky 冻结列，上面有一段 `table.tb.inv-stock-table td.inv-qty-col
   { background: transparent }`（2026-10-09 上一轮去掉那块独立白底时加的）——
   它的**特异性是 (0,3,2)，高过 `tr.inv-low-row td` 的 (0,2,4)**，会把低库存行的粉底盖掉：
   整行都是淡粉，只有数量那一格是白的（用户截图反馈「预警数量背景色和这一行其他背景色不一样」）。
   ⇒ 用更高的特异性补回来（4 个类），hover 态同理。
   教训：**给冻结列设了背景之后，任何「整行变色」的规则都要把这一列单独补一条**，
        否则它会安静地掉队。 */
table.tb.inv-stock-table tbody tr.inv-low-row td.inv-qty-col { background: #fffafa; }
table.tb.inv-stock-table tbody tr.inv-low-row:hover td.inv-qty-col { background: #fdf2f3; }

/* 安全库存设置弹窗内的内容 */
.safety-dlg .safety-global { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.safety-dlg .safety-global > label { font-size: 13px; font-weight: 600; color: var(--text); }
.safety-dlg .safety-global .hint { flex: 1 1 260px; }
table.tb.safety-table th, table.tb.safety-table td { padding: 7px 10px; font-size: 12.5px; }

/* ⚠ 安全库存弹窗内的表格必须 `table-layout: fixed` —— 名称列（无 width）在 auto 布局下
   会按内容（滤芯名称几百字）撑到极宽，把右边的「在库 / 安全库存」输入框**挤出可视区**，
   用户根本够不着（2026-10-09 截图实测踩到）。固定布局后各列按表头 width 分配，名称列吃剩余空间。 */
table.tb.safety-table { table-layout: fixed; }

/* ==================== 附件在线预览器（public/js/filePreview.js，2026-10-14） ====================
   底层是自托管的 Open File Viewer（public/lib/ofv/），支持 PDF 与图片。
   结构：左侧文件列表 + 右侧 工具条/预览区；单文件时不渲染左侧列表。
   ⚠ 预览区必须能"吃掉"剩余高度并允许内部滚动：stage 用 flex:1 + min-height:0，
     否则 OFV 的 canvas 会把弹窗越撑越高（flex 子项默认 min-height:auto 不会收缩）。 */
.fpv { display: flex; height: calc(100vh - 200px); min-height: 420px; }
.fpv-side { width: 218px; flex: none; min-height: 0; display: flex; flex-direction: column; border-right: 1px solid var(--border); }
.fpv-side-hd { flex: none; padding: 9px 12px; font-size: 12.5px; color: var(--text-3); border-bottom: 1px solid var(--border); }
.fpv-list { flex: 1; min-height: 0; overflow-y: auto; }
.fpv-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; font-size: 13px; cursor: pointer; border-bottom: 1px solid var(--border); }
.fpv-item:hover { background: var(--bg); }
.fpv-item.on { background: var(--primary-soft); }
.fpv-ico { flex: none; width: 24px; height: 24px; border-radius: 5px; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 700; color: #fff; background: #64748b; }
.fpv-ico-pdf { background: #dc2626; }
.fpv-ico-image { background: #0ea5e9; }
.fpv-ico-video { background: var(--purple); }
.fpv-item-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fpv-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.fpv-bar { flex: none; display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--border); font-size: 13px; }
.fpv-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.fpv-spacer { flex: 1; }
.fpv-idx { flex: none; font-size: 12.5px; color: var(--text-3); }
.fpv-stage { flex: 1; min-height: 0; position: relative; overflow: hidden; background: #e8ebf0; }
.fpv-host { width: 100%; height: 100%; }
.fpv-center { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.fpv-loading { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-size: 13px; color: var(--text-3); }
.fpv-unsupported { text-align: center; padding: 20px; }
.fpv-size { flex: none; font-size: 12px; color: var(--text-3); }

/* 慢网加载提示（2026-10-15）：只做「告诉你它在干活」，不挡预览、不挡点击。
   ⚠ 绝对定位贴在 stage 底部，绝不 replaceChildren —— 那会把库正在渲染的 DOM 干掉。 */
.fpv-hint { position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; display: none;
  align-items: center; justify-content: center; gap: 8px; padding: 8px 12px; font-size: 12.5px;
  color: #fff; background: rgba(15, 23, 42, .74); pointer-events: none; }
.fpv-hint.on { display: flex; }
.fpv-hint-s { opacity: .78; }
.fpv-spin { flex: none; width: 13px; height: 13px; border: 2px solid rgba(255, 255, 255, .32);
  border-top-color: #fff; border-radius: 50%; animation: fpv-spin .8s linear infinite; }
@keyframes fpv-spin { to { transform: rotate(360deg); } }

/* 维修工单详情页的附件缩略图：点它 → 页内统一预览器（原来 target="_blank" 开新标签） */
.rp-vw-zoom { display: block; cursor: zoom-in; }

/* 手机端 PDF：浏览器原生查看器（iframe），不依赖 pdf.js / ES Module —— 详见 filePreview.js。
   ⚠ iframe 必须 flex:1 + min-height:0，否则在 flex 列里不会收缩、把底栏顶出可视区。 */
.fpv-native { width: 100%; height: 100%; display: flex; flex-direction: column; background: #525659; position: relative; }
.fpv-native-frame { flex: 1; width: 100%; min-height: 0; border: 0; background: #fff; }
.fpv-native-bar { flex: none; padding: 8px 12px; text-align: center; font-size: 12.5px; color: #fff; background: rgba(0, 0, 0, .38); }
.fpv-native-bar a { color: #fff; text-decoration: underline; }
.fpv-native-sep { margin: 0 4px; opacity: .6; }
.fpv-native-sub { margin-top: 4px; font-size: 11.5px; opacity: .82; }

/* 移动端：文件列表改为顶部横向滚动条，预览区占满剩余高度 */
@media (max-width: 767.98px) {
  .fpv { flex-direction: column; height: calc(100vh - 150px); }
  .fpv-side { width: 100%; max-height: 120px; border-right: none; border-bottom: 1px solid var(--border); }
  .fpv-list { display: flex; overflow-x: auto; overflow-y: hidden; }
  .fpv-item { flex: none; border-bottom: none; border-right: 1px solid var(--border); }
  .fpv-item-name { max-width: 118px; }
}

/* ==================== 移动端：工单详情「产品信息」卡片（2026-11-03 用户要求） ====================
   仅在 <768 生效（renderProductsMobile），PC 端表格完全不动。
   结构：标题条「产品 N · 物流码」+ 右侧箭头 → 点箭头弹底部「选择产品」sheet；
   下方是当前产品的字段列表（编码/名称/物流码/购买/安装/保修起止/安装位置）。 */
/* 2026-11-03 二次调整：删掉产品子卡片的外边框/圆角/白底，左右间距缩小到与
   外层卡片其它区块一致（不再额外缩进）。标题条与字段列表的左右 padding 归零，
   只保留标题与字段之间的细分隔线。 */
.wo-mob-prod { background: transparent; overflow: hidden; }
.wo-mob-prod-pick {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 12px 0; border: 0; background: transparent; cursor: pointer;
  font-size: 15px; font-weight: 600; color: var(--text); text-align: left;
}
.wo-mob-prod-pick:active { background: var(--primary-soft); }
.wo-mob-prod-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wo-mob-prod-arrow { flex: 0 0 auto; color: var(--text-3); display: inline-flex; }
.wo-mob-arrow-ico { width: 16px; height: 16px; transform: rotate(-90deg); } /* 右箭头：把向下箭头转 90° */
.wo-mob-prod-detail { padding: 4px 0 14px; border-top: 1px solid #f0f3f8; }
.wo-mob-kv {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 9px 0; border-bottom: 1px solid #f5f7fa; font-size: 14px;
}
.wo-mob-kv:last-child { border-bottom: 0; }
.wo-mob-kv-l { flex: 0 0 85px; color: var(--text-3); font-size: 13px; line-height: 1.5; }
.wo-mob-kv-v { flex: 1; min-width: 0; color: var(--text); word-break: break-all; line-height: 1.5; }
/* 完工图片/视频那一行（2026-11-05 用户要求加进移动端卡片）：
   .wo-mob-kv 本身已是 align-items:flex-start，label 与缩略图顶端自然对齐；
   手机宽度下缩略图压到 84px，一行放得下 2~3 个。 */
.wo-mob-kv-files .finish-images-grid {
  gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
}
.wo-mob-kv-files .finish-thumb { height: 84px; }
.wo-mob-kv-files .empty-sm { padding: 0; }

/* 移动端隐藏产品信息卡片里的「服务费用合计」（2026-11-03 用户要求，PC 端不变） */
@media (max-width: 767.98px) {
  .wo-fee-total { display: none; }
}

/* 移动端：产品信息卡片的 card-bd 去掉左右 padding（2026-11-03 用户要求，PC 端不变）。
   ⚠ 不能写内联 style：内联样式不分端，会把 PC 端也污染掉（去掉左右 16px）。
     用类 + 媒体查询，只在 <768 生效。
   ⚠ 特异性必须压过 `.card .card-bd`（0,2,0）→ 写成 `.card .card-bd.wo-prod-card-bd`（0,3,0）。 */
@media (max-width: 767.98px) {
  .card .card-bd.wo-prod-card-bd { padding: 14px 0; }
}

/* 底部「选择产品」sheet（参考图 2） */
.wo-mob-sheet-mask {
  position: fixed; inset: 0; z-index: 300; background: rgba(16, 24, 40, .46);
  display: flex; align-items: flex-end; justify-content: center;
  animation: wo-mob-fade .16s ease-out;
}
@keyframes wo-mob-fade { from { opacity: 0 } to { opacity: 1 } }
.wo-mob-sheet {
  width: 100%; max-width: 600px; max-height: 70vh; background: #fff;
  border-radius: 16px 16px 0 0; display: flex; flex-direction: column;
  animation: wo-mob-up .2s ease-out;
}
@keyframes wo-mob-up { from { transform: translateY(30px); opacity: .6 } to { transform: none; opacity: 1 } }
.wo-mob-sheet-hd {
  display: flex; align-items: center; justify-content: center; position: relative;
  padding: 15px; font-size: 16px; font-weight: 600; color: var(--text);
  border-bottom: 1px solid var(--border);
}
.wo-mob-sheet-x {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  width: 30px; height: 30px; border: 0; border-radius: 8px; background: transparent;
  color: var(--text-3); font-size: 22px; line-height: 1; cursor: pointer;
}
.wo-mob-sheet-x:hover { background: #f1f5f9; color: var(--text-1); }
.wo-mob-sheet-list { overflow-y: auto; padding: 8px 0; }
.wo-mob-sheet-item {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 14px 18px; border: 0; background: #fff; cursor: pointer;
  font-size: 15px; color: var(--text); text-align: left;
}
.wo-mob-sheet-item:active { background: #f6f8fb; }
.wo-mob-sheet-item.active { color: var(--primary); font-weight: 600; }
.wo-mob-sheet-check { display: inline-flex; color: var(--primary); }
.wo-mob-sheet-check svg { width: 18px; height: 18px; }

/* ===== 工单信息看板：表格单行显示（2026-11-05 用户要求） =====
   工厂结算 / 配件更换 / 工单收费 / 工单明细 四张表：**文本一律不换行**，
   内容超出就由外层 .overflow-x-auto 横向滚动；相邻两列之间留 10px。
   ⚠ 必须写成「后一个 th/td 的 padding-left」，不是给每格左右各 5px ——
     后者会让首列左边多出一段空白、与卡片标题对不齐。 */
.wob-nw th,
.wob-nw td { white-space: nowrap; }
.wob-nw th + th,
.wob-nw td + td { padding-left: 10px; }

/* ===== 工单详情右下角浮动按钮（2026-11-05） =====
   移动端：分享（上）+ 回到顶部（下）两个圆按钮；PC 端只有分享。
   ⚠ 移动端要抬到底部固定导航（64px）之上，否则会被导航条盖住。 */
.wo-fab {
  position: fixed;
  right: 22px;
  bottom: 26px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.wo-fab-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  color: var(--primary);
  border: 1px solid var(--border);
  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.14);
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.15s;
}
.wo-fab-btn:hover { transform: translateY(-1px); box-shadow: 0 6px 18px rgba(15, 23, 42, 0.2); }
.wo-fab-btn:active { transform: scale(0.96); }
.wo-fab-ico { width: 20px; height: 20px; }
@media (max-width: 1023.98px) {
  .wo-fab { right: 14px; bottom: 80px; }
  .wo-fab-btn { width: 46px; height: 46px; }
}

/* 分享弹窗：二维码居中 + 链接一行 */
.wo-share { text-align: center; }
.wo-share-qrbox {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #fff;
}
.wo-share-qr { width: 180px; height: 180px; display: block; }
.wo-share-qrhint { font-size: 12px; color: var(--text-3); }
.wo-share-tip { margin: 12px 0 10px; font-size: 12.5px; color: var(--text-2); line-height: 1.7; text-align: left; }
/* 手机上再压一点，否则弹窗几乎占满整屏 */
@media (max-width: 767.98px) {
  .wo-share-qrbox { padding: 10px; }
  .wo-share-qr { width: 150px; height: 150px; }
}
.wo-share-link { display: flex; gap: 8px; }
.wo-share-link .input { flex: 1; min-width: 0; font-size: 12.5px; }
/* 分享弹窗：有效期选择 + 到期提示 + 作废态 */
.wo-share-exp { margin-top: 10px; font-size: 12.5px; color: var(--text-3); text-align: center; }
.wo-share-ttl { display: flex; gap: 8px; justify-content: center; margin: 10px 0 12px; flex-wrap: wrap; }
.wo-share-ttl button {
  padding: 5px 13px; border-radius: 999px; border: 1px solid var(--border);
  background: #fff; color: var(--text-2); font-size: 12.5px; cursor: pointer;
}
.wo-share-ttl button.on { border-color: var(--primary); background: var(--primary-soft); color: var(--primary); font-weight: 600; }
.wo-share-qrbox.is-off { opacity: .3; }
/* 分享弹窗：自定义时长输入行（2026-11-05）
   ⚠ [hidden] 必须显式 display:none —— 本行是 display:flex，会盖掉 UA 的 [hidden] 默认样式 */
.wo-share-custom { display: flex; align-items: center; gap: 8px; margin: -2px 0 12px; }
.wo-share-custom[hidden] { display: none; }
.wo-share-custom .input { flex: 1; min-width: 0; }
.wo-share-custom-unit { flex: 0 0 auto; font-size: 12.5px; color: var(--text-3); }
.wo-share-custom .btn { flex: 0 0 auto; }
/* 分享管理弹窗（2026-11-05）：正在分享的工单列表 */
.wo-sharelist { font-size: 13.5px; }
.wo-sharelist-tip { margin-bottom: 10px; font-size: 12.5px; color: var(--text-2); line-height: 1.7; }
.wo-sharelist-tip b { color: var(--primary); }
.wo-sharelist-tb th.ctr,
.wo-sharelist-tb td.ctr { text-align: center; }
.wo-sharelist-link { cursor: pointer; }
.wo-sharelist-link:hover { text-decoration: underline; }
/* ===== 复制新增提示条（2026-11-05） =====
   用户要求「加警示背景色，让人知道这是复制新增的工单」：
   淡琥珀底 + 左侧粗橙边 + 警示图标。⚠ 不要写成内联 style（颜色散在 JS 里改不动）。 */
.copy-notice {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin-bottom: 14px;
  padding: 12px 14px;
  background: #fff7e8;
  border: 1px solid #f7d9a8;
  border-left: 4px solid #f59e0b;
  border-radius: 8px;
  color: #8a5300;
  font-size: 13.5px;
  line-height: 1.75;
}
.copy-notice-ico { flex: 0 0 auto; color: #d97706; display: inline-flex; margin-top: 2px; }
.copy-notice-ico svg { width: 18px; height: 18px; }
.copy-notice-txt { flex: 1; min-width: 0; }
.copy-notice-txt b { color: #b45309; }
.copy-notice-txt strong { color: #b45309; font-weight: 600; }
